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

@ -97,7 +97,7 @@ Supported Cars
| Lexus | RX 2020-21 | All | openpilot | 0mph | 0mph |
| Lexus | RX Hybrid 2016-19 | All | Stock<sup>3</sup>| 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 2021 | All | openpilot | 0mph | 0mph |
| Toyota | Camry Hybrid 2018-20 | All | Stock | 0mph<sup>4</sup> | 0mph |
@ -130,58 +130,63 @@ Supported Cars
Community Maintained Cars and Features
------
| 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 Sportback e-tron 2017-18 | Prestige / Adv.Tech | Stock | 0mph | 0mph |
| Buick | Regal 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 | Volt 2017-18<sup>1</sup> | Adaptive Cruise | openpilot | 0mph | 7mph |
| Chrysler | Pacifica 2017-18 | Adaptive Cruise | Stock | 0mph | 9mph |
| Chrysler | Pacifica 2020 | Adaptive Cruise | Stock | 0mph | 39mph |
| Chrysler | Pacifica Hybrid 2017-18 | Adaptive Cruise | Stock | 0mph | 9mph |
| Chrysler | Pacifica Hybrid 2019-21 | Adaptive Cruise | Stock | 0mph | 39mph |
| Genesis | G70 2018 | All | Stock | 0mph | 0mph |
| Genesis | G80 2018 | All | Stock | 0mph | 0mph |
| Genesis | G90 2018 | All | Stock | 0mph | 0mph |
| GMC | Acadia 2018<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 | Genesis 2015-16 | SCC + LKAS | Stock | 19mph | 37mph |
| Hyundai | Ioniq Electric 2019 | SCC + LKAS | Stock | 0mph | 32mph |
| Hyundai | Ioniq Electric 2020 | SCC + LKAS | Stock | 0mph | 0mph |
| Hyundai | Kona 2020 | SCC + LKAS | Stock | 0mph | 0mph |
| Hyundai | Kona EV 2019 | SCC + LKAS | Stock | 0mph | 0mph |
| Hyundai | Santa Fe 2019-20 | All | Stock | 0mph | 0mph |
| Hyundai | Sonata 2018-2019 | SCC + LKAS | Stock | 0mph | 0mph |
| Hyundai | Veloster 2019 | SCC + LKAS | Stock | 5mph | 0mph |
| Jeep | Grand Cherokee 2016-18 | Adaptive Cruise | Stock | 0mph | 9mph |
| Jeep | Grand Cherokee 2019-20 | Adaptive Cruise | Stock | 0mph | 39mph |
| Kia | Forte 2018-19, 2021 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Niro EV 2020 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Optima 2017 | SCC + LKAS | Stock | 0mph | 32mph |
| Kia | Optima 2019 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Seltos 2021 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Sorento 2018 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Stinger 2018 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Ceed 2019 | SCC + LKAS | Stock | 0mph | 0mph |
| Nissan | Altima 2020 | ProPILOT | Stock | 0mph | 0mph |
| Nissan | Leaf 2018-20 | ProPILOT | Stock | 0mph | 0mph |
| Nissan | Rogue 2018-19 | ProPILOT | Stock | 0mph | 0mph |
| Nissan | X-Trail 2017 | ProPILOT | Stock | 0mph | 0mph |
| SEAT | Ateca 2018 | Driver Assistance | Stock | 0mph | 0mph |
| Škoda | Kodiaq 2018 | Driver Assistance | Stock | 0mph | 0mph |
| Škoda | Scala 2020 | Driver Assistance | Stock | 0mph | 0mph |
| Škoda | Superb 2018 | Driver Assistance | Stock | 0mph | 0mph |
| Subaru | Ascent 2019 | EyeSight | Stock | 0mph | 0mph |
| Subaru | Crosstrek 2018-19 | EyeSight | Stock | 0mph | 0mph |
| Subaru | Forester 2019-20 | EyeSight | Stock | 0mph | 0mph |
| Subaru | Impreza 2017-19 | EyeSight | 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| Golf 2015-19 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Tiguan 2020 | Driver Assistance | Stock | 0mph | 0mph |
| Make | Model (US Market Reference) | Supported Package | ACC | No ACC accel below | No ALC below |
| ----------| ------------------------------| ------------------| -----------------| -------------------| -------------|
| Audi | A3 2014, 2017 | Prestige | 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 |
| Cadillac | ATS 2018<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 |
| Chrysler | Pacifica 2017-18 | Adaptive Cruise | Stock | 0mph | 9mph |
| Chrysler | Pacifica 2020 | Adaptive Cruise | Stock | 0mph | 39mph |
| Chrysler | Pacifica Hybrid 2017-18 | Adaptive Cruise | Stock | 0mph | 9mph |
| Chrysler | Pacifica Hybrid 2019-21 | Adaptive Cruise | Stock | 0mph | 39mph |
| Genesis | G70 2018 | All | Stock | 0mph | 0mph |
| Genesis | G80 2018 | All | Stock | 0mph | 0mph |
| Genesis | G90 2018 | All | Stock | 0mph | 0mph |
| GMC | Acadia 2018<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 | Genesis 2015-16 | SCC + LKAS | Stock | 19mph | 37mph |
| Hyundai | Ioniq Electric 2019 | SCC + LKAS | Stock | 0mph | 32mph |
| Hyundai | Ioniq Electric 2020 | SCC + LKAS | Stock | 0mph | 0mph |
| Hyundai | Kona 2020 | SCC + LKAS | Stock | 0mph | 0mph |
| Hyundai | Kona EV 2019 | SCC + LKAS | Stock | 0mph | 0mph |
| Hyundai | Santa Fe 2019-20 | All | Stock | 0mph | 0mph |
| Hyundai | Sonata 2018-2019 | SCC + LKAS | Stock | 0mph | 0mph |
| Hyundai | Veloster 2019 | SCC + LKAS | Stock | 5mph | 0mph |
| Jeep | Grand Cherokee 2016-18 | Adaptive Cruise | Stock | 0mph | 9mph |
| Jeep | Grand Cherokee 2019-20 | Adaptive Cruise | Stock | 0mph | 39mph |
| Kia | Forte 2018-2021 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Niro EV 2020 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Optima 2017 | SCC + LKAS | Stock | 0mph | 32mph |
| Kia | Optima 2019 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Seltos 2021 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Sorento 2018 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Stinger 2018 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Ceed 2019 | SCC + LKAS | Stock | 0mph | 0mph |
| Nissan | Altima 2020 | ProPILOT | Stock | 0mph | 0mph |
| Nissan | Leaf 2018-20 | ProPILOT | Stock | 0mph | 0mph |
| Nissan | Rogue 2018-19 | ProPILOT | Stock | 0mph | 0mph |
| Nissan | X-Trail 2017 | ProPILOT | Stock | 0mph | 0mph |
| SEAT | Ateca 2018 | Driver Assistance | Stock | 0mph | 0mph |
| Škoda | Kodiaq 2018 | Driver Assistance | Stock | 0mph | 0mph |
| Škoda | Scala 2020 | Driver Assistance | Stock | 0mph | 0mph |
| Škoda | Superb 2018 | Driver Assistance | Stock | 0mph | 0mph |
| Subaru | Ascent 2019 | EyeSight | Stock | 0mph | 0mph |
| Subaru | Crosstrek 2018-19 | EyeSight | Stock | 0mph | 0mph |
| Subaru | Forester 2019-20 | EyeSight | Stock | 0mph | 0mph |
| Subaru | Impreza 2017-19 | EyeSight | Stock | 0mph | 0mph |
| Volkswagen| e-Golf 2014, 2020 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Golf 2015-19 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Golf GTE 2016 | 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>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)
if 'error' in kwargs:
self.error(evt)
if 'debug' in kwargs:
self.debug(evt)
else:
self.info(evt)

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

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

@ -42,7 +42,7 @@ git commit -m "openpilot v$VERSION"
# Build signed panda firmware
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
popd
@ -63,6 +63,7 @@ find . -name '*.pyc' -delete
find . -name '__pycache__' -delete
rm -rf panda/board panda/certs panda/crypto
rm -rf .sconsign.dblite Jenkinsfile release/
rm models/supercombo.dlc
# Move back signed panda fw
mkdir -p panda/board/obj

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

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

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

@ -71,12 +71,11 @@ void safety_setter_thread() {
return;
};
std::vector<char> value_vin = Params().read_db_bytes("CarVin");
std::string value_vin = Params().get("CarVin");
if (value_vin.size() > 0) {
// sanity check VIN format
assert(value_vin.size() == 17);
std::string str_vin(value_vin.begin(), value_vin.end());
LOGW("got CarVin %s", str_vin.c_str());
LOGW("got CarVin %s", value_vin.c_str());
break;
}
util::sleep_for(100);
@ -85,7 +84,7 @@ void safety_setter_thread() {
// VIN query done, stop listening to OBDII
panda->set_safety_model(cereal::CarParams::SafetyModel::NO_OUTPUT);
std::vector<char> params;
std::string params;
LOGW("waiting for params to set safety model");
while (true) {
if (do_exit || !panda->connected){
@ -93,7 +92,7 @@ void safety_setter_thread() {
return;
};
params = Params().read_db_bytes("CarParams");
params = Params().get("CarParams");
if (params.size() > 0) break;
util::sleep_for(100);
}
@ -133,7 +132,7 @@ bool usb_connect() {
}
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
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);
}
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);
} else { return false; }
// get panda 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());
} else { return false; }
@ -161,13 +160,18 @@ bool usb_connect() {
#endif
if (tmp_panda->has_rtc){
setenv("TZ","UTC",1);
struct tm sys_time = get_time();
struct tm rtc_time = tmp_panda->get_rtc();
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};
settimeofday(&tv, 0);
}
@ -313,9 +317,9 @@ void panda_state_thread(bool spoofing_started) {
// clear VIN, CarParams, and set new safety on car start
if (ignition && !ignition_last) {
int result = params.delete_db_value("CarVin");
int result = params.remove("CarVin");
assert((result == 0) || (result == ERR_NO_VALUE));
result = params.delete_db_value("CarParams");
result = params.remove("CarParams");
assert((result == 0) || (result == ERR_NO_VALUE));
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
if ((panda->has_rtc) && !ignition && (no_ignition_cnt % 120 == 1)){
// Write time to RTC if it looks reasonable
setenv("TZ","UTC",1);
struct tm sys_time = get_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;
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
if (cnt % 3 == 0) {

@ -380,6 +380,7 @@ FW_VERSIONS = {
(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.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): [
b'\xf1\xa01.06',
@ -392,15 +393,25 @@ FW_VERSIONS = {
(Ecu.fwdCamera, 0x7C4, None): [
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\000DEE MFC AT EUR LHD 1.00 1.00 99211-Q4100 200706',
],
},
CAR.KIA_SELTOS: {
(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.engine, 0x7e0, None): [b'\xf1\x81616D2051\000\000\000\000\000\000\000\000',],
(Ecu.esp, 0x7d1, None): [
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.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: {
(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): [
b'8646FF404000 ',
b'8821FF406000 ',
b'8646FF406000 ',
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'\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'\x02342Q2000\x00\x00\x00\x00\x00\x00\x00\x0054213000\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B42103\x00\x00\x00\x00\x00\x00',
@ -1127,6 +1126,7 @@ FW_VERSIONS = {
b'\x018966342W5000\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'\x02896634A13101\x00\x00\x00\x00897CF4801001\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'\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
if candidate == CAR.GOLF:
# Temporarily carry forward old tuning values while we test vehicle identification
ret.mass = 1500 + STD_CARGO_KG
ret.wheelbase = 2.64
if candidate == CAR.GOLF_MK7:
# Averages of all AU Golf variants
ret.mass = 1397 + STD_CARGO_KG
ret.wheelbase = 2.62
elif candidate == CAR.JETTA_MK7:
# Averages of all BU Jetta variants

@ -55,7 +55,7 @@ MQB_LDW_MESSAGES = {
# FW_VERSIONS for that existing 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
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
@ -66,7 +66,7 @@ class CAR:
SKODA_SUPERB_MK3 = "SKODA SUPERB 3RD GEN" # Chassis 3V/NP, Mk3 Skoda Superb and variants
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
}],
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]
FW_VERSIONS = {
CAR.GOLF: {
CAR.GOLF_MK7: {
(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\x878V0906259Q \xf1\x890002',
],
(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\x870GC300043T \xf1\x899999',
b'\xf1\x870GC300020G \xf1\x892404',
b'\xf1\x870GC300014B \xf1\x892405',
b'\xf1\x870DD300045K \xf1\x891120',
],
(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): [
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\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): [
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\x875Q0907572P \xf1\x890682',
],
},
CAR.JETTA_MK7: {
@ -281,7 +336,7 @@ FW_VERSIONS = {
}
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.PASSAT_MK8: 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/stat.h>
#include <map>
#include <string>
#include <iostream>
#include <csignal>
#include <string.h>
@ -83,15 +80,11 @@ Params::Params(bool persistent_param){
params_path = persistent_param ? persistent_params_path : default_params_path;
}
Params::Params(std::string path) {
Params::Params(const std::string &path) {
params_path = path;
}
int Params::write_db_value(std::string key, std::string dat){
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) {
int Params::put(const char* key, const char* value, size_t value_size) {
// Information about safely and atomically writing a file: https://lwn.net/Articles/457667/
// 1) Create temp file
// 2) Write data to temp file
@ -213,7 +206,7 @@ cleanup:
return result;
}
int Params::delete_db_value(std::string key) {
int Params::remove(const char *key) {
int lock_fd = -1;
int result;
std::string path;
@ -230,7 +223,7 @@ int Params::delete_db_value(std::string key) {
// Delete value.
path = params_path + "/d/" + key;
result = remove(path.c_str());
result = ::remove(path.c_str());
if (result != 0) {
result = ERR_NO_VALUE;
goto cleanup;
@ -251,52 +244,28 @@ cleanup:
return result;
}
std::string Params::get(std::string key, bool block){
char* value;
size_t size;
int r;
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;
std::string Params::get(const char *key, bool block) {
std::string path = params_path + "/d/" + key;
if (!block) {
return util::read_file(path);
} else {
return "";
}
}
int Params::read_db_value(const char* key, char** value, size_t* value_sz) {
std::string path = params_path + "/d/" + std::string(key);
*value = static_cast<char*>(read_file(path.c_str(), value_sz));
if (*value == NULL) {
return -22;
}
return 0;
}
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
// 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);
std::string value;
while (!params_do_exit) {
if (value = util::read_file(path); !value.empty()) {
break;
}
util::sleep_for(100); // 0.1 s
}
}
std::signal(SIGINT, prev_handler_sigint);
std::signal(SIGTERM, prev_handler_sigterm);
return params_do_exit; // Return 0 if we had no interrupt
std::signal(SIGINT, prev_handler_sigint);
std::signal(SIGTERM, prev_handler_sigterm);
return value;
}
}
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);
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
#include <stddef.h>
#include <map>
#include <string>
#include <vector>
#include <sstream>
#define ERR_NO_VALUE -33
@ -12,35 +13,44 @@ private:
public:
Params(bool persistent_param = false);
Params(std::string path);
int write_db_value(std::string key, std::string dat);
int write_db_value(const char* key, const char* value, size_t value_size);
// Reads a value from the params database.
// 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);
Params(const std::string &path);
// Delete a value
int remove(const char *key);
inline int remove(const std::string &key) {
return remove (key.c_str());
}
// read all values
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);
}
char levelnum_c = levelnum;
zmq_send(s.sock, &levelnum_c, 1, ZMQ_NOBLOCK | ZMQ_SNDMORE);
zmq_send(s.sock, log_s.c_str(), log_s.length(), ZMQ_NOBLOCK);
zmq_send(s.sock, (levelnum_c + log_s).c_str(), log_s.length() + 1, ZMQ_NOBLOCK);
}
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_; }
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(",")]
while 1:
polld = poller.poll(1000)
polld = poller.poll(100)
for sock in polld:
msg = sock.receive()
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::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 bool get_ssh_enabled() { return Params().read_db_bool("SshEnabled"); };
static void set_ssh_enabled(bool enabled) { Params().write_db_value("SshEnabled", (enabled ? "1" : "0")); };
static bool get_ssh_enabled() { return Params().getBool("SshEnabled"); };
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.setGitBranch(params.get("GitBranch"));
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;
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;
#if defined(QCOM) || defined(QCOM2)
bool record_front = Params().read_db_bool("RecordFront");
bool record_front = Params().getBool("RecordFront");
if (record_front) {
encoder_threads.push_back(std::thread(encoder_thread, LOG_CAMERA_ID_DCAMERA));
s.rotate_state[LOG_CAMERA_ID_DCAMERA].enabled = true;

@ -127,10 +127,10 @@ class Uploader():
url_resp_json = json.loads(url_resp.text)
url = url_resp_json['url']
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:
cloudlog.info("*** WARNING, THIS IS A FAKE UPLOAD TO %s ***" % url)
cloudlog.debug("*** WARNING, THIS IS A FAKE UPLOAD TO %s ***" % url)
class FakeResponse():
def __init__(self):
@ -164,7 +164,7 @@ class Uploader():
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:
try:
@ -174,10 +174,10 @@ class Uploader():
cloudlog.event("uploader_setxattr_failed", exc=self.last_exc, key=key, fn=fn, sz=sz)
success = True
else:
cloudlog.info("uploading %r", fn)
cloudlog.debug("uploading %r", fn)
stat = self.normal_upload(key, fn)
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:
# tag file as uploaded
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)
success = True
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
return success
def uploader_fn(exit_event):
cloudlog.info("uploader_fn")
params = Params()
dongle_id = params.get("DongleId").decode('utf8')
@ -209,8 +207,14 @@ def uploader_fn(exit_event):
backoff = 0.1
while not exit_event.is_set():
sm.update(0)
on_wifi = force_wifi or sm['deviceState'].networkType == NetworkType.wifi
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"
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
cloudlog.event("uploader_netcheck", is_on_wifi=on_wifi)
cloudlog.info("to upload %r", d)
cloudlog.debug("upload %r over %s", d, network_type)
success = uploader.upload(key, fn)
if success:
backoff = 0.1
elif allow_sleep:
cloudlog.info("backoff %r", backoff)
cloudlog.info("upload backoff %r", backoff)
time.sleep(backoff + random.uniform(0, backoff))
backoff = min(backoff*2, 120)
cloudlog.info("upload done, success=%r", success)

@ -72,10 +72,7 @@ void run_model(ModelState &model, VisionIpcClient &vipc_client) {
SubMaster sm({"lateralPlan", "roadCameraState"});
// setup filter to track dropped frames
const float dt = 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;
FirstOrderFilter frame_dropped_filter(0., 10., 1. / MODEL_FREQ);
uint32_t frame_id = 0, last_vipc_frame_id = 0;
double last = 0;
@ -114,8 +111,12 @@ void run_model(ModelState &model, VisionIpcClient &vipc_client) {
// tracked dropped frames
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);
if (run_count < 10) frames_dropped = 0; // let frame drops warm up
float frames_dropped = frame_dropped_filter.update((float)std::min(vipc_dropped_frames, 10U));
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);
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;
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>

@ -70,7 +70,7 @@ void model_init(ModelState* s, cl_device_id device_id, cl_context context) {
#endif
#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->m->addTrafficConvention(s->traffic_convention, TRAFFIC_CONVENTION_LEN);
#endif

@ -30,6 +30,8 @@ _BLINK_THRESHOLD_SLACK = 0.65
_BLINK_THRESHOLD_STRICT = 0.5
_PITCH_WEIGHT = 1.35 # pitch matters a lot more
_POSESTD_THRESHOLD = 0.14
_E2E_POSE_THRESHOLD = 0.9
_E2E_EYES_THRESHOLD = 0.75
_METRIC_THRESHOLD = 0.4
_METRIC_THRESHOLD_SLACK = 0.55
_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.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 \
driver_state.faceProb > _FACE_THRESHOLD and self.pose.low_std
self.driver_distracted = (self._is_driver_distracted(self.pose, self.blink) > 0 and
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)
# update offseter
@ -209,7 +213,7 @@ class DriverStatus():
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)
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
elif self.face_detected and self.pose.low_std:
self.hi_stds = 0

@ -1 +1 @@
3da639f754563292103525345ec26e384e4cb9a5
796df70c9c83a693f81e4a35d97f90dc5378ef06

@ -437,8 +437,8 @@ routes = {
'enableCamera': True,
'enableDsu': False,
},
"76b83eb0245de90e|2019-10-20--15-42-29": {
'carFingerprint': VOLKSWAGEN.GOLF,
"cae14e88932eb364|2021-03-26--14-43-28": {
'carFingerprint': VOLKSWAGEN.GOLF_MK7,
'enableCamera': True,
},
"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_usage = cpu_time / dt * 100.
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"
r = False
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
# 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:
return False
now = sec_since_boot()
panda_charging = (pandaState.pandaState.usbPowerMode != log.PandaState.UsbPowerMode.client)
BATT_PERC_OFF = 10 if LEON else 3
BATT_PERC_OFF = 10
should_shutdown = False
# Wait until we have shut down charging before powering down

@ -2,6 +2,7 @@
import datetime
import os
import time
from pathlib import Path
from typing import Dict, Optional, Tuple
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]]] = {}
LEON = False
last_eon_fan_val = None
def read_tz(x):
if x is None:
return 0
@ -62,44 +63,24 @@ def read_thermal(thermal_config):
def setup_eon_fan():
global LEON
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):
global LEON, last_eon_fan_val
global last_eon_fan_val
if last_eon_fan_val is None or last_eon_fan_val != val:
bus = SMBus(7, force=True)
if LEON:
try:
i = [0x1, 0x3 | 0, 0x3 | 0x08, 0x3 | 0x10][val]
bus.write_i2c_block_data(0x3d, 0, [i])
except IOError:
# tusb320
if val == 0:
bus.write_i2c_block_data(0x67, 0xa, [0])
#bus.write_i2c_block_data(0x67, 0x45, [1<<2])
else:
#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)
try:
i = [0x1, 0x3 | 0, 0x3 | 0x08, 0x3 | 0x10][val]
bus.write_i2c_block_data(0x3d, 0, [i])
except IOError:
# tusb320
if val == 0:
bus.write_i2c_block_data(0x67, 0xa, [0])
else:
bus.write_i2c_block_data(0x67, 0xa, [0x20])
bus.write_i2c_block_data(0x67, 0x8, [(val - 1) << 6])
bus.close()
last_eon_fan_val = val
@ -191,6 +172,19 @@ def thermald_thread():
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:
pandaState = messaging.recv_sock(pandaState_sock, wait=True)
msg = read_thermal(thermal_config)
@ -386,7 +380,7 @@ def thermald_thread():
msg.deviceState.chargingDisabled = power_monitor.should_disable_charging(pandaState, off_ts)
# 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}")
# TODO: add function for blocking cloudlog instead of sleep
time.sleep(10)

@ -16,7 +16,7 @@ if arch == "Darwin":
qt_env['FRAMEWORKS'] += ['OpenCL']
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",
"#phonelibs/qrcode/QrCode.cc"]
if arch != 'aarch64':

@ -50,19 +50,19 @@ static void draw_chevron(UIState *s, float x, float y, float sz, NVGcolor fillCo
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) {
const int img_size = size * 1.5;
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) {
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);
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 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) {
@ -215,18 +215,18 @@ static void ui_draw_vision_speed(UIState *s) {
static void ui_draw_vision_event(UIState *s) {
if (s->scene.controls_state.getEngageable()) {
// draw steering wheel
const int bg_wheel_size = 96;
const int bg_wheel_x = s->viz_rect.right() - bg_wheel_size - bdr_s * 2;
const int bg_wheel_y = s->viz_rect.y + (bg_wheel_size / 2) + (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);
const int radius = 96;
const int center_x = s->viz_rect.right() - radius - bdr_s * 2;
const int center_y = s->viz_rect.y + radius + (bdr_s * 1.5);
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) {
const int face_size = 96;
const int face_x = (s->viz_rect.x + face_size + (bdr_s * 2));
const int face_y = (s->viz_rect.bottom() - footer_h + ((footer_h - face_size) / 2));
ui_draw_circle_image(s, face_x, face_y, face_size, "driver_face", s->scene.dmonitoring_state.getIsActiveMode());
const int radius = 96;
const int center_x = s->viz_rect.x + radius + (bdr_s * 2);
const int center_y = s->viz_rect.bottom() - footer_h / 2;
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) {
@ -266,10 +266,10 @@ static void ui_draw_driver_view(UIState *s) {
}
// draw face icon
const int face_size = 85;
const int icon_x = is_rhd ? rect.right() - face_size - bdr_s * 2 : rect.x + face_size + bdr_s * 2;
const int icon_y = rect.bottom() - face_size - bdr_s * 2.5;
ui_draw_circle_image(s, icon_x, icon_y, face_size, "driver_face", face_detected);
const int face_radius = 85;
const int center_x = is_rhd ? rect.right() - face_radius - bdr_s * 2 : rect.x + face_radius + bdr_s * 2;
const int center_y = rect.bottom() - face_radius - bdr_s * 2.5;
ui_draw_circle_image(s, center_x, center_y, face_radius, "driver_face", face_detected);
}
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) {
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);
glDisable(GL_BLEND);
}

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

@ -51,7 +51,8 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
void HomeWindow::mousePressEvent(QMouseEvent* e) {
UIState* ui_state = &glWindow->ui_state;
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;
}
@ -87,7 +88,7 @@ OffroadHome::OffroadHome(QWidget* parent) : QWidget(parent) {
QObject::connect(alert_notification, SIGNAL(released()), this, SLOT(openAlerts()));
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")));
version->setStyleSheet(R"(font-size: 55px;)");
header_layout->addWidget(version, 0, Qt::AlignHCenter | Qt::AlignRight);
@ -153,7 +154,7 @@ void OffroadHome::refresh() {
// update alerts
alerts_widget->refresh();
if (!alerts_widget->alerts.size() && !alerts_widget->updateAvailable) {
if (!alerts_widget->alertCount && !alerts_widget->updateAvailable) {
emit closeAlerts();
alert_notification->setVisible(false);
return;
@ -162,7 +163,7 @@ void OffroadHome::refresh() {
if (alerts_widget->updateAvailable) {
alert_notification->setText("UPDATE");
} else {
int alerts = alerts_widget->alerts.size();
int alerts = alerts_widget->alertCount;
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);
QObject::connect(timer, SIGNAL(timeout()), this, SLOT(timerUpdate()));
backlight_timer = new QTimer(this);
QObject::connect(backlight_timer, SIGNAL(timeout()), this, SLOT(backlightUpdate()));
int result = read_param(&brightness_b, "BRIGHTNESS_B", true);
result += read_param(&brightness_m, "BRIGHTNESS_M", true);
if (result != 0) {
brightness_b = 10.0;
brightness_m = 0.1;
}
smooth_brightness = BACKLIGHT_OFFROAD;
brightness_b = Params().get<float>("BRIGHTNESS_B").value_or(10.0);
brightness_m = Params().get<float>("BRIGHTNESS_M").value_or(0.1);
}
GLWindow::~GLWindow() {
@ -264,16 +260,12 @@ void GLWindow::initializeGL() {
void GLWindow::backlightUpdate() {
// 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);
if (!ui_state.scene.started) {
clipped_brightness = BACKLIGHT_OFFROAD;
}
smooth_brightness = clipped_brightness * k + smooth_brightness * (1.0f - k);
int brightness = smooth_brightness;
int brightness = brightness_filter.update(clipped_brightness);
if (!ui_state.awake) {
brightness = 0;
emit screen_shutoff();
@ -321,7 +313,7 @@ void GLWindow::paintGL() {
double cur_draw_t = millis_since_boot();
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
LOGW("slow frame(%llu) time: %.2f", ui_state.sm->frame, dt);
}

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

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

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

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

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

@ -15,12 +15,11 @@
#include "common/params.h"
#include "common/util.h"
#include "selfdrive/hardware/hw.h"
#include "home.hpp"
QWidget * toggles_panel() {
QVBoxLayout *toggles_list = new QVBoxLayout();
toggles_list->setMargin(50);
toggles_list->addWidget(new ParamControl("OpenpilotEnabledToggle",
"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.",
@ -62,7 +61,7 @@ QWidget * toggles_panel() {
"In this mode openpilot will ignore lanelines and just drive how it thinks a human would.",
"../assets/offroad/icon_road.png"));
bool record_lock = Params().read_db_bool("RecordFrontLock");
bool record_lock = Params().getBool("RecordFrontLock");
record_toggle->setEnabled(!record_lock);
QWidget *widget = new QWidget;
@ -72,63 +71,53 @@ QWidget * toggles_panel() {
DevicePanel::DevicePanel(QWidget* parent) : QWidget(parent) {
QVBoxLayout *device_layout = new QVBoxLayout;
device_layout->setMargin(100);
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(new ButtonControl("Driver Camera", "PREVIEW",
"Preview the driver facing camera to help optimize device mounting position for best driver monitoring experience. (vehicle must be off)",
[=]() { Params().write_db_value("IsDriverViewEnabled", "1", 1); }));
QString serial = QString::fromStdString(params.get("HardwareSerial", false));
device_layout->addWidget(new LabelControl("Serial", serial));
device_layout->addWidget(horizontal_line());
// offroad-only buttons
QList<ButtonControl*> offroad_btns;
// TODO: show current calibration values
device_layout->addWidget(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().delete_db_value("CalibrationParams");
}
}));
offroad_btns.append(new ButtonControl("Driver Camera", "PREVIEW",
"Preview the driver facing camera to help optimize device mounting position for best driver monitoring experience. (vehicle must be off)",
[=]() {
Params().putBool("IsDriverViewEnabled", true);
GLWindow::ui_state.scene.driver_view = true; }
));
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",
"Review the rules, features, and limitations of openpilot",
[=]() {
if (ConfirmationDialog::confirm("Are you sure you want to review the training guide?")) {
Params().delete_db_value("CompletedTrainingVersion");
emit reviewTrainingGuide();
}
}));
offroad_btns.append(new ButtonControl("Review Training Guide", "REVIEW",
"Review the rules, features, and limitations of openpilot", [=]() {
if (ConfirmationDialog::confirm("Are you sure you want to review the training guide?")) {
Params().remove("CompletedTrainingVersion");
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";
device_layout->addWidget(new ButtonControl("Uninstall " + brand, "UNINSTALL",
"",
[=]() {
if (ConfirmationDialog::confirm("Are you sure you want to uninstall?")) {
Params().write_db_value("DoUninstall", "1");
}
}));
for(auto &btn : offroad_btns){
device_layout->addWidget(horizontal_line());
QObject::connect(parent, SIGNAL(offroadTransition(bool)), btn, SLOT(setEnabled(bool)));
device_layout->addWidget(btn);
}
// power buttons
QHBoxLayout *power_layout = new QHBoxLayout();
@ -166,16 +155,15 @@ DevicePanel::DevicePanel(QWidget* parent) : QWidget(parent) {
DeveloperPanel::DeveloperPanel(QWidget* parent) : QFrame(parent) {
QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout->setMargin(100);
setLayout(main_layout);
setStyleSheet(R"(QLabel {font-size: 50px;})");
}
void DeveloperPanel::showEvent(QShowEvent *event) {
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 = {
{"Version", brand + " v" + params.get("Version", false)},
{"Version", brand + " v" + params.get("Version", false).substr(0, 14)},
{"Git Branch", params.get("GitBranch", false)},
{"Git Commit", params.get("GitCommit", false).substr(0, 10)},
{"Panda Firmware", params.get("PandaFirmwareHex", false)},
@ -200,25 +188,15 @@ void DeveloperPanel::showEvent(QShowEvent *event) {
QWidget * network_panel(QWidget * parent) {
#ifdef QCOM
QVBoxLayout *layout = new QVBoxLayout;
layout->setMargin(100);
layout->setSpacing(30);
// simple 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 ''";
// wifi + tethering buttons
layout->addWidget(new ButtonControl("WiFi Settings", "OPEN", "",
[=]() { std::system(launch_wifi); }));
[=]() { HardwareEon::launch_wifi(); }));
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", "",
[=]() { std::system(launch_tethering); }));
[=]() { HardwareEon::launch_tethering(); }));
layout->addWidget(horizontal_line());
// SSH key management
@ -241,6 +219,10 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
QVBoxLayout *sidebar_layout = new QVBoxLayout();
sidebar_layout->setMargin(0);
panel_widget = new QStackedWidget();
panel_widget->setStyleSheet(R"(
border-radius: 30px;
background-color: #292929;
)");
// close button
QPushButton *close_btn = new QPushButton("X");
@ -273,7 +255,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
QPushButton *btn = new QPushButton(name);
btn->setCheckable(true);
btn->setStyleSheet(R"(
* {
QPushButton {
color: grey;
border: none;
background: none;
@ -290,8 +272,25 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
nav_btns->addButton(btn);
sidebar_layout->addWidget(btn, 0, Qt::AlignRight);
panel_widget->addWidget(panel);
QObject::connect(btn, &QPushButton::released, [=, w = panel]() {
panel->setContentsMargins(50, 25, 50, 25);
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);
});
}
@ -305,25 +304,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
sidebar_widget->setLayout(sidebar_layout);
sidebar_widget->setFixedWidth(500);
settings_layout->addWidget(sidebar_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);
settings_layout->addWidget(panel_widget);
setLayout(settings_layout);
setStyleSheet(R"(

@ -39,6 +39,7 @@ public:
signals:
void closeSettings();
void offroadTransition(bool offroad);
void reviewTrainingGuide();
private:
@ -46,5 +47,4 @@ private:
QWidget *sidebar_widget;
QButtonGroup *nav_btns;
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 "qt_sound.hpp"
#include "sound.hpp"
QtSound::QtSound() {
Sound::Sound() {
for (auto &kv : sound_map) {
auto path = QUrl::fromLocalFile(kv.second.first);
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;
sounds[alert].setLoopCount(loops);
sounds[alert].setVolume(volume);
sounds[alert].play();
return true;
}
void QtSound::stop() {
void Sound::stop() {
for (auto &kv : sounds) {
// Only stop repeating sounds
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
#include <map>
#include <QSoundEffect>
#include "cereal/gen/cpp/log.capnp.h"
typedef cereal::CarControl::HUDControl::AudibleAlert AudibleAlert;
@ -18,8 +20,11 @@ static std::map<AudibleAlert, std::pair<const char *, int>> sound_map {
class Sound {
public:
virtual ~Sound() {}
virtual bool play(AudibleAlert alert) = 0;
virtual void stop() = 0;
virtual void setVolume(int volume) = 0;
Sound();
void play(AudibleAlert alert);
void stop();
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->setMargin(0);
hlayout->setSpacing(50);
hlayout->setSpacing(20);
// left icon
if (!icon.isEmpty()) {
@ -33,7 +33,7 @@ AbstractControl::AbstractControl(const QString &title, const QString &desc, cons
// 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);
vlayout->addLayout(hlayout);
@ -53,4 +53,5 @@ AbstractControl::AbstractControl(const QString &title, const QString &desc, cons
}
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) {
btn.setText(text);
btn.setStyleSheet(R"(
padding: 0;
border-radius: 50px;
font-size: 35px;
font-weight: 500;
color: #E4E4E4;
background-color: #393939;
QPushButton {
padding: 0;
border-radius: 50px;
font-size: 35px;
font-weight: 500;
color: #E4E4E4;
background-color: #393939;
}
QPushButton:disabled {
color: #33E4E4E4;
}
)");
btn.setFixedSize(250, 100);
QObject::connect(&btn, &QPushButton::released, functor);
@ -66,6 +71,11 @@ public:
}
void setText(const QString &text) { btn.setText(text); }
public slots:
void setEnabled(bool enabled) {
btn.setEnabled(enabled);
};
private:
QPushButton btn;
};
@ -99,12 +109,11 @@ class ParamControl : public ToggleControl {
public:
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
if (Params().read_db_bool(param.toStdString().c_str())) {
if (Params().getBool(param.toStdString().c_str())) {
toggle.togglePosition();
}
QObject::connect(this, &ToggleControl::toggleFlipped, [=](int state) {
char value = state ? '1' : '0';
Params().write_db_value(param.toStdString().c_str(), &value, 1);
Params().putBool(param.toStdString().c_str(), (bool)state);
});
}
};

@ -36,7 +36,7 @@ void DriveStats::parseResponse(QString response) {
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();
update(json["all"].toObject(), all_, 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);
};
const char* distance_unit = Params().read_db_bool("IsMetric") ? "KM" : "MILES";
const char* distance_unit = Params().getBool("IsMetric") ? "KM" : "MILES";
QGridLayout* gl = new QGridLayout();
gl->setMargin(0);
gl->addWidget(new QLabel("ALL TIME"), 0, 0, 1, 3);

@ -1,47 +1,52 @@
#include <QFile>
#include <QLabel>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QJsonObject>
#include <QJsonDocument>
#include <QDebug>
#include "offroad_alerts.hpp"
#include "common/params.h"
#include "selfdrive/hardware/hw.h"
void cleanStackedWidget(QStackedWidget* swidget) {
while(swidget->count() > 0) {
QWidget *w = swidget->widget(0);
swidget->removeWidget(w);
w->deleteLater();
}
}
#include "selfdrive/common/util.h"
OffroadAlert::OffroadAlert(QWidget* parent) : QFrame(parent) {
QVBoxLayout *main_layout = new QVBoxLayout();
main_layout->setMargin(25);
QVBoxLayout *layout = new QVBoxLayout();
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();
main_layout->addWidget(alerts_stack, 1);
// release notes
releaseNotes.setVisible(false);
releaseNotes.setStyleSheet("font-size: 48px;");
layout->addWidget(&releaseNotes);
// bottom footer
// bottom footer, dismiss + reboot buttons
QHBoxLayout *footer_layout = new QHBoxLayout();
main_layout->addLayout(footer_layout);
layout->addLayout(footer_layout);
QPushButton *dismiss_btn = new QPushButton("Dismiss");
dismiss_btn->setFixedSize(400, 125);
footer_layout->addWidget(dismiss_btn, 0, 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);
footer_layout->addWidget(dismiss_btn, 0, Qt::AlignBottom | Qt::AlignLeft);
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"(
* {
font-size: 48px;
@ -58,54 +63,33 @@ OffroadAlert::OffroadAlert(QWidget* parent) : QFrame(parent) {
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() {
parse_alerts();
cleanStackedWidget(alerts_stack);
updateAvailable = Params().read_db_bool("UpdateAvailable");
reboot_btn->setVisible(updateAvailable);
updateAlerts();
QVBoxLayout *layout = new QVBoxLayout;
layout->setSpacing(20);
rebootBtn.setVisible(updateAvailable);
releaseNotes.setVisible(updateAvailable);
releaseNotes.setText(QString::fromStdString(params.get("ReleaseNotes")));
if (updateAvailable) {
QLabel *body = new QLabel(QString::fromStdString(Params().get("ReleaseNotes")));
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);
}
for (const auto& [k, label] : alerts) {
label->setVisible(!updateAvailable && !label->text().isEmpty());
}
QWidget *w = new QWidget();
w->setLayout(layout);
alerts_stack->addWidget(w);
}
void OffroadAlert::parse_alerts() {
alerts.clear();
for (const QString &key : alert_keys) {
std::vector<char> bytes = Params().read_db_bytes(key.toStdString().c_str());
void OffroadAlert::updateAlerts() {
alertCount = 0;
updateAvailable = params.getBool("UpdateAvailable");
for (const auto& [key, label] : alerts) {
auto bytes = params.get(key.c_str());
if (bytes.size()) {
QJsonDocument doc_par = QJsonDocument::fromJson(QByteArray(bytes.data(), bytes.size()));
QJsonObject obj = doc_par.object();
Alert alert = {obj.value("text").toString(), obj.value("severity").toInt()};
alerts.push_back(alert);
label->setText(obj.value("text").toString());
alertCount++;
} else {
label->setText("");
}
}
}

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

@ -1,12 +1,19 @@
#include <QNetworkReply>
#include <QHBoxLayout>
#include "widgets/input.hpp"
#include "widgets/ssh_keys.hpp"
#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.", "") {
// setup widget
hlayout->addStretch(1);
username_label.setAlignment(Qt::AlignVCenter);
username_label.setStyleSheet("color: #aaaaaa");
hlayout->addWidget(&username_label);
btn.setStyleSheet(R"(
padding: 0;
border-radius: 50px;
@ -27,7 +34,8 @@ SshControl::SshControl() : AbstractControl("SSH Keys", "Warning: This grants SSH
getUserKeys(username);
}
} else {
Params().delete_db_value("GithubSshKeys");
Params().remove("GithubUsername");
Params().remove("GithubSshKeys");
refresh();
}
});
@ -45,8 +53,10 @@ SshControl::SshControl() : AbstractControl("SSH Keys", "Warning: This grants SSH
void SshControl::refresh() {
QString param = QString::fromStdString(Params().get("GithubSshKeys"));
if (param.length()) {
username_label.setText(QString::fromStdString(Params().get("GithubUsername")));
btn.setText("REMOVE");
} else {
username_label.setText("");
btn.setText("ADD");
}
btn.setEnabled(true);
@ -79,7 +89,8 @@ void SshControl::parseResponse(){
networkTimer->stop();
QString response = reply->readAll();
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){
err = "Username '" + username + "' has no keys on GitHub";
} else {

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

@ -1,4 +1,5 @@
#include "window.hpp"
#include "selfdrive/hardware/hw.h"
MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
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(closeSettings()), this, SLOT(closeSettings()));
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(reviewTrainingGuide()), this, SLOT(reviewTrainingGuide()));
@ -54,8 +56,20 @@ void MainWindow::reviewTrainingGuide() {
}
bool MainWindow::eventFilter(QObject *obj, QEvent *event){
// wake screen on tap
if (event->type() == QEvent::MouseButtonPress) {
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;
}

@ -10,13 +10,6 @@
#include "ui.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
// image space.
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")) {
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")) {
for (auto sensor : sm["sensorEvents"].getSensorEvents()) {
@ -284,14 +272,13 @@ static void update_alert(UIState *s) {
static void update_params(UIState *s) {
const uint64_t frame = s->sm->frame;
UIScene &scene = s->scene;
Params params;
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) {
scene.athenaStatus = NET_DISCONNECTED;
uint64_t last_ping = 0;
if (read_param(&last_ping, "LastAthenaPingTime") == 0) {
scene.athenaStatus = nanos_since_boot() - last_ping < 70e9 ? NET_CONNECTED : NET_ERROR;
if (auto last_ping = params.get<float>("LastAthenaPingTime"); last_ping) {
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->scene.started_frame = s->sm->frame;
read_param(&s->scene.is_rhd, "IsRHD");
read_param(&s->scene.end_to_end, "EndToEndToggle");
s->scene.is_rhd = Params().getBool("IsRHD");
s->scene.end_to_end = Params().getBool("EndToEndToggle");
s->sidebar_collapsed = true;
s->scene.alert_size = cereal::ControlsState::AlertSize::NONE;
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/glutil.h"
#include "common/transformations/orientation.hpp"
#include "sound.hpp"
#include "qt/sound.hpp"
#include "visionipc.h"
#include "visionipc_client.h"
@ -171,28 +171,3 @@ typedef struct UIState {
void ui_init(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)
QLOG_FILENAMES = ['qlog.bz2']
QCAMERA_FILENAMES = ['qcamera.ts']
LOG_FILENAMES = ['rlog.bz2', 'raw_log.bz2']
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}
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):
api = CommaApi(get_token())
route_files = api.get('v1/route/' + self.route_name + '/files')
@ -53,14 +58,16 @@ class Route(object):
segment_name,
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 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:
segments[segment_name] = RouteSegment(
segment_name,
url if fn in LOG_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)
@ -110,18 +117,24 @@ class Route(object):
except StopIteration:
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:
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)
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.log_path = log_path
self.qlog_path = qlog_path
self.camera_path = camera_path
self.qcamera_path = qcamera_path
@property
def name(self):

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

Loading…
Cancel
Save