mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-04 11:33:47 +08:00
Recognize standard HDA2 wiring before diagnostic queries
This commit is contained in:
@@ -17,10 +17,17 @@ HDA1_NORMAL_BUS_LAYOUT = {(0, 0x120): 32, (2, 0x120): 32, (0, 0x35): 32, (0, 0xE
|
||||
(0, 0xA0): 24, (0, 0x1A0): 32, (0, 0x1CF): 8, (0, 0x130): 16}
|
||||
|
||||
|
||||
HDA2_NORMAL_BUS_LAYOUT = {(2, 0x50): 16, (2, 0x2A4): 24, (1, 0x35): 32, (1, 0xEA): 24,
|
||||
(1, 0xA0): 24, (1, 0x1A0): 32, (1, 0x1CF): 8, (1, 0x130): 16}
|
||||
|
||||
|
||||
def normal_bus_topology(can_recv, *, clock=time.monotonic):
|
||||
deadline = clock() + .12
|
||||
layouts = (NORMAL_BUS_LAYOUT, CLASSIC_NORMAL_BUS_LAYOUT)
|
||||
observed = [{} for _ in layouts]
|
||||
original_invalid = False
|
||||
hda2_observed = {}
|
||||
hda2_invalid = False
|
||||
hda1_observed = {}
|
||||
hda1_invalid = False
|
||||
for _ in range(24):
|
||||
@@ -33,14 +40,24 @@ def normal_bus_topology(can_recv, *, clock=time.monotonic):
|
||||
if key in layout:
|
||||
length = len(frame.dat)
|
||||
if length != layout[key]:
|
||||
return False
|
||||
# Standard HDA2 reuses the classic MDPS identifier with 16 bytes.
|
||||
if key != (0, 0x251) or length != 16:
|
||||
return False
|
||||
original_invalid = True
|
||||
received[key] = length
|
||||
if key in HDA2_NORMAL_BUS_LAYOUT:
|
||||
if len(frame.dat) != HDA2_NORMAL_BUS_LAYOUT[key]:
|
||||
hda2_invalid = True
|
||||
else:
|
||||
hda2_observed[key] = len(frame.dat)
|
||||
if key in HDA1_NORMAL_BUS_LAYOUT:
|
||||
if len(frame.dat) != HDA1_NORMAL_BUS_LAYOUT[key]:
|
||||
hda1_invalid = True
|
||||
else:
|
||||
hda1_observed[key] = len(frame.dat)
|
||||
if (any(received == layout for layout, received in zip(layouts, observed, strict=True)) or
|
||||
not hda1_invalid and hda1_observed == HDA1_NORMAL_BUS_LAYOUT):
|
||||
# Other families reuse HDA2 identifiers with different packet lengths.
|
||||
if (not hda2_invalid and hda2_observed == HDA2_NORMAL_BUS_LAYOUT or
|
||||
not original_invalid and (any(received == layout for layout, received in zip(layouts, observed, strict=True)) or
|
||||
not hda1_invalid and hda1_observed == HDA1_NORMAL_BUS_LAYOUT)):
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
"""Standard HDA2 wiring selects fresh normal-bus queries, never vehicle identity."""
|
||||
import json
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from opendbc.car import gen_empty_fingerprint
|
||||
from opendbc.car.can_definitions import CanData
|
||||
from opendbc.car.car_helpers import fingerprint
|
||||
from opendbc.car.hyundai.discovery import CLASSIC_NORMAL_BUS_LAYOUT, HDA1_NORMAL_BUS_LAYOUT, NORMAL_BUS_LAYOUT, normal_bus_topology
|
||||
from opendbc.car.hyundai.interface import CarInterface
|
||||
from opendbc.car.hyundai.values import CAR
|
||||
from opendbc.car.vin import VIN_UNKNOWN
|
||||
from openpilot.cereal import log
|
||||
from openpilot.starpilot.aol.panda_faults import blocking_fault_list
|
||||
from openpilot.starpilot.car.hyundai.aol import policy_for
|
||||
|
||||
CAPTURE = json.loads((Path(__file__).parent / 'testdata/hda2_discovery.json').read_text())
|
||||
|
||||
|
||||
def frames(collision=True):
|
||||
values = ([CAPTURE['classicIdCollision']] if collision else []) + CAPTURE['frames']
|
||||
return [CanData(f['address'], bytes.fromhex(f['data']), f['bus']) for f in values]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('collision', [False, True])
|
||||
def test_recorded_hda2_layout_selects_normal_wiring(collision):
|
||||
assert normal_bus_topology(lambda **kw: [frames(collision)]), 'HDA2_NORMAL_QUERY_LAYOUT_MISSING'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('index', range(8))
|
||||
@pytest.mark.parametrize('defect', ['missing', 'length', 'bus', 'echo', 'contradiction'])
|
||||
def test_incomplete_malformed_or_echoed_hda2_still_cannot_select_query_family(index, defect):
|
||||
packets = frames()
|
||||
original = packets.pop(index + 1)
|
||||
if defect in ('length', 'contradiction'):
|
||||
packets.append(CanData(original.address, original.dat[:-1], original.src))
|
||||
if defect == 'contradiction':
|
||||
packets.append(original)
|
||||
elif defect != 'missing':
|
||||
packets.append(CanData(original.address, original.dat, original.src + (128 if defect == 'echo' else 1)))
|
||||
assert not normal_bus_topology(lambda **kw: [packets])
|
||||
|
||||
|
||||
@pytest.mark.parametrize('layout', [NORMAL_BUS_LAYOUT, CLASSIC_NORMAL_BUS_LAYOUT, HDA1_NORMAL_BUS_LAYOUT])
|
||||
def test_original_family_contradictions_still_fail_without_complete_hda2_proof(layout):
|
||||
packets = [CanData(address, bytes(length), bus) for (bus, address), length in layout.items()]
|
||||
packets.insert(0, CanData(0x251, bytes(16), 0))
|
||||
assert not normal_bus_topology(lambda **kw: [packets])
|
||||
|
||||
|
||||
@pytest.mark.parametrize('collision', [False, True])
|
||||
def test_complete_hda2_proof_can_span_multiple_can_drains(collision):
|
||||
packets = frames(collision)
|
||||
batches = iter([[f] for f in packets])
|
||||
assert normal_bus_topology(lambda **kw: [next(batches, [])])
|
||||
|
||||
|
||||
@pytest.mark.parametrize('collision', [False, True])
|
||||
def test_actual_fingerprint_keeps_hda2_on_normal_wiring_and_requires_fresh_identity(collision):
|
||||
switches = []
|
||||
with patch('opendbc.car.car_helpers.get_vin', return_value=(-1, -1, VIN_UNKNOWN)) as vin, \
|
||||
patch('opendbc.car.car_helpers.get_fw_versions', return_value=[]) as query, \
|
||||
patch('opendbc.car.car_helpers.get_present_ecus', return_value=set()) as present, \
|
||||
patch('opendbc.car.car_helpers.get_fw_versions_ordered', return_value=[]) as generic, \
|
||||
patch('opendbc.car.car_helpers.match_fw_to_car', return_value=(True, set())), \
|
||||
patch('opendbc.car.car_helpers.can_fingerprint', return_value=(None, {})):
|
||||
actual = fingerprint(lambda wait_for_one=False: [frames(collision)], lambda packets: None, switches.append, None)
|
||||
assert actual[0] is None
|
||||
assert switches and not any(switches), 'HDA2_ENTERED_OBD_QUERY_FALLBACK'
|
||||
assert vin.call_args.args[2] == (0, 1)
|
||||
query.assert_called_once()
|
||||
assert query.call_args.kwargs == {'query_brand': 'hyundai', 'normal_bus_only': True}
|
||||
present.assert_not_called()
|
||||
generic.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('identity', [CAR.KIA_EV6, CAR.HYUNDAI_IONIQ_5])
|
||||
@pytest.mark.parametrize('release', [False, True])
|
||||
def test_exact_ev6_stock_aol_word_and_neighbor_keep_interrupt_fault_blocking(identity, release):
|
||||
observed = gen_empty_fingerprint()
|
||||
for frame in frames():
|
||||
observed[frame.src][frame.address] = len(frame.dat)
|
||||
cp = CarInterface.get_params(identity, observed, [], False, release, False)
|
||||
assert cp.pcmCruise and not cp.openpilotLongitudinalControl
|
||||
assert cp.safetyConfigs[0].safetyParam == 0x11
|
||||
cp.safetyConfigs[0].safetyParam |= policy_for(cp).safety_param_addition
|
||||
assert cp.safetyConfigs[0].safetyParam == 0x811
|
||||
if identity == CAR.KIA_EV6:
|
||||
assert cp.flags == 10241
|
||||
assert blocking_fault_list([log.PandaState.FaultType.interruptRateCan2], cp)
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"build": "6e11c50dfce6da18db9700b7305a2febcbd4ee9f",
|
||||
"frames": [
|
||||
{
|
||||
"bus": 1,
|
||||
"address": 53,
|
||||
"data": "c9f71a510500250526f5fe100040383f04000000203996d40500000000000000"
|
||||
},
|
||||
{
|
||||
"bus": 1,
|
||||
"address": 463,
|
||||
"data": "46e0002000000000"
|
||||
},
|
||||
{
|
||||
"bus": 1,
|
||||
"address": 304,
|
||||
"data": "004db302020300000400000000000000"
|
||||
},
|
||||
{
|
||||
"bus": 1,
|
||||
"address": 234,
|
||||
"data": "2d032110414300001408d50fe9ff0000e9ff000000000000"
|
||||
},
|
||||
{
|
||||
"bus": 1,
|
||||
"address": 160,
|
||||
"data": "d216a974be4feb05300230022f022e020040010000000000"
|
||||
},
|
||||
{
|
||||
"bus": 2,
|
||||
"address": 80,
|
||||
"data": "8d8c1f02400008006400010000000000"
|
||||
},
|
||||
{
|
||||
"bus": 1,
|
||||
"address": 416,
|
||||
"data": "3acc8b56083564000000000000080000fff33f130a000500fe07000000000000"
|
||||
},
|
||||
{
|
||||
"bus": 2,
|
||||
"address": 676,
|
||||
"data": "f64b0622115512338333a000d20000000000000000000000"
|
||||
}
|
||||
],
|
||||
"classicIdCollision": {
|
||||
"bus": 0,
|
||||
"address": 593,
|
||||
"data": "4f2f0515140206210400000000000000"
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"protected_inventory_sha256": "567658c0f7e93cec0b15bec55dcabded810f6cd2c1d3321009976fa147360f4a", "schema_version": 1}
|
||||
{"protected_inventory_sha256": "997dfa5352e8044453598a3f140eda0561e8ceb6a086696f7965545297e18a2e", "schema_version": 1}
|
||||
|
||||
Reference in New Issue
Block a user