diff --git a/opendbc_repo/opendbc/car/hyundai/carcontroller.py b/opendbc_repo/opendbc/car/hyundai/carcontroller.py index ab8ba506a2..aa092c18ff 100644 --- a/opendbc_repo/opendbc/car/hyundai/carcontroller.py +++ b/opendbc_repo/opendbc/car/hyundai/carcontroller.py @@ -297,6 +297,7 @@ class CarController(CarControllerBase): self.frame += 1 if _projection is not None: _projection.finish(new_actuators.torque) + can_sends.extend(CS.daw.command(now_nanos)) return new_actuators, can_sends def create_can_msgs(self, apply_steer_req, apply_torque, torque_fault, set_speed_in_units, accel, stopping, diff --git a/opendbc_repo/opendbc/car/hyundai/carstate.py b/opendbc_repo/opendbc/car/hyundai/carstate.py index 2442ac0a92..16ac83522e 100644 --- a/opendbc_repo/opendbc/car/hyundai/carstate.py +++ b/opendbc_repo/opendbc/car/hyundai/carstate.py @@ -62,6 +62,8 @@ def get_non_scc_cruise_signals(flags: int, car=None) -> tuple[str, str, str, str class CarState(CarStateBase): def __init__(self, CP): super().__init__(CP) + from opendbc.car.hyundai.daw import DawSource + self.daw = DawSource(CP) self.forte_lkas_sources = NonSccLkasSources() if qualified_non_scc(CP) and CP.flags & HyundaiFlags.HAS_LDA_BUTTON else None if (qualified_classic_scc(CP) or qualified_classic_long(CP)) and CP.flags & HyundaiFlags.HAS_LDA_BUTTON: self.forte_lkas_sources = ClassicSccLkasSources(CP.carFingerprint) diff --git a/opendbc_repo/opendbc/car/hyundai/daw.py b/opendbc_repo/opendbc/car/hyundai/daw.py new file mode 100644 index 0000000000..1963f0242d --- /dev/null +++ b/opendbc_repo/opendbc/car/hyundai/daw.py @@ -0,0 +1,47 @@ +from opendbc.car import CanData +from opendbc.car.hyundai.hyundaicanfd import CanBus, hkg_can_fd_checksum +from opendbc.car.hyundai.values import CAR, HyundaiFlags, is_blended + + +CANFD_DAW_WORDS = frozenset((0x11, 0x91, 0x811, 0x891, 0x8015, 0x8095, 0x8815, 0x8895)) + + +class DawSource: + def __init__(self, cp): + self.canfd = bool(cp.flags & HyundaiFlags.CANFD) + self.enabled = (cp.brand == 'hyundai' and not cp.passive and not cp.dashcamOnly and not cp.notCar and + not is_blended(cp) and (not self.canfd or cp.carFingerprint == CAR.HYUNDAI_IONIQ_6)) + if self.canfd: + self.enabled = bool(self.enabled and cp.safetyConfigs and + int(cp.safetyConfigs[-1].safetyParam) in CANFD_DAW_WORDS and CanBus(cp).ECAN % 4 == 1) + self.address = 0x11A if self.canfd else 0x53E + self.source_bus = CanBus(cp).ECAN if self.canfd else 2 + self.target_bus = CanBus(cp).ECAN if self.canfd else 0 + self.length = 16 if self.canfd else 6 + self.data = None + self.stamp = 0 + + def update(self, packets): + if not self.enabled: + return + for stamp, frames in packets: + for address, data, bus in frames: + if address != self.address or bus != self.source_bus: + continue + if stamp <= self.stamp or len(data) != self.length or (self.canfd and + int.from_bytes(data[:2], 'little') != hkg_can_fd_checksum(address, None, data)): + self.data = None + continue + 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: + return [] + data = bytearray(self.data) + if self.canfd: + if (data[7] >> 3) & 7 == 1: + data[7] &= ~0x38 + data[:2] = hkg_can_fd_checksum(self.address, None, data).to_bytes(2, 'little') + elif 1 <= data[5] & 7 <= 3: + data[5] &= ~7 + return [CanData(self.address, bytes(data), self.target_bus)] diff --git a/opendbc_repo/opendbc/car/hyundai/interface.py b/opendbc_repo/opendbc/car/hyundai/interface.py index ba71a644d7..73532156f8 100644 --- a/opendbc_repo/opendbc/car/hyundai/interface.py +++ b/opendbc_repo/opendbc/car/hyundai/interface.py @@ -396,6 +396,9 @@ class CarInterface(CarInterfaceBase): return ret def update(self, can_packets): + if self.CS.daw.enabled: + can_packets = list(can_packets) + self.CS.daw.update(can_packets) recovery = getattr(getattr(self, 'CC', None), 'angle_tx_recovery', None) if recovery is not None: can_packets = list(can_packets) diff --git a/opendbc_repo/opendbc/car/hyundai/tests/test_daw.py b/opendbc_repo/opendbc/car/hyundai/tests/test_daw.py new file mode 100644 index 0000000000..28acaa4992 --- /dev/null +++ b/opendbc_repo/opendbc/car/hyundai/tests/test_daw.py @@ -0,0 +1,104 @@ +import pytest + +from opendbc.car import CanData, gen_empty_fingerprint, structs +from opendbc.car.hyundai.daw import DawSource +from opendbc.car.hyundai.hyundaicanfd import hkg_can_fd_checksum +from opendbc.car.hyundai.interface import CarInterface +from opendbc.car.hyundai.tests.test_ioniq6_stock_parser import params as ioniq6_params +from opendbc.car.hyundai.values import CAR + +BASE = 1_000_000_000 + + +def params(canfd): + return ioniq6_params(True)[0] if canfd else CarInterface.get_params( + CAR.HYUNDAI_SONATA_HYBRID, gen_empty_fingerprint(), [], False, False, False) + + +def source(canfd, status): + data = bytearray.fromhex('1234567898f8') if not canfd else bytearray(range(16)) + if canfd: + data[7] = (data[7] & ~0x38) | (status << 3) + data[:2] = hkg_can_fd_checksum(0x11A, None, data).to_bytes(2, 'little') + else: + data[5] = (data[5] & ~7) | 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): + owner = DawSource(params(canfd)) + data = source(canfd, status) + assert not owner.command(BASE) + owner.update([(BASE, [CanData(owner.address, data, owner.source_bus)])]) + command, = owner.command(BASE) + expected = bytearray(data) + if canfd: + if status == 1: + expected[7] &= ~0x38 + expected[:2] = hkg_can_fd_checksum(owner.address, None, expected).to_bytes(2, 'little') + elif 1 <= status <= 3: + expected[5] &= ~7 + assert command == CanData(owner.address, bytes(expected), owner.target_bus) + assert owner.data == data + assert owner.command(BASE + 100_000_000) + assert not owner.command(BASE + 100_000_001) + + +@pytest.mark.parametrize('canfd,fault', [(canfd, fault) for canfd in (False, True) + for fault in ('future', 'old', 'reversed', 'length', 'returned', 'wrong_bus', 'wrong_address', 'crc') + if canfd or fault != 'crc']) +def test_bad_or_missing_sources_cannot_create_replacements(canfd, fault): + owner = DawSource(params(canfd)) + data = source(canfd, 1) + stamp, bus, addr = BASE, owner.source_bus, owner.address + if fault == 'length': + data = data[:-1] + elif fault == 'returned': + bus += 128 + elif fault == 'wrong_bus': + bus = 0 if canfd else 1 + elif fault == 'wrong_address': + addr += 1 + elif fault == 'crc': + data = bytes([data[0] ^ 1]) + data[1:] + elif fault == 'reversed': + owner.update([(BASE + 1, [CanData(addr, data, bus)])]) + owner.update([(stamp, [CanData(addr, data, bus)])]) + now = BASE - 1 if fault == 'future' else BASE + 100_000_001 if fault == 'old' else BASE + assert not owner.command(now) + + +def test_actual_parser_and_controller_keep_optional_message_out_of_driving_health(): + cp = params(False) + before = cp.to_dict() + first, second = CarInterface(cp.as_reader().as_builder()), CarInterface(cp.as_reader().as_builder()) + data = bytes.fromhex('000000000883') + plain = first.update([(BASE, [])]) + observed = second.update([(BASE, [CanData(0x53E, data, 2)])]) + 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 [frame for frame in actual if frame[0] != 0x53E] == baseline + assert [frame for frame in actual if frame[0] == 0x53E] == [CanData(0x53E, bytes.fromhex('000000000880'), 0)] + assert cp.to_dict() == before + + +def test_unrelated_canfd_profiles_do_not_emit_new_frames(): + cp = CarInterface.get_params(CAR.KIA_EV6, gen_empty_fingerprint(), [], False, False, False) + owner = DawSource(cp) + assert not owner.enabled + owner.update([(BASE, [CanData(0x11A, source(True, 1), 1)])]) + assert not owner.command(BASE) + + +@pytest.mark.parametrize('word', (0, 1, 9, 0x15, 0x5491, 0x5495)) +def test_unsupported_ioniq6_native_profiles_cannot_emit_advisories(word): + cp = params(True) + cp.safetyConfigs[-1].safetyParam = word + owner = DawSource(cp) + assert not owner.enabled + owner.update([(BASE, [CanData(0x11A, source(True, 1), owner.source_bus)])]) + assert not owner.command(BASE) diff --git a/opendbc_repo/opendbc/safety/modes/hyundai.h b/opendbc_repo/opendbc/safety/modes/hyundai.h index b6186b616d..1c1c601904 100644 --- a/opendbc_repo/opendbc/safety/modes/hyundai.h +++ b/opendbc_repo/opendbc/safety/modes/hyundai.h @@ -2,6 +2,7 @@ #include "opendbc/safety/declarations.h" #include "opendbc/safety/modes/hyundai_common.h" +#include "opendbc/safety/modes/hyundai_daw.h" #include "opendbc/safety/modes/hyundai_classic_non_scc_aol.h" #include "opendbc/safety/modes/hyundai_blended_stock_aol.h" @@ -350,7 +351,7 @@ static bool hyundai_tx_hook(const CANPacket_t *msg) { const TorqueSteeringLimits HYUNDAI_STEERING_LIMITS_ALT = HYUNDAI_LIMITS(270, 2, 3); const TorqueSteeringLimits HYUNDAI_STEERING_LIMITS_ALT_2 = HYUNDAI_LIMITS(170, 2, 3); - bool tx = true; + bool tx = (msg->addr != 0x53EU) || hyundai_daw_tx(msg); if (hyundai_ray_pedal && (msg->addr == 0x200U)) { const uint16_t track1 = ((uint16_t)msg->data[0] << 8U) | msg->data[1]; @@ -638,6 +639,7 @@ static safety_config hyundai_init(uint16_t param) { }; hyundai_common_init(param); + hyundai_daw_init(false, false); if ((param == 0x800EU) && (alternative_experience == 0)) { hyundai_long_main_policy = HYUNDAI_MAIN_TRACK; } @@ -900,7 +902,7 @@ static safety_config hyundai_init(uint16_t param) { const safety_config denied = {0}; ret = denied; } - return ret; + return hyundai_daw_configure(ret, false, !hyundai_blended_stock && !hyundai_blended_alpha); } static safety_config hyundai_legacy_init(uint16_t param) { @@ -922,6 +924,7 @@ static safety_config hyundai_legacy_init(uint16_t param) { } } hyundai_common_init(normalized); + hyundai_daw_init(false, false); classic_non_scc_aol_configure(normalized, !legacy_long); hyundai_legacy = true; hyundai_blended_stock = false; @@ -950,10 +953,11 @@ static safety_config hyundai_legacy_init(uint16_t param) { #endif } hyundai_classic_scc_aol_rx_config(&ret, true); - return ret; + return hyundai_daw_configure(ret, false, true); } static void hyundai_optional_rx_hook(const CANPacket_t *msg) { + hyundai_daw_rx(msg); if ((msg->addr == 0x391U) || (msg->addr == 0x50CU)) { blended_aol_rx(msg); } diff --git a/opendbc_repo/opendbc/safety/modes/hyundai_canfd.h b/opendbc_repo/opendbc/safety/modes/hyundai_canfd.h index 1b85c4653e..6ec8744bd4 100644 --- a/opendbc_repo/opendbc/safety/modes/hyundai_canfd.h +++ b/opendbc_repo/opendbc/safety/modes/hyundai_canfd.h @@ -2,6 +2,7 @@ #include "opendbc/safety/declarations.h" #include "opendbc/safety/modes/hyundai_common.h" +#include "opendbc/safety/modes/hyundai_daw.h" #include "opendbc/safety/modes/hyundai_canfd_aol.h" #include "opendbc/safety/modes/hyundai_canfd_angle_aol.h" @@ -527,6 +528,7 @@ static void hyundai_canfd_ioniq6_optional_rx_hook(const CANPacket_t *msg) { } static void hyundai_canfd_optional_rx_hook(const CANPacket_t *msg) { + hyundai_daw_rx(msg); if (hyundai_canfd_ioniq6_long && (msg->bus == 1U)) { hyundai_canfd_ioniq6_optional_rx_hook(msg); } @@ -1272,7 +1274,7 @@ static bool hyundai_canfd_tx_hook(const CANPacket_t *msg) { } } if (ordinary_angle_valid && !((hyundai_canfd_ioniq6_long || hyundai_canfd_ccnc_ev_long) && safety_rx_checks_invalid)) { - tx = hyundai_canfd_tx_hook_valid(msg); + tx = (msg->addr == 0x11AU) ? hyundai_daw_tx(msg) : hyundai_canfd_tx_hook_valid(msg); } if ((hyundai_ordinary_angle_stock && !hyundai_angle_aol_enabled && (msg->addr == 0x110U) && tx) || (independent_command && independent_active && tx)) { @@ -2221,6 +2223,7 @@ static safety_config hyundai_canfd_init_validated(uint16_t supplied_param) { } static safety_config hyundai_canfd_init(uint16_t param) { + hyundai_daw_init(true, false); hyundai_canfd_reset_ioniq6_state(); hyundai_ordinary_angle_reset(); #ifdef ALLOW_DEBUG @@ -2239,6 +2242,9 @@ static safety_config hyundai_canfd_init(uint16_t param) { (param == 0x8815U) || (param == 0x8895U); if (valid && marker_valid) { ret = hyundai_canfd_init_validated(param); + const bool daw_profile = (param == 0x0011U) || (param == 0x0091U) || (param == 0x0811U) || (param == 0x0891U) || + (param == 0x8015U) || (param == 0x8095U) || (param == 0x8815U) || (param == 0x8895U); + ret = hyundai_daw_configure(ret, true, daw_profile); } return ret; } diff --git a/opendbc_repo/opendbc/safety/modes/hyundai_daw.h b/opendbc_repo/opendbc/safety/modes/hyundai_daw.h new file mode 100644 index 0000000000..9d83d0dc33 --- /dev/null +++ b/opendbc_repo/opendbc/safety/modes/hyundai_daw.h @@ -0,0 +1,117 @@ +#pragma once + +#include "opendbc/safety/modes/hyundai_common.h" + +static uint16_t hyundai_daw_addr; +static unsigned int hyundai_daw_bus; +static unsigned int hyundai_daw_len; +static uint8_t hyundai_daw_data[16]; +static uint8_t hyundai_daw_previous[16]; +static bool hyundai_daw_previous_seen; +static uint32_t hyundai_daw_previous_ts; +static uint32_t hyundai_daw_rx_ts; +static uint32_t hyundai_daw_tx_ts; +static bool hyundai_daw_seen; +static bool hyundai_daw_counter_seen; +static bool hyundai_daw_sent; + +static void hyundai_daw_init(bool canfd, bool enabled) { + hyundai_daw_addr = enabled ? (canfd ? 0x11AU : 0x53EU) : 0U; + hyundai_daw_bus = canfd ? 1U : 0U; + hyundai_daw_len = canfd ? 16U : 6U; + hyundai_daw_previous_seen = false; + hyundai_daw_previous_ts = 0U; + hyundai_daw_seen = false; + hyundai_daw_counter_seen = false; + hyundai_daw_sent = false; + hyundai_daw_rx_ts = 0U; + hyundai_daw_tx_ts = 0U; +} + +static void hyundai_daw_rx(const CANPacket_t *msg) { + const bool canfd = hyundai_daw_len == 16U; + if ((hyundai_daw_addr != 0U) && (msg->addr == hyundai_daw_addr) && (msg->bus == (canfd ? hyundai_daw_bus : 2U))) { + const uint32_t now = microsecond_timer_get(); + const bool fresh = hyundai_daw_counter_seen && (safety_get_ts_elapsed(now, hyundai_daw_rx_ts) <= 100000U); + bool valid = GET_LEN(msg) == hyundai_daw_len; + if (valid && canfd) { + valid = GET_BYTES_LE(msg, 0, 2) == (hyundai_common_canfd_compute_checksum(msg) ^ 0x041DU); + if (fresh) { + valid &= (uint8_t)(msg->data[2] - hyundai_daw_data[2]) == 1U; + } + } + hyundai_daw_previous_seen = canfd && valid && fresh && hyundai_daw_seen; + if (hyundai_daw_previous_seen) { + (void)memcpy(hyundai_daw_previous, hyundai_daw_data, hyundai_daw_len); + hyundai_daw_previous_ts = hyundai_daw_rx_ts; + } + hyundai_daw_seen = valid; + if (valid) { + (void)memcpy(hyundai_daw_data, msg->data, hyundai_daw_len); + hyundai_daw_rx_ts = now; + hyundai_daw_counter_seen = true; + } + } +} + +static uint8_t hyundai_daw_expected(const uint8_t *data, unsigned int i) { + uint8_t expected = data[i]; + if ((hyundai_daw_len == 16U) && (i == 7U) && (((expected >> 3U) & 7U) == 1U)) { + expected &= (uint8_t)~0x38U; + } else if ((hyundai_daw_len == 6U) && (i == 5U) && ((expected & 7U) >= 1U) && ((expected & 7U) <= 3U)) { + expected &= (uint8_t)~7U; + } else { + // Non-advisory payload bits are copied exactly from the current source. + } + return expected; +} + +static bool hyundai_daw_tx(const CANPacket_t *msg) { + const uint32_t now = microsecond_timer_get(); + const bool canfd = hyundai_daw_len == 16U; + bool valid = hyundai_daw_seen && (hyundai_daw_addr != 0U) && (msg->addr == hyundai_daw_addr) && + (msg->bus == hyundai_daw_bus) && (GET_LEN(msg) == hyundai_daw_len) && + (safety_get_ts_elapsed(now, hyundai_daw_rx_ts) <= 100000U) && + (!hyundai_daw_sent || (safety_get_ts_elapsed(now, hyundai_daw_tx_ts) >= 5000U)); + if (valid) { + const uint8_t *source = hyundai_daw_data; + if (canfd && (msg->data[2] != hyundai_daw_data[2])) { + valid = hyundai_daw_previous_seen && (msg->data[2] == hyundai_daw_previous[2]) && + (safety_get_ts_elapsed(now, hyundai_daw_previous_ts) <= 100000U); + if (valid) { + // A one-frame receive race cannot replace newer warning or status data. + for (unsigned int i = 3U; i < hyundai_daw_len; i++) { + valid &= hyundai_daw_expected(hyundai_daw_previous, i) == hyundai_daw_expected(hyundai_daw_data, i); + } + source = hyundai_daw_previous; + } + } + for (unsigned int i = canfd ? 2U : 0U; i < hyundai_daw_len; i++) { + valid &= msg->data[i] == hyundai_daw_expected(source, i); + } + if (canfd) { + valid &= GET_BYTES_LE(msg, 0, 2) == (hyundai_common_canfd_compute_checksum(msg) ^ 0x041DU); + } + } + if (valid) { + hyundai_daw_sent = true; + hyundai_daw_tx_ts = now; + } + return valid; +} + +static safety_config hyundai_daw_configure(safety_config ret, bool canfd, bool enabled) { + hyundai_daw_init(canfd, false); + if (enabled && (ret.tx_msgs_len > 0) && (ret.tx_msgs_len < 64)) { + static CanMsg daw_tx_msgs[64]; + for (int i = 0; i < ret.tx_msgs_len; i++) { + daw_tx_msgs[i] = ret.tx_msgs[i]; + } + const CanMsg daw = {canfd ? 0x11AU : 0x53EU, canfd ? 1U : 0U, canfd ? 16U : 6U, .check_relay = false}; + daw_tx_msgs[ret.tx_msgs_len] = daw; + ret.tx_msgs_len++; + ret.tx_msgs = daw_tx_msgs; + hyundai_daw_init(canfd, true); + } + return ret; +} diff --git a/opendbc_repo/opendbc/safety/tests/test_hyundai_daw.py b/opendbc_repo/opendbc/safety/tests/test_hyundai_daw.py new file mode 100644 index 0000000000..da197ddde5 --- /dev/null +++ b/opendbc_repo/opendbc/safety/tests/test_hyundai_daw.py @@ -0,0 +1,127 @@ +import pytest + +from opendbc.car import structs +from opendbc.car.hyundai.daw import DawSource +from opendbc.car.hyundai.hyundaicanfd import hkg_can_fd_checksum +from opendbc.car.hyundai.tests.test_daw import BASE, params, source +from opendbc.safety.tests.libsafety import libsafety_py + + +PROFILES = [(False, 0), (False, 0xC02), (True, 0x11), (True, 0x91), (True, 0x811), (True, 0x891), + (True, 0x8015), (True, 0x8095), (True, 0x8815), (True, 0x8895)] + + +def packet(addr, data, bus): + return libsafety_py.make_CANPacket(addr, bus, data) + + +def setup(canfd, word): + safety = libsafety_py.libsafety + debug = safety.set_safety_hooks(structs.CarParams.SafetyModel.allOutput, 0) == 0 + safety.set_alternative_experience(32 if word & 0x800 else 0) + mode = structs.CarParams.SafetyModel.hyundaiCanfd if canfd else structs.CarParams.SafetyModel.hyundai + assert safety.set_safety_hooks(mode, word) == 0 + safety.init_tests() + safety.set_timer(1_000_000) + return safety, debug or not (canfd and word & 0x8000) + + +@pytest.mark.parametrize('canfd,word', PROFILES) +@pytest.mark.parametrize('status', range(8)) +def test_actual_host_commands_obey_native_source_payload_and_expiry(canfd, word, status): + safety, supported = setup(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] + 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 + if not supported: + return + assert not safety.safety_tx_hook(packet(addr, output, bus)) + safety.set_timer(1_010_000) + for bit in range(len(output) * 8): + bad = bytearray(output) + bad[bit // 8] ^= 1 << (bit % 8) + assert not safety.safety_tx_hook(packet(addr, bad, bus)), bit + assert safety.safety_tx_hook(packet(addr, output, bus)) + safety.set_timer(1_100_000) + assert safety.safety_tx_hook(packet(addr, output, bus)) + safety.set_timer(1_100_001) + assert not safety.safety_tx_hook(packet(addr, output, bus)) + assert not safety.get_controls_allowed() + + +@pytest.mark.parametrize('canfd,fault', [(canfd, fault) for canfd in (False, True) + for fault in ('length', 'bus', 'crc', 'counter', 'reset', 'relay') if canfd or fault not in ('crc', 'counter')]) +def test_source_rejection_reset_and_relay_are_preserved(canfd, fault): + safety, _ = setup(canfd, 0x11 if canfd else 0) + owner = DawSource(params(canfd)) + data = source(canfd, 1) + owner.update([(BASE, [(owner.address, data, owner.source_bus)])]) + addr, output, bus = owner.command(BASE)[0] + 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': + incoming = bytes([incoming[0] ^ 1]) + incoming[1:] + safety.safety_rx_hook(packet(addr, incoming, rx_bus)) + if fault == 'counter': + safety.set_timer(1_010_000) + safety.safety_rx_hook(packet(addr, incoming, rx_bus)) + elif fault == 'reset': + setup(canfd, 0x11 if canfd else 0) + elif fault == 'relay': + safety.set_relay_malfunction(True) + assert not safety.safety_tx_hook(packet(addr, output, bus)) + + +def test_forwarding_and_other_profiles_remain_unchanged(): + safety, _ = setup(False, 0) + assert safety.safety_fwd_hook(2, 0x53E) == 0 + assert safety.safety_fwd_hook(0, 0x53E) == 2 + safety.set_safety_hooks(structs.CarParams.SafetyModel.hyundaiCanfd, 0) + safety.init_tests() + data = source(True, 1) + safety.safety_rx_hook(packet(0x11A, data, 0)) + changed = bytearray(data) + changed[7] &= ~0x38 + changed[:2] = hkg_can_fd_checksum(0x11A, None, changed).to_bytes(2, 'little') + assert not safety.safety_tx_hook(packet(0x11A, changed, 0)) + + +def test_one_genuine_counter_predecessor_cannot_hide_newer_hands_off_request(): + safety, _ = setup(True, 0x11) + owner = DawSource(params(True)) + data = source(True, 1) + owner.update([(BASE, [(0x11A, data, 1)])]) + _, output, _ = owner.command(BASE)[0] + safety.safety_rx_hook(packet(0x11A, data, 1)) + following = bytearray(data) + following[2] += 1 + following[:2] = hkg_can_fd_checksum(0x11A, None, following).to_bytes(2, 'little') + safety.set_timer(1_010_000) + safety.safety_rx_hook(packet(0x11A, following, 1)) + assert safety.safety_tx_hook(packet(0x11A, output, 1)) + safety.set_timer(1_015_000) + newer = bytearray(following) + newer[2] += 1 + newer[:2] = hkg_can_fd_checksum(0x11A, None, newer).to_bytes(2, 'little') + 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] + assert safety.safety_tx_hook(packet(0x11A, prior_output, 1)) + hands_off = bytearray(newer) + hands_off[2] += 1 + hands_off[7] = (hands_off[7] & ~0x38) | (2 << 3) + hands_off[:2] = hkg_can_fd_checksum(0x11A, None, hands_off).to_bytes(2, 'little') + safety.set_timer(1_020_000) + 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] + 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] + assert safety.safety_tx_hook(packet(0x11A, current_output, 1)) diff --git a/prebuilt b/prebuilt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/prebuilt.json b/prebuilt.json deleted file mode 100644 index 45f4679f67..0000000000 --- a/prebuilt.json +++ /dev/null @@ -1 +0,0 @@ -{"protected_inventory_sha256": "fede7a3a054e8b7d37cc815a25f5d5296b19c34bc525281e97622bcbe1a569c0", "schema_version": 1} diff --git a/tools/ci/run_vehicle_tests.py b/tools/ci/run_vehicle_tests.py index a9198ebcd6..e238681c5e 100644 --- a/tools/ci/run_vehicle_tests.py +++ b/tools/ci/run_vehicle_tests.py @@ -44,6 +44,7 @@ AOL_LIFECYCLE_TARGETS = ( "opendbc_repo/opendbc/car/hyundai/tests/test_torque_ev_deferred_takeover.py", 'openpilot/starpilot/aol/tests/test_hkg_early_long_withdrawal.py', 'openpilot/starpilot/aol/tests/test_hkg_angle_eps_retention.py', + 'opendbc_repo/opendbc/car/hyundai/tests/test_daw.py', 'openpilot/starpilot/aol/tests/test_hkg_angle_limit_warning.py', 'openpilot/starpilot/car/toyota/tests/test_prius_retrofit_stock.py', 'openpilot/starpilot/car/toyota/tests/test_prius_retrofit_stock_callers.py', @@ -513,6 +514,7 @@ RELEASE_TARGETS = ( "opendbc_repo/opendbc/safety/tests/test_hyundai_ccnc_angle_two.py", "opendbc_repo/opendbc/safety/tests/test_hyundai_ioniq6_long.py", "opendbc_repo/opendbc/safety/tests/test_hyundai_ray_pedal.py", + "opendbc_repo/opendbc/safety/tests/test_hyundai_daw.py", "opendbc_repo/opendbc/safety/tests/test_honda_mvl_stock.py", "opendbc_repo/opendbc/safety/tests/test_honda_stock_aol.py", "opendbc_repo/opendbc/safety/tests/test_mazda_stock_aol.py",