mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-07-03 12:32:06 +08:00
191 lines
5.6 KiB
Python
191 lines
5.6 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
from collections import Counter
|
|
from dataclasses import dataclass
|
|
from functools import lru_cache
|
|
from pathlib import Path
|
|
|
|
FINGERPRINT_MAKE_TO_VALUES_DIR = {
|
|
"acura": "honda",
|
|
"audi": "volkswagen",
|
|
"buick": "gm",
|
|
"cadillac": "gm",
|
|
"chevrolet": "gm",
|
|
"chrysler": "chrysler",
|
|
"cupra": "volkswagen",
|
|
"dodge": "chrysler",
|
|
"ford": "ford",
|
|
"genesis": "hyundai",
|
|
"gmc": "gm",
|
|
"holden": "gm",
|
|
"honda": "honda",
|
|
"hyundai": "hyundai",
|
|
"jeep": "chrysler",
|
|
"kia": "hyundai",
|
|
"lexus": "toyota",
|
|
"lincoln": "ford",
|
|
"man": "volkswagen",
|
|
"mazda": "mazda",
|
|
"nissan": "nissan",
|
|
"peugeot": "psa",
|
|
"ram": "chrysler",
|
|
"rivian": "rivian",
|
|
"seat": "volkswagen",
|
|
"\u0161koda": "volkswagen",
|
|
"subaru": "subaru",
|
|
"tesla": "tesla",
|
|
"toyota": "toyota",
|
|
"volkswagen": "volkswagen",
|
|
}
|
|
|
|
_FINGERPRINT_CARDOCS_RE = re.compile(r'\w*CarDocs\(\s*"([^"]+)"')
|
|
_FINGERPRINT_PLATFORM_RE = re.compile(r'(\w+)\s*=\s*\w+\s*\(\s*\[([\s\S]*?)\]\s*,')
|
|
_FINGERPRINT_PLATFORM_NAME_RE = re.compile(r'^[A-Z0-9_]+$')
|
|
_FINGERPRINT_VALID_NAME_RE = re.compile(r'^[A-Za-z0-9 \u0160.()\-]+$')
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class FingerprintModelOption:
|
|
value: str
|
|
label: str
|
|
short_label: str
|
|
option_label: str
|
|
button_label: str
|
|
|
|
|
|
def format_fingerprint_value(value: str) -> str:
|
|
formatted_tokens: list[str] = []
|
|
for token in value.split("_"):
|
|
if not token:
|
|
continue
|
|
|
|
if token.isupper() and (len(token) <= 4 or any(char.isdigit() for char in token)):
|
|
formatted_tokens.append(token)
|
|
else:
|
|
formatted_tokens.append(token.title())
|
|
|
|
return " ".join(formatted_tokens)
|
|
|
|
|
|
def shorten_model_label(make: str, label: str) -> str:
|
|
prefix = f"{make} "
|
|
if label.lower().startswith(prefix.lower()):
|
|
return label[len(prefix):]
|
|
return label
|
|
|
|
|
|
def compact_model_label(label: str) -> str:
|
|
compact = re.sub(r'\s+\([^)]*\)', "", label)
|
|
compact = re.sub(r'\s+-\s+.*$', "", compact)
|
|
compact = re.sub(r'\s+(?:ASCM Harness|Camera Harness|SDGM Harness(?:.*)?|ACC w Pedal|Instrument Cluster)$', "", compact)
|
|
compact = re.sub(r'\s{2,}', " ", compact).strip()
|
|
return compact or label
|
|
|
|
|
|
def _get_openpilot_root() -> Path:
|
|
for parent in Path(__file__).resolve().parents:
|
|
if (parent / "opendbc" / "car").is_dir() or (parent / "selfdrive" / "car").is_dir():
|
|
return parent
|
|
|
|
return Path(__file__).resolve().parents[5]
|
|
|
|
|
|
def _extract_fingerprint_models_for_make(make_key: str) -> list[tuple[str, str, str]]:
|
|
source_make = FINGERPRINT_MAKE_TO_VALUES_DIR.get(make_key, make_key)
|
|
root = _get_openpilot_root()
|
|
values_candidates = (
|
|
root / "opendbc" / "car" / source_make / "values.py",
|
|
root / "selfdrive" / "car" / source_make / "values.py",
|
|
)
|
|
values_path = next((path for path in values_candidates if path.is_file()), None)
|
|
if values_path is None:
|
|
return []
|
|
|
|
try:
|
|
content = values_path.read_text(encoding="utf-8", errors="replace")
|
|
except Exception:
|
|
return []
|
|
|
|
content = re.sub(r'#[^\n]*', "", content)
|
|
content = re.sub(r'footnotes=\[[^\]]*\],\s*', "", content)
|
|
|
|
models: list[tuple[str, str, str]] = []
|
|
seen: set[tuple[str, str]] = set()
|
|
|
|
for platform_match in _FINGERPRINT_PLATFORM_RE.finditer(content):
|
|
platform_name = platform_match.group(1)
|
|
if not _FINGERPRINT_PLATFORM_NAME_RE.match(platform_name):
|
|
continue
|
|
|
|
platform_section = platform_match.group(2)
|
|
for name_match in _FINGERPRINT_CARDOCS_RE.finditer(platform_section):
|
|
car_name = name_match.group(1).strip()
|
|
if " " not in car_name or not _FINGERPRINT_VALID_NAME_RE.match(car_name):
|
|
continue
|
|
|
|
make_label = car_name.split(" ", 1)[0]
|
|
if make_label.lower() != make_key:
|
|
continue
|
|
|
|
dedupe_key = (car_name, platform_name)
|
|
if dedupe_key in seen:
|
|
continue
|
|
|
|
seen.add(dedupe_key)
|
|
models.append((platform_name, car_name, make_label))
|
|
|
|
models.sort(key=lambda entry: entry[1].lower())
|
|
return models
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def get_fingerprint_catalog() -> tuple[
|
|
tuple[str, ...],
|
|
dict[str, tuple[FingerprintModelOption, ...]],
|
|
dict[str, FingerprintModelOption],
|
|
dict[str, str],
|
|
]:
|
|
makes: list[str] = []
|
|
models_by_make: dict[str, tuple[FingerprintModelOption, ...]] = {}
|
|
model_by_value: dict[str, FingerprintModelOption] = {}
|
|
make_by_model: dict[str, str] = {}
|
|
|
|
for make_key in sorted(FINGERPRINT_MAKE_TO_VALUES_DIR):
|
|
entries = _extract_fingerprint_models_for_make(make_key)
|
|
if not entries:
|
|
continue
|
|
|
|
make_label = entries[0][2]
|
|
short_counts = Counter(shorten_model_label(make_label, label) for _, label, _ in entries)
|
|
options: list[FingerprintModelOption] = []
|
|
used_option_labels: set[str] = set()
|
|
|
|
for value, label, _ in entries:
|
|
short_label = shorten_model_label(make_label, label)
|
|
compact_label = compact_model_label(short_label)
|
|
|
|
# Prefer the shorter label in mici, but fall back when it would collide.
|
|
option_label = short_label if short_counts[short_label] == 1 else label
|
|
if option_label in used_option_labels:
|
|
option_label = f"{option_label} ({value})"
|
|
used_option_labels.add(option_label)
|
|
button_label = compact_label if len(compact_label) < len(short_label) else short_label
|
|
|
|
option = FingerprintModelOption(
|
|
value=value,
|
|
label=label,
|
|
short_label=short_label,
|
|
option_label=option_label,
|
|
button_label=button_label,
|
|
)
|
|
options.append(option)
|
|
model_by_value.setdefault(value, option)
|
|
make_by_model.setdefault(value, make_label)
|
|
|
|
makes.append(make_label)
|
|
models_by_make[make_label] = tuple(options)
|
|
|
|
makes.sort(key=str.lower)
|
|
return tuple(makes), models_by_make, model_by_value, make_by_model
|