|  |  |  | @ -1,6 +1,8 @@ | 
			
		
	
		
			
				
					|  |  |  |  | #!/usr/bin/env python3 | 
			
		
	
		
			
				
					|  |  |  |  | import re | 
			
		
	
		
			
				
					|  |  |  |  | from collections import defaultdict | 
			
		
	
		
			
				
					|  |  |  |  | import unittest | 
			
		
	
		
			
				
					|  |  |  |  | import datetime | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | from cereal import car | 
			
		
	
		
			
				
					|  |  |  |  | from selfdrive.car.hyundai.values import CAR, CANFD_CAR, FW_QUERY_CONFIG, FW_VERSIONS, CAN_GEARS, LEGACY_SAFETY_MODE_CAR, CHECKSUM, CAMERA_SCC_CAR | 
			
		
	
	
		
			
				
					|  |  |  | @ -10,95 +12,123 @@ ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | class TestHyundaiFingerprint(unittest.TestCase): | 
			
		
	
		
			
				
					|  |  |  |  |   def test_canfd_not_in_can_features(self): | 
			
		
	
		
			
				
					|  |  |  |  |     can_specific_feature_list = set.union(*CAN_GEARS.values(), *CHECKSUM.values(), LEGACY_SAFETY_MODE_CAR, CAMERA_SCC_CAR) | 
			
		
	
		
			
				
					|  |  |  |  |     for car_model in CANFD_CAR: | 
			
		
	
		
			
				
					|  |  |  |  |       self.assertNotIn(car_model, can_specific_feature_list, "CAN FD car unexpectedly found in a CAN feature list") | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   def test_auxiliary_request_ecu_whitelist(self): | 
			
		
	
		
			
				
					|  |  |  |  |     # Asserts only auxiliary Ecus can exist in database for CAN-FD cars | 
			
		
	
		
			
				
					|  |  |  |  |     whitelisted_ecus = {ecu for r in FW_QUERY_CONFIG.requests for ecu in r.whitelist_ecus if r.auxiliary} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     for car_model in CANFD_CAR: | 
			
		
	
		
			
				
					|  |  |  |  |       ecus = {fw[0] for fw in FW_VERSIONS[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}') | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   def test_certain_ecus_available(self): | 
			
		
	
		
			
				
					|  |  |  |  |     # Asserts certain ecu keys essential for fuzzy fingerprinting are available on all platforms | 
			
		
	
		
			
				
					|  |  |  |  |     for car, ecus in FW_VERSIONS.items(): | 
			
		
	
		
			
				
					|  |  |  |  |       for essential_ecu in FW_QUERY_CONFIG.fuzzy_ecus: | 
			
		
	
		
			
				
					|  |  |  |  |         with self.subTest(car=car): | 
			
		
	
		
			
				
					|  |  |  |  |           if car == CAR.HYUNDAI_GENESIS: | 
			
		
	
		
			
				
					|  |  |  |  |             raise unittest.SkipTest | 
			
		
	
		
			
				
					|  |  |  |  |           self.assertIn(essential_ecu, [e[0] for e in ecus]) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   def test_fuzzy_platform_codes(self): | 
			
		
	
		
			
				
					|  |  |  |  |     codes = FW_QUERY_CONFIG.fuzzy_get_platform_codes([b'\xf1\x00DH LKAS 1.1 -150210']) | 
			
		
	
		
			
				
					|  |  |  |  |     self.assertEqual(codes, {b"DH"}) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     codes = FW_QUERY_CONFIG.fuzzy_get_platform_codes([b'\xf1\x00AEhe SCC H-CUP      1.01 1.01 96400-G2000         ']) | 
			
		
	
		
			
				
					|  |  |  |  |     self.assertEqual(codes, {b"AEhe"}) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     codes = FW_QUERY_CONFIG.fuzzy_get_platform_codes([b'\xf1\x00CV1_ RDR -----      1.00 1.01 99110-CV000         ']) | 
			
		
	
		
			
				
					|  |  |  |  |     self.assertEqual(codes, {b"CV1"}) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     codes = FW_QUERY_CONFIG.fuzzy_get_platform_codes([ | 
			
		
	
		
			
				
					|  |  |  |  |       b'\xf1\x00DH LKAS 1.1 -150210', | 
			
		
	
		
			
				
					|  |  |  |  |       b'\xf1\x00AEhe SCC H-CUP      1.01 1.01 96400-G2000         ', | 
			
		
	
		
			
				
					|  |  |  |  |       b'\xf1\x00CV1_ RDR -----      1.00 1.01 99110-CV000         ', | 
			
		
	
		
			
				
					|  |  |  |  |     ]) | 
			
		
	
		
			
				
					|  |  |  |  |     self.assertEqual(codes, {b"DH", b"AEhe", b"CV1"}) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   def test_excluded_platforms(self): | 
			
		
	
		
			
				
					|  |  |  |  |     # Asserts a list of platforms that will not fuzzy fingerprint due to shared platform codes | 
			
		
	
		
			
				
					|  |  |  |  |     # This list can be shrunk as we combine platforms and detect features | 
			
		
	
		
			
				
					|  |  |  |  |     excluded_platforms = [ | 
			
		
	
		
			
				
					|  |  |  |  |       CAR.HYUNDAI_GENESIS, | 
			
		
	
		
			
				
					|  |  |  |  |       CAR.IONIQ, | 
			
		
	
		
			
				
					|  |  |  |  |       CAR.IONIQ_PHEV_2019, | 
			
		
	
		
			
				
					|  |  |  |  |       CAR.IONIQ_PHEV, | 
			
		
	
		
			
				
					|  |  |  |  |       CAR.IONIQ_EV_2020, | 
			
		
	
		
			
				
					|  |  |  |  |       CAR.IONIQ_EV_LTD, | 
			
		
	
		
			
				
					|  |  |  |  |       CAR.IONIQ_HEV_2022, | 
			
		
	
		
			
				
					|  |  |  |  |       CAR.SANTA_FE, | 
			
		
	
		
			
				
					|  |  |  |  |       CAR.SANTA_FE_2022, | 
			
		
	
		
			
				
					|  |  |  |  |       CAR.KIA_STINGER, | 
			
		
	
		
			
				
					|  |  |  |  |       CAR.KIA_STINGER_2022, | 
			
		
	
		
			
				
					|  |  |  |  |       CAR.GENESIS_G70, | 
			
		
	
		
			
				
					|  |  |  |  |       CAR.GENESIS_G70_2020, | 
			
		
	
		
			
				
					|  |  |  |  |       CAR.TUCSON_4TH_GEN, | 
			
		
	
		
			
				
					|  |  |  |  |       CAR.TUCSON_HYBRID_4TH_GEN, | 
			
		
	
		
			
				
					|  |  |  |  |       CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, | 
			
		
	
		
			
				
					|  |  |  |  |       CAR.SANTA_CRUZ_1ST_GEN, | 
			
		
	
		
			
				
					|  |  |  |  |       CAR.KIA_SPORTAGE_5TH_GEN, | 
			
		
	
		
			
				
					|  |  |  |  |     ] | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     all_platform_codes = defaultdict(set) | 
			
		
	
		
			
				
					|  |  |  |  |   # def test_canfd_not_in_can_features(self): | 
			
		
	
		
			
				
					|  |  |  |  |   #   can_specific_feature_list = set.union(*CAN_GEARS.values(), *CHECKSUM.values(), LEGACY_SAFETY_MODE_CAR, CAMERA_SCC_CAR) | 
			
		
	
		
			
				
					|  |  |  |  |   #   for car_model in CANFD_CAR: | 
			
		
	
		
			
				
					|  |  |  |  |   #     self.assertNotIn(car_model, can_specific_feature_list, "CAN FD car unexpectedly found in a CAN feature list") | 
			
		
	
		
			
				
					|  |  |  |  |   # | 
			
		
	
		
			
				
					|  |  |  |  |   # def test_auxiliary_request_ecu_whitelist(self): | 
			
		
	
		
			
				
					|  |  |  |  |   #   # Asserts only auxiliary Ecus can exist in database for CAN-FD cars | 
			
		
	
		
			
				
					|  |  |  |  |   #   whitelisted_ecus = {ecu for r in FW_QUERY_CONFIG.requests for ecu in r.whitelist_ecus if r.auxiliary} | 
			
		
	
		
			
				
					|  |  |  |  |   # | 
			
		
	
		
			
				
					|  |  |  |  |   #   for car_model in CANFD_CAR: | 
			
		
	
		
			
				
					|  |  |  |  |   #     ecus = {fw[0] for fw in FW_VERSIONS[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}') | 
			
		
	
		
			
				
					|  |  |  |  |   # | 
			
		
	
		
			
				
					|  |  |  |  |   # def test_certain_ecus_available(self): | 
			
		
	
		
			
				
					|  |  |  |  |   #   # Asserts certain ecu keys essential for fuzzy fingerprinting are available on all platforms | 
			
		
	
		
			
				
					|  |  |  |  |   #   for car, ecus in FW_VERSIONS.items(): | 
			
		
	
		
			
				
					|  |  |  |  |   #     for essential_ecu in FW_QUERY_CONFIG.fuzzy_ecus: | 
			
		
	
		
			
				
					|  |  |  |  |   #       with self.subTest(car=car): | 
			
		
	
		
			
				
					|  |  |  |  |   #         if car == CAR.HYUNDAI_GENESIS: | 
			
		
	
		
			
				
					|  |  |  |  |   #           raise unittest.SkipTest | 
			
		
	
		
			
				
					|  |  |  |  |   #         self.assertIn(essential_ecu, [e[0] for e in ecus]) | 
			
		
	
		
			
				
					|  |  |  |  |   # | 
			
		
	
		
			
				
					|  |  |  |  |   # def test_fuzzy_platform_codes(self): | 
			
		
	
		
			
				
					|  |  |  |  |   #   codes = FW_QUERY_CONFIG.fuzzy_get_platform_codes([b'\xf1\x00DH LKAS 1.1 -150210']) | 
			
		
	
		
			
				
					|  |  |  |  |   #   self.assertEqual(codes, {b"DH"}) | 
			
		
	
		
			
				
					|  |  |  |  |   # | 
			
		
	
		
			
				
					|  |  |  |  |   #   codes = FW_QUERY_CONFIG.fuzzy_get_platform_codes([b'\xf1\x00AEhe SCC H-CUP      1.01 1.01 96400-G2000         ']) | 
			
		
	
		
			
				
					|  |  |  |  |   #   self.assertEqual(codes, {b"AEhe"}) | 
			
		
	
		
			
				
					|  |  |  |  |   # | 
			
		
	
		
			
				
					|  |  |  |  |   #   codes = FW_QUERY_CONFIG.fuzzy_get_platform_codes([b'\xf1\x00CV1_ RDR -----      1.00 1.01 99110-CV000         ']) | 
			
		
	
		
			
				
					|  |  |  |  |   #   self.assertEqual(codes, {b"CV1"}) | 
			
		
	
		
			
				
					|  |  |  |  |   # | 
			
		
	
		
			
				
					|  |  |  |  |   #   codes = FW_QUERY_CONFIG.fuzzy_get_platform_codes([ | 
			
		
	
		
			
				
					|  |  |  |  |   #     b'\xf1\x00DH LKAS 1.1 -150210', | 
			
		
	
		
			
				
					|  |  |  |  |   #     b'\xf1\x00AEhe SCC H-CUP      1.01 1.01 96400-G2000         ', | 
			
		
	
		
			
				
					|  |  |  |  |   #     b'\xf1\x00CV1_ RDR -----      1.00 1.01 99110-CV000         ', | 
			
		
	
		
			
				
					|  |  |  |  |   #   ]) | 
			
		
	
		
			
				
					|  |  |  |  |   #   self.assertEqual(codes, {b"DH", b"AEhe", b"CV1"}) | 
			
		
	
		
			
				
					|  |  |  |  |   # | 
			
		
	
		
			
				
					|  |  |  |  |   # def test_excluded_platforms(self): | 
			
		
	
		
			
				
					|  |  |  |  |   #   # Asserts a list of platforms that will not fuzzy fingerprint due to shared platform codes | 
			
		
	
		
			
				
					|  |  |  |  |   #   # This list can be shrunk as we combine platforms and detect features | 
			
		
	
		
			
				
					|  |  |  |  |   #   excluded_platforms = [ | 
			
		
	
		
			
				
					|  |  |  |  |   #     CAR.HYUNDAI_GENESIS, | 
			
		
	
		
			
				
					|  |  |  |  |   #     CAR.IONIQ, | 
			
		
	
		
			
				
					|  |  |  |  |   #     CAR.IONIQ_PHEV_2019, | 
			
		
	
		
			
				
					|  |  |  |  |   #     CAR.IONIQ_PHEV, | 
			
		
	
		
			
				
					|  |  |  |  |   #     CAR.IONIQ_EV_2020, | 
			
		
	
		
			
				
					|  |  |  |  |   #     CAR.IONIQ_EV_LTD, | 
			
		
	
		
			
				
					|  |  |  |  |   #     CAR.IONIQ_HEV_2022, | 
			
		
	
		
			
				
					|  |  |  |  |   #     CAR.SANTA_FE, | 
			
		
	
		
			
				
					|  |  |  |  |   #     CAR.SANTA_FE_2022, | 
			
		
	
		
			
				
					|  |  |  |  |   #     CAR.KIA_STINGER, | 
			
		
	
		
			
				
					|  |  |  |  |   #     CAR.KIA_STINGER_2022, | 
			
		
	
		
			
				
					|  |  |  |  |   #     CAR.GENESIS_G70, | 
			
		
	
		
			
				
					|  |  |  |  |   #     CAR.GENESIS_G70_2020, | 
			
		
	
		
			
				
					|  |  |  |  |   #     CAR.TUCSON_4TH_GEN, | 
			
		
	
		
			
				
					|  |  |  |  |   #     CAR.TUCSON_HYBRID_4TH_GEN, | 
			
		
	
		
			
				
					|  |  |  |  |   #     CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, | 
			
		
	
		
			
				
					|  |  |  |  |   #     CAR.SANTA_CRUZ_1ST_GEN, | 
			
		
	
		
			
				
					|  |  |  |  |   #     CAR.KIA_SPORTAGE_5TH_GEN, | 
			
		
	
		
			
				
					|  |  |  |  |   #   ] | 
			
		
	
		
			
				
					|  |  |  |  |   # | 
			
		
	
		
			
				
					|  |  |  |  |   #   all_platform_codes = defaultdict(set) | 
			
		
	
		
			
				
					|  |  |  |  |   #   for candidate, fw_by_addr in FW_VERSIONS.items(): | 
			
		
	
		
			
				
					|  |  |  |  |   #     for addr, fws in fw_by_addr.items(): | 
			
		
	
		
			
				
					|  |  |  |  |   #       if addr[0] not in FW_QUERY_CONFIG.fuzzy_ecus: | 
			
		
	
		
			
				
					|  |  |  |  |   #         continue | 
			
		
	
		
			
				
					|  |  |  |  |   # | 
			
		
	
		
			
				
					|  |  |  |  |   #       for platform_code in FW_QUERY_CONFIG.fuzzy_get_platform_codes(fws): | 
			
		
	
		
			
				
					|  |  |  |  |   #         all_platform_codes[(addr[1], addr[2], platform_code)].add(candidate) | 
			
		
	
		
			
				
					|  |  |  |  |   # | 
			
		
	
		
			
				
					|  |  |  |  |   #   platforms_with_shared_codes = [] | 
			
		
	
		
			
				
					|  |  |  |  |   #   for platform, fw_by_addr in FW_VERSIONS.items(): | 
			
		
	
		
			
				
					|  |  |  |  |   #     shared_codes = [] | 
			
		
	
		
			
				
					|  |  |  |  |   #     for addr, fws in fw_by_addr.items(): | 
			
		
	
		
			
				
					|  |  |  |  |   #       if addr[0] not in FW_QUERY_CONFIG.fuzzy_ecus: | 
			
		
	
		
			
				
					|  |  |  |  |   #         continue | 
			
		
	
		
			
				
					|  |  |  |  |   # | 
			
		
	
		
			
				
					|  |  |  |  |   #       for f in fws: | 
			
		
	
		
			
				
					|  |  |  |  |   #         platform_code = list(FW_QUERY_CONFIG.fuzzy_get_platform_codes([f]))[0] | 
			
		
	
		
			
				
					|  |  |  |  |   #         shared_codes.append(len(all_platform_codes[(addr[1], addr[2], platform_code)]) > 1) | 
			
		
	
		
			
				
					|  |  |  |  |   # | 
			
		
	
		
			
				
					|  |  |  |  |   #     if all(shared_codes): | 
			
		
	
		
			
				
					|  |  |  |  |   #       platforms_with_shared_codes.append(platform) | 
			
		
	
		
			
				
					|  |  |  |  |   # | 
			
		
	
		
			
				
					|  |  |  |  |   #   self.assertEqual(set(platforms_with_shared_codes), set(excluded_platforms)) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   def test_fw_dates(self): | 
			
		
	
		
			
				
					|  |  |  |  |     # format is yymmdd, want to group the year month day | 
			
		
	
		
			
				
					|  |  |  |  |     # date_pattern = re.compile(br'(\d\d)(\d\d)(\d\d)') | 
			
		
	
		
			
				
					|  |  |  |  |     date_pattern = re.compile(b'[0-9]{6}') | 
			
		
	
		
			
				
					|  |  |  |  |     for candidate, fw_by_addr in FW_VERSIONS.items(): | 
			
		
	
		
			
				
					|  |  |  |  |       print(candidate) | 
			
		
	
		
			
				
					|  |  |  |  |       for addr, fws in fw_by_addr.items(): | 
			
		
	
		
			
				
					|  |  |  |  |         if addr[0] not in FW_QUERY_CONFIG.fuzzy_ecus: | 
			
		
	
		
			
				
					|  |  |  |  |           continue | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         for platform_code in FW_QUERY_CONFIG.fuzzy_get_platform_codes(fws): | 
			
		
	
		
			
				
					|  |  |  |  |           all_platform_codes[(addr[1], addr[2], platform_code)].add(candidate) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     platforms_with_shared_codes = [] | 
			
		
	
		
			
				
					|  |  |  |  |     for platform, fw_by_addr in FW_VERSIONS.items(): | 
			
		
	
		
			
				
					|  |  |  |  |       shared_codes = [] | 
			
		
	
		
			
				
					|  |  |  |  |       for addr, fws in fw_by_addr.items(): | 
			
		
	
		
			
				
					|  |  |  |  |         if addr[0] not in FW_QUERY_CONFIG.fuzzy_ecus: | 
			
		
	
		
			
				
					|  |  |  |  |           continue | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         for f in fws: | 
			
		
	
		
			
				
					|  |  |  |  |           platform_code = list(FW_QUERY_CONFIG.fuzzy_get_platform_codes([f]))[0] | 
			
		
	
		
			
				
					|  |  |  |  |           shared_codes.append(len(all_platform_codes[(addr[1], addr[2], platform_code)]) > 1) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |       if all(shared_codes): | 
			
		
	
		
			
				
					|  |  |  |  |         platforms_with_shared_codes.append(platform) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     self.assertEqual(set(platforms_with_shared_codes), set(excluded_platforms)) | 
			
		
	
		
			
				
					|  |  |  |  |         dates = [] | 
			
		
	
		
			
				
					|  |  |  |  |         for fw in fws: | 
			
		
	
		
			
				
					|  |  |  |  |           date = date_pattern.search(fw) | 
			
		
	
		
			
				
					|  |  |  |  |           if date is not None: | 
			
		
	
		
			
				
					|  |  |  |  |             dates.append(datetime.datetime.strptime(date.group().decode(), '%y%m%d')) | 
			
		
	
		
			
				
					|  |  |  |  |             print(date.group(), dates[-1]) | 
			
		
	
		
			
				
					|  |  |  |  |           # print(date) | 
			
		
	
		
			
				
					|  |  |  |  |           # print(addr, fw, date_pattern.findall(fw)) | 
			
		
	
		
			
				
					|  |  |  |  |         if len(dates) > 1: | 
			
		
	
		
			
				
					|  |  |  |  |           print('dates', dates) | 
			
		
	
		
			
				
					|  |  |  |  |           range_dates = [] | 
			
		
	
		
			
				
					|  |  |  |  |           cur_date = min(dates) | 
			
		
	
		
			
				
					|  |  |  |  |           while cur_date < max(dates): | 
			
		
	
		
			
				
					|  |  |  |  |             range_dates.append(cur_date) | 
			
		
	
		
			
				
					|  |  |  |  |             cur_date += datetime.timedelta(days=1) | 
			
		
	
		
			
				
					|  |  |  |  |           print('total dates', len(range_dates)) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |       print() | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | if __name__ == "__main__": | 
			
		
	
	
		
			
				
					|  |  |  | 
 |