diff --git a/selfdrive/car/car_helpers.py b/selfdrive/car/car_helpers.py index 4a8fd5fbd9..61e7a3d55d 100644 --- a/selfdrive/car/car_helpers.py +++ b/selfdrive/car/car_helpers.py @@ -76,7 +76,7 @@ interfaces = load_interfaces(interface_names) # **** for use live only **** -def fingerprint(logcan, sendcan): +def fingerprint(logcan, sendcan, num_pandas): fixed_fingerprint = os.environ.get('FINGERPRINT', "") skip_fw_query = os.environ.get('SKIP_FW_QUERY', False) ecu_rx_addrs = set() @@ -100,7 +100,7 @@ def fingerprint(logcan, sendcan): cloudlog.warning("Getting VIN & FW versions") vin_rx_addr, vin = get_vin(logcan, sendcan, bus) ecu_rx_addrs = get_present_ecus(logcan, sendcan) - car_fw = get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs) + car_fw = get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, num_pandas=num_pandas) cached = False exact_fw_match, fw_candidates = match_fw_to_car(car_fw) @@ -173,8 +173,8 @@ def fingerprint(logcan, sendcan): return car_fingerprint, finger, vin, car_fw, source, exact_match -def get_car(logcan, sendcan): - candidate, fingerprints, vin, car_fw, source, exact_match = fingerprint(logcan, sendcan) +def get_car(logcan, sendcan, num_pandas=1): + candidate, fingerprints, vin, car_fw, source, exact_match = fingerprint(logcan, sendcan, num_pandas) if candidate is None: cloudlog.warning("car doesn't match any fingerprints: %r", fingerprints) diff --git a/selfdrive/car/fw_versions.py b/selfdrive/car/fw_versions.py index d3e8eae0de..f4d92ab960 100755 --- a/selfdrive/car/fw_versions.py +++ b/selfdrive/car/fw_versions.py @@ -194,14 +194,14 @@ def get_brand_ecu_matches(ecu_rx_addrs): return brand_matches -def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, debug=False, progress=False): +def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pandas=1, debug=False, progress=False): """Queries for FW versions ordering brands by likelihood, breaks when exact match is found""" all_car_fw = [] brand_matches = get_brand_ecu_matches(ecu_rx_addrs) for brand in sorted(brand_matches, key=lambda b: len(brand_matches[b]), reverse=True): - car_fw = get_fw_versions(logcan, sendcan, query_brand=brand, timeout=timeout, debug=debug, progress=progress) + car_fw = get_fw_versions(logcan, sendcan, query_brand=brand, timeout=timeout, num_pandas=num_pandas, debug=debug, progress=progress) all_car_fw.extend(car_fw) # Try to match using FW returned from this brand only matches = match_fw_to_car_exact(build_fw_dict(car_fw)) @@ -211,7 +211,7 @@ def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, debug=Fa return all_car_fw -def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, debug=False, progress=False): +def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, num_pandas=1, debug=False, progress=False): versions = VERSIONS.copy() # Each brand can define extra ECUs to query for data collection @@ -252,6 +252,10 @@ def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, for addr in tqdm(addrs, disable=not progress): for addr_chunk in chunks(addr): for brand, r in requests: + # Skip query if no panda available + if r.bus > num_pandas * 4 - 1: + continue + try: addrs = [(a, s) for (b, a, s) in addr_chunk if b in (brand, 'any') and (len(r.whitelist_ecus) == 0 or ecu_types[(b, a, s)] in r.whitelist_ecus)] @@ -292,6 +296,7 @@ if __name__ == "__main__": args = parser.parse_args() logcan = messaging.sub_sock('can') + pandaStates_sock = messaging.sub_sock('pandaStates') sendcan = messaging.pub_sock('sendcan') extra: Any = None @@ -305,6 +310,7 @@ if __name__ == "__main__": extra = {"any": {"debug": extra}} time.sleep(1.) + num_pandas = len(messaging.recv_one_retry(pandaStates_sock).pandaStates) t = time.time() print("Getting vin...") @@ -314,7 +320,7 @@ if __name__ == "__main__": print() t = time.time() - fw_vers = get_fw_versions(logcan, sendcan, query_brand=args.brand, extra=extra, debug=args.debug, progress=True) + fw_vers = get_fw_versions(logcan, sendcan, query_brand=args.brand, extra=extra, num_pandas=num_pandas, debug=args.debug, progress=True) _, candidates = match_fw_to_car(fw_vers) print() diff --git a/selfdrive/car/hyundai/tests/test_hyundai.py b/selfdrive/car/hyundai/tests/test_hyundai.py new file mode 100755 index 0000000000..a52027f448 --- /dev/null +++ b/selfdrive/car/hyundai/tests/test_hyundai.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +import unittest + +from cereal import car +from selfdrive.car.car_helpers import get_interface_attr +from selfdrive.car.fw_versions import FW_QUERY_CONFIGS +from selfdrive.car.hyundai.values import CANFD_CAR + +Ecu = car.CarParams.Ecu + +ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()} +VERSIONS = get_interface_attr("FW_VERSIONS", ignore_none=True) + + +class TestHyundaiFingerprint(unittest.TestCase): + def test_auxiliary_request_ecu_whitelist(self): + # Asserts only auxiliary Ecus can exist in database for CAN-FD cars + config = FW_QUERY_CONFIGS['hyundai'] + whitelisted_ecus = {ecu for r in config.requests for ecu in r.whitelist_ecus if r.bus > 3} + + for car_model in CANFD_CAR: + ecus = {fw[0] for fw in VERSIONS['hyundai'][car_model].keys()} + ecus_not_in_whitelist = ecus - whitelisted_ecus + ecu_strings = ", ".join([f'Ecu.{ECU_NAME[ecu]}' for ecu in ecus_not_in_whitelist]) + self.assertEqual(len(ecus_not_in_whitelist), 0, f'{car_model}: Car model has ECUs not in auxiliary request whitelists: {ecu_strings}') + + +if __name__ == "__main__": + unittest.main() diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 856bf1abd9..1599005d1c 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -300,6 +300,19 @@ FW_QUERY_CONFIG = FwQueryConfig( [HYUNDAI_VERSION_RESPONSE], whitelist_ecus=[Ecu.engine, Ecu.transmission, Ecu.eps, Ecu.abs, Ecu.fwdRadar], ), + # CAN-FD queries + Request( + [HYUNDAI_VERSION_REQUEST_LONG], + [HYUNDAI_VERSION_RESPONSE], + whitelist_ecus=[Ecu.fwdRadar], + bus=4, + ), + Request( + [HYUNDAI_VERSION_REQUEST_LONG], + [HYUNDAI_VERSION_RESPONSE], + whitelist_ecus=[Ecu.fwdCamera, Ecu.adas], + bus=5, + ), ], extra_ecus=[ (Ecu.adas, 0x730, None), # ADAS Driving ECU on HDA2 platforms @@ -1326,15 +1339,6 @@ FW_VERSIONS = { ], }, CAR.KIA_EV6: { - (Ecu.abs, 0x7d1, None): [ - b'\xf1\x00CV IEB \x02 101!\x10\x18 58520-CV100', - b'\xf1\x00CV IEB \x03 101!\x10\x18 58520-CV100', - b'\xf1\x8758520CV100\xf1\x00CV IEB \x02 101!\x10\x18 58520-CV100', - ], - (Ecu.eps, 0x7d4, None): [ - b'\xf1\x00CV1 MDPS R 1.00 1.04 57700-CV000 1B30', - b'\xf1\x00CV1 MDPS R 1.00 1.05 57700-CV000 2425', - ], (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00CV1_ RDR ----- 1.00 1.01 99110-CV000 ', b'\xf1\x8799110CV000\xf1\x00CV1_ RDR ----- 1.00 1.01 99110-CV000 ', @@ -1346,16 +1350,6 @@ FW_VERSIONS = { ], }, CAR.IONIQ_5: { - (Ecu.abs, 0x7d1, None): [ - b'\xf1\x00NE1 IEB \x07 106!\x11) 58520-GI010', - b'\xf1\x8758520GI010\xf1\x00NE1 IEB \x07 106!\x11) 58520-GI010', - b'\xf1\x00NE1 IEB \x08 104!\x04\x05 58520-GI000', - b'\xf1\x8758520GI000\xf1\x00NE1 IEB \x08 104!\x04\x05 58520-GI000', - ], - (Ecu.eps, 0x7d4, None): [ - b'\xf1\x00NE MDPS R 1.00 1.06 57700GI000 4NEDR106', - b'\xf1\x8757700GI000 \xf1\x00NE MDPS R 1.00 1.06 57700GI000 4NEDR106', - ], (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00NE1_ RDR ----- 1.00 1.00 99110-GI000 ', b'\xf1\x8799110GI000\xf1\x00NE1_ RDR ----- 1.00 1.00 99110-GI000 ', @@ -1369,16 +1363,6 @@ FW_VERSIONS = { (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N9240 14Q', ], - (Ecu.eps, 0x7d4, None): [ - b'\xf1\x00NX4 MDPS C 1.00 1.01 56300-P0100 2228', - ], - (Ecu.engine, 0x7e0, None): [ - b'\xf1\x87391312MND0', - ], - (Ecu.transmission, 0x7e1, None): [ - b'\xf1\x00PSBG2441 G19_Rev\x00\x00\x00SNX4T16XXHS01NS2lS\xdfa', - b'\xf1\x8795441-3D220\x00\xf1\x81G19_Rev\x00\x00\x00\xf1\x00PSBG2441 G19_Rev\x00\x00\x00SNX4T16XXHS01NS2lS\xdfa', - ], }, } diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 8abb1a02a6..cbc54eadb8 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -82,30 +82,30 @@ class Controls: self.log_sock = messaging.sub_sock('androidLog') - if CI is None: - # wait for one pandaState and one CAN packet - print("Waiting for CAN messages...") - get_one_can(self.can_sock) - - self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan']) - else: - self.CI, self.CP = CI, CI.CP - params = Params() - self.joystick_mode = params.get_bool("JoystickDebugMode") or (self.CP.notCar and sm is None) - joystick_packet = ['testJoystick'] if self.joystick_mode else [] - self.sm = sm if self.sm is None: - ignore = [] + ignore = ['testJoystick'] if SIMULATION: ignore += ['driverCameraState', 'managerState'] if params.get_bool('WideCameraOnly'): ignore += ['roadCameraState'] self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration', 'driverMonitoringState', 'longitudinalPlan', 'lateralPlan', 'liveLocationKalman', - 'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters'] + self.camera_packets + joystick_packet, - ignore_alive=ignore, ignore_avg_freq=['radarState', 'longitudinalPlan']) + 'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters', 'testJoystick'] + self.camera_packets, + ignore_alive=ignore, ignore_avg_freq=['radarState', 'longitudinalPlan', 'testJoystick']) + + if CI is None: + # wait for one pandaState and one CAN packet + print("Waiting for CAN messages...") + get_one_can(self.can_sock) + + num_pandas = len(messaging.recv_one_retry(self.sm.sock['pandaStates']).pandaStates) + self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'], num_pandas) + else: + self.CI, self.CP = CI, CI.CP + + self.joystick_mode = params.get_bool("JoystickDebugMode") or (self.CP.notCar and sm is None) # set alternative experiences from parameters self.disengage_on_accelerator = params.get_bool("DisengageOnAccelerator") diff --git a/selfdrive/controls/tests/test_startup.py b/selfdrive/controls/tests/test_startup.py index 63af4c7d95..ba2d2f5c02 100755 --- a/selfdrive/controls/tests/test_startup.py +++ b/selfdrive/controls/tests/test_startup.py @@ -94,6 +94,9 @@ class TestStartup(unittest.TestCase): time.sleep(2) # wait for controlsd to be ready + pm.send('can', can_list_to_can_capnp([[0, 0, b"", 0]])) + time.sleep(0.1) + msg = messaging.new_message('pandaStates', 1) msg.pandaStates[0].pandaType = log.PandaState.PandaType.uno pm.send('pandaStates', msg)