Merge remote-tracking branch 'comma/master' into mqb-a3

# Conflicts:
#	README.md
#	selfdrive/car/volkswagen/interface.py
#	selfdrive/car/volkswagen/values.py
pull/20489/head
Jason Young 4 years ago
commit b394f89bf5
  1. 7
      Dockerfile.openpilot_base
  2. 111
      README.md
  3. 2
      common/logging_extra.py
  4. 4
      common/params_pxd.pxd
  5. 5
      common/params_pyx.pyx
  6. 3
      release/build_release2.sh
  7. 1
      release/files_common
  8. 2
      selfdrive/athena/athenad.py
  9. 7
      selfdrive/athena/tests/test_athenad.py
  10. 44
      selfdrive/boardd/boardd.cc
  11. 2
      selfdrive/camerad/cameras/camera_common.cc
  12. 17
      selfdrive/car/hyundai/values.py
  13. 4
      selfdrive/car/toyota/values.py
  14. 8
      selfdrive/car/volkswagen/interface.py
  15. 65
      selfdrive/car/volkswagen/values.py
  16. 94
      selfdrive/common/params.cc
  17. 68
      selfdrive/common/params.h
  18. 3
      selfdrive/common/swaglog.cc
  19. 64
      selfdrive/common/test_params.c
  20. 16
      selfdrive/common/util.h
  21. 2
      selfdrive/debug/dump.py
  22. 22
      selfdrive/hardware/eon/hardware.h
  23. 4
      selfdrive/hardware/tici/hardware.h
  24. 2
      selfdrive/loggerd/logger.cc
  25. 2
      selfdrive/loggerd/loggerd.cc
  26. 27
      selfdrive/loggerd/uploader.py
  27. 13
      selfdrive/modeld/modeld.cc
  28. 2
      selfdrive/modeld/models/dmonitoring.cc
  29. 2
      selfdrive/modeld/models/driving.cc
  30. 10
      selfdrive/monitoring/driver_monitor.py
  31. 2
      selfdrive/test/process_replay/ref_commit
  32. 4
      selfdrive/test/test_car_models.py
  33. 3
      selfdrive/test/test_onroad.py
  34. 4
      selfdrive/thermald/power_monitoring.py
  35. 60
      selfdrive/thermald/thermald.py
  36. 2
      selfdrive/ui/SConscript
  37. 41
      selfdrive/ui/paint.cc
  38. 4
      selfdrive/ui/qt/api.cc
  39. 28
      selfdrive/ui/qt/home.cc
  40. 7
      selfdrive/ui/qt/home.hpp
  41. 149
      selfdrive/ui/qt/offroad/networking.cc
  42. 11
      selfdrive/ui/qt/offroad/networking.hpp
  43. 25
      selfdrive/ui/qt/offroad/onboarding.cc
  44. 1
      selfdrive/ui/qt/offroad/onboarding.hpp
  45. 155
      selfdrive/ui/qt/offroad/settings.cc
  46. 2
      selfdrive/ui/qt/offroad/settings.hpp
  47. 16
      selfdrive/ui/qt/qt_sound.hpp
  48. 13
      selfdrive/ui/qt/sound.cc
  49. 13
      selfdrive/ui/qt/sound.hpp
  50. 5
      selfdrive/ui/qt/widgets/controls.cc
  51. 27
      selfdrive/ui/qt/widgets/controls.hpp
  52. 4
      selfdrive/ui/qt/widgets/drive_stats.cc
  53. 110
      selfdrive/ui/qt/widgets/offroad_alerts.cc
  54. 20
      selfdrive/ui/qt/widgets/offroad_alerts.hpp
  55. 17
      selfdrive/ui/qt/widgets/ssh_keys.cc
  56. 1
      selfdrive/ui/qt/widgets/ssh_keys.hpp
  57. 14
      selfdrive/ui/qt/window.cc
  58. 25
      selfdrive/ui/ui.cc
  59. 27
      selfdrive/ui/ui.hpp
  60. 21
      tools/lib/route.py
  61. 1
      tools/ubuntu_setup.sh

@ -8,7 +8,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
bzip2 \ bzip2 \
ca-certificates \ ca-certificates \
capnproto \ capnproto \
libcapnp-dev \
clang \ clang \
cmake \ cmake \
cppcheck \ cppcheck \
@ -19,17 +18,18 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
iputils-ping \ iputils-ping \
libarchive-dev \ libarchive-dev \
libbz2-dev \ libbz2-dev \
libcapnp-dev \
libcurl4-openssl-dev \ libcurl4-openssl-dev \
libeigen3-dev \ libeigen3-dev \
libffi-dev \ libffi-dev \
libglew-dev \
libgles2-mesa-dev \ libgles2-mesa-dev \
libglew-dev \
libglib2.0-0 \ libglib2.0-0 \
liblzma-dev \ liblzma-dev \
libomp-dev \ libomp-dev \
libopencv-dev \ libopencv-dev \
libssl-dev \
libsqlite3-dev \ libsqlite3-dev \
libssl-dev \
libsystemd-dev \ libsystemd-dev \
libusb-1.0-0-dev \ libusb-1.0-0-dev \
libzmq3-dev \ libzmq3-dev \
@ -38,6 +38,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
ocl-icd-opencl-dev \ ocl-icd-opencl-dev \
opencl-headers \ opencl-headers \
python-dev \ python-dev \
qml-module-qtquick2 \
qt5-default \ qt5-default \
qtmultimedia5-dev \ qtmultimedia5-dev \
qtwebengine5-dev \ qtwebengine5-dev \

@ -97,7 +97,7 @@ Supported Cars
| Lexus | RX 2020-21 | All | openpilot | 0mph | 0mph | | Lexus | RX 2020-21 | All | openpilot | 0mph | 0mph |
| Lexus | RX Hybrid 2016-19 | All | Stock<sup>3</sup>| 0mph | 0mph | | Lexus | RX Hybrid 2016-19 | All | Stock<sup>3</sup>| 0mph | 0mph |
| Lexus | RX Hybrid 2020 | All | openpilot | 0mph | 0mph | | Lexus | RX Hybrid 2020 | All | openpilot | 0mph | 0mph |
| Toyota | Avalon 2016-18, 2020-21 | TSS-P | Stock<sup>3</sup>| 20mph<sup>1</sup> | 0mph | | Toyota | Avalon 2016-21 | TSS-P | Stock<sup>3</sup>| 20mph<sup>1</sup> | 0mph |
| Toyota | Camry 2018-20 | All | Stock | 0mph<sup>4</sup> | 0mph | | Toyota | Camry 2018-20 | All | Stock | 0mph<sup>4</sup> | 0mph |
| Toyota | Camry 2021 | All | openpilot | 0mph | 0mph | | Toyota | Camry 2021 | All | openpilot | 0mph | 0mph |
| Toyota | Camry Hybrid 2018-20 | All | Stock | 0mph<sup>4</sup> | 0mph | | Toyota | Camry Hybrid 2018-20 | All | Stock | 0mph<sup>4</sup> | 0mph |
@ -130,58 +130,63 @@ Supported Cars
Community Maintained Cars and Features Community Maintained Cars and Features
------ ------
| Make | Model (US Market Reference) | Supported Package | ACC | No ACC accel below | No ALC below | | Make | Model (US Market Reference) | Supported Package | ACC | No ACC accel below | No ALC below |
| ----------| ------------------------------| --------------------| -----------------| -------------------| -------------| | ----------| ------------------------------| ------------------| -----------------| -------------------| -------------|
| Audi | A3 2014, 2017 | Prestige / Adv.Tech | Stock | 0mph | 0mph | | Audi | A3 2014, 2017 | Prestige | Stock | 0mph | 0mph |
| Audi | A3 Sportback e-tron 2017-18 | Prestige / Adv.Tech | Stock | 0mph | 0mph | | Audi | A3 Sportback e-tron 2017-18 | Prestige | Stock | 0mph | 0mph |
| Buick | Regal 2018<sup>1</sup> | Adaptive Cruise | openpilot | 0mph | 7mph | | Buick | Regal 2018<sup>1</sup> | Adaptive Cruise | openpilot | 0mph | 7mph |
| Cadillac | ATS 2018<sup>1</sup> | Adaptive Cruise | openpilot | 0mph | 7mph | | Cadillac | ATS 2018<sup>1</sup> | Adaptive Cruise | openpilot | 0mph | 7mph |
| Chevrolet | Malibu 2017<sup>1</sup> | Adaptive Cruise | openpilot | 0mph | 7mph | | Chevrolet | Malibu 2017<sup>1</sup> | Adaptive Cruise | openpilot | 0mph | 7mph |
| Chevrolet | Volt 2017-18<sup>1</sup> | Adaptive Cruise | openpilot | 0mph | 7mph | | Chevrolet | Volt 2017-18<sup>1</sup> | Adaptive Cruise | openpilot | 0mph | 7mph |
| Chrysler | Pacifica 2017-18 | Adaptive Cruise | Stock | 0mph | 9mph | | Chrysler | Pacifica 2017-18 | Adaptive Cruise | Stock | 0mph | 9mph |
| Chrysler | Pacifica 2020 | Adaptive Cruise | Stock | 0mph | 39mph | | Chrysler | Pacifica 2020 | Adaptive Cruise | Stock | 0mph | 39mph |
| Chrysler | Pacifica Hybrid 2017-18 | Adaptive Cruise | Stock | 0mph | 9mph | | Chrysler | Pacifica Hybrid 2017-18 | Adaptive Cruise | Stock | 0mph | 9mph |
| Chrysler | Pacifica Hybrid 2019-21 | Adaptive Cruise | Stock | 0mph | 39mph | | Chrysler | Pacifica Hybrid 2019-21 | Adaptive Cruise | Stock | 0mph | 39mph |
| Genesis | G70 2018 | All | Stock | 0mph | 0mph | | Genesis | G70 2018 | All | Stock | 0mph | 0mph |
| Genesis | G80 2018 | All | Stock | 0mph | 0mph | | Genesis | G80 2018 | All | Stock | 0mph | 0mph |
| Genesis | G90 2018 | All | Stock | 0mph | 0mph | | Genesis | G90 2018 | All | Stock | 0mph | 0mph |
| GMC | Acadia 2018<sup>1</sup> | Adaptive Cruise | openpilot | 0mph | 7mph | | GMC | Acadia 2018<sup>1</sup> | Adaptive Cruise | openpilot | 0mph | 7mph |
| Holden | Astra 2017<sup>1</sup> | Adaptive Cruise | openpilot | 0mph | 7mph | | Holden | Astra 2017<sup>1</sup> | Adaptive Cruise | openpilot | 0mph | 7mph |
| Hyundai | Elantra 2017-19 | SCC + LKAS | Stock | 19mph | 34mph | | Hyundai | Elantra 2017-19 | SCC + LKAS | Stock | 19mph | 34mph |
| Hyundai | Genesis 2015-16 | SCC + LKAS | Stock | 19mph | 37mph | | Hyundai | Genesis 2015-16 | SCC + LKAS | Stock | 19mph | 37mph |
| Hyundai | Ioniq Electric 2019 | SCC + LKAS | Stock | 0mph | 32mph | | Hyundai | Ioniq Electric 2019 | SCC + LKAS | Stock | 0mph | 32mph |
| Hyundai | Ioniq Electric 2020 | SCC + LKAS | Stock | 0mph | 0mph | | Hyundai | Ioniq Electric 2020 | SCC + LKAS | Stock | 0mph | 0mph |
| Hyundai | Kona 2020 | SCC + LKAS | Stock | 0mph | 0mph | | Hyundai | Kona 2020 | SCC + LKAS | Stock | 0mph | 0mph |
| Hyundai | Kona EV 2019 | SCC + LKAS | Stock | 0mph | 0mph | | Hyundai | Kona EV 2019 | SCC + LKAS | Stock | 0mph | 0mph |
| Hyundai | Santa Fe 2019-20 | All | Stock | 0mph | 0mph | | Hyundai | Santa Fe 2019-20 | All | Stock | 0mph | 0mph |
| Hyundai | Sonata 2018-2019 | SCC + LKAS | Stock | 0mph | 0mph | | Hyundai | Sonata 2018-2019 | SCC + LKAS | Stock | 0mph | 0mph |
| Hyundai | Veloster 2019 | SCC + LKAS | Stock | 5mph | 0mph | | Hyundai | Veloster 2019 | SCC + LKAS | Stock | 5mph | 0mph |
| Jeep | Grand Cherokee 2016-18 | Adaptive Cruise | Stock | 0mph | 9mph | | Jeep | Grand Cherokee 2016-18 | Adaptive Cruise | Stock | 0mph | 9mph |
| Jeep | Grand Cherokee 2019-20 | Adaptive Cruise | Stock | 0mph | 39mph | | Jeep | Grand Cherokee 2019-20 | Adaptive Cruise | Stock | 0mph | 39mph |
| Kia | Forte 2018-19, 2021 | SCC + LKAS | Stock | 0mph | 0mph | | Kia | Forte 2018-2021 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Niro EV 2020 | SCC + LKAS | Stock | 0mph | 0mph | | Kia | Niro EV 2020 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Optima 2017 | SCC + LKAS | Stock | 0mph | 32mph | | Kia | Optima 2017 | SCC + LKAS | Stock | 0mph | 32mph |
| Kia | Optima 2019 | SCC + LKAS | Stock | 0mph | 0mph | | Kia | Optima 2019 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Seltos 2021 | SCC + LKAS | Stock | 0mph | 0mph | | Kia | Seltos 2021 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Sorento 2018 | SCC + LKAS | Stock | 0mph | 0mph | | Kia | Sorento 2018 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Stinger 2018 | SCC + LKAS | Stock | 0mph | 0mph | | Kia | Stinger 2018 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Ceed 2019 | SCC + LKAS | Stock | 0mph | 0mph | | Kia | Ceed 2019 | SCC + LKAS | Stock | 0mph | 0mph |
| Nissan | Altima 2020 | ProPILOT | Stock | 0mph | 0mph | | Nissan | Altima 2020 | ProPILOT | Stock | 0mph | 0mph |
| Nissan | Leaf 2018-20 | ProPILOT | Stock | 0mph | 0mph | | Nissan | Leaf 2018-20 | ProPILOT | Stock | 0mph | 0mph |
| Nissan | Rogue 2018-19 | ProPILOT | Stock | 0mph | 0mph | | Nissan | Rogue 2018-19 | ProPILOT | Stock | 0mph | 0mph |
| Nissan | X-Trail 2017 | ProPILOT | Stock | 0mph | 0mph | | Nissan | X-Trail 2017 | ProPILOT | Stock | 0mph | 0mph |
| SEAT | Ateca 2018 | Driver Assistance | Stock | 0mph | 0mph | | SEAT | Ateca 2018 | Driver Assistance | Stock | 0mph | 0mph |
| Škoda | Kodiaq 2018 | Driver Assistance | Stock | 0mph | 0mph | | Škoda | Kodiaq 2018 | Driver Assistance | Stock | 0mph | 0mph |
| Škoda | Scala 2020 | Driver Assistance | Stock | 0mph | 0mph | | Škoda | Scala 2020 | Driver Assistance | Stock | 0mph | 0mph |
| Škoda | Superb 2018 | Driver Assistance | Stock | 0mph | 0mph | | Škoda | Superb 2018 | Driver Assistance | Stock | 0mph | 0mph |
| Subaru | Ascent 2019 | EyeSight | Stock | 0mph | 0mph | | Subaru | Ascent 2019 | EyeSight | Stock | 0mph | 0mph |
| Subaru | Crosstrek 2018-19 | EyeSight | Stock | 0mph | 0mph | | Subaru | Crosstrek 2018-19 | EyeSight | Stock | 0mph | 0mph |
| Subaru | Forester 2019-20 | EyeSight | Stock | 0mph | 0mph | | Subaru | Forester 2019-20 | EyeSight | Stock | 0mph | 0mph |
| Subaru | Impreza 2017-19 | EyeSight | Stock | 0mph | 0mph | | Subaru | Impreza 2017-19 | EyeSight | Stock | 0mph | 0mph |
| Volkswagen| Jetta 2018-21 | Driver Assistance | Stock | 0mph | 0mph | | Volkswagen| e-Golf 2014, 2020 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Passat 2016-17<sup>2</sup> | Driver Assistance | Stock | 0mph | 0mph | | Volkswagen| Golf 2015-19 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Golf 2015-19 | Driver Assistance | Stock | 0mph | 0mph | | Volkswagen| Golf GTE 2016 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Tiguan 2020 | Driver Assistance | Stock | 0mph | 0mph | | Volkswagen| Golf GTI 2018-19 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Golf R 2016-19 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Golf SportsVan 2016 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Jetta 2018-21 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Passat 2016-17<sup>2</sup> | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Tiguan 2020 | Driver Assistance | Stock | 0mph | 0mph |
<sup>1</sup>Requires an [OBD-II car harness](https://comma.ai/shop/products/comma-car-harness) and [community built ASCM harness](https://github.com/commaai/openpilot/wiki/GM#hardware). ***NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).*** <br /> <sup>1</sup>Requires an [OBD-II car harness](https://comma.ai/shop/products/comma-car-harness) and [community built ASCM harness](https://github.com/commaai/openpilot/wiki/GM#hardware). ***NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).*** <br />
<sup>2</sup>Only includes the MQB Passat sold outside of North America. The NMS Passat made in Chattanooga TN is not yet supported. <sup>2</sup>Only includes the MQB Passat sold outside of North America. The NMS Passat made in Chattanooga TN is not yet supported.

@ -158,6 +158,8 @@ class SwagLogger(logging.Logger):
evt.update(kwargs) evt.update(kwargs)
if 'error' in kwargs: if 'error' in kwargs:
self.error(evt) self.error(evt)
if 'debug' in kwargs:
self.debug(evt)
else: else:
self.info(evt) self.info(evt)

@ -12,5 +12,5 @@ cdef extern from "selfdrive/common/params.h":
Params(bool) Params(bool)
Params(string) Params(string)
string get(string, bool) nogil string get(string, bool) nogil
int delete_db_value(string) int remove(string)
int write_db_value(string, string) int put(string, string)

@ -36,6 +36,7 @@ keys = {
b"GitCommit": [TxType.PERSISTENT], b"GitCommit": [TxType.PERSISTENT],
b"GitRemote": [TxType.PERSISTENT], b"GitRemote": [TxType.PERSISTENT],
b"GithubSshKeys": [TxType.PERSISTENT], b"GithubSshKeys": [TxType.PERSISTENT],
b"GithubUsername": [TxType.PERSISTENT],
b"HardwareSerial": [TxType.PERSISTENT], b"HardwareSerial": [TxType.PERSISTENT],
b"HasAcceptedTerms": [TxType.PERSISTENT], b"HasAcceptedTerms": [TxType.PERSISTENT],
b"HasCompletedSetup": [TxType.PERSISTENT], b"HasCompletedSetup": [TxType.PERSISTENT],
@ -156,11 +157,11 @@ cdef class Params:
if key not in keys: if key not in keys:
raise UnknownKeyName(key) raise UnknownKeyName(key)
self.p.write_db_value(key, dat) self.p.put(key, dat)
def delete(self, key): def delete(self, key):
key = ensure_bytes(key) key = ensure_bytes(key)
self.p.delete_db_value(key) self.p.remove(key)
def put_nonblocking(key, val, d=None): def put_nonblocking(key, val, d=None):

@ -42,7 +42,7 @@ git commit -m "openpilot v$VERSION"
# Build signed panda firmware # Build signed panda firmware
pushd panda/ pushd panda/
CERT=/tmp/pandaextra/certs/release RELEASE=1 scons CERT=/tmp/pandaextra/certs/release RELEASE=1 scons -u .
mv board/obj/panda.bin.signed /tmp/panda.bin.signed mv board/obj/panda.bin.signed /tmp/panda.bin.signed
popd popd
@ -63,6 +63,7 @@ find . -name '*.pyc' -delete
find . -name '__pycache__' -delete find . -name '__pycache__' -delete
rm -rf panda/board panda/certs panda/crypto rm -rf panda/board panda/certs panda/crypto
rm -rf .sconsign.dblite Jenkinsfile release/ rm -rf .sconsign.dblite Jenkinsfile release/
rm models/supercombo.dlc
# Move back signed panda fw # Move back signed panda fw
mkdir -p panda/board/obj mkdir -p panda/board/obj

@ -348,6 +348,7 @@ selfdrive/ui/qt/*.cc
selfdrive/ui/qt/*.hpp selfdrive/ui/qt/*.hpp
selfdrive/ui/qt/offroad/*.cc selfdrive/ui/qt/offroad/*.cc
selfdrive/ui/qt/offroad/*.hpp selfdrive/ui/qt/offroad/*.hpp
selfdrive/ui/qt/offroad/*.qml
selfdrive/ui/qt/widgets/*.cc selfdrive/ui/qt/widgets/*.cc
selfdrive/ui/qt/widgets/*.hpp selfdrive/ui/qt/widgets/*.hpp
selfdrive/ui/qt/spinner_aarch64 selfdrive/ui/qt/spinner_aarch64

@ -82,6 +82,8 @@ def jsonrpc_handler(end_event):
send_queue.put_nowait(response.json) send_queue.put_nowait(response.json)
elif "result" in data and "id" in data: elif "result" in data and "id" in data:
log_recv_queue.put_nowait(data) log_recv_queue.put_nowait(data)
else:
raise Exception("not a valid request or response")
except queue.Empty: except queue.Empty:
pass pass
except Exception as e: except Exception as e:

@ -180,10 +180,15 @@ class TestAthenadMethods(unittest.TestCase):
thread.daemon = True thread.daemon = True
thread.start() thread.start()
try: try:
# with params
athenad.recv_queue.put_nowait(json.dumps({"method": "echo", "params": ["hello"], "jsonrpc": "2.0", "id": 0})) athenad.recv_queue.put_nowait(json.dumps({"method": "echo", "params": ["hello"], "jsonrpc": "2.0", "id": 0}))
resp = athenad.send_queue.get(timeout=3) resp = athenad.send_queue.get(timeout=3)
self.assertDictEqual(json.loads(resp), {'result': 'hello', 'id': 0, 'jsonrpc': '2.0'}) self.assertDictEqual(json.loads(resp), {'result': 'hello', 'id': 0, 'jsonrpc': '2.0'})
# without params
athenad.recv_queue.put_nowait(json.dumps({"method": "getNetworkType", "jsonrpc": "2.0", "id": 0}))
resp = athenad.send_queue.get(timeout=3)
self.assertDictEqual(json.loads(resp), {'result': 1, 'id': 0, 'jsonrpc': '2.0'})
# log forwarding
athenad.recv_queue.put_nowait(json.dumps({'result': {'success': 1}, 'id': 0, 'jsonrpc': '2.0'})) athenad.recv_queue.put_nowait(json.dumps({'result': {'success': 1}, 'id': 0, 'jsonrpc': '2.0'}))
resp = athenad.log_recv_queue.get(timeout=3) resp = athenad.log_recv_queue.get(timeout=3)
self.assertDictEqual(json.loads(resp), {'result': {'success': 1}, 'id': 0, 'jsonrpc': '2.0'}) self.assertDictEqual(json.loads(resp), {'result': {'success': 1}, 'id': 0, 'jsonrpc': '2.0'})

@ -71,12 +71,11 @@ void safety_setter_thread() {
return; return;
}; };
std::vector<char> value_vin = Params().read_db_bytes("CarVin"); std::string value_vin = Params().get("CarVin");
if (value_vin.size() > 0) { if (value_vin.size() > 0) {
// sanity check VIN format // sanity check VIN format
assert(value_vin.size() == 17); assert(value_vin.size() == 17);
std::string str_vin(value_vin.begin(), value_vin.end()); LOGW("got CarVin %s", value_vin.c_str());
LOGW("got CarVin %s", str_vin.c_str());
break; break;
} }
util::sleep_for(100); util::sleep_for(100);
@ -85,7 +84,7 @@ void safety_setter_thread() {
// VIN query done, stop listening to OBDII // VIN query done, stop listening to OBDII
panda->set_safety_model(cereal::CarParams::SafetyModel::NO_OUTPUT); panda->set_safety_model(cereal::CarParams::SafetyModel::NO_OUTPUT);
std::vector<char> params; std::string params;
LOGW("waiting for params to set safety model"); LOGW("waiting for params to set safety model");
while (true) { while (true) {
if (do_exit || !panda->connected){ if (do_exit || !panda->connected){
@ -93,7 +92,7 @@ void safety_setter_thread() {
return; return;
}; };
params = Params().read_db_bytes("CarParams"); params = Params().get("CarParams");
if (params.size() > 0) break; if (params.size() > 0) break;
util::sleep_for(100); util::sleep_for(100);
} }
@ -133,7 +132,7 @@ bool usb_connect() {
} }
if (auto fw_sig = tmp_panda->get_firmware_version(); fw_sig) { if (auto fw_sig = tmp_panda->get_firmware_version(); fw_sig) {
params.write_db_value("PandaFirmware", (const char *)fw_sig->data(), fw_sig->size()); params.put("PandaFirmware", (const char *)fw_sig->data(), fw_sig->size());
// Convert to hex for offroad // Convert to hex for offroad
char fw_sig_hex_buf[16] = {0}; char fw_sig_hex_buf[16] = {0};
@ -143,13 +142,13 @@ bool usb_connect() {
fw_sig_hex_buf[2*i+1] = NIBBLE_TO_HEX((uint8_t)fw_sig_buf[i] & 0xF); fw_sig_hex_buf[2*i+1] = NIBBLE_TO_HEX((uint8_t)fw_sig_buf[i] & 0xF);
} }
params.write_db_value("PandaFirmwareHex", fw_sig_hex_buf, 16); params.put("PandaFirmwareHex", fw_sig_hex_buf, 16);
LOGW("fw signature: %.*s", 16, fw_sig_hex_buf); LOGW("fw signature: %.*s", 16, fw_sig_hex_buf);
} else { return false; } } else { return false; }
// get panda serial // get panda serial
if (auto serial = tmp_panda->get_serial(); serial) { if (auto serial = tmp_panda->get_serial(); serial) {
params.write_db_value("PandaDongleId", serial->c_str(), serial->length()); params.put("PandaDongleId", serial->c_str(), serial->length());
LOGW("panda serial: %s", serial->c_str()); LOGW("panda serial: %s", serial->c_str());
} else { return false; } } else { return false; }
@ -161,13 +160,18 @@ bool usb_connect() {
#endif #endif
if (tmp_panda->has_rtc){ if (tmp_panda->has_rtc){
setenv("TZ","UTC",1);
struct tm sys_time = get_time(); struct tm sys_time = get_time();
struct tm rtc_time = tmp_panda->get_rtc(); struct tm rtc_time = tmp_panda->get_rtc();
if (!time_valid(sys_time) && time_valid(rtc_time)) { if (!time_valid(sys_time) && time_valid(rtc_time)) {
LOGE("System time wrong, setting from RTC"); LOGE("System time wrong, setting from RTC. "
"System: %d-%02d-%02d %02d:%02d:%02d RTC: %d-%02d-%02d %02d:%02d:%02d",
sys_time.tm_year + 1900, sys_time.tm_mon + 1, sys_time.tm_mday,
sys_time.tm_hour, sys_time.tm_min, sys_time.tm_sec,
rtc_time.tm_year + 1900, rtc_time.tm_mon + 1, rtc_time.tm_mday,
rtc_time.tm_hour, rtc_time.tm_min, rtc_time.tm_sec);
setenv("TZ","UTC",1);
const struct timeval tv = {mktime(&rtc_time), 0}; const struct timeval tv = {mktime(&rtc_time), 0};
settimeofday(&tv, 0); settimeofday(&tv, 0);
} }
@ -313,9 +317,9 @@ void panda_state_thread(bool spoofing_started) {
// clear VIN, CarParams, and set new safety on car start // clear VIN, CarParams, and set new safety on car start
if (ignition && !ignition_last) { if (ignition && !ignition_last) {
int result = params.delete_db_value("CarVin"); int result = params.remove("CarVin");
assert((result == 0) || (result == ERR_NO_VALUE)); assert((result == 0) || (result == ERR_NO_VALUE));
result = params.delete_db_value("CarParams"); result = params.remove("CarParams");
assert((result == 0) || (result == ERR_NO_VALUE)); assert((result == 0) || (result == ERR_NO_VALUE));
if (!safety_setter_thread_running) { if (!safety_setter_thread_running) {
@ -329,9 +333,23 @@ void panda_state_thread(bool spoofing_started) {
// Write to rtc once per minute when no ignition present // Write to rtc once per minute when no ignition present
if ((panda->has_rtc) && !ignition && (no_ignition_cnt % 120 == 1)){ if ((panda->has_rtc) && !ignition && (no_ignition_cnt % 120 == 1)){
// Write time to RTC if it looks reasonable // Write time to RTC if it looks reasonable
setenv("TZ","UTC",1);
struct tm sys_time = get_time(); struct tm sys_time = get_time();
if (time_valid(sys_time)){ if (time_valid(sys_time)){
panda->set_rtc(sys_time); struct tm rtc_time = panda->get_rtc();
double seconds = difftime(mktime(&rtc_time), mktime(&sys_time));
if (std::abs(seconds) > 1.1) {
panda->set_rtc(sys_time);
LOGW("Updating panda RTC. dt = %.2f "
"System: %d-%02d-%02d %02d:%02d:%02d RTC: %d-%02d-%02d %02d:%02d:%02d",
seconds,
sys_time.tm_year + 1900, sys_time.tm_mon + 1, sys_time.tm_mday,
sys_time.tm_hour, sys_time.tm_min, sys_time.tm_sec,
rtc_time.tm_year + 1900, rtc_time.tm_mon + 1, rtc_time.tm_mday,
rtc_time.tm_hour, rtc_time.tm_min, rtc_time.tm_sec);
}
} }
} }

@ -352,7 +352,7 @@ void common_process_driver_camera(SubMaster *sm, PubMaster *pm, CameraState *c,
const CameraBuf *b = &c->buf; const CameraBuf *b = &c->buf;
static int x_min = 0, x_max = 0, y_min = 0, y_max = 0; static int x_min = 0, x_max = 0, y_min = 0, y_max = 0;
static const bool is_rhd = Params().read_db_bool("IsRHD"); static const bool is_rhd = Params().getBool("IsRHD");
// auto exposure // auto exposure
if (cnt % 3 == 0) { if (cnt % 3 == 0) {

@ -380,6 +380,7 @@ FW_VERSIONS = {
(Ecu.fwdRadar, 0x7D0, None): [ (Ecu.fwdRadar, 0x7D0, None): [
b'\xf1\x00DEev SCC F-CUP 1.00 1.03 96400-Q4100 \xf1\xa01.03', b'\xf1\x00DEev SCC F-CUP 1.00 1.03 96400-Q4100 \xf1\xa01.03',
b'\xf1\x00DEev SCC F-CUP 1.00 1.00 99110-Q4000 \xf1\xa01.00', b'\xf1\x00DEev SCC F-CUP 1.00 1.00 99110-Q4000 \xf1\xa01.00',
b'\xf1\x8799110Q4500\xf1\000DEev SCC F-CUP 1.00 1.00 99110-Q4500 \xf1\xa01.00',
], ],
(Ecu.esp, 0x7D1, None): [ (Ecu.esp, 0x7D1, None): [
b'\xf1\xa01.06', b'\xf1\xa01.06',
@ -392,15 +393,25 @@ FW_VERSIONS = {
(Ecu.fwdCamera, 0x7C4, None): [ (Ecu.fwdCamera, 0x7C4, None): [
b'\xf1\x00DEE MFC AT USA LHD 1.00 1.03 95740-Q4000 180821', b'\xf1\x00DEE MFC AT USA LHD 1.00 1.03 95740-Q4000 180821',
b'\xf1\x00DEE MFC AT EUR LHD 1.00 1.00 99211-Q4000 191211', b'\xf1\x00DEE MFC AT EUR LHD 1.00 1.00 99211-Q4000 191211',
b'\xf1\000DEE MFC AT EUR LHD 1.00 1.00 99211-Q4100 200706',
], ],
}, },
CAR.KIA_SELTOS: { CAR.KIA_SELTOS: {
(Ecu.fwdRadar, 0x7d0, None): [b'\xf1\x8799110Q5100\xf1\000SP2_ SCC FHCUP 1.01 1.05 99110-Q5100 \xf1\xa01.05',], (Ecu.fwdRadar, 0x7d0, None): [b'\xf1\x8799110Q5100\xf1\000SP2_ SCC FHCUP 1.01 1.05 99110-Q5100 \xf1\xa01.05',],
(Ecu.esp, 0x7d1, None): [b'\xf1\x8758910-Q5450\xf1\000SP ESC \t 101\031\t\005 58910-Q5450\xf1\xa01.01',], (Ecu.esp, 0x7d1, None): [
(Ecu.engine, 0x7e0, None): [b'\xf1\x81616D2051\000\000\000\000\000\000\000\000',], b'\xf1\x8758910-Q5450\xf1\000SP ESC \a 101\031\t\005 58910-Q5450\xf1\xa01.01',
b'\xf1\x8758910-Q5450\xf1\000SP ESC \t 101\031\t\005 58910-Q5450\xf1\xa01.01',
],
(Ecu.engine, 0x7e0, None): [
b'\xf1\x81616D2051\000\000\000\000\000\000\000\000',
b'\001TSP2KNL06F100J0K',
],
(Ecu.eps, 0x7d4, None): [b'\xf1\000SP2 MDPS C 1.00 1.04 56300Q5200 ',], (Ecu.eps, 0x7d4, None): [b'\xf1\000SP2 MDPS C 1.00 1.04 56300Q5200 ',],
(Ecu.fwdCamera, 0x7c4, None): [b'\xf1\000SP2 MFC AT USA LHD 1.00 1.04 99210-Q5000 191114',], (Ecu.fwdCamera, 0x7c4, None): [b'\xf1\000SP2 MFC AT USA LHD 1.00 1.04 99210-Q5000 191114',],
(Ecu.transmission, 0x7e1, None): [b'\xf1\x87CZLUB49370612JF7h\xa8y\x87\x99\xa7hv\x99\x97fv\x88\x87x\x89x\x96O\xff\x88\xff\xff\xff.@\xf1\x816V2C2051\000\000\xf1\0006V2B0_C2\000\0006V2C2051\000\000CSP4N20NS3\000\000\000\000',], (Ecu.transmission, 0x7e1, None): [
b'\xf1\x87CZLUB49370612JF7h\xa8y\x87\x99\xa7hv\x99\x97fv\x88\x87x\x89x\x96O\xff\x88\xff\xff\xff.@\xf1\x816V2C2051\000\000\xf1\0006V2B0_C2\000\0006V2C2051\000\000CSP4N20NS3\000\000\000\000',
b'\xf1\x87954A22D200\xf1\x81T01950A1 \xf1\000T0190XBL T01950A1 DSP2T16X4X950NS6\xd30\xa5\xb9',
],
}, },
CAR.KIA_OPTIMA: { CAR.KIA_OPTIMA: {
(Ecu.fwdRadar, 0x7d0, None): [b'\xf1\x00JF__ SCC F-CUP 1.00 1.00 96400-D4110 '], (Ecu.fwdRadar, 0x7d0, None): [b'\xf1\x00JF__ SCC F-CUP 1.00 1.00 96400-D4110 '],

@ -597,7 +597,7 @@ FW_VERSIONS = {
], ],
(Ecu.fwdCamera, 0x750, 0x6d): [ (Ecu.fwdCamera, 0x750, 0x6d): [
b'8646FF404000 ', b'8646FF404000 ',
b'8821FF406000 ', b'8646FF406000 ',
b'8646FF407000 ', b'8646FF407000 ',
], ],
}, },
@ -1022,7 +1022,6 @@ FW_VERSIONS = {
b'\x02342N9000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00', b'\x02342N9000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x02342N9100\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00', b'\x02342N9100\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x02342P0000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00', b'\x02342P0000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x02342Q2000\x00\x00\x00\x00\x00\x00\x00\x0054213000\x00\x00\x00\x00\x00\x00\x00\x00',
], ],
(Ecu.eps, 0x7a1, None): [ (Ecu.eps, 0x7a1, None): [
b'8965B42103\x00\x00\x00\x00\x00\x00', b'8965B42103\x00\x00\x00\x00\x00\x00',
@ -1127,6 +1126,7 @@ FW_VERSIONS = {
b'\x018966342W5000\x00\x00\x00\x00', b'\x018966342W5000\x00\x00\x00\x00',
b'\x028966342W4001\x00\x00\x00\x00897CF1203001\x00\x00\x00\x00', b'\x028966342W4001\x00\x00\x00\x00897CF1203001\x00\x00\x00\x00',
b'\x02896634A13001\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896634A13001\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',
b'\x02896634A13101\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',
b'\x02896634A14001\x00\x00\x00\x00897CF1203001\x00\x00\x00\x00', b'\x02896634A14001\x00\x00\x00\x00897CF1203001\x00\x00\x00\x00',
b'\x02896634A23001\x00\x00\x00\x00897CF1203001\x00\x00\x00\x00', b'\x02896634A23001\x00\x00\x00\x00897CF1203001\x00\x00\x00\x00',
b'\x02896634A14001\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896634A14001\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',

@ -57,10 +57,10 @@ class CarInterface(CarInterfaceBase):
# Per-chassis tuning values, override tuning defaults here if desired # Per-chassis tuning values, override tuning defaults here if desired
if candidate == CAR.GOLF: if candidate == CAR.GOLF_MK7:
# Temporarily carry forward old tuning values while we test vehicle identification # Averages of all AU Golf variants
ret.mass = 1500 + STD_CARGO_KG ret.mass = 1397 + STD_CARGO_KG
ret.wheelbase = 2.64 ret.wheelbase = 2.62
elif candidate == CAR.JETTA_MK7: elif candidate == CAR.JETTA_MK7:
# Averages of all BU Jetta variants # Averages of all BU Jetta variants

@ -55,7 +55,7 @@ MQB_LDW_MESSAGES = {
# FW_VERSIONS for that existing CAR. # FW_VERSIONS for that existing CAR.
class CAR: class CAR:
GOLF = "VOLKSWAGEN GOLF" # Chassis 5G/AU/BA/BE, Mk7 VW Golf and variants GOLF_MK7 = "VOLKSWAGEN GOLF 7TH GEN" # Chassis 5G/AU/BA/BE, Mk7 VW Golf and variants
JETTA_MK7 = "VOLKSWAGEN JETTA 7TH GEN" # Chassis BU, Mk7 Jetta JETTA_MK7 = "VOLKSWAGEN JETTA 7TH GEN" # Chassis BU, Mk7 Jetta
PASSAT_MK8 = "VOLKSWAGEN PASSAT 8TH GEN" # Chassis 3G, Mk8 Passat and variants PASSAT_MK8 = "VOLKSWAGEN PASSAT 8TH GEN" # Chassis 3G, Mk8 Passat and variants
TIGUAN_MK2 = "VOLKSWAGEN TIGUAN 2ND GEN" # Chassis AD/BW, Mk2 VW Tiguan and variants TIGUAN_MK2 = "VOLKSWAGEN TIGUAN 2ND GEN" # Chassis AD/BW, Mk2 VW Tiguan and variants
@ -66,7 +66,7 @@ class CAR:
SKODA_SUPERB_MK3 = "SKODA SUPERB 3RD GEN" # Chassis 3V/NP, Mk3 Skoda Superb and variants SKODA_SUPERB_MK3 = "SKODA SUPERB 3RD GEN" # Chassis 3V/NP, Mk3 Skoda Superb and variants
FINGERPRINTS = { FINGERPRINTS = {
CAR.GOLF: [{ CAR.GOLF_MK7: [{
64: 8, 134: 8, 159: 8, 173: 8, 178: 8, 253: 8, 257: 8, 260: 8, 262: 8, 264: 8, 278: 8, 279: 8, 283: 8, 286: 8, 288: 8, 289: 8, 290: 8, 294: 8, 299: 8, 302: 8, 346: 8, 385: 8, 418: 8, 427: 8, 668: 8, 679: 8, 681: 8, 695: 8, 779: 8, 780: 8, 783: 8, 792: 8, 795: 8, 804: 8, 806: 8, 807: 8, 808: 8, 809: 8, 870: 8, 896: 8, 897: 8, 898: 8, 901: 8, 917: 8, 919: 8, 927: 8, 949: 8, 958: 8, 960: 4, 981: 8, 987: 8, 988: 8, 991: 8, 997: 8, 1000: 8, 1019: 8, 1120: 8, 1122: 8, 1123: 8, 1124: 8, 1153: 8, 1162: 8, 1175: 8, 1312: 8, 1385: 8, 1413: 8, 1440: 5, 1514: 8, 1515: 8, 1520: 8, 1529: 8, 1600: 8, 1601: 8, 1603: 8, 1605: 8, 1624: 8, 1626: 8, 1629: 8, 1631: 8, 1646: 8, 1648: 8, 1712: 6, 1714: 8, 1716: 8, 1717: 8, 1719: 8, 1720: 8, 1721: 8 64: 8, 134: 8, 159: 8, 173: 8, 178: 8, 253: 8, 257: 8, 260: 8, 262: 8, 264: 8, 278: 8, 279: 8, 283: 8, 286: 8, 288: 8, 289: 8, 290: 8, 294: 8, 299: 8, 302: 8, 346: 8, 385: 8, 418: 8, 427: 8, 668: 8, 679: 8, 681: 8, 695: 8, 779: 8, 780: 8, 783: 8, 792: 8, 795: 8, 804: 8, 806: 8, 807: 8, 808: 8, 809: 8, 870: 8, 896: 8, 897: 8, 898: 8, 901: 8, 917: 8, 919: 8, 927: 8, 949: 8, 958: 8, 960: 4, 981: 8, 987: 8, 988: 8, 991: 8, 997: 8, 1000: 8, 1019: 8, 1120: 8, 1122: 8, 1123: 8, 1124: 8, 1153: 8, 1162: 8, 1175: 8, 1312: 8, 1385: 8, 1413: 8, 1440: 5, 1514: 8, 1515: 8, 1520: 8, 1529: 8, 1600: 8, 1601: 8, 1603: 8, 1605: 8, 1624: 8, 1626: 8, 1629: 8, 1631: 8, 1646: 8, 1648: 8, 1712: 6, 1714: 8, 1716: 8, 1717: 8, 1719: 8, 1720: 8, 1721: 8
}], }],
CAR.JETTA_MK7: [{ CAR.JETTA_MK7: [{
@ -98,21 +98,76 @@ FINGERPRINTS = {
IGNORED_FINGERPRINTS = [CAR.JETTA_MK7, CAR.PASSAT_MK8, CAR.TIGUAN_MK2, CAR.SEAT_ATECA_MK1, CAR.SKODA_KODIAQ_MK1, CAR.SKODA_SCALA_MK1, CAR.SKODA_SUPERB_MK3] IGNORED_FINGERPRINTS = [CAR.JETTA_MK7, CAR.PASSAT_MK8, CAR.TIGUAN_MK2, CAR.SEAT_ATECA_MK1, CAR.SKODA_KODIAQ_MK1, CAR.SKODA_SCALA_MK1, CAR.SKODA_SUPERB_MK3]
FW_VERSIONS = { FW_VERSIONS = {
CAR.GOLF: { CAR.GOLF_MK7: {
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'\xf1\x8704E906016A \xf1\x897697',
b'\xf1\x8704E906016AD\xf1\x895758',
b'\xf1\x8704E906023AG\xf1\x891726',
b'\xf1\x8704E906023BN\xf1\x894518',
b'\xf1\x8704E906027GR\xf1\x892394',
b'\xf1\x8704L906026NF\xf1\x899528',
b'\xf1\x8704L906056HE\xf1\x893758',
b'\xf1\x870EA906016A \xf1\x898343',
b'\xf1\x870EA906016S \xf1\x897207',
b'\xf1\x875G0906259 \xf1\x890007',
b'\xf1\x875G0906259J \xf1\x890002',
b'\xf1\x875G0906259L \xf1\x890002',
b'\xf1\x875G0906259Q \xf1\x890002',
b'\xf1\x878V0906259P \xf1\x890001', b'\xf1\x878V0906259P \xf1\x890001',
b'\xf1\x878V0906259Q \xf1\x890002',
], ],
(Ecu.transmission, 0x7e1, None): [ (Ecu.transmission, 0x7e1, None): [
b'\xf1\x870CW300045 \xf1\x894531',
b'\xf1\x870D9300040S \xf1\x894311',
b'\xf1\x870CW300047D \xf1\x895261',
b'\xf1\x870D9300012 \xf1\x894913',
b'\xf1\x870CW300042F \xf1\x891604',
b'\xf1\x870DD300046F \xf1\x891601',
b'\xf1\x870D9300020S \xf1\x895201',
b'\xf1\x870GC300012A \xf1\x891403', b'\xf1\x870GC300012A \xf1\x891403',
b'\xf1\x870GC300043T \xf1\x899999',
b'\xf1\x870GC300020G \xf1\x892404',
b'\xf1\x870GC300014B \xf1\x892405',
b'\xf1\x870DD300045K \xf1\x891120',
], ],
(Ecu.srs, 0x715, None): [ (Ecu.srs, 0x715, None): [
b'\xf1\x875Q0959655J \xf1\x890830\xf1\x82\x13271212111312--071104171838103891131211', b'\xf1\x875Q0959655J \xf1\x890830\xf1\x82\023271212111312--071104171838103891131211',
b'\xf1\x875Q0959655J \xf1\x890830\xf1\x82\x13272512111312--07110417182C102C91131211',
b'\xf1\x875Q0959655M \xf1\x890361\xf1\x82\0211413001112120041114115121611169112',
b'\xf1\x875Q0959655S \xf1\x890870\xf1\x82\02324230011211200621143171724112491132111',
b'\xf1\x875Q0959655S \xf1\x890870\xf1\x82\02315120011211200621143171717111791132111',
b'\xf1\x875Q0959655S \xf1\x890870\xf1\x82\x1315120011211200061104171717101791132111',
b'\xf1\x875Q0959655S \xf1\x890870\xf1\x82\02324230011211200061104171724102491132111',
b'\xf1\x875Q0959655AA\xf1\x890386\xf1\x82\0211413001113120053114317121C111C9113',
b'\xf1\x875Q0959655AA\xf1\x890386\xf1\x82\0211413001113120043114317121C111C9113',
b'\xf1\x875Q0959655AA\xf1\x890388\xf1\x82\0211413001113120053114317121C111C9113',
b'\xf1\x875Q0959655AA\xf1\x890388\xf1\x82\0211413001113120043114417121411149113',
b'\xf1\x875Q0959655BH\xf1\x890336\xf1\x82\02314160011123300314211012230229333463100',
], ],
(Ecu.eps, 0x712, None): [ (Ecu.eps, 0x712, None): [
b'\xf1\x873Q0909144F \xf1\x895043\xf1\x82\00561A01612A0',
b'\xf1\x873Q0909144H \xf1\x895061\xf1\x82\00566A0J612A1',
b'\xf1\x873Q0909144L \xf1\x895081\xf1\x82\x0571A0JA15A1', b'\xf1\x873Q0909144L \xf1\x895081\xf1\x82\x0571A0JA15A1',
b'\xf1\x873Q0909144M \xf1\x895082\xf1\x82\00571A0JA16A1',
b'\xf1\x875Q0909143K \xf1\x892033\xf1\x820519A9040203',
b'\xf1\x875Q0909144L \xf1\x891021\xf1\x82\00522A00402A0',
b'\xf1\x875Q0909144P \xf1\x891043\xf1\x82\00511A00403A0',
b'\xf1\x875Q0909144S \xf1\x891063\xf1\x82\00516A07A02A1',
b'\xf1\x875Q0909144AA\xf1\x891081\xf1\x82\00521A00441A1',
b'\xf1\x875Q0909144AA\xf1\x891081\xf1\x82\x0521A00641A1',
b'\xf1\x875Q0909144AB\xf1\x891082\xf1\x82\00521A00642A1',
b'\xf1\x875Q0909144AB\xf1\x891082\xf1\x82\00521A07B05A1',
b'\xf1\x875QN909144A \xf1\x895081\xf1\x82\x0571A01A17A1',
], ],
(Ecu.fwdRadar, 0x757, None): [ (Ecu.fwdRadar, 0x757, None): [
b'\xf1\x875Q0907572A \xf1\x890141\xf1\x82\00101',
b'\xf1\x875Q0907572B \xf1\x890200\xf1\x82\00101',
b'\xf1\x875Q0907572C \xf1\x890210\xf1\x82\00101',
b'\xf1\x875Q0907572D \xf1\x890304\xf1\x82\00101',
b'\xf1\x875Q0907572F \xf1\x890400\xf1\x82\00101',
b'\xf1\x875Q0907572H \xf1\x890620',
b'\xf1\x875Q0907572J \xf1\x890654', b'\xf1\x875Q0907572J \xf1\x890654',
b'\xf1\x875Q0907572P \xf1\x890682',
], ],
}, },
CAR.JETTA_MK7: { CAR.JETTA_MK7: {
@ -281,7 +336,7 @@ FW_VERSIONS = {
} }
DBC = { DBC = {
CAR.GOLF: dbc_dict('vw_mqb_2010', None), CAR.GOLF_MK7: dbc_dict('vw_mqb_2010', None),
CAR.JETTA_MK7: dbc_dict('vw_mqb_2010', None), CAR.JETTA_MK7: dbc_dict('vw_mqb_2010', None),
CAR.PASSAT_MK8: dbc_dict('vw_mqb_2010', None), CAR.PASSAT_MK8: dbc_dict('vw_mqb_2010', None),
CAR.TIGUAN_MK2: dbc_dict('vw_mqb_2010', None), CAR.TIGUAN_MK2: dbc_dict('vw_mqb_2010', None),

@ -11,9 +11,6 @@
#include <sys/file.h> #include <sys/file.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <map>
#include <string>
#include <iostream>
#include <csignal> #include <csignal>
#include <string.h> #include <string.h>
@ -83,15 +80,11 @@ Params::Params(bool persistent_param){
params_path = persistent_param ? persistent_params_path : default_params_path; params_path = persistent_param ? persistent_params_path : default_params_path;
} }
Params::Params(std::string path) { Params::Params(const std::string &path) {
params_path = path; params_path = path;
} }
int Params::write_db_value(std::string key, std::string dat){ int Params::put(const char* key, const char* value, size_t value_size) {
return write_db_value(key.c_str(), dat.c_str(), dat.length());
}
int Params::write_db_value(const char* key, const char* value, size_t value_size) {
// Information about safely and atomically writing a file: https://lwn.net/Articles/457667/ // Information about safely and atomically writing a file: https://lwn.net/Articles/457667/
// 1) Create temp file // 1) Create temp file
// 2) Write data to temp file // 2) Write data to temp file
@ -213,7 +206,7 @@ cleanup:
return result; return result;
} }
int Params::delete_db_value(std::string key) { int Params::remove(const char *key) {
int lock_fd = -1; int lock_fd = -1;
int result; int result;
std::string path; std::string path;
@ -230,7 +223,7 @@ int Params::delete_db_value(std::string key) {
// Delete value. // Delete value.
path = params_path + "/d/" + key; path = params_path + "/d/" + key;
result = remove(path.c_str()); result = ::remove(path.c_str());
if (result != 0) { if (result != 0) {
result = ERR_NO_VALUE; result = ERR_NO_VALUE;
goto cleanup; goto cleanup;
@ -251,52 +244,28 @@ cleanup:
return result; return result;
} }
std::string Params::get(std::string key, bool block){ std::string Params::get(const char *key, bool block) {
char* value; std::string path = params_path + "/d/" + key;
size_t size; if (!block) {
int r; return util::read_file(path);
if (block) {
r = read_db_value_blocking((const char*)key.c_str(), &value, &size);
} else {
r = read_db_value((const char*)key.c_str(), &value, &size);
}
if (r == 0){
std::string s(value, size);
free(value);
return s;
} else { } else {
return ""; // blocking read until successful
} params_do_exit = 0;
} void (*prev_handler_sigint)(int) = std::signal(SIGINT, params_sig_handler);
void (*prev_handler_sigterm)(int) = std::signal(SIGTERM, params_sig_handler);
int Params::read_db_value(const char* key, char** value, size_t* value_sz) {
std::string path = params_path + "/d/" + std::string(key); std::string value;
*value = static_cast<char*>(read_file(path.c_str(), value_sz)); while (!params_do_exit) {
if (*value == NULL) { if (value = util::read_file(path); !value.empty()) {
return -22; break;
} }
return 0; util::sleep_for(100); // 0.1 s
}
int Params::read_db_value_blocking(const char* key, char** value, size_t* value_sz) {
params_do_exit = 0;
void (*prev_handler_sigint)(int) = std::signal(SIGINT, params_sig_handler);
void (*prev_handler_sigterm)(int) = std::signal(SIGTERM, params_sig_handler);
while (!params_do_exit) {
const int result = read_db_value(key, value, value_sz);
if (result == 0) {
break;
} else {
util::sleep_for(100); // 0.1 s
} }
}
std::signal(SIGINT, prev_handler_sigint); std::signal(SIGINT, prev_handler_sigint);
std::signal(SIGTERM, prev_handler_sigterm); std::signal(SIGTERM, prev_handler_sigterm);
return params_do_exit; // Return 0 if we had no interrupt return value;
}
} }
int Params::read_db_all(std::map<std::string, std::string> *params) { int Params::read_db_all(std::map<std::string, std::string> *params) {
@ -334,20 +303,3 @@ int Params::read_db_all(std::map<std::string, std::string> *params) {
close(lock_fd); close(lock_fd);
return 0; return 0;
} }
std::vector<char> Params::read_db_bytes(const char* param_name) {
std::vector<char> bytes;
char* value;
size_t sz;
int result = read_db_value(param_name, &value, &sz);
if (result == 0) {
bytes.assign(value, value+sz);
free(value);
}
return bytes;
}
bool Params::read_db_bool(const char* param_name) {
std::vector<char> bytes = read_db_bytes(param_name);
return bytes.size() > 0 and bytes[0] == '1';
}

@ -1,8 +1,9 @@
#pragma once #pragma once
#include <stddef.h> #include <stddef.h>
#include <map> #include <map>
#include <string> #include <string>
#include <vector> #include <sstream>
#define ERR_NO_VALUE -33 #define ERR_NO_VALUE -33
@ -12,35 +13,44 @@ private:
public: public:
Params(bool persistent_param = false); Params(bool persistent_param = false);
Params(std::string path); Params(const std::string &path);
int write_db_value(std::string key, std::string dat); // Delete a value
int write_db_value(const char* key, const char* value, size_t value_size); int remove(const char *key);
inline int remove(const std::string &key) {
// Reads a value from the params database. return remove (key.c_str());
// Inputs: }
// key: The key to read.
// value: A pointer where a newly allocated string containing the db value will
// be written.
// value_sz: A pointer where the size of value will be written. Does not
// include the NULL terminator.
// persistent_param: Boolean indicating if the param store in the /persist partition is to be used.
// e.g. for sensor calibration files. Will not be cleared after wipe or re-install.
//
// Returns: Negative on failure, otherwise 0.
int read_db_value(const char* key, char** value, size_t* value_sz);
// Delete a value from the params database.
// Inputs are the same as read_db_value, without value and value_sz.
int delete_db_value(std::string key);
// Reads a value from the params database, blocking until successful.
// Inputs are the same as read_db_value.
int read_db_value_blocking(const char* key, char** value, size_t* value_sz);
// read all values
int read_db_all(std::map<std::string, std::string> *params); int read_db_all(std::map<std::string, std::string> *params);
std::vector<char> read_db_bytes(const char* param_name);
bool read_db_bool(const char* param_name);
std::string get(std::string key, bool block=false); // read a value
std::string get(const char *key, bool block = false);
inline std::string get(const std::string &key, bool block = false) {
return get(key.c_str(), block);
}
template <class T>
std::optional<T> get(const char *key, bool block = false) {
std::istringstream iss(get(key, block));
T value{};
iss >> value;
return iss.fail() ? std::nullopt : std::optional(value);
}
inline bool getBool(const char *key) {
return get(key) == "1";
}
// write a value
int put(const char* key, const char* val, size_t value_size);
inline int put(const std::string &key, const std::string &val) {
return put(key.c_str(), val.data(), val.size());
}
inline int putBool(const char *key, bool val) {
return put(key, val ? "1" : "0", 1);
}
}; };

@ -89,8 +89,7 @@ void log(int levelnum, const char* filename, int lineno, const char* func, const
printf("%s: %s\n", filename, msg); printf("%s: %s\n", filename, msg);
} }
char levelnum_c = levelnum; char levelnum_c = levelnum;
zmq_send(s.sock, &levelnum_c, 1, ZMQ_NOBLOCK | ZMQ_SNDMORE); zmq_send(s.sock, (levelnum_c + log_s).c_str(), log_s.length() + 1, ZMQ_NOBLOCK);
zmq_send(s.sock, log_s.c_str(), log_s.length(), ZMQ_NOBLOCK);
} }
void cloudlog_e(int levelnum, const char* filename, int lineno, const char* func, void cloudlog_e(int levelnum, const char* filename, int lineno, const char* func,

@ -1,64 +0,0 @@
#include "selfdrive/common/params.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static const char* const kUsage = "%s: read|write|read_block params_path key [value]\n";
int main(int argc, const char* argv[]) {
if (argc < 4) {
printf(kUsage, argv[0]);
return 0;
}
const char* params_path = argv[2];
const char* key = argv[3];
if (strcmp(argv[1], "read") == 0) {
char* value;
size_t value_size;
int result = read_db_value(params_path, key, &value, &value_size);
if (result >= 0) {
fprintf(stdout, "Read %zu bytes: ", value_size);
fwrite(value, 1, value_size, stdout);
fprintf(stdout, "\n");
free(value);
} else {
fprintf(stderr, "Error reading: %d\n", result);
return -1;
}
} else if (strcmp(argv[1], "write") == 0) {
if (argc < 5) {
fprintf(stderr, "Error: write value required\n");
return 1;
}
const char* value = argv[4];
const size_t value_size = strlen(value);
int result = write_db_value(params_path, key, value, value_size);
if (result >= 0) {
fprintf(stdout, "Wrote %s to %s\n", value, key);
} else {
fprintf(stderr, "Error writing: %d\n", result);
return -1;
}
} else if (strcmp(argv[1], "read_block") == 0) {
char* value;
size_t value_size;
read_db_value_blocking(params_path, key, &value, &value_size);
fprintf(stdout, "Read %zu bytes: ", value_size);
fwrite(value, 1, value_size, stdout);
fprintf(stdout, "\n");
free(value);
} else {
printf(kUsage, argv[0]);
return 1;
}
return 0;
}
// BUILD:
// $ gcc -I$HOME/one selfdrive/common/test_params.c selfdrive/common/params.c selfdrive/common/util.cc -o ./test_params
// $ seq 0 100000 | xargs -P20 -I{} ./test_params write /data/params DongleId {} && sleep 0.1 &
// $ while ./test_params read /data/params DongleId; do sleep 0.05; done

@ -161,3 +161,19 @@ struct unique_fd {
operator int() const { return fd_; } operator int() const { return fd_; }
int fd_; int fd_;
}; };
class FirstOrderFilter {
public:
FirstOrderFilter(float x0, float ts, float dt) {
k_ = (dt / ts) / (1.0 + dt / ts);
x_ = x0;
}
inline float update(float x) {
x_ = (1. - k_) * x_ + k_ * x;
return x_;
}
inline void reset(float x) { x_ = x; }
private:
float x_, k_;
};

@ -38,7 +38,7 @@ if __name__ == "__main__":
values = [s.strip().split(".") for s in args.values.split(",")] values = [s.strip().split(".") for s in args.values.split(",")]
while 1: while 1:
polld = poller.poll(1000) polld = poller.poll(100)
for sock in polld: for sock in polld:
msg = sock.receive() msg = sock.receive()
evt = log.Event.from_bytes(msg) evt = log.Event.from_bytes(msg)

@ -40,4 +40,26 @@ public:
std::string cmd = util::string_format("setprop persist.neos.ssh %d", enabled ? 1 : 0); std::string cmd = util::string_format("setprop persist.neos.ssh %d", enabled ? 1 : 0);
std::system(cmd.c_str()); std::system(cmd.c_str());
}; };
// android only
inline static bool launched_activity = false;
static void check_activity() {
int ret = std::system("dumpsys SurfaceFlinger --list | grep -Fq 'com.android.settings'");
launched_activity = ret == 0;
}
static void launch_activity(std::string activity, std::string opts = "") {
if (!launched_activity) {
std::string cmd = "am start -n " + activity + " " + opts +
" --ez extra_prefs_show_button_bar true \
--es extra_prefs_set_next_text ''";
std::system(cmd.c_str());
}
launched_activity = true;
}
static void launch_wifi() {
launch_activity("com.android.settings/.wifi.WifiPickerActivity", "-a android.net.wifi.PICK_WIFI_NETWORK");
}
static void launch_tethering() {
launch_activity("com.android.settings/.TetherSettings");
}
}; };

@ -27,6 +27,6 @@ public:
}; };
static void set_display_power(bool on) {}; static void set_display_power(bool on) {};
static bool get_ssh_enabled() { return Params().read_db_bool("SshEnabled"); }; static bool get_ssh_enabled() { return Params().getBool("SshEnabled"); };
static void set_ssh_enabled(bool enabled) { Params().write_db_value("SshEnabled", (enabled ? "1" : "0")); }; static void set_ssh_enabled(bool enabled) { Params().putBool("SshEnabled", enabled); };
}; };

@ -102,7 +102,7 @@ kj::Array<capnp::word> logger_build_init_data() {
init.setGitCommit(params.get("GitCommit")); init.setGitCommit(params.get("GitCommit"));
init.setGitBranch(params.get("GitBranch")); init.setGitBranch(params.get("GitBranch"));
init.setGitRemote(params.get("GitRemote")); init.setGitRemote(params.get("GitRemote"));
init.setPassive(params.read_db_bool("Passive")); init.setPassive(params.getBool("Passive"));
{ {
std::map<std::string, std::string> params_map; std::map<std::string, std::string> params_map;
params.read_db_all(&params_map); params.read_db_all(&params_map);

@ -369,7 +369,7 @@ int main(int argc, char** argv) {
s.rotate_state[LOG_CAMERA_ID_FCAMERA].enabled = true; s.rotate_state[LOG_CAMERA_ID_FCAMERA].enabled = true;
#if defined(QCOM) || defined(QCOM2) #if defined(QCOM) || defined(QCOM2)
bool record_front = Params().read_db_bool("RecordFront"); bool record_front = Params().getBool("RecordFront");
if (record_front) { if (record_front) {
encoder_threads.push_back(std::thread(encoder_thread, LOG_CAMERA_ID_DCAMERA)); encoder_threads.push_back(std::thread(encoder_thread, LOG_CAMERA_ID_DCAMERA));
s.rotate_state[LOG_CAMERA_ID_DCAMERA].enabled = true; s.rotate_state[LOG_CAMERA_ID_DCAMERA].enabled = true;

@ -127,10 +127,10 @@ class Uploader():
url_resp_json = json.loads(url_resp.text) url_resp_json = json.loads(url_resp.text)
url = url_resp_json['url'] url = url_resp_json['url']
headers = url_resp_json['headers'] headers = url_resp_json['headers']
cloudlog.info("upload_url v1.3 %s %s", url, str(headers)) cloudlog.debug("upload_url v1.3 %s %s", url, str(headers))
if fake_upload: if fake_upload:
cloudlog.info("*** WARNING, THIS IS A FAKE UPLOAD TO %s ***" % url) cloudlog.debug("*** WARNING, THIS IS A FAKE UPLOAD TO %s ***" % url)
class FakeResponse(): class FakeResponse():
def __init__(self): def __init__(self):
@ -164,7 +164,7 @@ class Uploader():
cloudlog.event("upload", key=key, fn=fn, sz=sz) cloudlog.event("upload", key=key, fn=fn, sz=sz)
cloudlog.info("checking %r with size %r", key, sz) cloudlog.debug("checking %r with size %r", key, sz)
if sz == 0: if sz == 0:
try: try:
@ -174,10 +174,10 @@ class Uploader():
cloudlog.event("uploader_setxattr_failed", exc=self.last_exc, key=key, fn=fn, sz=sz) cloudlog.event("uploader_setxattr_failed", exc=self.last_exc, key=key, fn=fn, sz=sz)
success = True success = True
else: else:
cloudlog.info("uploading %r", fn) cloudlog.debug("uploading %r", fn)
stat = self.normal_upload(key, fn) stat = self.normal_upload(key, fn)
if stat is not None and stat.status_code in (200, 201, 412): if stat is not None and stat.status_code in (200, 201, 412):
cloudlog.event("upload_success" if stat.status_code != 412 else "upload_ignored", key=key, fn=fn, sz=sz) cloudlog.event("upload_success" if stat.status_code != 412 else "upload_ignored", key=key, fn=fn, sz=sz, debug=True)
try: try:
# tag file as uploaded # tag file as uploaded
setxattr(fn, UPLOAD_ATTR_NAME, UPLOAD_ATTR_VALUE) setxattr(fn, UPLOAD_ATTR_NAME, UPLOAD_ATTR_VALUE)
@ -185,14 +185,12 @@ class Uploader():
cloudlog.event("uploader_setxattr_failed", exc=self.last_exc, key=key, fn=fn, sz=sz) cloudlog.event("uploader_setxattr_failed", exc=self.last_exc, key=key, fn=fn, sz=sz)
success = True success = True
else: else:
cloudlog.event("upload_failed", stat=stat, exc=self.last_exc, key=key, fn=fn, sz=sz) cloudlog.event("upload_failed", stat=stat, exc=self.last_exc, key=key, fn=fn, sz=sz, debug=True)
success = False success = False
return success return success
def uploader_fn(exit_event): def uploader_fn(exit_event):
cloudlog.info("uploader_fn")
params = Params() params = Params()
dongle_id = params.get("DongleId").decode('utf8') dongle_id = params.get("DongleId").decode('utf8')
@ -209,8 +207,14 @@ def uploader_fn(exit_event):
backoff = 0.1 backoff = 0.1
while not exit_event.is_set(): while not exit_event.is_set():
sm.update(0) sm.update(0)
on_wifi = force_wifi or sm['deviceState'].networkType == NetworkType.wifi
offroad = params.get("IsOffroad") == b'1' offroad = params.get("IsOffroad") == b'1'
network_type = sm['deviceState'].networkType if not force_wifi else NetworkType.wifi
if network_type == NetworkType.none:
if allow_sleep:
time.sleep(60 if offroad else 5)
continue
on_wifi = network_type == NetworkType.wifi
allow_raw_upload = params.get("IsUploadRawEnabled") != b"0" allow_raw_upload = params.get("IsUploadRawEnabled") != b"0"
d = uploader.next_file_to_upload(with_raw=allow_raw_upload and on_wifi and offroad) d = uploader.next_file_to_upload(with_raw=allow_raw_upload and on_wifi and offroad)
@ -221,13 +225,12 @@ def uploader_fn(exit_event):
key, fn = d key, fn = d
cloudlog.event("uploader_netcheck", is_on_wifi=on_wifi) cloudlog.debug("upload %r over %s", d, network_type)
cloudlog.info("to upload %r", d)
success = uploader.upload(key, fn) success = uploader.upload(key, fn)
if success: if success:
backoff = 0.1 backoff = 0.1
elif allow_sleep: elif allow_sleep:
cloudlog.info("backoff %r", backoff) cloudlog.info("upload backoff %r", backoff)
time.sleep(backoff + random.uniform(0, backoff)) time.sleep(backoff + random.uniform(0, backoff))
backoff = min(backoff*2, 120) backoff = min(backoff*2, 120)
cloudlog.info("upload done, success=%r", success) cloudlog.info("upload done, success=%r", success)

@ -72,10 +72,7 @@ void run_model(ModelState &model, VisionIpcClient &vipc_client) {
SubMaster sm({"lateralPlan", "roadCameraState"}); SubMaster sm({"lateralPlan", "roadCameraState"});
// setup filter to track dropped frames // setup filter to track dropped frames
const float dt = 1. / MODEL_FREQ; FirstOrderFilter frame_dropped_filter(0., 10., 1. / MODEL_FREQ);
const float ts = 10.0; // filter time constant (s)
const float frame_filter_k = (dt / ts) / (1. + dt / ts);
float frames_dropped = 0;
uint32_t frame_id = 0, last_vipc_frame_id = 0; uint32_t frame_id = 0, last_vipc_frame_id = 0;
double last = 0; double last = 0;
@ -114,8 +111,12 @@ void run_model(ModelState &model, VisionIpcClient &vipc_client) {
// tracked dropped frames // tracked dropped frames
uint32_t vipc_dropped_frames = extra.frame_id - last_vipc_frame_id - 1; uint32_t vipc_dropped_frames = extra.frame_id - last_vipc_frame_id - 1;
frames_dropped = (1. - frame_filter_k) * frames_dropped + frame_filter_k * (float)std::min(vipc_dropped_frames, 10U); float frames_dropped = frame_dropped_filter.update((float)std::min(vipc_dropped_frames, 10U));
if (run_count < 10) frames_dropped = 0; // let frame drops warm up if (run_count < 10) { // let frame drops warm up
frame_dropped_filter.reset(0);
frames_dropped = 0.;
}
float frame_drop_ratio = frames_dropped / (1 + frames_dropped); float frame_drop_ratio = frames_dropped / (1 + frames_dropped);
model_publish(pm, extra.frame_id, frame_id, frame_drop_ratio, model_buf, extra.timestamp_eof, model_execution_time, model_publish(pm, extra.frame_id, frame_id, frame_drop_ratio, model_buf, extra.timestamp_eof, model_execution_time,

@ -25,7 +25,7 @@ void dmonitoring_init(DMonitoringModelState* s) {
int runtime = USE_DSP_RUNTIME; int runtime = USE_DSP_RUNTIME;
s->m = new DefaultRunModel(model_path, &s->output[0], OUTPUT_SIZE, runtime); s->m = new DefaultRunModel(model_path, &s->output[0], OUTPUT_SIZE, runtime);
s->is_rhd = Params().read_db_bool("IsRHD"); s->is_rhd = Params().getBool("IsRHD");
} }
template <class T> template <class T>

@ -70,7 +70,7 @@ void model_init(ModelState* s, cl_device_id device_id, cl_context context) {
#endif #endif
#ifdef TRAFFIC_CONVENTION #ifdef TRAFFIC_CONVENTION
const int idx = Params().read_db_bool("IsRHD") ? 1 : 0; const int idx = Params().getBool("IsRHD") ? 1 : 0;
s->traffic_convention[idx] = 1.0; s->traffic_convention[idx] = 1.0;
s->m->addTrafficConvention(s->traffic_convention, TRAFFIC_CONVENTION_LEN); s->m->addTrafficConvention(s->traffic_convention, TRAFFIC_CONVENTION_LEN);
#endif #endif

@ -30,6 +30,8 @@ _BLINK_THRESHOLD_SLACK = 0.65
_BLINK_THRESHOLD_STRICT = 0.5 _BLINK_THRESHOLD_STRICT = 0.5
_PITCH_WEIGHT = 1.35 # pitch matters a lot more _PITCH_WEIGHT = 1.35 # pitch matters a lot more
_POSESTD_THRESHOLD = 0.14 _POSESTD_THRESHOLD = 0.14
_E2E_POSE_THRESHOLD = 0.9
_E2E_EYES_THRESHOLD = 0.75
_METRIC_THRESHOLD = 0.4 _METRIC_THRESHOLD = 0.4
_METRIC_THRESHOLD_SLACK = 0.55 _METRIC_THRESHOLD_SLACK = 0.55
_METRIC_THRESHOLD_STRICT = 0.4 _METRIC_THRESHOLD_STRICT = 0.4
@ -194,8 +196,10 @@ class DriverStatus():
self.blink.left_blink = driver_state.leftBlinkProb * (driver_state.leftEyeProb > _EYE_THRESHOLD) * (driver_state.sunglassesProb < _SG_THRESHOLD) self.blink.left_blink = driver_state.leftBlinkProb * (driver_state.leftEyeProb > _EYE_THRESHOLD) * (driver_state.sunglassesProb < _SG_THRESHOLD)
self.blink.right_blink = driver_state.rightBlinkProb * (driver_state.rightEyeProb > _EYE_THRESHOLD) * (driver_state.sunglassesProb < _SG_THRESHOLD) self.blink.right_blink = driver_state.rightBlinkProb * (driver_state.rightEyeProb > _EYE_THRESHOLD) * (driver_state.sunglassesProb < _SG_THRESHOLD)
self.driver_distracted = self._is_driver_distracted(self.pose, self.blink) > 0 and \ self.driver_distracted = (self._is_driver_distracted(self.pose, self.blink) > 0 and
driver_state.faceProb > _FACE_THRESHOLD and self.pose.low_std driver_state.faceProb > _FACE_THRESHOLD and self.pose.low_std) or \
((driver_state.distractedPose > _E2E_POSE_THRESHOLD or driver_state.distractedEyes > _E2E_EYES_THRESHOLD) and
(self.face_detected and not self.face_partial))
self.driver_distraction_filter.update(self.driver_distracted) self.driver_distraction_filter.update(self.driver_distracted)
# update offseter # update offseter
@ -209,7 +213,7 @@ class DriverStatus():
self.is_model_uncertain = self.hi_stds * DT_DMON > _HI_STD_FALLBACK_TIME self.is_model_uncertain = self.hi_stds * DT_DMON > _HI_STD_FALLBACK_TIME
self._set_timers(self.face_detected and not self.is_model_uncertain) self._set_timers(self.face_detected and not self.is_model_uncertain)
if self.face_detected and not self.pose.low_std: if self.face_detected and not self.pose.low_std and not self.driver_distracted:
self.hi_stds += 1 self.hi_stds += 1
elif self.face_detected and self.pose.low_std: elif self.face_detected and self.pose.low_std:
self.hi_stds = 0 self.hi_stds = 0

@ -1 +1 @@
3da639f754563292103525345ec26e384e4cb9a5 796df70c9c83a693f81e4a35d97f90dc5378ef06

@ -437,8 +437,8 @@ routes = {
'enableCamera': True, 'enableCamera': True,
'enableDsu': False, 'enableDsu': False,
}, },
"76b83eb0245de90e|2019-10-20--15-42-29": { "cae14e88932eb364|2021-03-26--14-43-28": {
'carFingerprint': VOLKSWAGEN.GOLF, 'carFingerprint': VOLKSWAGEN.GOLF_MK7,
'enableCamera': True, 'enableCamera': True,
}, },
"58a7d3b707987d65|2021-03-25--17-26-37": { "58a7d3b707987d65|2021-03-25--17-26-37": {

@ -57,6 +57,9 @@ def check_cpu_usage(first_proc, last_proc):
cpu_time = cputime_total(last) - cputime_total(first) cpu_time = cputime_total(last) - cputime_total(first)
cpu_usage = cpu_time / dt * 100. cpu_usage = cpu_time / dt * 100.
if cpu_usage > max(normal_cpu_usage * 1.1, normal_cpu_usage + 5.0): if cpu_usage > max(normal_cpu_usage * 1.1, normal_cpu_usage + 5.0):
# TODO: fix high CPU when playing sounds constantly in UI
if proc_name == "./_ui" and cpu_usage < 40.:
continue
result += f"Warning {proc_name} using more CPU than normal\n" result += f"Warning {proc_name} using more CPU than normal\n"
r = False r = False
elif cpu_usage < min(normal_cpu_usage * 0.65, max(normal_cpu_usage - 1.0, 0.0)): elif cpu_usage < min(normal_cpu_usage * 0.65, max(normal_cpu_usage - 1.0, 0.0)):

@ -169,13 +169,13 @@ class PowerMonitoring:
return disable_charging return disable_charging
# See if we need to shutdown # See if we need to shutdown
def should_shutdown(self, pandaState, offroad_timestamp, started_seen, LEON): def should_shutdown(self, pandaState, offroad_timestamp, started_seen):
if pandaState is None or offroad_timestamp is None: if pandaState is None or offroad_timestamp is None:
return False return False
now = sec_since_boot() now = sec_since_boot()
panda_charging = (pandaState.pandaState.usbPowerMode != log.PandaState.UsbPowerMode.client) panda_charging = (pandaState.pandaState.usbPowerMode != log.PandaState.UsbPowerMode.client)
BATT_PERC_OFF = 10 if LEON else 3 BATT_PERC_OFF = 10
should_shutdown = False should_shutdown = False
# Wait until we have shut down charging before powering down # Wait until we have shut down charging before powering down

@ -2,6 +2,7 @@
import datetime import datetime
import os import os
import time import time
from pathlib import Path
from typing import Dict, Optional, Tuple from typing import Dict, Optional, Tuple
import psutil import psutil
@ -37,9 +38,9 @@ DISCONNECT_TIMEOUT = 5. # wait 5 seconds before going offroad after disconnect
prev_offroad_states: Dict[str, Tuple[bool, Optional[str]]] = {} prev_offroad_states: Dict[str, Tuple[bool, Optional[str]]] = {}
LEON = False
last_eon_fan_val = None last_eon_fan_val = None
def read_tz(x): def read_tz(x):
if x is None: if x is None:
return 0 return 0
@ -62,44 +63,24 @@ def read_thermal(thermal_config):
def setup_eon_fan(): def setup_eon_fan():
global LEON
os.system("echo 2 > /sys/module/dwc3_msm/parameters/otg_switch") os.system("echo 2 > /sys/module/dwc3_msm/parameters/otg_switch")
bus = SMBus(7, force=True)
try:
bus.write_byte_data(0x21, 0x10, 0xf) # mask all interrupts
bus.write_byte_data(0x21, 0x03, 0x1) # set drive current and global interrupt disable
bus.write_byte_data(0x21, 0x02, 0x2) # needed?
bus.write_byte_data(0x21, 0x04, 0x4) # manual override source
except IOError:
print("LEON detected")
LEON = True
bus.close()
def set_eon_fan(val): def set_eon_fan(val):
global LEON, last_eon_fan_val global last_eon_fan_val
if last_eon_fan_val is None or last_eon_fan_val != val: if last_eon_fan_val is None or last_eon_fan_val != val:
bus = SMBus(7, force=True) bus = SMBus(7, force=True)
if LEON: try:
try: i = [0x1, 0x3 | 0, 0x3 | 0x08, 0x3 | 0x10][val]
i = [0x1, 0x3 | 0, 0x3 | 0x08, 0x3 | 0x10][val] bus.write_i2c_block_data(0x3d, 0, [i])
bus.write_i2c_block_data(0x3d, 0, [i]) except IOError:
except IOError: # tusb320
# tusb320 if val == 0:
if val == 0: bus.write_i2c_block_data(0x67, 0xa, [0])
bus.write_i2c_block_data(0x67, 0xa, [0]) else:
#bus.write_i2c_block_data(0x67, 0x45, [1<<2]) bus.write_i2c_block_data(0x67, 0xa, [0x20])
else: bus.write_i2c_block_data(0x67, 0x8, [(val - 1) << 6])
#bus.write_i2c_block_data(0x67, 0x45, [0])
bus.write_i2c_block_data(0x67, 0xa, [0x20])
bus.write_i2c_block_data(0x67, 0x8, [(val - 1) << 6])
else:
bus.write_byte_data(0x21, 0x04, 0x2)
bus.write_byte_data(0x21, 0x03, (val*2)+1)
bus.write_byte_data(0x21, 0x04, 0x4)
bus.close() bus.close()
last_eon_fan_val = val last_eon_fan_val = val
@ -191,6 +172,19 @@ def thermald_thread():
thermal_config = HARDWARE.get_thermal_config() thermal_config = HARDWARE.get_thermal_config()
# CPR3 logging
if EON:
base_path = "/sys/kernel/debug/cpr3-regulator/"
cpr_files = [p for p in Path(base_path).glob("**/*") if p.is_file()]
cpr_data = {}
for cf in cpr_files:
with open(cf, "r") as f:
try:
cpr_data[str(cf)] = f.read().strip()
except Exception:
pass
cloudlog.event("CPR", data=cpr_data)
while 1: while 1:
pandaState = messaging.recv_sock(pandaState_sock, wait=True) pandaState = messaging.recv_sock(pandaState_sock, wait=True)
msg = read_thermal(thermal_config) msg = read_thermal(thermal_config)
@ -386,7 +380,7 @@ def thermald_thread():
msg.deviceState.chargingDisabled = power_monitor.should_disable_charging(pandaState, off_ts) msg.deviceState.chargingDisabled = power_monitor.should_disable_charging(pandaState, off_ts)
# Check if we need to shut down # Check if we need to shut down
if power_monitor.should_shutdown(pandaState, off_ts, started_seen, LEON): if power_monitor.should_shutdown(pandaState, off_ts, started_seen):
cloudlog.info(f"shutting device down, offroad since {off_ts}") cloudlog.info(f"shutting device down, offroad since {off_ts}")
# TODO: add function for blocking cloudlog instead of sleep # TODO: add function for blocking cloudlog instead of sleep
time.sleep(10) time.sleep(10)

@ -16,7 +16,7 @@ if arch == "Darwin":
qt_env['FRAMEWORKS'] += ['OpenCL'] qt_env['FRAMEWORKS'] += ['OpenCL']
widgets_src = ["qt/widgets/input.cc", "qt/widgets/drive_stats.cc", widgets_src = ["qt/widgets/input.cc", "qt/widgets/drive_stats.cc",
"qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc", "qt/qt_sound.cc", "qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc", "qt/sound.cc",
"qt/widgets/offroad_alerts.cc", "qt/widgets/setup.cc", "qt/widgets/keyboard.cc", "qt/widgets/offroad_alerts.cc", "qt/widgets/setup.cc", "qt/widgets/keyboard.cc",
"#phonelibs/qrcode/QrCode.cc"] "#phonelibs/qrcode/QrCode.cc"]
if arch != 'aarch64': if arch != 'aarch64':

@ -50,19 +50,19 @@ static void draw_chevron(UIState *s, float x, float y, float sz, NVGcolor fillCo
nvgFill(s->vg); nvgFill(s->vg);
} }
static void ui_draw_circle_image(const UIState *s, int x, int y, int size, const char *image, NVGcolor color, float img_alpha, int img_y = 0) { static void ui_draw_circle_image(const UIState *s, int center_x, int center_y, int radius, const char *image, NVGcolor color, float img_alpha) {
const int img_size = size * 1.5;
nvgBeginPath(s->vg); nvgBeginPath(s->vg);
nvgCircle(s->vg, x, y + (bdr_s * 1.5), size); nvgCircle(s->vg, center_x, center_y, radius);
nvgFillColor(s->vg, color); nvgFillColor(s->vg, color);
nvgFill(s->vg); nvgFill(s->vg);
ui_draw_image(s, {x - (img_size / 2), img_y ? img_y : y - (size / 4), img_size, img_size}, image, img_alpha); const int img_size = radius * 1.5;
ui_draw_image(s, {center_x - (img_size / 2), center_y - (img_size / 2), img_size, img_size}, image, img_alpha);
} }
static void ui_draw_circle_image(const UIState *s, int x, int y, int size, const char *image, bool active) { static void ui_draw_circle_image(const UIState *s, int center_x, int center_y, int radius, const char *image, bool active) {
float bg_alpha = active ? 0.3f : 0.1f; float bg_alpha = active ? 0.3f : 0.1f;
float img_alpha = active ? 1.0f : 0.15f; float img_alpha = active ? 1.0f : 0.15f;
ui_draw_circle_image(s, x, y, size, image, nvgRGBA(0, 0, 0, (255 * bg_alpha)), img_alpha); ui_draw_circle_image(s, center_x, center_y, radius, image, nvgRGBA(0, 0, 0, (255 * bg_alpha)), img_alpha);
} }
static void draw_lead(UIState *s, int idx) { static void draw_lead(UIState *s, int idx) {
@ -215,18 +215,18 @@ static void ui_draw_vision_speed(UIState *s) {
static void ui_draw_vision_event(UIState *s) { static void ui_draw_vision_event(UIState *s) {
if (s->scene.controls_state.getEngageable()) { if (s->scene.controls_state.getEngageable()) {
// draw steering wheel // draw steering wheel
const int bg_wheel_size = 96; const int radius = 96;
const int bg_wheel_x = s->viz_rect.right() - bg_wheel_size - bdr_s * 2; const int center_x = s->viz_rect.right() - radius - bdr_s * 2;
const int bg_wheel_y = s->viz_rect.y + (bg_wheel_size / 2) + (bdr_s * 1.5); const int center_y = s->viz_rect.y + radius + (bdr_s * 1.5);
ui_draw_circle_image(s, bg_wheel_x, bg_wheel_y, bg_wheel_size, "wheel", bg_colors[s->status], 1.0f, bg_wheel_y - 25); ui_draw_circle_image(s, center_x, center_y, radius, "wheel", bg_colors[s->status], 1.0f);
} }
} }
static void ui_draw_vision_face(UIState *s) { static void ui_draw_vision_face(UIState *s) {
const int face_size = 96; const int radius = 96;
const int face_x = (s->viz_rect.x + face_size + (bdr_s * 2)); const int center_x = s->viz_rect.x + radius + (bdr_s * 2);
const int face_y = (s->viz_rect.bottom() - footer_h + ((footer_h - face_size) / 2)); const int center_y = s->viz_rect.bottom() - footer_h / 2;
ui_draw_circle_image(s, face_x, face_y, face_size, "driver_face", s->scene.dmonitoring_state.getIsActiveMode()); ui_draw_circle_image(s, center_x, center_y, radius, "driver_face", s->scene.dmonitoring_state.getIsActiveMode());
} }
static void ui_draw_driver_view(UIState *s) { static void ui_draw_driver_view(UIState *s) {
@ -266,10 +266,10 @@ static void ui_draw_driver_view(UIState *s) {
} }
// draw face icon // draw face icon
const int face_size = 85; const int face_radius = 85;
const int icon_x = is_rhd ? rect.right() - face_size - bdr_s * 2 : rect.x + face_size + bdr_s * 2; const int center_x = is_rhd ? rect.right() - face_radius - bdr_s * 2 : rect.x + face_radius + bdr_s * 2;
const int icon_y = rect.bottom() - face_size - bdr_s * 2.5; const int center_y = rect.bottom() - face_radius - bdr_s * 2.5;
ui_draw_circle_image(s, icon_x, icon_y, face_size, "driver_face", face_detected); ui_draw_circle_image(s, center_x, center_y, face_radius, "driver_face", face_detected);
} }
static void ui_draw_vision_header(UIState *s) { static void ui_draw_vision_header(UIState *s) {
@ -396,6 +396,11 @@ void ui_draw(UIState *s) {
if (draw_alerts && s->scene.alert_size != cereal::ControlsState::AlertSize::NONE) { if (draw_alerts && s->scene.alert_size != cereal::ControlsState::AlertSize::NONE) {
ui_draw_vision_alert(s); ui_draw_vision_alert(s);
} }
if (s->scene.driver_view && !s->vipc_client->connected) {
nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
ui_draw_text(s, s->viz_rect.centerX(), s->viz_rect.centerY(), "Please wait for camera to start", 40 * 2.5, COLOR_WHITE, "sans-bold");
}
nvgEndFrame(s->vg); nvgEndFrame(s->vg);
glDisable(GL_BLEND); glDisable(GL_BLEND);
} }

@ -134,12 +134,12 @@ void RequestRepeater::requestFinished(){
if (reply->error() == QNetworkReply::NoError) { if (reply->error() == QNetworkReply::NoError) {
// save to cache // save to cache
if (!cache_key.isEmpty()) { if (!cache_key.isEmpty()) {
Params().write_db_value(cache_key.toStdString(), response.toStdString()); Params().put(cache_key.toStdString(), response.toStdString());
} }
emit receivedResponse(response); emit receivedResponse(response);
} else { } else {
if (!cache_key.isEmpty()) { if (!cache_key.isEmpty()) {
Params().delete_db_value(cache_key.toStdString()); Params().remove(cache_key.toStdString());
} }
emit failedResponse(reply->errorString()); emit failedResponse(reply->errorString());
} }

@ -51,7 +51,8 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
void HomeWindow::mousePressEvent(QMouseEvent* e) { void HomeWindow::mousePressEvent(QMouseEvent* e) {
UIState* ui_state = &glWindow->ui_state; UIState* ui_state = &glWindow->ui_state;
if (GLWindow::ui_state.scene.driver_view) { if (GLWindow::ui_state.scene.driver_view) {
Params().write_db_value("IsDriverViewEnabled", "0", 1); Params().putBool("IsDriverViewEnabled", false);
GLWindow::ui_state.scene.driver_view = false;
return; return;
} }
@ -87,7 +88,7 @@ OffroadHome::OffroadHome(QWidget* parent) : QWidget(parent) {
QObject::connect(alert_notification, SIGNAL(released()), this, SLOT(openAlerts())); QObject::connect(alert_notification, SIGNAL(released()), this, SLOT(openAlerts()));
header_layout->addWidget(alert_notification, 0, Qt::AlignHCenter | Qt::AlignRight); header_layout->addWidget(alert_notification, 0, Qt::AlignHCenter | Qt::AlignRight);
std::string brand = Params().read_db_bool("Passive") ? "dashcam" : "openpilot"; std::string brand = Params().getBool("Passive") ? "dashcam" : "openpilot";
QLabel* version = new QLabel(QString::fromStdString(brand + " v" + Params().get("Version"))); QLabel* version = new QLabel(QString::fromStdString(brand + " v" + Params().get("Version")));
version->setStyleSheet(R"(font-size: 55px;)"); version->setStyleSheet(R"(font-size: 55px;)");
header_layout->addWidget(version, 0, Qt::AlignHCenter | Qt::AlignRight); header_layout->addWidget(version, 0, Qt::AlignHCenter | Qt::AlignRight);
@ -153,7 +154,7 @@ void OffroadHome::refresh() {
// update alerts // update alerts
alerts_widget->refresh(); alerts_widget->refresh();
if (!alerts_widget->alerts.size() && !alerts_widget->updateAvailable) { if (!alerts_widget->alertCount && !alerts_widget->updateAvailable) {
emit closeAlerts(); emit closeAlerts();
alert_notification->setVisible(false); alert_notification->setVisible(false);
return; return;
@ -162,7 +163,7 @@ void OffroadHome::refresh() {
if (alerts_widget->updateAvailable) { if (alerts_widget->updateAvailable) {
alert_notification->setText("UPDATE"); alert_notification->setText("UPDATE");
} else { } else {
int alerts = alerts_widget->alerts.size(); int alerts = alerts_widget->alertCount;
alert_notification->setText(QString::number(alerts) + " ALERT" + (alerts == 1 ? "" : "S")); alert_notification->setText(QString::number(alerts) + " ALERT" + (alerts == 1 ? "" : "S"));
} }
@ -222,20 +223,15 @@ static void handle_display_state(UIState* s, bool user_input) {
} }
} }
GLWindow::GLWindow(QWidget* parent) : QOpenGLWidget(parent) { GLWindow::GLWindow(QWidget* parent) : brightness_filter(BACKLIGHT_OFFROAD, BACKLIGHT_TS, BACKLIGHT_DT), QOpenGLWidget(parent) {
timer = new QTimer(this); timer = new QTimer(this);
QObject::connect(timer, SIGNAL(timeout()), this, SLOT(timerUpdate())); QObject::connect(timer, SIGNAL(timeout()), this, SLOT(timerUpdate()));
backlight_timer = new QTimer(this); backlight_timer = new QTimer(this);
QObject::connect(backlight_timer, SIGNAL(timeout()), this, SLOT(backlightUpdate())); QObject::connect(backlight_timer, SIGNAL(timeout()), this, SLOT(backlightUpdate()));
int result = read_param(&brightness_b, "BRIGHTNESS_B", true); brightness_b = Params().get<float>("BRIGHTNESS_B").value_or(10.0);
result += read_param(&brightness_m, "BRIGHTNESS_M", true); brightness_m = Params().get<float>("BRIGHTNESS_M").value_or(0.1);
if (result != 0) {
brightness_b = 10.0;
brightness_m = 0.1;
}
smooth_brightness = BACKLIGHT_OFFROAD;
} }
GLWindow::~GLWindow() { GLWindow::~GLWindow() {
@ -264,16 +260,12 @@ void GLWindow::initializeGL() {
void GLWindow::backlightUpdate() { void GLWindow::backlightUpdate() {
// Update brightness // Update brightness
float k = (BACKLIGHT_DT / BACKLIGHT_TS) / (1.0f + BACKLIGHT_DT / BACKLIGHT_TS);
float clipped_brightness = std::min(100.0f, (ui_state.scene.light_sensor * brightness_m) + brightness_b); float clipped_brightness = std::min(100.0f, (ui_state.scene.light_sensor * brightness_m) + brightness_b);
if (!ui_state.scene.started) { if (!ui_state.scene.started) {
clipped_brightness = BACKLIGHT_OFFROAD; clipped_brightness = BACKLIGHT_OFFROAD;
} }
smooth_brightness = clipped_brightness * k + smooth_brightness * (1.0f - k); int brightness = brightness_filter.update(clipped_brightness);
int brightness = smooth_brightness;
if (!ui_state.awake) { if (!ui_state.awake) {
brightness = 0; brightness = 0;
emit screen_shutoff(); emit screen_shutoff();
@ -321,7 +313,7 @@ void GLWindow::paintGL() {
double cur_draw_t = millis_since_boot(); double cur_draw_t = millis_since_boot();
double dt = cur_draw_t - prev_draw_t; double dt = cur_draw_t - prev_draw_t;
if (dt > 66 && onroad){ if (dt > 66 && onroad && !ui_state.scene.driver_view) {
// warn on sub 15fps // warn on sub 15fps
LOGW("slow frame(%llu) time: %.2f", ui_state.sm->frame, dt); LOGW("slow frame(%llu) time: %.2f", ui_state.sm->frame, dt);
} }

@ -9,8 +9,9 @@
#include <QTimer> #include <QTimer>
#include <QWidget> #include <QWidget>
#include "qt_sound.hpp" #include "sound.hpp"
#include "ui/ui.hpp" #include "ui/ui.hpp"
#include "common/util.h"
#include "widgets/offroad_alerts.hpp" #include "widgets/offroad_alerts.hpp"
// container window for onroad NVG UI // container window for onroad NVG UI
@ -38,7 +39,7 @@ private:
QTimer* timer; QTimer* timer;
QTimer* backlight_timer; QTimer* backlight_timer;
QtSound sound; Sound sound;
bool onroad = true; bool onroad = true;
double prev_draw_t = 0; double prev_draw_t = 0;
@ -46,8 +47,8 @@ private:
// TODO: make a nice abstraction to handle embedded device stuff // TODO: make a nice abstraction to handle embedded device stuff
float brightness_b = 0; float brightness_b = 0;
float brightness_m = 0; float brightness_m = 0;
float smooth_brightness = 0;
float last_brightness = 0; float last_brightness = 0;
FirstOrderFilter brightness_filter;
public slots: public slots:
void timerUpdate(); void timerUpdate();

@ -2,14 +2,8 @@
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QLabel> #include <QLabel>
#include <QPixmap> #include <QPixmap>
#include <QPushButton>
#include <QRandomGenerator>
#include <algorithm>
#include "common/params.h"
#include "hardware/hw.h"
#include "networking.hpp" #include "networking.hpp"
#include "util.h"
void clearLayout(QLayout* layout) { void clearLayout(QLayout* layout) {
while (QLayoutItem* item = layout->takeAt(0)) { while (QLayoutItem* item = layout->takeAt(0)) {
@ -23,12 +17,6 @@ void clearLayout(QLayout* layout) {
} }
} }
QWidget* layoutToWidget(QLayout* l, QWidget* parent){
QWidget* q = new QWidget(parent);
q->setLayout(l);
return q;
}
// Networking functions // Networking functions
Networking::Networking(QWidget* parent, bool show_advanced) : QWidget(parent), show_advanced(show_advanced){ Networking::Networking(QWidget* parent, bool show_advanced) : QWidget(parent), show_advanced(show_advanced){
@ -61,9 +49,9 @@ void Networking::attemptInitialization(){
if (show_advanced) { if (show_advanced) {
QPushButton* advancedSettings = new QPushButton("Advanced"); QPushButton* advancedSettings = new QPushButton("Advanced");
advancedSettings->setStyleSheet(R"(margin-right: 30px)"); advancedSettings->setStyleSheet("margin-right: 30px;");
advancedSettings->setFixedSize(350, 100); advancedSettings->setFixedSize(350, 100);
connect(advancedSettings, &QPushButton::released, [=](){s->setCurrentWidget(an);}); connect(advancedSettings, &QPushButton::released, [=](){ s->setCurrentWidget(an); });
vlayout->addSpacing(10); vlayout->addSpacing(10);
vlayout->addWidget(advancedSettings, 0, Qt::AlignRight); vlayout->addWidget(advancedSettings, 0, Qt::AlignRight);
vlayout->addSpacing(10); vlayout->addSpacing(10);
@ -73,7 +61,8 @@ void Networking::attemptInitialization(){
connect(wifiWidget, SIGNAL(connectToNetwork(Network)), this, SLOT(connectToNetwork(Network))); connect(wifiWidget, SIGNAL(connectToNetwork(Network)), this, SLOT(connectToNetwork(Network)));
vlayout->addWidget(wifiWidget, 1); vlayout->addWidget(wifiWidget, 1);
wifiScreen = layoutToWidget(vlayout, this); QWidget* wifiScreen = new QWidget(this);
wifiScreen->setLayout(vlayout);
s->addWidget(wifiScreen); s->addWidget(wifiScreen);
an = new AdvancedNetworking(this, wifi); an = new AdvancedNetworking(this, wifi);
@ -138,6 +127,7 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
QVBoxLayout* vlayout = new QVBoxLayout; QVBoxLayout* vlayout = new QVBoxLayout;
vlayout->setMargin(40); vlayout->setMargin(40);
vlayout->setSpacing(20);
// Back button // Back button
QPushButton* back = new QPushButton("Back"); QPushButton* back = new QPushButton("Back");
@ -146,41 +136,24 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
vlayout->addWidget(back, 0, Qt::AlignLeft); vlayout->addWidget(back, 0, Qt::AlignLeft);
// Enable tethering layout // Enable tethering layout
QHBoxLayout* tetheringToggleLayout = new QHBoxLayout; ToggleControl *tetheringToggle = new ToggleControl("Enable Tethering", "", "", wifi->tetheringEnabled());
tetheringToggleLayout->addWidget(new QLabel("Enable tethering")); vlayout->addWidget(tetheringToggle);
Toggle* toggle_switch = new Toggle; QObject::connect(tetheringToggle, SIGNAL(toggleFlipped(bool)), this, SLOT(toggleTethering(bool)));
toggle_switch->setFixedSize(150, 100);
tetheringToggleLayout->addWidget(toggle_switch);
tetheringToggleLayout->addSpacing(40);
if (wifi->tetheringEnabled()) {
toggle_switch->togglePosition();
}
QObject::connect(toggle_switch, SIGNAL(stateChanged(bool)), this, SLOT(toggleTethering(bool)));
vlayout->addWidget(layoutToWidget(tetheringToggleLayout, this), 0);
vlayout->addWidget(horizontal_line(), 0); vlayout->addWidget(horizontal_line(), 0);
// Change tethering password // Change tethering password
QHBoxLayout *tetheringPassword = new QHBoxLayout; editPasswordButton = new ButtonControl("Tethering Password", "EDIT", "", [=](){
tetheringPassword->addWidget(new QLabel("Edit tethering password"), 1);
editPasswordButton = new QPushButton("EDIT");
editPasswordButton->setFixedWidth(500);
connect(editPasswordButton, &QPushButton::released, [=](){
QString pass = InputDialog::getText("Enter new tethering password", 8); QString pass = InputDialog::getText("Enter new tethering password", 8);
if (pass.size()) { if (pass.size()) {
wifi->changeTetheringPassword(pass); wifi->changeTetheringPassword(pass);
} }
}); });
tetheringPassword->addWidget(editPasswordButton, 1, Qt::AlignRight); vlayout->addWidget(editPasswordButton, 0);
vlayout->addWidget(layoutToWidget(tetheringPassword, this), 0);
vlayout->addWidget(horizontal_line(), 0); vlayout->addWidget(horizontal_line(), 0);
// IP adress // IP address
QHBoxLayout* IPlayout = new QHBoxLayout; ipLabel = new LabelControl("IP Address", wifi->ipv4_address);
IPlayout->addWidget(new QLabel("IP address"), 0); vlayout->addWidget(ipLabel, 0);
ipLabel = new QLabel(wifi->ipv4_address);
ipLabel->setStyleSheet("color: #aaaaaa");
IPlayout->addWidget(ipLabel, 0, Qt::AlignRight);
vlayout->addWidget(layoutToWidget(IPlayout, this), 0);
vlayout->addWidget(horizontal_line(), 0); vlayout->addWidget(horizontal_line(), 0);
// SSH keys // SSH keys
@ -188,6 +161,7 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
vlayout->addWidget(horizontal_line(), 0); vlayout->addWidget(horizontal_line(), 0);
vlayout->addWidget(new SshControl()); vlayout->addWidget(new SshControl());
vlayout->addStretch(1);
setLayout(vlayout); setLayout(vlayout);
} }
@ -218,7 +192,6 @@ WifiUI::WifiUI(QWidget *parent, WifiManager* wifi) : QWidget(parent), wifi(wifi)
vlayout->setSpacing(25); vlayout->setSpacing(25);
setLayout(vlayout); setLayout(vlayout);
page = 0;
} }
void WifiUI::refresh() { void WifiUI::refresh() {
@ -229,70 +202,40 @@ void WifiUI::refresh() {
connectButtons = new QButtonGroup(this); // TODO check if this is a leak connectButtons = new QButtonGroup(this); // TODO check if this is a leak
QObject::connect(connectButtons, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(handleButton(QAbstractButton*))); QObject::connect(connectButtons, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(handleButton(QAbstractButton*)));
int networks_per_page = height() / 180;
int i = 0; int i = 0;
int pageCount = (wifi->seen_networks.size() - 1) / networks_per_page;
page = std::max(0, std::min(page, pageCount));
for (Network &network : wifi->seen_networks) { for (Network &network : wifi->seen_networks) {
QHBoxLayout *hlayout = new QHBoxLayout; QHBoxLayout *hlayout = new QHBoxLayout;
if (page * networks_per_page <= i && i < (page + 1) * networks_per_page) { hlayout->addSpacing(50);
// SSID
hlayout->addSpacing(50); QLabel *ssid_label = new QLabel(QString::fromUtf8(network.ssid));
QString ssid = QString::fromUtf8(network.ssid); ssid_label->setStyleSheet("font-size: 55px;");
if(ssid.length() > 20){ hlayout->addWidget(ssid_label, 1, Qt::AlignLeft);
ssid = ssid.left(20 - 3) + "";
} // TODO: don't use images for this
// strength indicator
QLabel *ssid_label = new QLabel(ssid); unsigned int strength_scale = network.strength / 17;
ssid_label->setStyleSheet(R"( QPixmap pix("../assets/images/network_" + QString::number(strength_scale) + ".png");
font-size: 55px; QLabel *icon = new QLabel();
)"); icon->setPixmap(pix.scaledToWidth(100, Qt::SmoothTransformation));
ssid_label->setFixedWidth(this->width()*0.5); icon->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
hlayout->addWidget(ssid_label, 0, Qt::AlignLeft); hlayout->addWidget(icon, 0, Qt::AlignRight);
// TODO: don't use images for this // connect button
// strength indicator QPushButton* btn = new QPushButton(network.security_type == SecurityType::UNSUPPORTED ? "Unsupported" : (network.connected == ConnectedType::CONNECTED ? "Connected" : (network.connected == ConnectedType::CONNECTING ? "Connecting" : "Connect")));
unsigned int strength_scale = network.strength / 17; btn->setDisabled(network.connected == ConnectedType::CONNECTED || network.connected == ConnectedType::CONNECTING || network.security_type == SecurityType::UNSUPPORTED);
QPixmap pix("../assets/images/network_" + QString::number(strength_scale) + ".png"); btn->setFixedWidth(350);
QLabel *icon = new QLabel(); hlayout->addWidget(btn, 0, Qt::AlignRight);
icon->setPixmap(pix.scaledToWidth(100, Qt::SmoothTransformation));
icon->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); connectButtons->addButton(btn, i);
hlayout->addWidget(icon, 0, Qt::AlignRight);
vlayout->addLayout(hlayout, 1);
// connect button // Don't add the last horizontal line
QPushButton* btn = new QPushButton(network.security_type == SecurityType::UNSUPPORTED ? "Unsupported" : (network.connected == ConnectedType::CONNECTED ? "Connected" : (network.connected == ConnectedType::CONNECTING ? "Connecting" : "Connect"))); if (i+1 < wifi->seen_networks.size()) {
btn->setDisabled(network.connected == ConnectedType::CONNECTED || network.connected == ConnectedType::CONNECTING || network.security_type == SecurityType::UNSUPPORTED); vlayout->addWidget(horizontal_line(), 0);
btn->setFixedWidth(350);
hlayout->addWidget(btn, 0, Qt::AlignRight);
connectButtons->addButton(btn, i);
vlayout->addLayout(hlayout, 1);
// Don't add the last horizontal line
if (page * networks_per_page <= i+1 && i+1 < (page + 1) * networks_per_page && i+1 < wifi->seen_networks.size()) {
vlayout->addWidget(horizontal_line(), 0);
}
} }
i++; i++;
} }
vlayout->addStretch(3); vlayout->addStretch(3);
// Setup buttons for pagination
QHBoxLayout *prev_next_buttons = new QHBoxLayout;
QPushButton* prev = new QPushButton("Previous");
prev->setEnabled(page);
QObject::connect(prev, SIGNAL(released()), this, SLOT(prevPage()));
prev_next_buttons->addWidget(prev);
QPushButton* next = new QPushButton("Next");
next->setEnabled(wifi->seen_networks.size() > (page + 1) * networks_per_page);
QObject::connect(next, SIGNAL(released()), this, SLOT(nextPage()));
prev_next_buttons->addWidget(next);
vlayout->addLayout(prev_next_buttons, 2);
} }
void WifiUI::handleButton(QAbstractButton* button) { void WifiUI::handleButton(QAbstractButton* button) {
@ -300,13 +243,3 @@ void WifiUI::handleButton(QAbstractButton* button) {
Network n = wifi->seen_networks[connectButtons->id(btn)]; Network n = wifi->seen_networks[connectButtons->id(btn)];
emit connectToNetwork(n); emit connectToNetwork(n);
} }
void WifiUI::prevPage() {
page--;
refresh();
}
void WifiUI::nextPage() {
page++;
refresh();
}

@ -15,7 +15,6 @@ class WifiUI : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
int page;
explicit WifiUI(QWidget *parent = 0, WifiManager* wifi = 0); explicit WifiUI(QWidget *parent = 0, WifiManager* wifi = 0);
private: private:
@ -29,10 +28,8 @@ signals:
void connectToNetwork(Network n); void connectToNetwork(Network n);
public slots: public slots:
void handleButton(QAbstractButton* m_button);
void refresh(); void refresh();
void prevPage(); void handleButton(QAbstractButton* m_button);
void nextPage();
}; };
class AdvancedNetworking : public QWidget { class AdvancedNetworking : public QWidget {
@ -41,8 +38,8 @@ public:
explicit AdvancedNetworking(QWidget* parent = 0, WifiManager* wifi = 0); explicit AdvancedNetworking(QWidget* parent = 0, WifiManager* wifi = 0);
private: private:
QLabel* ipLabel; LabelControl* ipLabel;
QPushButton* editPasswordButton; ButtonControl* editPasswordButton;
WifiManager* wifi = nullptr; WifiManager* wifi = nullptr;
signals: signals:
@ -60,7 +57,7 @@ public:
explicit Networking(QWidget* parent = 0, bool show_advanced = true); explicit Networking(QWidget* parent = 0, bool show_advanced = true);
private: private:
QStackedLayout* s = nullptr; // nm_warning, keyboard, wifiScreen, advanced QStackedLayout* s = nullptr; // nm_warning, wifiScreen, advanced
QWidget* wifiScreen = nullptr; QWidget* wifiScreen = nullptr;
AdvancedNetworking* an = nullptr; AdvancedNetworking* an = nullptr;
bool ui_setup_complete = false; bool ui_setup_complete = false;

@ -12,14 +12,15 @@
void TrainingGuide::mouseReleaseEvent(QMouseEvent *e) { void TrainingGuide::mouseReleaseEvent(QMouseEvent *e) {
//qDebug() << e->x() << ", " << e->y(); QPoint touch = QPoint(e->x(), e->y()) - imageCorner;
//qDebug() << touch.x() << ", " << touch.y();
// Check for restart // Check for restart
if (currentIndex == (boundingBox.size() - 1) && 200 <= e->x() && e->x() <= 920 && if (currentIndex == (boundingBox.size() - 1) && 200 <= touch.x() && touch.x() <= 920 &&
760 <= e->y() && e->y() <= 960) { 760 <= touch.y() && touch.y() <= 960) {
currentIndex = 0; currentIndex = 0;
} else if (boundingBox[currentIndex][0] <= e->x() && e->x() <= boundingBox[currentIndex][1] && } else if (boundingBox[currentIndex][0] <= touch.x() && touch.x() <= boundingBox[currentIndex][1] &&
boundingBox[currentIndex][2] <= e->y() && e->y() <= boundingBox[currentIndex][3]) { boundingBox[currentIndex][2] <= touch.y() && touch.y() <= boundingBox[currentIndex][3]) {
currentIndex += 1; currentIndex += 1;
} }
@ -39,17 +40,17 @@ void TrainingGuide::showEvent(QShowEvent *event) {
void TrainingGuide::paintEvent(QPaintEvent *event) { void TrainingGuide::paintEvent(QPaintEvent *event) {
QPainter painter(this); QPainter painter(this);
QRect devRect(0, 0, painter.device()->width(), painter.device()->height()); QRect bg(0, 0, painter.device()->width(), painter.device()->height());
QBrush bgBrush("#072339"); QBrush bgBrush("#000000");
painter.fillRect(devRect, bgBrush); painter.fillRect(bg, bgBrush);
QRect rect(image.rect()); QRect rect(image.rect());
rect.moveCenter(devRect.center()); rect.moveCenter(bg.center());
painter.drawImage(rect.topLeft(), image); painter.drawImage(rect.topLeft(), image);
imageCorner = rect.topLeft();
} }
TermsPage::TermsPage(QWidget *parent) : QFrame(parent){ TermsPage::TermsPage(QWidget *parent) : QFrame(parent){
QVBoxLayout *main_layout = new QVBoxLayout; QVBoxLayout *main_layout = new QVBoxLayout;
main_layout->setMargin(40); main_layout->setMargin(40);
main_layout->setSpacing(40); main_layout->setSpacing(40);
@ -126,13 +127,13 @@ OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) {
addWidget(terms); addWidget(terms);
connect(terms, &TermsPage::acceptedTerms, [=](){ connect(terms, &TermsPage::acceptedTerms, [=](){
Params().write_db_value("HasAcceptedTerms", current_terms_version); Params().put("HasAcceptedTerms", current_terms_version);
updateActiveScreen(); updateActiveScreen();
}); });
TrainingGuide* tr = new TrainingGuide(this); TrainingGuide* tr = new TrainingGuide(this);
connect(tr, &TrainingGuide::completedTraining, [=](){ connect(tr, &TrainingGuide::completedTraining, [=](){
Params().write_db_value("CompletedTrainingVersion", current_training_version); Params().put("CompletedTrainingVersion", current_training_version);
updateActiveScreen(); updateActiveScreen();
}); });
addWidget(tr); addWidget(tr);

@ -22,6 +22,7 @@ protected:
private: private:
QImage image; QImage image;
QPoint imageCorner;
int currentIndex = 0; int currentIndex = 0;
// Bounding boxes for the a given training guide step // Bounding boxes for the a given training guide step

@ -15,12 +15,11 @@
#include "common/params.h" #include "common/params.h"
#include "common/util.h" #include "common/util.h"
#include "selfdrive/hardware/hw.h" #include "selfdrive/hardware/hw.h"
#include "home.hpp"
QWidget * toggles_panel() { QWidget * toggles_panel() {
QVBoxLayout *toggles_list = new QVBoxLayout(); QVBoxLayout *toggles_list = new QVBoxLayout();
toggles_list->setMargin(50);
toggles_list->addWidget(new ParamControl("OpenpilotEnabledToggle", toggles_list->addWidget(new ParamControl("OpenpilotEnabledToggle",
"Enable openpilot", "Enable openpilot",
"Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off.", "Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off.",
@ -62,7 +61,7 @@ QWidget * toggles_panel() {
"In this mode openpilot will ignore lanelines and just drive how it thinks a human would.", "In this mode openpilot will ignore lanelines and just drive how it thinks a human would.",
"../assets/offroad/icon_road.png")); "../assets/offroad/icon_road.png"));
bool record_lock = Params().read_db_bool("RecordFrontLock"); bool record_lock = Params().getBool("RecordFrontLock");
record_toggle->setEnabled(!record_lock); record_toggle->setEnabled(!record_lock);
QWidget *widget = new QWidget; QWidget *widget = new QWidget;
@ -72,63 +71,53 @@ QWidget * toggles_panel() {
DevicePanel::DevicePanel(QWidget* parent) : QWidget(parent) { DevicePanel::DevicePanel(QWidget* parent) : QWidget(parent) {
QVBoxLayout *device_layout = new QVBoxLayout; QVBoxLayout *device_layout = new QVBoxLayout;
device_layout->setMargin(100);
Params params = Params(); Params params = Params();
std::vector<std::pair<std::string, std::string>> labels = {
{"Dongle ID", params.get("DongleId", false)},
};
// get serial number
//std::string cmdline = util::read_file("/proc/cmdline");
//auto delim = cmdline.find("serialno=");
//if (delim != std::string::npos) {
// labels.push_back({"Serial", cmdline.substr(delim, cmdline.find(" ", delim))});
//}
for (auto &l : labels) {
device_layout->addWidget(new LabelControl(QString::fromStdString(l.first),
QString::fromStdString(l.second)));
}
QString dongle = QString::fromStdString(params.get("DongleId", false));
device_layout->addWidget(new LabelControl("Dongle ID", dongle));
device_layout->addWidget(horizontal_line()); device_layout->addWidget(horizontal_line());
device_layout->addWidget(new ButtonControl("Driver Camera", "PREVIEW", QString serial = QString::fromStdString(params.get("HardwareSerial", false));
"Preview the driver facing camera to help optimize device mounting position for best driver monitoring experience. (vehicle must be off)", device_layout->addWidget(new LabelControl("Serial", serial));
[=]() { Params().write_db_value("IsDriverViewEnabled", "1", 1); }));
device_layout->addWidget(horizontal_line()); // offroad-only buttons
QList<ButtonControl*> offroad_btns;
// TODO: show current calibration values offroad_btns.append(new ButtonControl("Driver Camera", "PREVIEW",
device_layout->addWidget(new ButtonControl("Reset Calibration", "RESET", "Preview the driver facing camera to help optimize device mounting position for best driver monitoring experience. (vehicle must be off)",
"openpilot requires the device to be mounted within 4° left or right and within 5° up or down. openpilot is continuously calibrating, resetting is rarely required.", [=]() {
[=]() { Params().putBool("IsDriverViewEnabled", true);
if (ConfirmationDialog::confirm("Are you sure you want to reset calibration?")) { GLWindow::ui_state.scene.driver_view = true; }
Params().delete_db_value("CalibrationParams"); ));
}
}));
device_layout->addWidget(horizontal_line()); offroad_btns.append(new ButtonControl("Reset Calibration", "RESET",
"openpilot requires the device to be mounted within 4° left or right and within 5° up or down. openpilot is continuously calibrating, resetting is rarely required.", [=]() {
if (ConfirmationDialog::confirm("Are you sure you want to reset calibration?")) {
Params().remove("CalibrationParams");
}
}));
device_layout->addWidget(new ButtonControl("Review Training Guide", "REVIEW", offroad_btns.append(new ButtonControl("Review Training Guide", "REVIEW",
"Review the rules, features, and limitations of openpilot", "Review the rules, features, and limitations of openpilot", [=]() {
[=]() { if (ConfirmationDialog::confirm("Are you sure you want to review the training guide?")) {
if (ConfirmationDialog::confirm("Are you sure you want to review the training guide?")) { Params().remove("CompletedTrainingVersion");
Params().delete_db_value("CompletedTrainingVersion"); emit reviewTrainingGuide();
emit reviewTrainingGuide(); }
} }));
}));
device_layout->addWidget(horizontal_line()); QString brand = params.getBool("Passive") ? "dashcam" : "openpilot";
offroad_btns.append(new ButtonControl("Uninstall " + brand, "UNINSTALL", "", [=]() {
if (ConfirmationDialog::confirm("Are you sure you want to uninstall?")) {
Params().putBool("DoUninstall", true);
}
}));
QString brand = params.read_db_bool("Passive") ? "dashcam" : "openpilot"; for(auto &btn : offroad_btns){
device_layout->addWidget(new ButtonControl("Uninstall " + brand, "UNINSTALL", device_layout->addWidget(horizontal_line());
"", QObject::connect(parent, SIGNAL(offroadTransition(bool)), btn, SLOT(setEnabled(bool)));
[=]() { device_layout->addWidget(btn);
if (ConfirmationDialog::confirm("Are you sure you want to uninstall?")) { }
Params().write_db_value("DoUninstall", "1");
}
}));
// power buttons // power buttons
QHBoxLayout *power_layout = new QHBoxLayout(); QHBoxLayout *power_layout = new QHBoxLayout();
@ -166,16 +155,15 @@ DevicePanel::DevicePanel(QWidget* parent) : QWidget(parent) {
DeveloperPanel::DeveloperPanel(QWidget* parent) : QFrame(parent) { DeveloperPanel::DeveloperPanel(QWidget* parent) : QFrame(parent) {
QVBoxLayout *main_layout = new QVBoxLayout(this); QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout->setMargin(100);
setLayout(main_layout); setLayout(main_layout);
setStyleSheet(R"(QLabel {font-size: 50px;})"); setStyleSheet(R"(QLabel {font-size: 50px;})");
} }
void DeveloperPanel::showEvent(QShowEvent *event) { void DeveloperPanel::showEvent(QShowEvent *event) {
Params params = Params(); Params params = Params();
std::string brand = params.read_db_bool("Passive") ? "dashcam" : "openpilot"; std::string brand = params.getBool("Passive") ? "dashcam" : "openpilot";
QList<QPair<QString, std::string>> dev_params = { QList<QPair<QString, std::string>> dev_params = {
{"Version", brand + " v" + params.get("Version", false)}, {"Version", brand + " v" + params.get("Version", false).substr(0, 14)},
{"Git Branch", params.get("GitBranch", false)}, {"Git Branch", params.get("GitBranch", false)},
{"Git Commit", params.get("GitCommit", false).substr(0, 10)}, {"Git Commit", params.get("GitCommit", false).substr(0, 10)},
{"Panda Firmware", params.get("PandaFirmwareHex", false)}, {"Panda Firmware", params.get("PandaFirmwareHex", false)},
@ -200,25 +188,15 @@ void DeveloperPanel::showEvent(QShowEvent *event) {
QWidget * network_panel(QWidget * parent) { QWidget * network_panel(QWidget * parent) {
#ifdef QCOM #ifdef QCOM
QVBoxLayout *layout = new QVBoxLayout; QVBoxLayout *layout = new QVBoxLayout;
layout->setMargin(100);
layout->setSpacing(30); layout->setSpacing(30);
// simple wifi + tethering buttons // wifi + tethering buttons
const char* launch_wifi = "am start -n com.android.settings/.wifi.WifiPickerActivity \
-a android.net.wifi.PICK_WIFI_NETWORK \
--ez extra_prefs_show_button_bar true \
--es extra_prefs_set_next_text ''";
layout->addWidget(new ButtonControl("WiFi Settings", "OPEN", "", layout->addWidget(new ButtonControl("WiFi Settings", "OPEN", "",
[=]() { std::system(launch_wifi); })); [=]() { HardwareEon::launch_wifi(); }));
layout->addWidget(horizontal_line()); layout->addWidget(horizontal_line());
const char* launch_tethering = "am start -n com.android.settings/.TetherSettings \
--ez extra_prefs_show_button_bar true \
--es extra_prefs_set_next_text ''";
layout->addWidget(new ButtonControl("Tethering Settings", "OPEN", "", layout->addWidget(new ButtonControl("Tethering Settings", "OPEN", "",
[=]() { std::system(launch_tethering); })); [=]() { HardwareEon::launch_tethering(); }));
layout->addWidget(horizontal_line()); layout->addWidget(horizontal_line());
// SSH key management // SSH key management
@ -241,6 +219,10 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
QVBoxLayout *sidebar_layout = new QVBoxLayout(); QVBoxLayout *sidebar_layout = new QVBoxLayout();
sidebar_layout->setMargin(0); sidebar_layout->setMargin(0);
panel_widget = new QStackedWidget(); panel_widget = new QStackedWidget();
panel_widget->setStyleSheet(R"(
border-radius: 30px;
background-color: #292929;
)");
// close button // close button
QPushButton *close_btn = new QPushButton("X"); QPushButton *close_btn = new QPushButton("X");
@ -273,7 +255,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
QPushButton *btn = new QPushButton(name); QPushButton *btn = new QPushButton(name);
btn->setCheckable(true); btn->setCheckable(true);
btn->setStyleSheet(R"( btn->setStyleSheet(R"(
* { QPushButton {
color: grey; color: grey;
border: none; border: none;
background: none; background: none;
@ -290,8 +272,25 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
nav_btns->addButton(btn); nav_btns->addButton(btn);
sidebar_layout->addWidget(btn, 0, Qt::AlignRight); sidebar_layout->addWidget(btn, 0, Qt::AlignRight);
panel_widget->addWidget(panel); panel->setContentsMargins(50, 25, 50, 25);
QObject::connect(btn, &QPushButton::released, [=, w = panel]() { QScrollArea *panel_frame = new QScrollArea;
panel_frame->setWidget(panel);
panel_frame->setWidgetResizable(true);
panel_frame->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
panel_frame->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
panel_frame->setStyleSheet("background-color:transparent;");
QScroller *scroller = QScroller::scroller(panel_frame->viewport());
auto sp = scroller->scrollerProperties();
sp.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QVariant::fromValue<QScrollerProperties::OvershootPolicy>(QScrollerProperties::OvershootAlwaysOff));
scroller->grabGesture(panel_frame->viewport(), QScroller::LeftMouseButtonGesture);
scroller->setScrollerProperties(sp);
panel_widget->addWidget(panel_frame);
QObject::connect(btn, &QPushButton::released, [=, w = panel_frame]() {
panel_widget->setCurrentWidget(w); panel_widget->setCurrentWidget(w);
}); });
} }
@ -305,25 +304,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
sidebar_widget->setLayout(sidebar_layout); sidebar_widget->setLayout(sidebar_layout);
sidebar_widget->setFixedWidth(500); sidebar_widget->setFixedWidth(500);
settings_layout->addWidget(sidebar_widget); settings_layout->addWidget(sidebar_widget);
settings_layout->addWidget(panel_widget);
panel_frame = new QScrollArea;
panel_frame->setWidget(panel_widget);
panel_frame->setWidgetResizable(true);
panel_frame->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
panel_frame->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
panel_frame->setStyleSheet(R"(
border-radius: 30px;
background-color: #292929;
)");
settings_layout->addWidget(panel_frame);
// setup panel scrolling
QScroller *scroller = QScroller::scroller(panel_frame);
auto sp = scroller->scrollerProperties();
sp.setScrollMetric(QScrollerProperties::FrameRate, QVariant::fromValue<QScrollerProperties::FrameRates>(QScrollerProperties::Fps30));
sp.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QVariant::fromValue<QScrollerProperties::OvershootPolicy>(QScrollerProperties::OvershootAlwaysOff));
scroller->setScrollerProperties(sp);
scroller->grabGesture(panel_frame->viewport(), QScroller::LeftMouseButtonGesture);
setLayout(settings_layout); setLayout(settings_layout);
setStyleSheet(R"( setStyleSheet(R"(

@ -39,6 +39,7 @@ public:
signals: signals:
void closeSettings(); void closeSettings();
void offroadTransition(bool offroad);
void reviewTrainingGuide(); void reviewTrainingGuide();
private: private:
@ -46,5 +47,4 @@ private:
QWidget *sidebar_widget; QWidget *sidebar_widget;
QButtonGroup *nav_btns; QButtonGroup *nav_btns;
QStackedWidget *panel_widget; QStackedWidget *panel_widget;
QScrollArea *panel_frame;
}; };

@ -1,16 +0,0 @@
#pragma once
#include <QSoundEffect>
#include "sound.hpp"
class QtSound : public Sound {
public:
QtSound();
bool play(AudibleAlert alert);
void stop();
void setVolume(int volume);
float volume = 0;
private:
std::map<AudibleAlert, QSoundEffect> sounds;
};

@ -1,22 +1,21 @@
#include <QUrl> #include <QUrl>
#include "qt_sound.hpp" #include "sound.hpp"
QtSound::QtSound() { Sound::Sound() {
for (auto &kv : sound_map) { for (auto &kv : sound_map) {
auto path = QUrl::fromLocalFile(kv.second.first); auto path = QUrl::fromLocalFile(kv.second.first);
sounds[kv.first].setSource(path); sounds[kv.first].setSource(path);
} }
} }
bool QtSound::play(AudibleAlert alert) { void Sound::play(AudibleAlert alert) {
int loops = sound_map[alert].second> - 1 ? sound_map[alert].second : QSoundEffect::Infinite; int loops = sound_map[alert].second> - 1 ? sound_map[alert].second : QSoundEffect::Infinite;
sounds[alert].setLoopCount(loops); sounds[alert].setLoopCount(loops);
sounds[alert].setVolume(volume); sounds[alert].setVolume(volume);
sounds[alert].play(); sounds[alert].play();
return true;
} }
void QtSound::stop() { void Sound::stop() {
for (auto &kv : sounds) { for (auto &kv : sounds) {
// Only stop repeating sounds // Only stop repeating sounds
if (sound_map[kv.first].second != 0) { if (sound_map[kv.first].second != 0) {
@ -24,7 +23,3 @@ void QtSound::stop() {
} }
} }
} }
void QtSound::setVolume(int volume) {
// TODO: implement this
}

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <map> #include <map>
#include <QSoundEffect>
#include "cereal/gen/cpp/log.capnp.h" #include "cereal/gen/cpp/log.capnp.h"
typedef cereal::CarControl::HUDControl::AudibleAlert AudibleAlert; typedef cereal::CarControl::HUDControl::AudibleAlert AudibleAlert;
@ -18,8 +20,11 @@ static std::map<AudibleAlert, std::pair<const char *, int>> sound_map {
class Sound { class Sound {
public: public:
virtual ~Sound() {} Sound();
virtual bool play(AudibleAlert alert) = 0; void play(AudibleAlert alert);
virtual void stop() = 0; void stop();
virtual void setVolume(int volume) = 0; float volume = 0;
private:
std::map<AudibleAlert, QSoundEffect> sounds;
}; };

@ -20,7 +20,7 @@ AbstractControl::AbstractControl(const QString &title, const QString &desc, cons
hlayout = new QHBoxLayout; hlayout = new QHBoxLayout;
hlayout->setMargin(0); hlayout->setMargin(0);
hlayout->setSpacing(50); hlayout->setSpacing(20);
// left icon // left icon
if (!icon.isEmpty()) { if (!icon.isEmpty()) {
@ -33,7 +33,7 @@ AbstractControl::AbstractControl(const QString &title, const QString &desc, cons
// title // title
title_label = new QPushButton(title); title_label = new QPushButton(title);
title_label->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left; background: none;"); title_label->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left;");
hlayout->addWidget(title_label); hlayout->addWidget(title_label);
vlayout->addLayout(hlayout); vlayout->addLayout(hlayout);
@ -53,4 +53,5 @@ AbstractControl::AbstractControl(const QString &title, const QString &desc, cons
} }
setLayout(vlayout); setLayout(vlayout);
setStyleSheet("background-color: transparent;");
} }

@ -53,12 +53,17 @@ public:
ButtonControl(const QString &title, const QString &text, const QString &desc, Functor functor, const QString &icon = "", QWidget *parent = nullptr) : AbstractControl(title, desc, icon, parent) { ButtonControl(const QString &title, const QString &text, const QString &desc, Functor functor, const QString &icon = "", QWidget *parent = nullptr) : AbstractControl(title, desc, icon, parent) {
btn.setText(text); btn.setText(text);
btn.setStyleSheet(R"( btn.setStyleSheet(R"(
padding: 0; QPushButton {
border-radius: 50px; padding: 0;
font-size: 35px; border-radius: 50px;
font-weight: 500; font-size: 35px;
color: #E4E4E4; font-weight: 500;
background-color: #393939; color: #E4E4E4;
background-color: #393939;
}
QPushButton:disabled {
color: #33E4E4E4;
}
)"); )");
btn.setFixedSize(250, 100); btn.setFixedSize(250, 100);
QObject::connect(&btn, &QPushButton::released, functor); QObject::connect(&btn, &QPushButton::released, functor);
@ -66,6 +71,11 @@ public:
} }
void setText(const QString &text) { btn.setText(text); } void setText(const QString &text) { btn.setText(text); }
public slots:
void setEnabled(bool enabled) {
btn.setEnabled(enabled);
};
private: private:
QPushButton btn; QPushButton btn;
}; };
@ -99,12 +109,11 @@ class ParamControl : public ToggleControl {
public: public:
ParamControl(const QString &param, const QString &title, const QString &desc, const QString &icon, QWidget *parent = nullptr) : ToggleControl(title, desc, icon, parent) { ParamControl(const QString &param, const QString &title, const QString &desc, const QString &icon, QWidget *parent = nullptr) : ToggleControl(title, desc, icon, parent) {
// set initial state from param // set initial state from param
if (Params().read_db_bool(param.toStdString().c_str())) { if (Params().getBool(param.toStdString().c_str())) {
toggle.togglePosition(); toggle.togglePosition();
} }
QObject::connect(this, &ToggleControl::toggleFlipped, [=](int state) { QObject::connect(this, &ToggleControl::toggleFlipped, [=](int state) {
char value = state ? '1' : '0'; Params().putBool(param.toStdString().c_str(), (bool)state);
Params().write_db_value(param.toStdString().c_str(), &value, 1);
}); });
} }
}; };

@ -36,7 +36,7 @@ void DriveStats::parseResponse(QString response) {
labels.hours->setText(QString::number((int)(obj["minutes"].toDouble() / 60))); labels.hours->setText(QString::number((int)(obj["minutes"].toDouble() / 60)));
}; };
bool metric = Params().read_db_bool("IsMetric"); bool metric = Params().getBool("IsMetric");
QJsonObject json = doc.object(); QJsonObject json = doc.object();
update(json["all"].toObject(), all_, metric); update(json["all"].toObject(), all_, metric);
update(json["week"].toObject(), week_, metric); update(json["week"].toObject(), week_, metric);
@ -51,7 +51,7 @@ DriveStats::DriveStats(QWidget* parent) : QWidget(parent) {
gl->addLayout(build_stat_layout(&labels.hours, "HOURS"), row, 2, 3, 1); gl->addLayout(build_stat_layout(&labels.hours, "HOURS"), row, 2, 3, 1);
}; };
const char* distance_unit = Params().read_db_bool("IsMetric") ? "KM" : "MILES"; const char* distance_unit = Params().getBool("IsMetric") ? "KM" : "MILES";
QGridLayout* gl = new QGridLayout(); QGridLayout* gl = new QGridLayout();
gl->setMargin(0); gl->setMargin(0);
gl->addWidget(new QLabel("ALL TIME"), 0, 0, 1, 3); gl->addWidget(new QLabel("ALL TIME"), 0, 0, 1, 3);

@ -1,47 +1,52 @@
#include <QFile>
#include <QLabel>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QJsonObject> #include <QJsonObject>
#include <QJsonDocument> #include <QJsonDocument>
#include <QDebug>
#include "offroad_alerts.hpp" #include "offroad_alerts.hpp"
#include "common/params.h"
#include "selfdrive/hardware/hw.h" #include "selfdrive/hardware/hw.h"
#include "selfdrive/common/util.h"
void cleanStackedWidget(QStackedWidget* swidget) {
while(swidget->count() > 0) {
QWidget *w = swidget->widget(0);
swidget->removeWidget(w);
w->deleteLater();
}
}
OffroadAlert::OffroadAlert(QWidget* parent) : QFrame(parent) { OffroadAlert::OffroadAlert(QWidget* parent) : QFrame(parent) {
QVBoxLayout *main_layout = new QVBoxLayout(); QVBoxLayout *layout = new QVBoxLayout();
main_layout->setMargin(25); layout->setMargin(50);
// setup labels for each alert
QString json = QString::fromStdString(util::read_file("../controls/lib/alerts_offroad.json"));
QJsonObject obj = QJsonDocument::fromJson(json.toUtf8()).object();
for (auto &k : obj.keys()) {
QLabel *l = new QLabel(this);
alerts[k.toStdString()] = l;
int severity = obj[k].toObject()["severity"].toInt();
l->setMargin(60);
l->setWordWrap(true);
l->setStyleSheet("background-color: " + QString(severity ? "#E22C2C" : "#292929"));
l->setVisible(false);
layout->addWidget(l);
}
alerts_stack = new QStackedWidget(); // release notes
main_layout->addWidget(alerts_stack, 1); releaseNotes.setVisible(false);
releaseNotes.setStyleSheet("font-size: 48px;");
layout->addWidget(&releaseNotes);
// bottom footer // bottom footer, dismiss + reboot buttons
QHBoxLayout *footer_layout = new QHBoxLayout(); QHBoxLayout *footer_layout = new QHBoxLayout();
main_layout->addLayout(footer_layout); layout->addLayout(footer_layout);
QPushButton *dismiss_btn = new QPushButton("Dismiss"); QPushButton *dismiss_btn = new QPushButton("Dismiss");
dismiss_btn->setFixedSize(400, 125); dismiss_btn->setFixedSize(400, 125);
footer_layout->addWidget(dismiss_btn, 0, Qt::AlignLeft); footer_layout->addWidget(dismiss_btn, 0, Qt::AlignBottom | Qt::AlignLeft);
reboot_btn = new QPushButton("Reboot and Update");
reboot_btn->setFixedSize(600, 125);
reboot_btn->setVisible(false);
footer_layout->addWidget(reboot_btn, 0, Qt::AlignRight);
QObject::connect(dismiss_btn, SIGNAL(released()), this, SIGNAL(closeAlerts())); QObject::connect(dismiss_btn, SIGNAL(released()), this, SIGNAL(closeAlerts()));
QObject::connect(reboot_btn, &QPushButton::released, [=]() { Hardware::reboot(); });
setLayout(main_layout); rebootBtn.setText("Reboot and Update");
rebootBtn.setFixedSize(600, 125);
rebootBtn.setVisible(false);
footer_layout->addWidget(&rebootBtn, 0, Qt::AlignBottom | Qt::AlignRight);
QObject::connect(&rebootBtn, &QPushButton::released, [=]() { Hardware::reboot(); });
setLayout(layout);
setStyleSheet(R"( setStyleSheet(R"(
* { * {
font-size: 48px; font-size: 48px;
@ -58,54 +63,33 @@ OffroadAlert::OffroadAlert(QWidget* parent) : QFrame(parent) {
background-color: white; background-color: white;
} }
)"); )");
main_layout->setMargin(50);
QFile inFile("../controls/lib/alerts_offroad.json");
bool ret = inFile.open(QIODevice::ReadOnly | QIODevice::Text);
assert(ret);
QJsonDocument doc = QJsonDocument::fromJson(inFile.readAll());
assert(!doc.isNull());
alert_keys = doc.object().keys();
} }
void OffroadAlert::refresh() { void OffroadAlert::refresh() {
parse_alerts(); updateAlerts();
cleanStackedWidget(alerts_stack);
updateAvailable = Params().read_db_bool("UpdateAvailable");
reboot_btn->setVisible(updateAvailable);
QVBoxLayout *layout = new QVBoxLayout; rebootBtn.setVisible(updateAvailable);
layout->setSpacing(20); releaseNotes.setVisible(updateAvailable);
releaseNotes.setText(QString::fromStdString(params.get("ReleaseNotes")));
if (updateAvailable) { for (const auto& [k, label] : alerts) {
QLabel *body = new QLabel(QString::fromStdString(Params().get("ReleaseNotes"))); label->setVisible(!updateAvailable && !label->text().isEmpty());
body->setStyleSheet(R"(font-size: 48px;)");
layout->addWidget(body, 0, Qt::AlignLeft | Qt::AlignTop);
} else {
for (const auto &alert : alerts) {
QLabel *l = new QLabel(alert.text);
l->setMargin(60);
l->setWordWrap(true);
l->setStyleSheet("background-color: " + QString(alert.severity ? "#E22C2C" : "#292929"));
layout->addWidget(l, 0, Qt::AlignTop);
}
} }
QWidget *w = new QWidget();
w->setLayout(layout);
alerts_stack->addWidget(w);
} }
void OffroadAlert::parse_alerts() { void OffroadAlert::updateAlerts() {
alerts.clear(); alertCount = 0;
for (const QString &key : alert_keys) { updateAvailable = params.getBool("UpdateAvailable");
std::vector<char> bytes = Params().read_db_bytes(key.toStdString().c_str()); for (const auto& [key, label] : alerts) {
auto bytes = params.get(key.c_str());
if (bytes.size()) { if (bytes.size()) {
QJsonDocument doc_par = QJsonDocument::fromJson(QByteArray(bytes.data(), bytes.size())); QJsonDocument doc_par = QJsonDocument::fromJson(QByteArray(bytes.data(), bytes.size()));
QJsonObject obj = doc_par.object(); QJsonObject obj = doc_par.object();
Alert alert = {obj.value("text").toString(), obj.value("severity").toInt()}; label->setText(obj.value("text").toString());
alerts.push_back(alert); alertCount++;
} else {
label->setText("");
} }
} }
} }

@ -1,28 +1,26 @@
#pragma once #pragma once
#include <map>
#include <QFrame> #include <QFrame>
#include <QStackedWidget>
#include <QPushButton> #include <QPushButton>
#include <QStringList> #include <QLabel>
struct Alert { #include "common/params.h"
QString text;
int severity;
};
class OffroadAlert : public QFrame { class OffroadAlert : public QFrame {
Q_OBJECT Q_OBJECT
public: public:
explicit OffroadAlert(QWidget *parent = 0); explicit OffroadAlert(QWidget *parent = 0);
QVector<Alert> alerts; int alertCount = 0;
QStringList alert_keys;
bool updateAvailable; bool updateAvailable;
private: private:
QStackedWidget *alerts_stack; Params params;
QPushButton *reboot_btn; QLabel releaseNotes;
void parse_alerts(); std::map<std::string, QLabel*> alerts;
QPushButton rebootBtn;
void updateAlerts();
signals: signals:
void closeAlerts(); void closeAlerts();

@ -1,12 +1,19 @@
#include <QNetworkReply> #include <QNetworkReply>
#include <QHBoxLayout>
#include "widgets/input.hpp" #include "widgets/input.hpp"
#include "widgets/ssh_keys.hpp" #include "widgets/ssh_keys.hpp"
#include "common/params.h" #include "common/params.h"
SshControl::SshControl() : AbstractControl("SSH Keys", "Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username.", "") { SshControl::SshControl() : AbstractControl("SSH Keys", "Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username.", "") {
// setup widget // setup widget
hlayout->addStretch(1);
username_label.setAlignment(Qt::AlignVCenter);
username_label.setStyleSheet("color: #aaaaaa");
hlayout->addWidget(&username_label);
btn.setStyleSheet(R"( btn.setStyleSheet(R"(
padding: 0; padding: 0;
border-radius: 50px; border-radius: 50px;
@ -27,7 +34,8 @@ SshControl::SshControl() : AbstractControl("SSH Keys", "Warning: This grants SSH
getUserKeys(username); getUserKeys(username);
} }
} else { } else {
Params().delete_db_value("GithubSshKeys"); Params().remove("GithubUsername");
Params().remove("GithubSshKeys");
refresh(); refresh();
} }
}); });
@ -45,8 +53,10 @@ SshControl::SshControl() : AbstractControl("SSH Keys", "Warning: This grants SSH
void SshControl::refresh() { void SshControl::refresh() {
QString param = QString::fromStdString(Params().get("GithubSshKeys")); QString param = QString::fromStdString(Params().get("GithubSshKeys"));
if (param.length()) { if (param.length()) {
username_label.setText(QString::fromStdString(Params().get("GithubUsername")));
btn.setText("REMOVE"); btn.setText("REMOVE");
} else { } else {
username_label.setText("");
btn.setText("ADD"); btn.setText("ADD");
} }
btn.setEnabled(true); btn.setEnabled(true);
@ -79,7 +89,8 @@ void SshControl::parseResponse(){
networkTimer->stop(); networkTimer->stop();
QString response = reply->readAll(); QString response = reply->readAll();
if (reply->error() == QNetworkReply::NoError && response.length()) { if (reply->error() == QNetworkReply::NoError && response.length()) {
Params().write_db_value("GithubSshKeys", response.toStdString()); Params().put("GithubUsername", username.toStdString());
Params().put("GithubSshKeys", response.toStdString());
} else if(reply->error() == QNetworkReply::NoError){ } else if(reply->error() == QNetworkReply::NoError){
err = "Username '" + username + "' has no keys on GitHub"; err = "Username '" + username + "' has no keys on GitHub";
} else { } else {

@ -29,6 +29,7 @@ public:
private: private:
QPushButton btn; QPushButton btn;
QString username; QString username;
QLabel username_label;
// networking // networking
QTimer* networkTimer; QTimer* networkTimer;

@ -1,4 +1,5 @@
#include "window.hpp" #include "window.hpp"
#include "selfdrive/hardware/hw.h"
MainWindow::MainWindow(QWidget *parent) : QWidget(parent) { MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
main_layout = new QStackedLayout; main_layout = new QStackedLayout;
@ -16,6 +17,7 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
QObject::connect(homeWindow, SIGNAL(openSettings()), this, SLOT(openSettings())); QObject::connect(homeWindow, SIGNAL(openSettings()), this, SLOT(openSettings()));
QObject::connect(homeWindow, SIGNAL(closeSettings()), this, SLOT(closeSettings())); QObject::connect(homeWindow, SIGNAL(closeSettings()), this, SLOT(closeSettings()));
QObject::connect(homeWindow, SIGNAL(offroadTransition(bool)), this, SLOT(offroadTransition(bool))); QObject::connect(homeWindow, SIGNAL(offroadTransition(bool)), this, SLOT(offroadTransition(bool)));
QObject::connect(homeWindow, SIGNAL(offroadTransition(bool)), settingsWindow, SIGNAL(offroadTransition(bool)));
QObject::connect(settingsWindow, SIGNAL(closeSettings()), this, SLOT(closeSettings())); QObject::connect(settingsWindow, SIGNAL(closeSettings()), this, SLOT(closeSettings()));
QObject::connect(settingsWindow, SIGNAL(reviewTrainingGuide()), this, SLOT(reviewTrainingGuide())); QObject::connect(settingsWindow, SIGNAL(reviewTrainingGuide()), this, SLOT(reviewTrainingGuide()));
@ -54,8 +56,20 @@ void MainWindow::reviewTrainingGuide() {
} }
bool MainWindow::eventFilter(QObject *obj, QEvent *event){ bool MainWindow::eventFilter(QObject *obj, QEvent *event){
// wake screen on tap
if (event->type() == QEvent::MouseButtonPress) { if (event->type() == QEvent::MouseButtonPress) {
homeWindow->glWindow->wake(); homeWindow->glWindow->wake();
} }
// filter out touches while in android activity
#ifdef QCOM
const QList<QEvent::Type> filter_events = {QEvent::MouseButtonPress, QEvent::MouseMove, QEvent::TouchBegin, QEvent::TouchUpdate, QEvent::TouchEnd};
if (HardwareEon::launched_activity && filter_events.contains(event->type())) {
HardwareEon::check_activity();
if (HardwareEon::launched_activity) {
return true;
}
}
#endif
return false; return false;
} }

@ -10,13 +10,6 @@
#include "ui.hpp" #include "ui.hpp"
#include "paint.hpp" #include "paint.hpp"
int write_param_float(float param, const char* param_name, bool persistent_param) {
char s[16];
int size = snprintf(s, sizeof(s), "%f", param);
return Params(persistent_param).write_db_value(param_name, s, size < sizeof(s) ? size : sizeof(s));
}
// Projects a point in car to space to the corresponding point in full frame // Projects a point in car to space to the corresponding point in full frame
// image space. // image space.
static bool calib_frame_to_full_frame(const UIState *s, float in_x, float in_y, float in_z, vertex_data *out) { static bool calib_frame_to_full_frame(const UIState *s, float in_x, float in_y, float in_z, vertex_data *out) {
@ -205,11 +198,6 @@ static void update_sockets(UIState *s) {
} }
if (sm.updated("driverMonitoringState")) { if (sm.updated("driverMonitoringState")) {
scene.dmonitoring_state = sm["driverMonitoringState"].getDriverMonitoringState(); scene.dmonitoring_state = sm["driverMonitoringState"].getDriverMonitoringState();
if(!scene.driver_view && !scene.ignition) {
read_param(&scene.driver_view, "IsDriverViewEnabled");
}
} else if ((sm.frame - sm.rcv_frame("driverMonitoringState")) > UI_FREQ/2) {
scene.driver_view = false;
} }
if (sm.updated("sensorEvents")) { if (sm.updated("sensorEvents")) {
for (auto sensor : sm["sensorEvents"].getSensorEvents()) { for (auto sensor : sm["sensorEvents"].getSensorEvents()) {
@ -284,14 +272,13 @@ static void update_alert(UIState *s) {
static void update_params(UIState *s) { static void update_params(UIState *s) {
const uint64_t frame = s->sm->frame; const uint64_t frame = s->sm->frame;
UIScene &scene = s->scene; UIScene &scene = s->scene;
Params params;
if (frame % (5*UI_FREQ) == 0) { if (frame % (5*UI_FREQ) == 0) {
read_param(&scene.is_metric, "IsMetric"); scene.is_metric = params.getBool("IsMetric");
} else if (frame % (6*UI_FREQ) == 0) { } else if (frame % (6*UI_FREQ) == 0) {
scene.athenaStatus = NET_DISCONNECTED; scene.athenaStatus = NET_DISCONNECTED;
uint64_t last_ping = 0; if (auto last_ping = params.get<float>("LastAthenaPingTime"); last_ping) {
if (read_param(&last_ping, "LastAthenaPingTime") == 0) { scene.athenaStatus = nanos_since_boot() - *last_ping < 70e9 ? NET_CONNECTED : NET_ERROR;
scene.athenaStatus = nanos_since_boot() - last_ping < 70e9 ? NET_CONNECTED : NET_ERROR;
} }
} }
} }
@ -334,8 +321,8 @@ static void update_status(UIState *s) {
s->status = STATUS_DISENGAGED; s->status = STATUS_DISENGAGED;
s->scene.started_frame = s->sm->frame; s->scene.started_frame = s->sm->frame;
read_param(&s->scene.is_rhd, "IsRHD"); s->scene.is_rhd = Params().getBool("IsRHD");
read_param(&s->scene.end_to_end, "EndToEndToggle"); s->scene.end_to_end = Params().getBool("EndToEndToggle");
s->sidebar_collapsed = true; s->sidebar_collapsed = true;
s->scene.alert_size = cereal::ControlsState::AlertSize::NONE; s->scene.alert_size = cereal::ControlsState::AlertSize::NONE;
s->vipc_client = s->scene.driver_view ? s->vipc_client_front : s->vipc_client_rear; s->vipc_client = s->scene.driver_view ? s->vipc_client_front : s->vipc_client_rear;

@ -25,7 +25,7 @@
#include "common/params.h" #include "common/params.h"
#include "common/glutil.h" #include "common/glutil.h"
#include "common/transformations/orientation.hpp" #include "common/transformations/orientation.hpp"
#include "sound.hpp" #include "qt/sound.hpp"
#include "visionipc.h" #include "visionipc.h"
#include "visionipc_client.h" #include "visionipc_client.h"
@ -171,28 +171,3 @@ typedef struct UIState {
void ui_init(UIState *s); void ui_init(UIState *s);
void ui_update(UIState *s); void ui_update(UIState *s);
int write_param_float(float param, const char* param_name, bool persistent_param = false);
template <class T>
int read_param(T* param, const char *param_name, bool persistent_param = false){
T param_orig = *param;
char *value;
size_t sz;
int result = Params(persistent_param).read_db_value(param_name, &value, &sz);
if (result == 0){
std::string s = std::string(value, sz); // value is not null terminated
free(value);
// Parse result
std::istringstream iss(s);
iss >> *param;
// Restore original value if parsing failed
if (iss.fail()) {
*param = param_orig;
result = -1;
}
}
return result;
}

@ -12,6 +12,7 @@ EXPLORER_FILE_RE = r'^({})--([a-z]+\.[a-z0-9]+)$'.format(SEGMENT_NAME_RE)
OP_SEGMENT_DIR_RE = r'^({})$'.format(SEGMENT_NAME_RE) OP_SEGMENT_DIR_RE = r'^({})$'.format(SEGMENT_NAME_RE)
QLOG_FILENAMES = ['qlog.bz2'] QLOG_FILENAMES = ['qlog.bz2']
QCAMERA_FILENAMES = ['qcamera.ts']
LOG_FILENAMES = ['rlog.bz2', 'raw_log.bz2'] LOG_FILENAMES = ['rlog.bz2', 'raw_log.bz2']
CAMERA_FILENAMES = ['fcamera.hevc', 'video.hevc'] CAMERA_FILENAMES = ['fcamera.hevc', 'video.hevc']
@ -40,6 +41,10 @@ class Route(object):
camera_path_by_seg_num = {s.canonical_name.segment_num: s.camera_path for s in self._segments} camera_path_by_seg_num = {s.canonical_name.segment_num: s.camera_path for s in self._segments}
return [camera_path_by_seg_num.get(i, None) for i in range(self.max_seg_number+1)] return [camera_path_by_seg_num.get(i, None) for i in range(self.max_seg_number+1)]
def qcamera_paths(self):
qcamera_path_by_seg_num = {s.canonical_name.segment_num: s.qcamera_path for s in self._segments}
return [qcamera_path_by_seg_num.get(i, None) for i in range(self.max_seg_number+1)]
def _get_segments_remote(self): def _get_segments_remote(self):
api = CommaApi(get_token()) api = CommaApi(get_token())
route_files = api.get('v1/route/' + self.route_name + '/files') route_files = api.get('v1/route/' + self.route_name + '/files')
@ -53,14 +58,16 @@ class Route(object):
segment_name, segment_name,
url if fn in LOG_FILENAMES else segments[segment_name].log_path, url if fn in LOG_FILENAMES else segments[segment_name].log_path,
url if fn in QLOG_FILENAMES else segments[segment_name].qlog_path, url if fn in QLOG_FILENAMES else segments[segment_name].qlog_path,
url if fn in CAMERA_FILENAMES else segments[segment_name].camera_path url if fn in CAMERA_FILENAMES else segments[segment_name].camera_path,
url if fn in QCAMERA_FILENAMES else segments[segment_name].qcamera_path,
) )
else: else:
segments[segment_name] = RouteSegment( segments[segment_name] = RouteSegment(
segment_name, segment_name,
url if fn in LOG_FILENAMES else None, url if fn in LOG_FILENAMES else None,
url if fn in QLOG_FILENAMES else None, url if fn in QLOG_FILENAMES else None,
url if fn in CAMERA_FILENAMES else None url if fn in CAMERA_FILENAMES else None,
url if fn in QCAMERA_FILENAMES else None,
) )
return sorted(segments.values(), key=lambda seg: seg.canonical_name.segment_num) return sorted(segments.values(), key=lambda seg: seg.canonical_name.segment_num)
@ -110,18 +117,24 @@ class Route(object):
except StopIteration: except StopIteration:
camera_path = None camera_path = None
segments.append(RouteSegment(segment, log_path, qlog_path, camera_path)) try:
qcamera_path = next(path for path, filename in files if filename in QCAMERA_FILENAMES)
except StopIteration:
qcamera_path = None
segments.append(RouteSegment(segment, log_path, qlog_path, camera_path, qcamera_path))
if len(segments) == 0: if len(segments) == 0:
raise ValueError('Could not find segments for route {} in data directory {}'.format(self.route_name, data_dir)) raise ValueError('Could not find segments for route {} in data directory {}'.format(self.route_name, data_dir))
return sorted(segments, key=lambda seg: seg.canonical_name.segment_num) return sorted(segments, key=lambda seg: seg.canonical_name.segment_num)
class RouteSegment(object): class RouteSegment(object):
def __init__(self, name, log_path, qlog_path, camera_path): def __init__(self, name, log_path, qlog_path, camera_path, qcamera_path):
self._name = RouteSegmentName(name) self._name = RouteSegmentName(name)
self.log_path = log_path self.log_path = log_path
self.qlog_path = qlog_path self.qlog_path = qlog_path
self.camera_path = camera_path self.camera_path = camera_path
self.qcamera_path = qcamera_path
@property @property
def name(self): def name(self):

@ -43,6 +43,7 @@ sudo apt-get update && sudo apt-get install -y \
opencl-headers \ opencl-headers \
python-dev \ python-dev \
python3-pip \ python3-pip \
qml-module-qtquick2 \
qt5-default \ qt5-default \
qtmultimedia5-dev \ qtmultimedia5-dev \
qtwebengine5-dev \ qtwebengine5-dev \

Loading…
Cancel
Save