diff --git a/Jenkinsfile b/Jenkinsfile index 3ededf4506..78a3a0fa63 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -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"], + ]) + } + } + } } } diff --git a/README.md b/README.md index 590fba0cdd..d8ead59e82 100644 --- a/README.md +++ b/README.md @@ -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-172 | Driver Assistance | Stock | 0mph | 0mph | | Volkswagen| Golf 2015-19 | Driver Assistance | Stock | 0mph | 0mph | | Volkswagen| Tiguan 2020 | Driver Assistance | Stock | 0mph | 0mph | 1Requires 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).***
+2Only 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/) diff --git a/RELEASES.md b/RELEASES.md index fd1058e187..4bdc9d0755 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -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) ======================== diff --git a/common/params_pyx.pyx b/common/params_pyx.pyx index 2add3a0772..499e6ea6e7 100755 --- a/common/params_pyx.pyx +++ b/common/params_pyx.pyx @@ -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], diff --git a/installer/updater/update.json b/installer/updater/update.json index 18d2139572..faff0c52d7 100644 --- a/installer/updater/update.json +++ b/installer/updater/update.json @@ -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" } diff --git a/launch_chffrplus.sh b/launch_chffrplus.sh index cc6e8f4973..c9605cbab3 100755 --- a/launch_chffrplus.sh +++ b/launch_chffrplus.sh @@ -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 diff --git a/launch_env.sh b/launch_env.sh index 6bc84c7068..da81aa4126 100755 --- a/launch_env.sh +++ b/launch_env.sh @@ -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 diff --git a/panda b/panda index ad9ecefe65..f146aa367b 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit ad9ecefe65ce4ea7bcaf3bfe692ee2a3fc6b0b63 +Subproject commit f146aa367b82f041b4eadcaab0bb2fc79e388b94 diff --git a/release/files_common b/release/files_common index 4a4c185233..8536da8758 100644 --- a/release/files_common +++ b/release/files_common @@ -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 diff --git a/selfdrive/camerad/test/test_camerad.py b/selfdrive/camerad/test/test_camerad.py index e2333b3839..11e070f328 100755 --- a/selfdrive/camerad/test/test_camerad.py +++ b/selfdrive/camerad/test/test_camerad.py @@ -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") diff --git a/selfdrive/camerad/test/test_exposure.py b/selfdrive/camerad/test/test_exposure.py new file mode 100755 index 0000000000..0b5b5ace88 --- /dev/null +++ b/selfdrive/camerad/test/test_exposure.py @@ -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() diff --git a/selfdrive/car/volkswagen/interface.py b/selfdrive/car/volkswagen/interface.py index dd774c3596..f33eba532b 100644 --- a/selfdrive/car/volkswagen/interface.py +++ b/selfdrive/car/volkswagen/interface.py @@ -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 diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index 3e59e8324a..f9ffd0143a 100644 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -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), } diff --git a/selfdrive/debug/internal/run_paramsd_on_route.py b/selfdrive/debug/internal/run_paramsd_on_route.py new file mode 100755 index 0000000000..54519e3777 --- /dev/null +++ b/selfdrive/debug/internal/run_paramsd_on_route.py @@ -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() + diff --git a/selfdrive/test/test_car_models.py b/selfdrive/test/test_car_models.py index 5b0bf81e32..fbf0d382f5 100755 --- a/selfdrive/test/test_car_models.py +++ b/selfdrive/test/test_car_models.py @@ -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, diff --git a/selfdrive/ui/qt/api.cc b/selfdrive/ui/qt/api.cc index 48726b989b..5522705c4f 100644 --- a/selfdrive/ui/qt/api.cc +++ b/selfdrive/ui/qt/api.cc @@ -76,8 +76,8 @@ QString CommaApi::create_jwt() { return create_jwt(*(new QVector>())); } -RequestRepeater::RequestRepeater(QWidget* parent, QString requestURL, int period_seconds, QVector> payloads, bool disableWithScreen) - : disableWithScreen(disableWithScreen), QObject(parent) { +RequestRepeater::RequestRepeater(QWidget* parent, QString requestURL, int period_seconds, const QString &cache_key, QVector> 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> 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; diff --git a/selfdrive/ui/qt/api.hpp b/selfdrive/ui/qt/api.hpp index 43bf7b13ba..3772727be9 100644 --- a/selfdrive/ui/qt/api.hpp +++ b/selfdrive/ui/qt/api.hpp @@ -33,7 +33,7 @@ class RequestRepeater : public QObject { Q_OBJECT public: - explicit RequestRepeater(QWidget* parent, QString requestURL, int period = 10, QVector> payloads = *(new QVector>()), bool disableWithScreen = true); + explicit RequestRepeater(QWidget* parent, QString requestURL, int period = 10, const QString &cache_key = "", QVector> payloads = *(new QVector>()), 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> payloads); private slots: @@ -50,4 +51,5 @@ private slots: signals: void receivedResponse(QString response); void failedResponse(QString errorString); + void timeoutResponse(QString errorString); }; diff --git a/selfdrive/ui/qt/widgets/drive_stats.cc b/selfdrive/ui/qt/widgets/drive_stats.cc index 1c2af95a9a..7cbcc0aabd 100644 --- a/selfdrive/ui/qt/widgets/drive_stats.cc +++ b/selfdrive/ui/qt/widgets/drive_stats.cc @@ -1,8 +1,6 @@ #include #include #include -#include -#include #include #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))); } diff --git a/selfdrive/ui/qt/widgets/drive_stats.hpp b/selfdrive/ui/qt/widgets/drive_stats.hpp index 40cefbb4d9..64b93cfe73 100644 --- a/selfdrive/ui/qt/widgets/drive_stats.hpp +++ b/selfdrive/ui/qt/widgets/drive_stats.hpp @@ -1,7 +1,6 @@ #pragma once -#include -#include +#include 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); }; diff --git a/selfdrive/ui/qt/widgets/setup.cc b/selfdrive/ui/qt/widgets/setup.cc index 64a2f68265..32e14f20dd 100644 --- a/selfdrive/ui/qt/widgets/setup.cc +++ b/selfdrive/ui/qt/widgets/setup.cc @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include @@ -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"; diff --git a/selfdrive/ui/qt/widgets/setup.hpp b/selfdrive/ui/qt/widgets/setup.hpp index 6cd18b1bc7..0479db8f95 100644 --- a/selfdrive/ui/qt/widgets/setup.hpp +++ b/selfdrive/ui/qt/widgets/setup.hpp @@ -1,7 +1,8 @@ #pragma once #include -#include +#include +#include #include #include "api.hpp" @@ -48,7 +49,7 @@ public: explicit SetupWidget(QWidget* parent = 0); private: - QStackedLayout* mainLayout; + QStackedWidget* mainLayout; CommaApi* api; PrimeAdWidget *primeAd; PrimeUserWidget *primeUser; diff --git a/selfdrive/ui/sidebar.cc b/selfdrive/ui/sidebar.cc index 150841008e..805e1520cd 100644 --- a/selfdrive/ui/sidebar.cc +++ b/selfdrive/ui/sidebar.cc @@ -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); } diff --git a/selfdrive/ui/ui.hpp b/selfdrive/ui/ui.hpp index b32116e006..e5e8790b1a 100644 --- a/selfdrive/ui/ui.hpp +++ b/selfdrive/ui/ui.hpp @@ -74,11 +74,7 @@ typedef enum UIStatus { } UIStatus; static std::map 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)},