diff --git a/selfdrive/car/fw_versions.py b/selfdrive/car/fw_versions.py index c7256e7438..9db03390b2 100755 --- a/selfdrive/car/fw_versions.py +++ b/selfdrive/car/fw_versions.py @@ -228,13 +228,13 @@ def chunks(l, n=128): def build_fw_dict(fw_versions, filter_brand=None): - fw_versions_dict = {} + fw_versions_dict = defaultdict(set) for fw in fw_versions: if filter_brand is None or fw.brand == filter_brand: addr = fw.address sub_addr = fw.subAddress if fw.subAddress != 0 else None - fw_versions_dict[(addr, sub_addr)] = fw.fwVersion - return fw_versions_dict + fw_versions_dict[(addr, sub_addr)].add(fw.fwVersion) + return dict(fw_versions_dict) def get_brand_addrs(): @@ -271,17 +271,18 @@ def match_fw_to_car_fuzzy(fw_versions_dict, log=True, exclude=None): match_count = 0 candidate = None - for addr, version in fw_versions_dict.items(): - # All cars that have this FW response on the specified address - candidates = all_fw_versions[(addr[0], addr[1], version)] - - if len(candidates) == 1: - match_count += 1 - if candidate is None: - candidate = candidates[0] - # We uniquely matched two different cars. No fuzzy match possible - elif candidate != candidates[0]: - return set() + for addr, versions in fw_versions_dict.items(): + for version in versions: + # All cars that have this FW response on the specified address + candidates = all_fw_versions[(addr[0], addr[1], version)] + + if len(candidates) == 1: + match_count += 1 + if candidate is None: + candidate = candidates[0] + # We uniquely matched two different cars. No fuzzy match possible + elif candidate != candidates[0]: + return set() if match_count >= 2: if log: @@ -303,23 +304,23 @@ def match_fw_to_car_exact(fw_versions_dict): for ecu, expected_versions in fws.items(): ecu_type = ecu[0] addr = ecu[1:] - found_version = fw_versions_dict.get(addr, None) - if ecu_type == Ecu.esp and candidate in (TOYOTA.RAV4, TOYOTA.COROLLA, TOYOTA.HIGHLANDER, TOYOTA.SIENNA, TOYOTA.LEXUS_IS) and found_version is None: + found_versions = fw_versions_dict.get(addr, set()) + if ecu_type == Ecu.esp and candidate in (TOYOTA.RAV4, TOYOTA.COROLLA, TOYOTA.HIGHLANDER, TOYOTA.SIENNA, TOYOTA.LEXUS_IS) and not len(found_versions): continue # On some Toyota models, the engine can show on two different addresses - if ecu_type == Ecu.engine and candidate in (TOYOTA.CAMRY, TOYOTA.COROLLA_TSS2, TOYOTA.CHR, TOYOTA.LEXUS_IS) and found_version is None: + if ecu_type == Ecu.engine and candidate in (TOYOTA.CAMRY, TOYOTA.COROLLA_TSS2, TOYOTA.CHR, TOYOTA.LEXUS_IS) and not len(found_versions): continue # Ignore non essential ecus - if ecu_type not in ESSENTIAL_ECUS and found_version is None: + if ecu_type not in ESSENTIAL_ECUS and not len(found_versions): continue # Virtual debug ecu doesn't need to match the database if ecu_type == Ecu.debug: continue - if found_version not in expected_versions: + if not any([found_version in expected_versions for found_version in found_versions]): invalid.append(candidate) break @@ -327,19 +328,16 @@ def match_fw_to_car_exact(fw_versions_dict): def match_fw_to_car(fw_versions, allow_fuzzy=True): - versions = get_interface_attr('FW_VERSIONS', ignore_none=True) - # Try exact matching first exact_matches = [(True, match_fw_to_car_exact)] if allow_fuzzy: exact_matches.append((False, match_fw_to_car_fuzzy)) for exact_match, match_func in exact_matches: - # For each brand, attempt to fingerprint using FW returned from its queries + # TODO: For each brand, attempt to fingerprint using only FW returned from its queries matches = set() - for brand in versions.keys(): - fw_versions_dict = build_fw_dict(fw_versions, filter_brand=brand) - matches |= match_func(fw_versions_dict) + fw_versions_dict = build_fw_dict(fw_versions, filter_brand=None) + matches |= match_func(fw_versions_dict) if len(matches): return exact_match, matches @@ -408,7 +406,9 @@ def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, debug=Fa 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) all_car_fw.extend(car_fw) - matches = match_fw_to_car_exact(build_fw_dict(car_fw)) + + # TODO: Until erroneous FW versions are removed, try to fingerprint on all possible combinations so far + _, matches = match_fw_to_car(all_car_fw, allow_fuzzy=False) if len(matches) == 1: break diff --git a/selfdrive/debug/test_fw_query_on_routes.py b/selfdrive/debug/test_fw_query_on_routes.py index 9ce0ebb3f5..191411f45a 100755 --- a/selfdrive/debug/test_fw_query_on_routes.py +++ b/selfdrive/debug/test_fw_query_on_routes.py @@ -89,24 +89,9 @@ if __name__ == "__main__": print("not in supported cars") break - # Older routes only have carFw from their brand - old_route = not any([len(fw.brand) for fw in car_fw]) - brands = SUPPORTED_BRANDS if not old_route else [None] - - # Exact match - exact_matches, fuzzy_matches = [], [] - for brand in brands: - fw_versions_dict = build_fw_dict(car_fw, filter_brand=brand) - exact_matches = match_fw_to_car_exact(fw_versions_dict) - if len(exact_matches) == 1: - break - - # Fuzzy match - for brand in brands: - fw_versions_dict = build_fw_dict(car_fw, filter_brand=brand) - fuzzy_matches = match_fw_to_car_fuzzy(fw_versions_dict) - if len(fuzzy_matches) == 1: - break + fw_versions_dict = build_fw_dict(car_fw) + exact_matches = match_fw_to_car_exact(fw_versions_dict) + fuzzy_matches = match_fw_to_car_fuzzy(fw_versions_dict) if (len(exact_matches) == 1) and (list(exact_matches)[0] == live_fingerprint): good_exact += 1