From 7920339796213ca5696b728a1786e583f2e2b0cd Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Tue, 3 Nov 2026 12:33:24 -0600 Subject: [PATCH] Prevent same-bus HKG advisory packet retransmission --- opendbc_repo/opendbc/car/hyundai/daw.py | 3 +- .../opendbc/car/hyundai/tests/test_daw.py | 39 ++++++++++++++++++- .../opendbc/safety/tests/test_hyundai_daw.py | 23 ++++++++--- prebuilt.json | 2 +- 4 files changed, 57 insertions(+), 10 deletions(-) diff --git a/opendbc_repo/opendbc/car/hyundai/daw.py b/opendbc_repo/opendbc/car/hyundai/daw.py index 1963f0242d..712e899544 100644 --- a/opendbc_repo/opendbc/car/hyundai/daw.py +++ b/opendbc_repo/opendbc/car/hyundai/daw.py @@ -35,7 +35,8 @@ class DawSource: self.data, self.stamp = bytes(data), stamp def command(self, now_ns): - if not self.enabled or self.data is None or not 0 < self.stamp <= now_ns <= self.stamp + 100_000_000: + if (not self.enabled or self.source_bus == self.target_bus or self.data is None or + not 0 < self.stamp <= now_ns <= self.stamp + 100_000_000): return [] data = bytearray(self.data) if self.canfd: diff --git a/opendbc_repo/opendbc/car/hyundai/tests/test_daw.py b/opendbc_repo/opendbc/car/hyundai/tests/test_daw.py index 28acaa4992..56f8d74c23 100644 --- a/opendbc_repo/opendbc/car/hyundai/tests/test_daw.py +++ b/opendbc_repo/opendbc/car/hyundai/tests/test_daw.py @@ -25,9 +25,9 @@ def source(canfd, status): return bytes(data) -@pytest.mark.parametrize('canfd', (False, True)) @pytest.mark.parametrize('status', range(8)) -def test_exact_current_advisory_only_transform(canfd, status): +def test_exact_current_advisory_only_transform(status): + canfd = False owner = DawSource(params(canfd)) data = source(canfd, status) assert not owner.command(BASE) @@ -102,3 +102,38 @@ def test_unsupported_ioniq6_native_profiles_cannot_emit_advisories(word): assert not owner.enabled owner.update([(BASE, [CanData(0x11A, source(True, 1), owner.source_bus)])]) assert not owner.command(BASE) + + +@pytest.mark.parametrize('word', (0x11, 0x91, 0x811, 0x891, 0x8015, 0x8095, 0x8815, 0x8895)) +@pytest.mark.parametrize('status', range(8)) +def test_live_same_bus_canfd_source_is_never_retransmitted(word, status): + cp = params(True) + cp.safetyConfigs[-1].safetyParam = word + owner = DawSource(cp) + assert owner.enabled and owner.source_bus == owner.target_bus + for tick in range(20): + stamp = BASE + tick * 10_000_000 + data = bytearray(source(True, status)) + data[2] = tick + data[:2] = hkg_can_fd_checksum(owner.address, None, data).to_bytes(2, 'little') + owner.update([(stamp, [CanData(owner.address, bytes(data), owner.source_bus)])]) + assert owner.data == bytes(data) + assert owner.command(stamp) == [] + assert owner.command(stamp + 5_000_000) == [] + + +@pytest.mark.parametrize('alternate', (False, True)) +def test_actual_ioniq6_controller_keeps_live_source_off_sendcan(alternate): + cp, _ = ioniq6_params(alternate) + before = cp.to_dict() + first, second = CarInterface(cp.as_reader().as_builder()), CarInterface(cp.as_reader().as_builder()) + data = source(True, 1) + plain = first.update([(BASE, [])]) + observed = second.update([(BASE, [CanData(0x11A, data, 1)])]) + assert observed.to_dict() == plain.to_dict() + control = structs.CarControl() + baseline = first.apply(control.as_reader(), BASE)[1] + actual = second.apply(control.as_reader(), BASE)[1] + assert actual == baseline + assert all(frame[0] != 0x11A for frame in actual) + assert cp.to_dict() == before diff --git a/opendbc_repo/opendbc/safety/tests/test_hyundai_daw.py b/opendbc_repo/opendbc/safety/tests/test_hyundai_daw.py index da197ddde5..33a6b0374b 100644 --- a/opendbc_repo/opendbc/safety/tests/test_hyundai_daw.py +++ b/opendbc_repo/opendbc/safety/tests/test_hyundai_daw.py @@ -15,6 +15,17 @@ def packet(addr, data, bus): return libsafety_py.make_CANPacket(addr, bus, data) +def advisory_candidate(owner, now): + if not owner.canfd: + return owner.command(now)[0] + assert owner.command(now) == [] + data = bytearray(owner.data) + if (data[7] >> 3) & 7 == 1: + data[7] &= ~0x38 + data[:2] = hkg_can_fd_checksum(owner.address, None, data).to_bytes(2, 'little') + return owner.address, bytes(data), owner.target_bus + + def setup(canfd, word): safety = libsafety_py.libsafety debug = safety.set_safety_hooks(structs.CarParams.SafetyModel.allOutput, 0) == 0 @@ -33,7 +44,7 @@ def test_actual_host_commands_obey_native_source_payload_and_expiry(canfd, word, owner = DawSource(params(canfd)) data = source(canfd, status) owner.update([(BASE, [(owner.address, data, owner.source_bus)])]) - addr, output, bus = owner.command(BASE)[0] + addr, output, bus = advisory_candidate(owner, BASE) assert not safety.safety_tx_hook(packet(addr, output, bus)) safety.safety_rx_hook(packet(addr, data, owner.source_bus)) assert bool(safety.safety_tx_hook(packet(addr, output, bus))) == supported @@ -60,7 +71,7 @@ def test_source_rejection_reset_and_relay_are_preserved(canfd, fault): owner = DawSource(params(canfd)) data = source(canfd, 1) owner.update([(BASE, [(owner.address, data, owner.source_bus)])]) - addr, output, bus = owner.command(BASE)[0] + addr, output, bus = advisory_candidate(owner, BASE) incoming = data[:-4] if fault == 'length' and canfd else data[:-1] if fault == 'length' else data rx_bus = owner.source_bus + 1 if fault == 'bus' else owner.source_bus if fault == 'crc': @@ -95,7 +106,7 @@ def test_one_genuine_counter_predecessor_cannot_hide_newer_hands_off_request(): owner = DawSource(params(True)) data = source(True, 1) owner.update([(BASE, [(0x11A, data, 1)])]) - _, output, _ = owner.command(BASE)[0] + _, output, _ = advisory_candidate(owner, BASE) safety.safety_rx_hook(packet(0x11A, data, 1)) following = bytearray(data) following[2] += 1 @@ -110,7 +121,7 @@ def test_one_genuine_counter_predecessor_cannot_hide_newer_hands_off_request(): safety.safety_rx_hook(packet(0x11A, newer, 1)) assert not safety.safety_tx_hook(packet(0x11A, output, 1)) owner.update([(BASE + 10_000_000, [(0x11A, bytes(following), 1)])]) - _, prior_output, _ = owner.command(BASE + 10_000_000)[0] + _, prior_output, _ = advisory_candidate(owner, BASE + 10_000_000) assert safety.safety_tx_hook(packet(0x11A, prior_output, 1)) hands_off = bytearray(newer) hands_off[2] += 1 @@ -120,8 +131,8 @@ def test_one_genuine_counter_predecessor_cannot_hide_newer_hands_off_request(): safety.safety_rx_hook(packet(0x11A, hands_off, 1)) assert not safety.safety_tx_hook(packet(0x11A, prior_output, 1)) owner.update([(BASE + 20_000_000, [(0x11A, bytes(newer), 1)])]) - _, prior_output, _ = owner.command(BASE + 20_000_000)[0] + _, prior_output, _ = advisory_candidate(owner, BASE + 20_000_000) assert not safety.safety_tx_hook(packet(0x11A, prior_output, 1)) owner.update([(BASE + 30_000_000, [(0x11A, bytes(hands_off), 1)])]) - _, current_output, _ = owner.command(BASE + 30_000_000)[0] + _, current_output, _ = advisory_candidate(owner, BASE + 30_000_000) assert safety.safety_tx_hook(packet(0x11A, current_output, 1)) diff --git a/prebuilt.json b/prebuilt.json index ed4e1ae659..ede7a91ac1 100644 --- a/prebuilt.json +++ b/prebuilt.json @@ -1 +1 @@ -{"protected_inventory_sha256": "16b87cb64ab51930429e6251dad19d486b669b0c86e4f005f97fa7a5f9045661", "schema_version": 1} +{"protected_inventory_sha256": "fabbd14a4c2dfdaf5c55e515d9b8ba8e9b35f495f28b017bde6ec5df54406c34", "schema_version": 1}