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

# Conflicts:
#	selfdrive/car/volkswagen/interface.py
#	selfdrive/car/volkswagen/values.py
pull/20489/head
Jason Young 4 years ago
commit a3ddd1fcb6
  1. 25
      Jenkinsfile
  2. 4
      README.md
  3. 2
      RELEASES.md
  4. 3
      common/params_pyx.pyx
  5. 8
      installer/updater/update.json
  6. 3
      launch_chffrplus.sh
  7. 2
      launch_env.sh
  8. 2
      panda
  9. 1
      release/files_common
  10. 66
      selfdrive/camerad/test/test_camerad.py
  11. 68
      selfdrive/camerad/test/test_exposure.py
  12. 17
      selfdrive/car/volkswagen/interface.py
  13. 81
      selfdrive/car/volkswagen/values.py
  14. 150
      selfdrive/debug/internal/run_paramsd_on_route.py
  15. 12
      selfdrive/test/test_car_models.py
  16. 20
      selfdrive/ui/qt/api.cc
  17. 4
      selfdrive/ui/qt/api.hpp
  18. 94
      selfdrive/ui/qt/widgets/drive_stats.cc
  19. 8
      selfdrive/ui/qt/widgets/drive_stats.hpp
  20. 23
      selfdrive/ui/qt/widgets/setup.cc
  21. 5
      selfdrive/ui/qt/widgets/setup.hpp
  22. 4
      selfdrive/ui/sidebar.cc
  23. 4
      selfdrive/ui/ui.hpp

25
Jenkinsfile vendored

@ -11,6 +11,9 @@ export GIT_BRANCH=${env.GIT_BRANCH}
export GIT_COMMIT=${env.GIT_COMMIT}
source ~/.bash_profile
if [ -f /TICI ]; then
source /etc/profile
fi
ln -snf ${env.TEST_DIR} /data/pythonpath
@ -151,7 +154,6 @@ pipeline {
["test boardd loopback", "nosetests -s selfdrive/boardd/tests/test_boardd_loopback.py"],
["test loggerd", "python selfdrive/loggerd/tests/test_loggerd.py"],
["test encoder", "python selfdrive/loggerd/tests/test_encoder.py"],
["test camerad", "python selfdrive/camerad/test/test_camerad.py"],
["test logcatd", "python selfdrive/logcatd/tests/test_logcatd_android.py"],
//["test updater", "python installer/updater/test_updater.py"],
])
@ -167,12 +169,31 @@ pipeline {
["build", "SCONS_CACHE=1 scons -j16"],
["test loggerd", "python selfdrive/loggerd/tests/test_loggerd.py"],
["test encoder", "LD_LIBRARY_PATH=/usr/local/lib python selfdrive/loggerd/tests/test_encoder.py"],
["test camerad", "python selfdrive/camerad/test/test_camerad.py"],
//["build release3-staging", "cd release && PUSH=${env.R3_PUSH} ./build_release3.sh"],
])
}
}
stage('camerad') {
steps {
phone_steps("eon-party", [
["build", "SCONS_CACHE=1 scons -j16"],
["test camerad", "python selfdrive/camerad/test/test_camerad.py"],
["test exposure", "python selfdrive/camerad/test/test_exposure.py"],
])
}
}
stage('Tici camerad') {
steps {
phone_steps("tici-party", [
["build", "SCONS_CACHE=1 scons -j16"],
["test camerad", "python selfdrive/camerad/test/test_camerad.py"],
["test exposure", "python selfdrive/camerad/test/test_exposure.py"],
])
}
}
}
}
}

@ -169,16 +169,20 @@ Community Maintained Cars and Features
| 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 |
| 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 |
<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.
Although it's not upstream, there's a community of people getting openpilot to run on Tesla's [here](https://tinkla.us/)

@ -10,7 +10,9 @@ Version 0.8.3 (2021-XX-XX)
* Default SSH key only used for setup
* Kia Ceed 2019 support thanks to ZanZaD13!
* Kia Seltos 2021 support thanks to speedking456!
* Škoda Kodiaq 2018 support thanks to jyoung8607!
* Volkswagen Tiguan 2020 support thanks to jyoung8607!
* Volkswagen Jetta 2018-2021 support thanks to jyoung8607!
Version 0.8.2 (2021-02-26)
========================

@ -15,6 +15,9 @@ cdef enum TxType:
keys = {
b"AccessToken": [TxType.CLEAR_ON_MANAGER_START],
b"ApiCache_DriveStats": [TxType.PERSISTENT],
b"ApiCache_Device": [TxType.PERSISTENT],
b"ApiCache_Owner": [TxType.PERSISTENT],
b"AthenadPid": [TxType.PERSISTENT],
b"CalibrationParams": [TxType.PERSISTENT],
b"CarBatteryCapacity": [TxType.PERSISTENT],

@ -1,7 +1,7 @@
{
"ota_url": "https://commadist.azureedge.net/neosupdate/ota-signed-c082c7c3365829b9df9cbbc5b20a9aed3cc5c98ebb17351e7e00e6285072c403.zip",
"ota_hash": "c082c7c3365829b9df9cbbc5b20a9aed3cc5c98ebb17351e7e00e6285072c403",
"recovery_url": "https://commadist.azureedge.net/neosupdate/recovery-a7c7d5d54b9f3afa6ae3d22ceab44f018b819c02443e4a09578089fbdb2ec4ee.img",
"ota_url": "https://commadist.azureedge.net/neosupdate/ota-signed-969e22c42e5c6314e54bc3ccaa5c6a684f3130a53a7a70e0cea9f1453ceb0b06.zip",
"ota_hash": "969e22c42e5c6314e54bc3ccaa5c6a684f3130a53a7a70e0cea9f1453ceb0b06",
"recovery_url": "https://commadist.azureedge.net/neosupdate/recovery-9c784a24826c25df315d0ace864224478e9c0e86b904f5d1f8e18ea1037e842b.img",
"recovery_len": 15209772,
"recovery_hash": "a7c7d5d54b9f3afa6ae3d22ceab44f018b819c02443e4a09578089fbdb2ec4ee"
"recovery_hash": "9c784a24826c25df315d0ace864224478e9c0e86b904f5d1f8e18ea1037e842b"
}

@ -10,9 +10,6 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
function two_init {
export QT_QPA_EGLFS_PHYSICAL_WIDTH=151
export QT_QPA_EGLFS_PHYSICAL_HEIGHT=74
# Wifi scan
wpa_cli IFNAME=wlan0 SCAN

@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1
export VECLIB_MAXIMUM_THREADS=1
if [ -z "$REQUIRED_NEOS_VERSION" ]; then
export REQUIRED_NEOS_VERSION="16.1"
export REQUIRED_NEOS_VERSION="16.2"
fi
if [ -z "$AGNOS_VERSION" ]; then

@ -1 +1 @@
Subproject commit ad9ecefe65ce4ea7bcaf3bfe692ee2a3fc6b0b63
Subproject commit f146aa367b82f041b4eadcaab0bb2fc79e388b94

@ -336,6 +336,7 @@ selfdrive/test/helpers.py
selfdrive/test/setup_device_ci.sh
selfdrive/test/test_fingerprints.py
selfdrive/ui/.gitignore
selfdrive/ui/SConscript
selfdrive/ui/*.cc
selfdrive/ui/*.hpp

@ -2,11 +2,9 @@
import time
import unittest
import numpy as np
import cereal.messaging as messaging
from selfdrive.test.helpers import with_processes
from selfdrive.camerad.snapshot.snapshot import get_snapshots
# only tests for EON and TICI
from selfdrive.hardware import EON, TICI
@ -31,70 +29,6 @@ class TestCamerad(unittest.TestCase):
if not (EON or TICI):
raise unittest.SkipTest
# assert "SEND_REAR" in os.environ
# assert "SEND_FRONT" in os.environ
def _numpy_bgr2gray(self, im):
ret = np.clip(im[:,:,0] * 0.114 + im[:,:,1] * 0.587 + im[:,:,2] * 0.299, 0, 255).astype(np.uint8)
return ret
def _numpy_lap(self, im):
ret = np.zeros(im.shape)
ret += -4 * im
ret += np.concatenate([np.zeros((im.shape[0],1)),im[:,:-1]], axis=1)
ret += np.concatenate([im[:,1:],np.zeros((im.shape[0],1))], axis=1)
ret += np.concatenate([np.zeros((1,im.shape[1])),im[:-1,:]], axis=0)
ret += np.concatenate([im[1:,:],np.zeros((1,im.shape[1]))], axis=0)
ret = np.clip(ret, 0, 255).astype(np.uint8)
return ret
def _is_really_sharp(self, i, threshold=800, roi_max=np.array([8,6]), roi_xxyy=np.array([1,6,2,3])):
i = self._numpy_bgr2gray(i)
x_pitch = i.shape[1] // roi_max[0]
y_pitch = i.shape[0] // roi_max[1]
lap = self._numpy_lap(i)
lap_map = np.zeros((roi_max[1], roi_max[0]))
for r in range(lap_map.shape[0]):
for c in range(lap_map.shape[1]):
selected_lap = lap[r*y_pitch:(r+1)*y_pitch, c*x_pitch:(c+1)*x_pitch]
lap_map[r][c] = 5*selected_lap.var() + selected_lap.max()
print(lap_map[roi_xxyy[2]:roi_xxyy[3]+1,roi_xxyy[0]:roi_xxyy[1]+1])
if (lap_map[roi_xxyy[2]:roi_xxyy[3]+1,roi_xxyy[0]:roi_xxyy[1]+1] > threshold).sum() > \
(roi_xxyy[1]+1-roi_xxyy[0]) * (roi_xxyy[3]+1-roi_xxyy[2]) * 0.9:
return True
else:
return False
def _is_exposure_okay(self, i, med_ex=np.array([0.2,0.4]), mean_ex=np.array([0.2,0.6])):
i = self._numpy_bgr2gray(i)
i_median = np.median(i) / 256
i_mean = np.mean(i) / 256
print([i_median, i_mean])
return med_ex[0] < i_median < med_ex[1] and mean_ex[0] < i_mean < mean_ex[1]
@unittest.skip # skip for now
@with_processes(['camerad'])
def test_camera_operation(self):
print("checking image outputs")
if EON:
# run checks similar to prov
time.sleep(15) # wait for startup and AF
pic, fpic = get_snapshots()
self.assertTrue(self._is_really_sharp(pic))
self.assertTrue(self._is_exposure_okay(pic))
self.assertTrue(self._is_exposure_okay(fpic))
time.sleep(30)
# check again for consistency
pic, fpic = get_snapshots()
self.assertTrue(self._is_really_sharp(pic))
self.assertTrue(self._is_exposure_okay(pic))
self.assertTrue(self._is_exposure_okay(fpic))
elif TICI:
raise unittest.SkipTest # TBD
else:
raise unittest.SkipTest
@with_processes(['camerad'])
def test_frame_packets(self):
print("checking frame pkts continuity")

@ -0,0 +1,68 @@
#!/usr/bin/env python3
import time
import unittest
import os
import numpy as np
from selfdrive.test.helpers import with_processes
from selfdrive.camerad.snapshot.snapshot import get_snapshots
from selfdrive.hardware import EON, TICI
TEST_TIME = 45
REPEAT = 5
os.environ["SEND_ROAD"] = "1"
os.environ["SEND_DRIVER"] = "1"
if TICI:
os.environ["SEND_WIDE_ROAD"] = "1"
class TestCamerad(unittest.TestCase):
@classmethod
def setUpClass(cls):
if not (EON or TICI):
raise unittest.SkipTest
def _numpy_rgb2gray(self, im):
ret = np.clip(im[:,:,2] * 0.114 + im[:,:,1] * 0.587 + im[:,:,0] * 0.299, 0, 255).astype(np.uint8)
return ret
def _is_exposure_okay(self, i, med_mean=np.array([[0.2,0.4],[0.2,0.6]])):
h, w = i.shape[:2]
i = i[h//10:9*h//10,w//10:9*w//10]
med_ex, mean_ex = med_mean
i = self._numpy_rgb2gray(i)
i_median = np.median(i) / 255.
i_mean = np.mean(i) / 255.
print([i_median, i_mean])
return med_ex[0] < i_median < med_ex[1] and mean_ex[0] < i_mean < mean_ex[1]
@with_processes(['camerad'])
def test_camera_operation(self):
print("checking image outputs")
start = time.time()
passed = 0
while(time.time() - start < TEST_TIME and passed < REPEAT):
rpic, dpic = get_snapshots(frame="roadCameraState", front_frame="driverCameraState")
res = self._is_exposure_okay(rpic)
res = res and self._is_exposure_okay(dpic)
if TICI:
wpic, _ = get_snapshots(frame="wideRoadCameraState")
res = res and self._is_exposure_okay(wpic)
if passed > 0 and not res:
passed = -passed # fails test if any failure after first sus
break
passed += int(res)
time.sleep(2)
print(passed)
self.assertTrue(passed >= REPEAT)
if __name__ == "__main__":
unittest.main()

@ -67,21 +67,36 @@ class CarInterface(CarInterfaceBase):
ret.mass = 1328 + STD_CARGO_KG
ret.wheelbase = 2.71
elif candidate == CAR.PASSAT_MK8:
# Averages of all 3C Passat variants
ret.mass = 1551 + STD_CARGO_KG
ret.wheelbase = 2.79
elif candidate == CAR.TIGUAN_MK2:
# Average of SWB and LWB variants
ret.mass = 1715 + STD_CARGO_KG
ret.wheelbase = 2.74
elif candidate == CAR.AUDI_A3_MK3:
elif candidate == CAR.AUDI_A3:
# Averages of all 8V A3 variants
ret.mass = 1335 + STD_CARGO_KG
ret.wheelbase = 2.61
elif candidate == CAR.SEAT_ATECA_MK1:
# Averages of all 5F Ateca variants
ret.mass = 1900 + STD_CARGO_KG
ret.wheelbase = 2.64
elif candidate == CAR.SKODA_KODIAQ_MK1:
# Averages of all 5N Kodiaq variants
ret.mass = 1569 + STD_CARGO_KG
ret.wheelbase = 2.79
elif candidate == CAR.SKODA_SCALA_MK1:
# Averages of all NW Scala variants
ret.mass = 1192 + STD_CARGO_KG
ret.wheelbase = 2.65
ret.centerToFront = ret.wheelbase * 0.45
ret.enableCamera = True # Stock camera detection doesn't apply to VW

@ -57,9 +57,12 @@ MQB_LDW_MESSAGES = {
class CAR:
GOLF = "VOLKSWAGEN GOLF" # 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
AUDI_A3_MK3 = "AUDI A3 3RD GEN" # Chassis 8V/FF, Mk3 Audi A3 and variants
AUDI_A3 = "AUDI A3" # Chassis 8V/FF, Mk3 Audi A3 and variants
SEAT_ATECA_MK1 = "SEAT ATECA 1ST GEN" # Chassis 5F, Mk1 SEAT Ateca and CUPRA Ateca
SKODA_KODIAQ_MK1 = "SKODA KODIAQ 1ST GEN" # Chassis NS, Mk1 Skoda Kodiaq
SKODA_SCALA_MK1 = "SKODA SCALA 1ST GEN" # Chassis NW, Mk1 Skoda Scala and Skoda Kamiq
FINGERPRINTS = {
CAR.GOLF: [{
@ -68,18 +71,27 @@ FINGERPRINTS = {
CAR.JETTA_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, 376: 8, 418: 8, 427: 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, 828: 8, 870: 8, 879: 8, 884: 8, 888: 8, 891: 8, 901: 8, 913: 8, 919: 8, 949: 8, 958: 8, 960: 4, 981: 8, 987: 8, 988: 8, 991: 8, 997: 8, 1000: 8, 1019: 8, 1122: 8, 1123: 8, 1124: 8, 1153: 8, 1156: 8, 1157: 8, 1158: 8, 1162: 8, 1312: 8, 1343: 8, 1385: 8, 1413: 8, 1440: 5, 1471: 4, 1514: 8, 1515: 8, 1520: 8, 1600: 8, 1601: 8, 1603: 8, 1605: 8, 1624: 8, 1626: 8, 1629: 8, 1631: 8, 1635: 8, 1646: 8, 1648: 8, 1712: 6, 1714: 8, 1716: 8, 1717: 8, 1719: 8, 1720: 8
}],
CAR.PASSAT_MK8: [{
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, 295: 8, 299: 8, 302: 8, 346: 8, 385: 8, 391: 8, 427: 8, 668: 8, 679: 8, 681: 8, 695: 8, 779: 8, 780: 8, 783: 8, 787: 8, 788: 8, 789: 8, 791: 8, 792: 8, 799: 8, 802: 8, 804: 8, 806: 8, 807: 8, 808: 8, 809: 8, 828: 8, 838: 8, 839: 8, 840: 8, 841: 8, 842: 8, 843: 8, 844: 8, 845: 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, 1438: 8, 1440: 5, 1461: 8, 1514: 8, 1515: 8, 1520: 8, 1529: 8, 1600: 8, 1601: 8, 1603: 8, 1624: 8, 1629: 8, 1631: 8, 1646: 8, 1648: 8, 1712: 6, 1714: 8, 1716: 8, 1717: 8, 1719: 8, 1720: 8, 1721: 8
}],
CAR.TIGUAN_MK2: [{
64: 8, 134: 8, 159: 8, 173: 8, 178: 8, 253: 8, 257: 8, 260: 8, 262: 8, 278: 8, 279: 8, 283: 8, 286: 8, 288: 8, 289: 8, 290: 8, 294: 8, 299: 8, 302: 8, 346: 8, 376: 8, 418: 8, 427: 8, 573: 8, 679: 8, 681: 8, 684: 8, 695: 8, 779: 8, 780: 8, 783: 8, 787: 8, 788: 8, 789: 8, 792: 8, 795: 8, 804: 8, 806: 8, 807: 8, 808: 8, 809: 8, 828: 8, 870: 8, 879: 8, 884: 8, 888: 8, 891: 8, 896: 8, 897: 8, 898: 8, 901: 8, 913: 8, 917: 8, 919: 8, 949: 8, 958: 8, 960: 4, 981: 8, 987: 8, 988: 8, 991: 8, 997: 8, 1000: 8, 1019: 8, 1122: 8, 1123: 8, 1124: 8, 1153: 8, 1156: 8, 1157: 8, 1158: 8, 1162: 8, 1175: 8, 1312: 8, 1343: 8, 1385: 8, 1413: 8, 1440: 5, 1471: 4, 1514: 8, 1515: 8, 1520: 8, 1600: 8, 1601: 8, 1603: 8, 1605: 8, 1624: 8, 1626: 8, 1629: 8, 1631: 8, 1635: 8, 1646: 8, 1648: 8, 1712: 6, 1714: 8, 1716: 8, 1717: 8, 1719: 8, 1720: 8, 1721: 8
}],
CAR.AUDI_A3_MK3: [{
CAR.AUDI_A3: [{
64: 8, 134: 8, 159: 8, 173: 8, 178: 8, 253: 8, 257: 8, 260: 8, 262: 8, 278: 8, 279: 8, 283: 8, 285: 8, 286: 8, 288: 8, 289: 8, 290: 8, 294: 8, 295: 8, 299: 8, 302: 8, 346: 8, 418: 8, 427: 8, 506: 8, 679: 8, 681: 8, 695: 8, 779: 8, 780: 8, 783: 8, 787: 8, 788: 8, 789: 8, 792: 8, 802: 8, 804: 8, 806: 8, 807: 8, 808: 8, 809: 8, 846: 8, 847: 8, 870: 8, 896: 8, 897: 8, 898: 8, 901: 8, 917: 8, 919: 8, 949: 8, 958: 8, 960: 4, 981: 8, 987: 8, 988: 8, 991: 8, 997: 8, 1000: 8, 1019: 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, 1600: 8, 1601: 8, 1603: 8, 1624: 8, 1629: 8, 1631: 8, 1646: 8, 1648: 8, 1712: 6, 1714: 8, 1716: 8, 1717: 8, 1719: 8, 1720: 8, 1721: 8, 1792: 8, 1872: 8, 1976: 8, 1977: 8, 1982: 8, 1985: 8
}],
CAR.SEAT_ATECA_MK1: [{
64: 8, 134: 8, 159: 8, 173: 8, 178: 8, 253: 8, 257: 8, 260: 8, 262: 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, 684: 8, 779: 8, 780: 8, 792: 8, 795: 8, 804: 8, 806: 8, 807: 8, 808: 8, 809: 8, 870: 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, 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.SKODA_KODIAQ_MK1: [{
64: 8, 134: 8, 159: 8, 173: 8, 178: 8, 253: 8, 257: 8, 260: 8, 262: 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, 573: 8, 668: 8, 679: 8, 681: 8, 684: 8, 695: 8, 779: 8, 780: 8, 783: 8, 787: 8, 788: 8, 789: 8, 792: 8, 795: 8, 802: 8, 804: 8, 806: 8, 807: 8, 808: 8, 809: 8, 828: 8, 870: 8, 896: 8, 897: 8, 898: 8, 901: 8, 917: 8, 919: 8, 949: 8, 958: 8, 960: 4, 981: 8, 987: 8, 988: 8, 991: 8, 997: 8, 1000: 8, 1019: 8, 1120: 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, 1792: 8, 1871: 8, 1872: 8, 1879: 8, 1909: 8, 1976: 8, 1977: 8, 1985: 8
}],
CAR.SKODA_SCALA_MK1: [{
64: 8, 134: 8, 159: 8, 173: 8, 178: 8, 253: 8, 257: 8, 262: 8, 278: 8, 279: 8, 283: 8, 286: 8, 288: 8, 289: 8, 290: 8, 294: 8, 299: 8, 302: 8, 346: 8, 418: 8, 427: 8, 506: 8, 568: 8, 569: 8, 572: 8, 573: 8, 679: 8, 681: 8, 684: 8, 695: 8, 779: 8, 780: 8, 783: 8, 787: 8, 788: 8, 789: 8, 792: 8, 795: 8, 804: 8, 806: 8, 807: 8, 808: 8, 809: 8, 826: 8, 827: 8, 828: 8, 870: 8, 879: 8, 884: 8, 888: 8, 891: 8, 901: 8, 913: 8, 917: 8, 919: 8, 949: 8, 958: 8, 960: 4, 981: 8, 987: 8, 988: 8, 991: 8, 997: 8, 1000: 8, 1019: 8, 1122: 8, 1123: 8, 1124: 8, 1153: 8, 1156: 8, 1157: 8, 1158: 8, 1162: 8, 1175: 8, 1312: 8, 1343: 8, 1385: 8, 1413: 8, 1440: 5, 1514: 8, 1515: 8, 1520: 8, 1600: 8, 1601: 8, 1603: 8, 1605: 8, 1624: 8, 1626: 8, 1629: 8, 1631: 8, 1635: 8, 1646: 8, 1648: 8, 1712: 6, 1714: 8, 1716: 8, 1717: 8, 1719: 8, 1720: 8, 1721: 8, 1792: 8, 1872: 8, 1879: 8, 1976: 8, 1977: 8, 1982: 8, 1985: 8
}],
}
IGNORED_FINGERPRINTS = [CAR.JETTA_MK7, CAR.TIGUAN_MK2, CAR.SKODA_KODIAQ_MK1]
IGNORED_FINGERPRINTS = [CAR.JETTA_MK7, CAR.PASSAT_MK8, CAR.TIGUAN_MK2, CAR.SEAT_ATECA_MK1, CAR.SKODA_KODIAQ_MK1, CAR.SKODA_SCALA_MK1]
FW_VERSIONS = {
CAR.GOLF: {
@ -127,6 +139,28 @@ FW_VERSIONS = {
b'\xf1\x875Q0907572R \xf1\x890771',
],
},
CAR.PASSAT_MK8: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8704E906023AH\xf1\x893379',
b'\xf1\x8704L906026GA\xf1\x892013',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x870DD300045T \xf1\x891601',
b'\xf1\x870D9300014L \xf1\x895002',
],
(Ecu.srs, 0x715, None): [
b'\xf1\x875Q0959655S \xf1\x890870\xf1\x82\02315120011111200631145171716121691132111',
b'\xf1\x873Q0959655AN\xf1\x890306\xf1\x82\r58160058140013036914110311',
],
(Ecu.eps, 0x712, None): [
b'\xf1\x875Q0909143M \xf1\x892041\xf1\x820522B0080803',
b'\xf1\x875Q0909144T \xf1\x891072\xf1\x82\00521B00703A1',
],
(Ecu.fwdRadar, 0x757, None): [
b'\xf1\x875Q0907572R \xf1\x890771',
b'\xf1\x873Q0907572C \xf1\x890195',
],
},
CAR.TIGUAN_MK2: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8783A907115B \xf1\x890005',
@ -144,7 +178,7 @@ FW_VERSIONS = {
b'\xf1\x872Q0907572R \xf1\x890372',
],
},
CAR.AUDI_A3_MK3: {
CAR.AUDI_A3: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x878V0906264B \xf1\x890003',
b'\xf1\x875G0906259L \xf1\x890002',
@ -169,6 +203,23 @@ FW_VERSIONS = {
b'\xf1\x875Q0907572G \xf1\x890571',
],
},
CAR.SEAT_ATECA_MK1: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8704E906027KA\xf1\x893749',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x870D9300014S \xf1\x895202',
],
(Ecu.srs, 0x715, None): [
b'\xf1\x873Q0959655BH\xf1\x890703\xf1\x82\0161212001211001305121211052900',
],
(Ecu.eps, 0x712, None): [
b'\xf1\x873Q0909144L \xf1\x895081\xf1\x82\00571N60511A1',
],
(Ecu.fwdRadar, 0x757, None): [
b'\xf1\x872Q0907572M \xf1\x890233',
],
},
CAR.SKODA_KODIAQ_MK1: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8704E906027DD\xf1\x893123',
@ -189,12 +240,32 @@ FW_VERSIONS = {
b'\xf1\x872Q0907572R \xf1\x890372',
],
},
CAR.SKODA_SCALA_MK1: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8704C906025AK\xf1\x897053',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x870CW300050 \xf1\x891709',
],
(Ecu.srs, 0x715, None): [
b'\xf1\x872Q0959655AM\xf1\x890351\xf1\x82\022111104111104112104040404111111112H14',
],
(Ecu.eps, 0x712, None): [
b'\xf1\x872Q1909144M \xf1\x896041',
],
(Ecu.fwdRadar, 0x757, None): [
b'\xf1\x872Q0907572R \xf1\x890372',
],
},
}
DBC = {
CAR.GOLF: 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),
CAR.AUDI_A3_MK3: dbc_dict('vw_mqb_2010', None),
CAR.AUDI_A3: dbc_dict('vw_mqb_2010', None),
CAR.SEAT_ATECA_MK1: dbc_dict('vw_mqb_2010', None),
CAR.SKODA_KODIAQ_MK1: dbc_dict('vw_mqb_2010', None),
CAR.SKODA_SCALA_MK1: dbc_dict('vw_mqb_2010', None),
}

@ -0,0 +1,150 @@
#!/usr/bin/env python3
# pylint: skip-file
# flake8: noqa
# type: ignore
import math
import multiprocessing
import numpy as np
from tqdm import tqdm
from selfdrive.locationd.paramsd import ParamsLearner, States
from tools.lib.logreader import LogReader
from tools.lib.route import Route
ROUTE = "b2f1615665781088|2021-03-14--17-27-47"
PLOT = True
def load_segment(segment_name):
print(f"Loading {segment_name}")
if segment_name is None:
return []
try:
return list(LogReader(segment_name))
except ValueError as e:
print(f"Error parsing {segment_name}: {e}")
return []
if __name__ == "__main__":
route = Route(ROUTE)
msgs = []
with multiprocessing.Pool(24) as pool:
for d in pool.map(load_segment, route.log_paths()):
msgs += d
for m in msgs:
if m.which() == 'carParams':
CP = m.carParams
break
params = {
'carFingerprint': CP.carFingerprint,
'steerRatio': CP.steerRatio,
'stiffnessFactor': 1.0,
'angleOffsetAverageDeg': 0.0,
}
for m in msgs:
if m.which() == 'liveParameters':
params['steerRatio'] = m.liveParameters.steerRatio
params['angleOffsetAverageDeg'] = m.liveParameters.angleOffsetAverageDeg
break
for m in msgs:
if m.which() == 'carState':
last_carstate = m
break
print(params)
learner = ParamsLearner(CP, params['steerRatio'], params['stiffnessFactor'], math.radians(params['angleOffsetAverageDeg']))
msgs = [m for m in tqdm(msgs) if m.which() in ('liveLocationKalman', 'carState', 'liveParameters')]
msgs = sorted(msgs, key=lambda m: m.logMonoTime)
ts = []
ts_log = []
results = []
results_log = []
for m in tqdm(msgs):
if m.which() == 'carState':
last_carstate = m
elif m.which() == 'liveLocationKalman':
t = last_carstate.logMonoTime / 1e9
learner.handle_log(t, 'carState', last_carstate.carState)
t = m.logMonoTime / 1e9
learner.handle_log(t, 'liveLocationKalman', m.liveLocationKalman)
x = learner.kf.x
sr = float(x[States.STEER_RATIO])
st = float(x[States.STIFFNESS])
ao_avg = math.degrees(x[States.ANGLE_OFFSET])
ao = ao_avg + math.degrees(x[States.ANGLE_OFFSET_FAST])
r = [sr, st, ao_avg, ao]
if any(math.isnan(v) for v in r):
print("NaN", t)
ts.append(t)
results.append(r)
elif m.which() == 'liveParameters':
t = m.logMonoTime / 1e9
mm = m.liveParameters
r = [mm.steerRatio, mm.stiffnessFactor, mm.angleOffsetAverageDeg, mm.angleOffsetDeg]
if any(math.isnan(v) for v in r):
print("NaN in log", t)
ts_log.append(t)
results_log.append(r)
results = np.asarray(results)
results_log = np.asarray(results_log)
if PLOT:
import matplotlib.pyplot as plt
plt.figure()
plt.subplot(3, 2, 1)
plt.plot(ts, results[:, 0], label='Steer Ratio')
plt.grid()
plt.ylim([0, 20])
plt.legend()
plt.subplot(3, 2, 3)
plt.plot(ts, results[:, 1], label='Stiffness')
plt.ylim([0, 2])
plt.grid()
plt.legend()
plt.subplot(3, 2, 5)
plt.plot(ts, results[:, 2], label='Angle offset (average)')
plt.plot(ts, results[:, 3], label='Angle offset (instant)')
plt.ylim([-5, 5])
plt.grid()
plt.legend()
plt.subplot(3, 2, 2)
plt.plot(ts_log, results_log[:, 0], label='Steer Ratio')
plt.grid()
plt.ylim([0, 20])
plt.legend()
plt.subplot(3, 2, 4)
plt.plot(ts_log, results_log[:, 1], label='Stiffness')
plt.ylim([0, 2])
plt.grid()
plt.legend()
plt.subplot(3, 2, 6)
plt.plot(ts_log, results_log[:, 2], label='Angle offset (average)')
plt.plot(ts_log, results_log[:, 3], label='Angle offset (instant)')
plt.ylim([-5, 5])
plt.grid()
plt.legend()
plt.show()

@ -445,6 +445,10 @@ routes = {
'carFingerprint': VOLKSWAGEN.JETTA_MK7,
'enableCamera': True,
},
"4d134e099430fba2|2021-03-26--00-26-06": {
'carFingerprint': VOLKSWAGEN.PASSAT_MK8,
'enableCamera': True,
},
"2cef8a0b898f331a|2021-03-25--20-13-57": {
'carFingerprint': VOLKSWAGEN.TIGUAN_MK2,
'enableCamera': True,
@ -453,10 +457,18 @@ routes = {
'carFingerprint': VOLKSWAGEN.AUDI_A3_MK3,
'enableCamera': True,
},
"8f205bdd11bcbb65|2021-03-26--01-00-17": {
'carFingerprint': VOLKSWAGEN.SEAT_ATECA_MK1,
'enableCamera': True,
},
"90434ff5d7c8d603|2021-03-15--12-07-31": {
'carFingerprint': VOLKSWAGEN.SKODA_KODIAQ_MK1,
'enableCamera': True,
},
"026b6d18fba6417f|2021-03-26--09-17-04": {
'carFingerprint': VOLKSWAGEN.SKODA_SCALA_MK1,
'enableCamera': True,
},
"3c8f0c502e119c1c|2020-06-30--12-58-02": {
'carFingerprint': SUBARU.ASCENT,
'enableCamera': True,

@ -76,8 +76,8 @@ QString CommaApi::create_jwt() {
return create_jwt(*(new QVector<QPair<QString, QJsonValue>>()));
}
RequestRepeater::RequestRepeater(QWidget* parent, QString requestURL, int period_seconds, QVector<QPair<QString, QJsonValue>> payloads, bool disableWithScreen)
: disableWithScreen(disableWithScreen), QObject(parent) {
RequestRepeater::RequestRepeater(QWidget* parent, QString requestURL, int period_seconds, const QString &cache_key, QVector<QPair<QString, QJsonValue>> payloads, bool disableWithScreen)
: disableWithScreen(disableWithScreen), cache_key(cache_key), QObject(parent) {
networkAccessManager = new QNetworkAccessManager(this);
reply = NULL;
@ -90,6 +90,12 @@ RequestRepeater::RequestRepeater(QWidget* parent, QString requestURL, int period
networkTimer->setSingleShot(true);
networkTimer->setInterval(20000);
connect(networkTimer, SIGNAL(timeout()), this, SLOT(requestTimeout()));
if (!cache_key.isEmpty()) {
if (std::string cached_resp = Params().get(cache_key.toStdString()); !cached_resp.empty()) {
QTimer::singleShot(0, [=]() { emit receivedResponse(QString::fromStdString(cached_resp)); });
}
}
}
void RequestRepeater::sendRequest(QString requestURL, QVector<QPair<QString, QJsonValue>> payloads){
@ -126,13 +132,19 @@ void RequestRepeater::requestFinished(){
networkTimer->stop();
QString response = reply->readAll();
if (reply->error() == QNetworkReply::NoError) {
// save to cache
if (!cache_key.isEmpty()) {
Params().write_db_value(cache_key.toStdString(), response.toStdString());
}
emit receivedResponse(response);
} else {
qDebug() << reply->errorString();
if (!cache_key.isEmpty()) {
Params().delete_db_value(cache_key.toStdString());
}
emit failedResponse(reply->errorString());
}
} else {
emit failedResponse("network timeout");
emit timeoutResponse("timeout");
}
reply->deleteLater();
reply = NULL;

@ -33,7 +33,7 @@ class RequestRepeater : public QObject {
Q_OBJECT
public:
explicit RequestRepeater(QWidget* parent, QString requestURL, int period = 10, QVector<QPair<QString, QJsonValue>> payloads = *(new QVector<QPair<QString, QJsonValue>>()), bool disableWithScreen = true);
explicit RequestRepeater(QWidget* parent, QString requestURL, int period = 10, const QString &cache_key = "", QVector<QPair<QString, QJsonValue>> payloads = *(new QVector<QPair<QString, QJsonValue>>()), bool disableWithScreen = true);
bool active = true;
private:
@ -41,6 +41,7 @@ private:
QNetworkReply* reply;
QNetworkAccessManager* networkAccessManager;
QTimer* networkTimer;
QString cache_key;
void sendRequest(QString requestURL, QVector<QPair<QString, QJsonValue>> payloads);
private slots:
@ -50,4 +51,5 @@ private slots:
signals:
void receivedResponse(QString response);
void failedResponse(QString errorString);
void timeoutResponse(QString errorString);
};

@ -1,8 +1,6 @@
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLabel>
#include <QStackedLayout>
#include <QVBoxLayout>
#include "api.hpp"
@ -11,94 +9,62 @@
const double MILE_TO_KM = 1.60934;
void clearLayouts(QLayout* layout) {
while (QLayoutItem* item = layout->takeAt(0)) {
if (QWidget* widget = item->widget()) {
widget->deleteLater();
}
if (QLayout* childLayout = item->layout()) {
clearLayouts(childLayout);
}
delete item;
}
}
QLayout* build_stat(QString name, int stat) {
static QLayout* build_stat_layout(QLabel** metric, const QString& name) {
QVBoxLayout* layout = new QVBoxLayout;
layout->setMargin(0);
QLabel* metric = new QLabel(QString("%1").arg(stat));
metric->setStyleSheet(R"(
font-size: 80px;
font-weight: 600;
)");
layout->addWidget(metric, 0, Qt::AlignLeft);
*metric = new QLabel("0");
(*metric)->setStyleSheet("font-size: 80px; font-weight: 600;");
layout->addWidget(*metric, 0, Qt::AlignLeft);
QLabel* label = new QLabel(name);
label->setStyleSheet(R"(
font-size: 45px;
font-weight: 500;
)");
label->setStyleSheet("font-size: 45px; font-weight: 500;");
layout->addWidget(label, 0, Qt::AlignLeft);
return layout;
}
void DriveStats::parseError(QString response) {
clearLayouts(vlayout);
vlayout->addWidget(new QLabel("No Internet connection"), 0, Qt::AlignCenter);
}
void DriveStats::parseResponse(QString response) {
response.chop(1);
clearLayouts(vlayout);
response = response.trimmed();
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
if (doc.isNull()) {
qDebug() << "JSON Parse failed on getting past drives statistics";
return;
}
bool metric = Params().read_db_bool("IsMetric");
auto update = [](const QJsonObject &obj, StatsLabels& labels, bool metric) {
labels.routes->setText(QString::number((int)obj["routes"].toDouble()));
labels.distance->setText(QString::number(int(obj["distance"].toDouble() * (metric ? MILE_TO_KM : 1))));
labels.hours->setText(QString::number((int)(obj["minutes"].toDouble() / 60)));
};
bool metric = Params().read_db_bool("IsMetric");
QJsonObject json = doc.object();
auto all = json["all"].toObject();
auto week = json["week"].toObject();
update(json["all"].toObject(), all_, metric);
update(json["week"].toObject(), week_, metric);
}
DriveStats::DriveStats(QWidget* parent) : QWidget(parent) {
setStyleSheet("QLabel {font-size: 48px; font-weight: 500;}");
auto add_stats_layouts = [&](QGridLayout* gl, StatsLabels& labels, int row, const char* distance_unit) {
gl->addLayout(build_stat_layout(&labels.routes, "DRIVES"), row, 0, 3, 1);
gl->addLayout(build_stat_layout(&labels.distance, distance_unit), row, 1, 3, 1);
gl->addLayout(build_stat_layout(&labels.hours, "HOURS"), row, 2, 3, 1);
};
const char* distance_unit = Params().read_db_bool("IsMetric") ? "KM" : "MILES";
QGridLayout* gl = new QGridLayout();
gl->setMargin(0);
int all_distance = all["distance"].toDouble() * (metric ? MILE_TO_KM : 1);
gl->addWidget(new QLabel("ALL TIME"), 0, 0, 1, 3);
gl->addLayout(build_stat("DRIVES", all["routes"].toDouble()), 1, 0, 3, 1);
gl->addLayout(build_stat(metric ? "KM" : "MILES", all_distance), 1, 1, 3, 1);
gl->addLayout(build_stat("HOURS", all["minutes"].toDouble() / 60), 1, 2, 3, 1);
int week_distance = week["distance"].toDouble() * (metric ? MILE_TO_KM : 1);
add_stats_layouts(gl, all_, 1, distance_unit);
gl->addWidget(new QLabel("PAST WEEK"), 6, 0, 1, 3);
gl->addLayout(build_stat("DRIVES", week["routes"].toDouble()), 7, 0, 3, 1);
gl->addLayout(build_stat(metric ? "KM" : "MILES", week_distance), 7, 1, 3, 1);
gl->addLayout(build_stat("HOURS", week["minutes"].toDouble() / 60), 7, 2, 3, 1);
QWidget* q = new QWidget;
q->setLayout(gl);
vlayout->addWidget(q);
}
add_stats_layouts(gl, week_, 7, distance_unit);
DriveStats::DriveStats(QWidget* parent) : QWidget(parent) {
vlayout = new QVBoxLayout(this);
vlayout->setMargin(0);
setLayout(vlayout);
setStyleSheet(R"(
QLabel {
font-size: 48px;
font-weight: 500;
}
)");
QVBoxLayout* vlayout = new QVBoxLayout(this);
vlayout->addLayout(gl);
// TODO: do we really need to update this frequently?
QString dongleId = QString::fromStdString(Params().get("DongleId"));
QString url = "https://api.commadotai.com/v1.1/devices/" + dongleId + "/stats";
RequestRepeater* repeater = new RequestRepeater(this, url, 13);
RequestRepeater* repeater = new RequestRepeater(this, url, 13, "ApiCache_DriveStats");
QObject::connect(repeater, SIGNAL(receivedResponse(QString)), this, SLOT(parseResponse(QString)));
QObject::connect(repeater, SIGNAL(failedResponse(QString)), this, SLOT(parseError(QString)));
}

@ -1,7 +1,6 @@
#pragma once
#include <QVBoxLayout>
#include <QWidget>
#include <QLabel>
class DriveStats : public QWidget {
Q_OBJECT
@ -10,9 +9,10 @@ public:
explicit DriveStats(QWidget* parent = 0);
private:
QVBoxLayout* vlayout;
struct StatsLabels {
QLabel *routes, *distance, *hours;
} all_, week_;
private slots:
void parseError(QString response);
void parseResponse(QString response);
};

@ -3,7 +3,7 @@
#include <QJsonObject>
#include <QLabel>
#include <QPushButton>
#include <QStackedLayout>
#include <QStackedWidget>
#include <QTimer>
#include <QVBoxLayout>
@ -107,7 +107,7 @@ PrimeUserWidget::PrimeUserWidget(QWidget* parent) : QWidget(parent) {
// TODO: only send the request when widget is shown
QString url = "https://api.commadotai.com/v1/devices/" + dongleId + "/owner";
RequestRepeater* repeater = new RequestRepeater(this, url, 6);
RequestRepeater* repeater = new RequestRepeater(this, url, 6, "ApiCache_Owner");
QObject::connect(repeater, SIGNAL(receivedResponse(QString)), this, SLOT(replyFinished(QString)));
}
@ -156,7 +156,7 @@ PrimeAdWidget::PrimeAdWidget(QWidget* parent) : QWidget(parent) {
SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) {
mainLayout = new QStackedLayout;
mainLayout = new QStackedWidget;
// Unpaired, registration prompt layout
@ -214,7 +214,12 @@ SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) {
primeUser = new PrimeUserWidget;
mainLayout->addWidget(primeUser);
setLayout(mainLayout);
mainLayout->setCurrentWidget(primeAd);
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(mainLayout);
setLayout(layout);
setStyleSheet(R"(
SetupWidget {
background-color: #292929;
@ -226,16 +231,23 @@ SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) {
}
)");
// Retain size while hidden
QSizePolicy sp_retain = sizePolicy();
sp_retain.setRetainSizeWhenHidden(true);
setSizePolicy(sp_retain);
// set up API requests
QString dongleId = QString::fromStdString(Params().get("DongleId"));
QString url = "https://api.commadotai.com/v1.1/devices/" + dongleId + "/";
RequestRepeater* repeater = new RequestRepeater(this, url, 5);
RequestRepeater* repeater = new RequestRepeater(this, url, 5, "ApiCache_Device");
QObject::connect(repeater, SIGNAL(receivedResponse(QString)), this, SLOT(replyFinished(QString)));
QObject::connect(repeater, SIGNAL(failedResponse(QString)), this, SLOT(parseError(QString)));
hide(); // Only show when first request comes back
}
void SetupWidget::parseError(QString response) {
show();
showQr = false;
mainLayout->setCurrentIndex(0);
}
@ -246,6 +258,7 @@ void SetupWidget::showQrCode(){
}
void SetupWidget::replyFinished(QString response) {
show();
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
if (doc.isNull()) {
qDebug() << "JSON Parse failed on getting pairing and prime status";

@ -1,7 +1,8 @@
#pragma once
#include <QLabel>
#include <QStackedLayout>
#include <QStackedWidget>
#include <QVBoxLayout>
#include <QWidget>
#include "api.hpp"
@ -48,7 +49,7 @@ public:
explicit SetupWidget(QWidget* parent = 0);
private:
QStackedLayout* mainLayout;
QStackedWidget* mainLayout;
CommaApi* api;
PrimeAdWidget *primeAd;
PrimeUserWidget *primeUser;

@ -7,11 +7,7 @@
#include "sidebar.hpp"
static void draw_background(UIState *s) {
#ifndef QT_GUI_LIB
const NVGcolor color = COLOR_BLACK_ALPHA(85);
#else
const NVGcolor color = nvgRGBA(0x39, 0x39, 0x39, 0xff);
#endif
ui_fill_rect(s->vg, {0, 0, sbr_w, s->fb_h}, color);
}

@ -74,11 +74,7 @@ typedef enum UIStatus {
} UIStatus;
static std::map<UIStatus, NVGcolor> bg_colors = {
#ifndef QT_GUI_LIB
{STATUS_OFFROAD, nvgRGBA(0x07, 0x23, 0x39, 0xf1)},
#else
{STATUS_OFFROAD, nvgRGBA(0x0, 0x0, 0x0, 0xff)},
#endif
{STATUS_DISENGAGED, nvgRGBA(0x17, 0x33, 0x49, 0xc8)},
{STATUS_ENGAGED, nvgRGBA(0x17, 0x86, 0x44, 0xf1)},
{STATUS_WARNING, nvgRGBA(0xDA, 0x6F, 0x25, 0xf1)},

Loading…
Cancel
Save