mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-28 00:53:45 +08:00
e9dd458699
Provide route replay, log migration and recorded vehicle fixtures for regression checks. Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com> Co-authored-by: Dom <168790843+firestar5683@users.noreply.github.com> Co-authored-by: Kacper Rączy <gfw.kra@gmail.com> Co-authored-by: YassineYousfi <yyousfi1@binghamton.edu> Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com>
99 lines
5.3 KiB
Python
99 lines
5.3 KiB
Python
"""Explicit cache conversion for reviewed, immutable replay recordings.
|
|
|
|
Published reference Event and CarParams use their producing schemas; input decoding
|
|
only compares firmware/VIN identity and does not prove the original input schema.
|
|
Input, reference and schemas are pinned before a current typed cache is made.
|
|
Unknown recordings require their own reviewed conversion, never current decoding.
|
|
"""
|
|
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
import tempfile
|
|
|
|
import capnp
|
|
|
|
from opendbc.car.structs import car
|
|
from openpilot.starpilot.schema_cache import put_cache
|
|
from openpilot.tools.lib.logreader import decompress_stream
|
|
from openpilot.tools.lib.url_file import URLFile
|
|
|
|
|
|
OPENPILOT_COMMIT = 'ec95db3f1fa19f76940497fedfdb62e09ea19912'
|
|
ARTIFACT_COMMIT = '9ae451c262c7402c9623f3baa1580c6242d6777b'
|
|
OPENDBC_COMMIT = '35f7e0813462607ef1d703e52313e7571e31405d'
|
|
TARGET_CAR_SHA256 = '1511eaaa36581a2182694e3cdf5a9b3cf05276e90c36bb5d82ee095e84c23d69'
|
|
FIXTURES = json.loads(Path(__file__).with_name('replay-fixture-cache.json').read_text())
|
|
# The reviewed target differs from the source only by SafetyModel.teslaPreap @39.
|
|
# No source field is dropped, renamed, defaulted or reinterpreted by this adapter.
|
|
SOURCE_FILES = {
|
|
'car.capnp': ('opendbc', OPENDBC_COMMIT, 'opendbc/car/car.capnp',
|
|
'098a19d69e8d233ac653503b53761a94824f31812757a11a7ded1589fe1e0788'),
|
|
'include/c++.capnp': ('opendbc', OPENDBC_COMMIT, 'opendbc/car/include/c++.capnp',
|
|
'fb306076cd38c27af1aed20fac6395e9a46fbe5b5df6a248b5f1b6845a079c44'),
|
|
'log.capnp': ('openpilot', OPENPILOT_COMMIT, 'openpilot/cereal/log.capnp',
|
|
'3f8906bd908494e20eb77b7d27536be541d4cf9f8d5277ae8528b3ee59a55673'),
|
|
'custom.capnp': ('openpilot', OPENPILOT_COMMIT, 'openpilot/cereal/custom.capnp',
|
|
'6b06a0d2d5ac6b902ca2f433c1663ed83ea4b41ba4c16cddfc7acd8a630be82d'),
|
|
'deprecated.capnp': ('openpilot', OPENPILOT_COMMIT, 'openpilot/cereal/deprecated.capnp',
|
|
'b3aa5c13de40001ed7376cef858500fb003f7698f96131cd0db18d878fa1af0d'),
|
|
}
|
|
|
|
|
|
def _digest(raw):
|
|
return hashlib.sha256(raw).hexdigest()
|
|
|
|
|
|
class _Sink:
|
|
def put(self, key, value, *, block):
|
|
self.raw = value
|
|
|
|
|
|
def verified_fixture_params(segment: str, raw: bytes) -> dict[str, bytes]:
|
|
"""Create a cache only after independent source decoding and lossless conversion."""
|
|
expected = FIXTURES.get(segment)
|
|
if expected is None or len(raw) != expected['input_bytes'] or _digest(raw) != expected['input_sha256']:
|
|
raise ValueError(f'{segment}: no reviewed immutable recording cache conversion')
|
|
target_path = Path(__file__).resolve().parents[2] / 'opendbc_repo/opendbc/car/car.capnp'
|
|
if _digest(target_path.read_bytes()) != TARGET_CAR_SHA256:
|
|
raise ValueError('Replay cache target schema changed; review the source conversion')
|
|
with tempfile.TemporaryDirectory(prefix='replay-source-schema-') as directory:
|
|
root = Path(directory)
|
|
for local, (repo, commit, path, digest) in SOURCE_FILES.items():
|
|
source = URLFile(f'https://raw.githubusercontent.com/commaai/{repo}/{commit}/{path}').read()
|
|
if _digest(source) != digest:
|
|
raise ValueError(f'{local}: replay producing schema digest mismatch')
|
|
destination = root / local
|
|
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
destination.write_bytes(source)
|
|
parser = capnp.SchemaParser()
|
|
source_log = parser.load(str(root / 'log.capnp'), imports=[str(root)])
|
|
reference_name = f'{segment.replace("|", "_")}_card_{OPENPILOT_COMMIT}.zst'
|
|
reference = URLFile(f'https://raw.githubusercontent.com/commaai/ci-artifacts/{ARTIFACT_COMMIT}/{reference_name}').read()
|
|
if len(reference) != expected['reference_bytes'] or _digest(reference) != expected['reference_sha256']:
|
|
raise ValueError('Published replay CarParams reference digest mismatch')
|
|
source_cp = next((message.carParams.to_dict()
|
|
for message in source_log.Event.read_multiple_bytes(decompress_stream(reference))
|
|
if message.which() == 'carParams'), None)
|
|
if source_cp is None:
|
|
raise ValueError('Published card reference is missing CarParams')
|
|
# Compare only the firmware/VIN identity consumed by the current fingerprint
|
|
# path. Historical input control settings never seed the verified cache.
|
|
input_cp = next((message.carParams.to_dict()
|
|
for message in source_log.Event.read_multiple_bytes(decompress_stream(raw))
|
|
if message.which() == 'carParams'), None)
|
|
# The pinned Rivian input predates the existing R1 name migration. Its
|
|
# reference uses the current platform name; firmware and VIN remain exact.
|
|
if segment == 'regen5FCAC896BBE|2025-04-08--23-13-35--0' and input_cp is not None:
|
|
if input_cp.get('carFingerprint') == 'RIVIAN_R1_GEN1':
|
|
input_cp['carFingerprint'] = 'RIVIAN_R1'
|
|
identity = ('carFingerprint', 'carVin', 'carFw')
|
|
if input_cp is None or any(input_cp[key] != source_cp[key] for key in identity):
|
|
raise ValueError('Recording firmware/VIN identity differs from its published card reference')
|
|
converted = car.CarParams.new_message(**source_cp)
|
|
if converted.to_dict() != source_cp:
|
|
raise ValueError('Replay CarParams source conversion changed typed values')
|
|
sink = _Sink()
|
|
put_cache(sink, 'CarParamsCache', converted, block=True)
|
|
return {'CarParamsCache': sink.raw}
|