From 1dbed65e38d6f89bcd6879a3b6b9a82222ff3f98 Mon Sep 17 00:00:00 2001 From: Adeeb <8762862+quillford@users.noreply.github.com> Date: Mon, 27 Apr 2020 19:40:15 -0700 Subject: [PATCH] Safety Test Refactor: Honda (#495) * nidec * bosch * button * more common * packer * more packer * readability * tx too * don't need that * delete StdTest * don't need to duplicate this * only nidec has interceptor --- tests/safety/common.py | 56 ---- tests/safety/test_honda.py | 568 +++++++++++++++---------------------- 2 files changed, 231 insertions(+), 393 deletions(-) diff --git a/tests/safety/common.py b/tests/safety/common.py index 464d9149775f3e..4f56f9693a74a4 100644 --- a/tests/safety/common.py +++ b/tests/safety/common.py @@ -197,59 +197,3 @@ def test_not_allow_brake_when_moving(self): self.assertFalse(self.safety.get_controls_allowed()) self._rx(self._speed_msg(0)) -# TODO: use PandaSafetyTest for all tests and delete this -class StdTest: - @staticmethod - def test_relay_malfunction(test, addr, bus=0): - # input is a test class and the address that, if seen on specified bus, triggers - # the relay_malfunction protection logic: both tx_hook and fwd_hook are - # expected to return failure - test.assertFalse(test.safety.get_relay_malfunction()) - test.safety.safety_rx_hook(make_msg(bus, addr, 8)) - test.assertTrue(test.safety.get_relay_malfunction()) - for a in range(1, 0x800): - for b in range(0, 3): - test.assertFalse(test.safety.safety_tx_hook(make_msg(b, a, 8))) - test.assertEqual(-1, test.safety.safety_fwd_hook(b, make_msg(b, a, 8))) - - @staticmethod - def test_manually_enable_controls_allowed(test): - test.safety.set_controls_allowed(1) - test.assertTrue(test.safety.get_controls_allowed()) - test.safety.set_controls_allowed(0) - test.assertFalse(test.safety.get_controls_allowed()) - - @staticmethod - def test_spam_can_buses(test, TX_MSGS): - for addr in range(1, 0x800): - for bus in range(0, 4): - if all(addr != m[0] or bus != m[1] for m in TX_MSGS): - test.assertFalse(test.safety.safety_tx_hook(make_msg(bus, addr, 8))) - - @staticmethod - def test_allow_brake_at_zero_speed(test): - # Brake was already pressed - test.safety.safety_rx_hook(test._speed_msg(0)) - test.safety.safety_rx_hook(test._brake_msg(1)) - test.safety.set_controls_allowed(1) - test.safety.safety_rx_hook(test._brake_msg(1)) - test.assertTrue(test.safety.get_controls_allowed()) - test.safety.safety_rx_hook(test._brake_msg(0)) - test.assertTrue(test.safety.get_controls_allowed()) - # rising edge of brake should disengage - test.safety.safety_rx_hook(test._brake_msg(1)) - test.assertFalse(test.safety.get_controls_allowed()) - test.safety.safety_rx_hook(test._brake_msg(0)) # reset no brakes - - @staticmethod - def test_not_allow_brake_when_moving(test, standstill_threshold): - # Brake was already pressed - test.safety.safety_rx_hook(test._brake_msg(1)) - test.safety.set_controls_allowed(1) - test.safety.safety_rx_hook(test._speed_msg(standstill_threshold)) - test.safety.safety_rx_hook(test._brake_msg(1)) - test.assertTrue(test.safety.get_controls_allowed()) - test.safety.safety_rx_hook(test._speed_msg(standstill_threshold + 1)) - test.safety.safety_rx_hook(test._brake_msg(1)) - test.assertFalse(test.safety.get_controls_allowed()) - test.safety.safety_rx_hook(test._speed_msg(0)) diff --git a/tests/safety/test_honda.py b/tests/safety/test_honda.py index 39d7dc91ce7d9f..761dd53cf07cf5 100755 --- a/tests/safety/test_honda.py +++ b/tests/safety/test_honda.py @@ -3,15 +3,18 @@ import numpy as np from panda import Panda from panda.tests.safety import libpandasafety_py -from panda.tests.safety.common import StdTest, make_msg, MAX_WRONG_COUNTERS, UNSAFE_MODE +import panda.tests.safety.common as common +from panda.tests.safety.common import CANPackerPanda, make_msg, \ + MAX_WRONG_COUNTERS, UNSAFE_MODE MAX_BRAKE = 255 INTERCEPTOR_THRESHOLD = 344 -N_TX_MSGS = [[0xE4, 0], [0x194, 0], [0x1FA, 0], [0x200, 0], [0x30C, 0], [0x33D, 0]] -BH_TX_MSGS = [[0xE4, 0], [0x296, 1], [0x33D, 0]] # Bosch Harness -BG_TX_MSGS = [[0xE4, 2], [0x296, 0], [0x33D, 2]] # Bosch Giraffe +class Btn: + CANCEL = 2 + SET = 3 + RESUME = 4 HONDA_N_HW = 0 HONDA_BG_HW = 1 @@ -25,283 +28,141 @@ def honda_interceptor_msg(gas, addr): ((gas2 & 0xff) << 24) | ((gas2 & 0xff00) << 8) return to_send -def honda_checksum(msg, addr, len_msg): - checksum = 0 - while addr > 0: - checksum += addr - addr >>= 4 - for i in range (0, 2*len_msg): - if i < 8: - checksum += (msg.RDLR >> (4 * i)) - else: - checksum += (msg.RDHR >> (4 * (i - 8))) - return (8 - checksum) & 0xF - -class TestHondaSafety(unittest.TestCase): +class TestHondaSafety(common.PandaSafetyTest): cnt_speed = 0 cnt_gas = 0 cnt_button = 0 + PT_BUS = 0 + @classmethod - def setUp(cls): - cls.safety = libpandasafety_py.libpandasafety - cls.safety.set_safety_hooks(Panda.SAFETY_HONDA_NIDEC, 0) - cls.safety.init_tests_honda() + def setUpClass(cls): + if cls.__name__ == "TestHondaSafety": + cls.packer = None + cls.safety = None + raise unittest.SkipTest + + # override these inherited tests. honda doesn't use pcm enable + def test_disable_control_allowed_from_cruise(self): pass + def test_enable_control_allowed_from_cruise(self): pass def _speed_msg(self, speed): - bus = 1 if self.safety.get_honda_hw() == HONDA_BH_HW else 0 - to_send = make_msg(bus, 0x158) - to_send[0].RDLR = speed - to_send[0].RDHR |= (self.cnt_speed % 4) << 28 - to_send[0].RDHR |= honda_checksum(to_send[0], 0x158, 8) << 24 + values = {"XMISSION_SPEED": speed, "COUNTER": self.cnt_speed % 4} self.__class__.cnt_speed += 1 - return to_send + return self.packer.make_can_msg_panda("ENGINE_DATA", self.PT_BUS, values) - def _button_msg(self, buttons, addr): - bus = 1 if self.safety.get_honda_hw() == HONDA_BH_HW else 0 - to_send = make_msg(bus, addr) - to_send[0].RDLR = buttons << 5 - to_send[0].RDHR |= (self.cnt_button % 4) << 28 - to_send[0].RDHR |= honda_checksum(to_send[0], addr, 8) << 24 + def _button_msg(self, buttons): + values = {"CRUISE_BUTTONS": buttons, "COUNTER": self.cnt_button % 4} self.__class__.cnt_button += 1 - return to_send + return self.packer.make_can_msg_panda("SCM_BUTTONS", self.PT_BUS, values) def _brake_msg(self, brake): - bus = 1 if self.safety.get_honda_hw() == HONDA_BH_HW else 0 - to_send = make_msg(bus, 0x17C) - to_send[0].RDHR = 0x200000 if brake else 0 - to_send[0].RDHR |= (self.cnt_gas % 4) << 28 - to_send[0].RDHR |= honda_checksum(to_send[0], 0x17C, 8) << 24 + values = {"BRAKE_PRESSED": brake, "COUNTER": self.cnt_gas % 4} self.__class__.cnt_gas += 1 - return to_send - - def _alt_brake_msg(self, brake): - to_send = make_msg(0, 0x1BE) - to_send[0].RDLR = 0x10 if brake else 0 - return to_send + return self.packer.make_can_msg_panda("POWERTRAIN_DATA", self.PT_BUS, values) def _gas_msg(self, gas): - bus = 1 if self.safety.get_honda_hw() == HONDA_BH_HW else 0 - to_send = make_msg(bus, 0x17C) - to_send[0].RDLR = 1 if gas else 0 - to_send[0].RDHR |= (self.cnt_gas % 4) << 28 - to_send[0].RDHR |= honda_checksum(to_send[0], 0x17C, 8) << 24 + values = {"PEDAL_GAS": gas, "COUNTER": self.cnt_gas % 4} self.__class__.cnt_gas += 1 - return to_send + return self.packer.make_can_msg_panda("POWERTRAIN_DATA", self.PT_BUS, values) def _send_brake_msg(self, brake): - to_send = make_msg(0, 0x1FA) - to_send[0].RDLR = ((brake & 0x3) << 14) | ((brake & 0x3FF) >> 2) - return to_send + values = {} + if self.safety.get_honda_hw() == HONDA_N_HW: + values = {"COMPUTER_BRAKE": brake} + return self.packer.make_can_msg_panda("BRAKE_COMMAND", 0, values) def _send_steer_msg(self, steer): - bus = 2 if self.safety.get_honda_hw() == HONDA_BG_HW else 0 - to_send = make_msg(bus, 0xE4, 6) - to_send[0].RDLR = steer - return to_send - - def test_spam_can_buses(self): - hw_type = self.safety.get_honda_hw() - if hw_type == HONDA_N_HW: - tx_msgs = N_TX_MSGS - elif hw_type == HONDA_BH_HW: - tx_msgs = BH_TX_MSGS - elif hw_type == HONDA_BG_HW: - tx_msgs = BG_TX_MSGS - StdTest.test_spam_can_buses(self, tx_msgs) - - def test_relay_malfunction(self): - hw = self.safety.get_honda_hw() - bus = 2 if hw == HONDA_BG_HW else 0 - StdTest.test_relay_malfunction(self, 0xE4, bus=bus) - - def test_default_controls_not_allowed(self): - self.assertFalse(self.safety.get_controls_allowed()) - - def test_manually_enable_controls_allowed(self): - StdTest.test_manually_enable_controls_allowed(self) + values = {"STEER_TORQUE": steer} + return self.packer.make_can_msg_panda("STEERING_CONTROL", 0, values) def test_resume_button(self): - RESUME_BTN = 4 self.safety.set_controls_allowed(0) - self.safety.safety_rx_hook(self._button_msg(RESUME_BTN, 0x296)) + self._rx(self._button_msg(Btn.RESUME)) self.assertTrue(self.safety.get_controls_allowed()) def test_set_button(self): - SET_BTN = 3 self.safety.set_controls_allowed(0) - self.safety.safety_rx_hook(self._button_msg(SET_BTN, 0x296)) + self._rx(self._button_msg(Btn.SET)) self.assertTrue(self.safety.get_controls_allowed()) def test_cancel_button(self): - CANCEL_BTN = 2 self.safety.set_controls_allowed(1) - self.safety.safety_rx_hook(self._button_msg(CANCEL_BTN, 0x296)) + self._rx(self._button_msg(Btn.CANCEL)) self.assertFalse(self.safety.get_controls_allowed()) def test_sample_speed(self): self.assertEqual(0, self.safety.get_honda_moving()) - self.safety.safety_rx_hook(self._speed_msg(100)) + self._rx(self._speed_msg(100)) self.assertEqual(1, self.safety.get_honda_moving()) - def test_prev_brake(self): - self.assertFalse(self.safety.get_brake_pressed_prev()) - self.safety.safety_rx_hook(self._brake_msg(True)) - self.assertTrue(self.safety.get_brake_pressed_prev()) - def test_disengage_on_brake(self): self.safety.set_controls_allowed(1) - self.safety.safety_rx_hook(self._brake_msg(1)) + self._rx(self._brake_msg(1)) self.assertFalse(self.safety.get_controls_allowed()) - def test_alt_disengage_on_brake(self): - self.safety.set_honda_alt_brake_msg(1) - self.safety.set_controls_allowed(1) - self.safety.safety_rx_hook(self._alt_brake_msg(1)) - self.assertFalse(self.safety.get_controls_allowed()) - - self.safety.set_honda_alt_brake_msg(0) - self.safety.set_controls_allowed(1) - self.safety.safety_rx_hook(self._alt_brake_msg(1)) - self.assertTrue(self.safety.get_controls_allowed()) - - def test_brake_disengage(self): - StdTest.test_allow_brake_at_zero_speed(self) - StdTest.test_not_allow_brake_when_moving(self, 0) - - def test_prev_gas(self): - self.safety.safety_rx_hook(self._gas_msg(False)) - self.assertFalse(self.safety.get_gas_pressed_prev()) - self.safety.safety_rx_hook(self._gas_msg(True)) - self.assertTrue(self.safety.get_gas_pressed_prev()) - def test_prev_gas_interceptor(self): - self.safety.safety_rx_hook(honda_interceptor_msg(0x0, 0x201)) + self._rx(honda_interceptor_msg(0x0, 0x201)) self.assertFalse(self.safety.get_gas_interceptor_prev()) - self.safety.safety_rx_hook(honda_interceptor_msg(0x1000, 0x201)) + self._rx(honda_interceptor_msg(0x1000, 0x201)) self.assertTrue(self.safety.get_gas_interceptor_prev()) - self.safety.safety_rx_hook(honda_interceptor_msg(0x0, 0x201)) + self._rx(honda_interceptor_msg(0x0, 0x201)) self.safety.set_gas_interceptor_detected(False) - def test_disengage_on_gas(self): - self.safety.safety_rx_hook(self._gas_msg(0)) - self.safety.set_controls_allowed(1) - self.safety.safety_rx_hook(self._gas_msg(1)) - self.assertFalse(self.safety.get_controls_allowed()) - - def test_unsafe_mode_no_disengage_on_gas(self): - self.safety.safety_rx_hook(self._gas_msg(0)) - self.safety.set_controls_allowed(1) - self.safety.set_unsafe_mode(UNSAFE_MODE.DISABLE_DISENGAGE_ON_GAS) - self.safety.safety_rx_hook(self._gas_msg(1)) - self.assertTrue(self.safety.get_controls_allowed()) - self.safety.set_unsafe_mode(UNSAFE_MODE.DEFAULT) - - def test_allow_engage_with_gas_pressed(self): - self.safety.safety_rx_hook(self._gas_msg(1)) - self.safety.set_controls_allowed(1) - self.safety.safety_rx_hook(self._gas_msg(1)) - self.assertTrue(self.safety.get_controls_allowed()) - def test_disengage_on_gas_interceptor(self): for g in range(0, 0x1000): - self.safety.safety_rx_hook(honda_interceptor_msg(0, 0x201)) + self._rx(honda_interceptor_msg(0, 0x201)) self.safety.set_controls_allowed(True) - self.safety.safety_rx_hook(honda_interceptor_msg(g, 0x201)) + self._rx(honda_interceptor_msg(g, 0x201)) remain_enabled = g <= INTERCEPTOR_THRESHOLD self.assertEqual(remain_enabled, self.safety.get_controls_allowed()) - self.safety.safety_rx_hook(honda_interceptor_msg(0, 0x201)) + self._rx(honda_interceptor_msg(0, 0x201)) self.safety.set_gas_interceptor_detected(False) def test_unsafe_mode_no_disengage_on_gas_interceptor(self): self.safety.set_controls_allowed(True) self.safety.set_unsafe_mode(UNSAFE_MODE.DISABLE_DISENGAGE_ON_GAS) for g in range(0, 0x1000): - self.safety.safety_rx_hook(honda_interceptor_msg(g, 0x201)) + self._rx(honda_interceptor_msg(g, 0x201)) self.assertTrue(self.safety.get_controls_allowed()) - self.safety.safety_rx_hook(honda_interceptor_msg(0, 0x201)) + self._rx(honda_interceptor_msg(0, 0x201)) self.safety.set_gas_interceptor_detected(False) self.safety.set_unsafe_mode(UNSAFE_MODE.DEFAULT) self.safety.set_controls_allowed(False) def test_allow_engage_with_gas_interceptor_pressed(self): - self.safety.safety_rx_hook(honda_interceptor_msg(0x1000, 0x201)) + self._rx(honda_interceptor_msg(0x1000, 0x201)) self.safety.set_controls_allowed(1) - self.safety.safety_rx_hook(honda_interceptor_msg(0x1000, 0x201)) + self._rx(honda_interceptor_msg(0x1000, 0x201)) self.assertTrue(self.safety.get_controls_allowed()) - self.safety.safety_rx_hook(honda_interceptor_msg(0, 0x201)) + self._rx(honda_interceptor_msg(0, 0x201)) self.safety.set_gas_interceptor_detected(False) - def test_brake_safety_check(self): - hw = self.safety.get_honda_hw() - if hw == HONDA_N_HW: - for fwd_brake in [False, True]: - self.safety.set_honda_fwd_brake(fwd_brake) - for brake in np.arange(0, MAX_BRAKE + 10, 1): - for controls_allowed in [True, False]: - self.safety.set_controls_allowed(controls_allowed) - if fwd_brake: - send = False # block openpilot brake msg when fwd'ing stock msg - elif controls_allowed: - send = MAX_BRAKE >= brake >= 0 - else: - send = brake == 0 - self.assertEqual(send, self.safety.safety_tx_hook(self._send_brake_msg(brake))) - self.safety.set_honda_fwd_brake(False) - - def test_gas_interceptor_safety_check(self): - if self.safety.get_honda_hw() == HONDA_N_HW: - for gas in np.arange(0, 4000, 100): - for controls_allowed in [True, False]: - self.safety.set_controls_allowed(controls_allowed) - if controls_allowed: - send = True - else: - send = gas == 0 - self.assertEqual(send, self.safety.safety_tx_hook(honda_interceptor_msg(gas, 0x200))) - def test_steer_safety_check(self): self.safety.set_controls_allowed(0) - self.assertTrue(self.safety.safety_tx_hook(self._send_steer_msg(0x0000))) - self.assertFalse(self.safety.safety_tx_hook(self._send_steer_msg(0x1000))) - - def test_spam_cancel_safety_check(self): - hw = self.safety.get_honda_hw() - if hw != HONDA_N_HW: - RESUME_BTN = 4 - SET_BTN = 3 - CANCEL_BTN = 2 - BUTTON_MSG = 0x296 - self.safety.set_controls_allowed(0) - self.assertTrue(self.safety.safety_tx_hook(self._button_msg(CANCEL_BTN, BUTTON_MSG))) - self.assertFalse(self.safety.safety_tx_hook(self._button_msg(RESUME_BTN, BUTTON_MSG))) - self.assertFalse(self.safety.safety_tx_hook(self._button_msg(SET_BTN, BUTTON_MSG))) - # do not block resume if we are engaged already - self.safety.set_controls_allowed(1) - self.assertTrue(self.safety.safety_tx_hook(self._button_msg(RESUME_BTN, BUTTON_MSG))) + self.assertTrue(self._tx(self._send_steer_msg(0x0000))) + self.assertFalse(self._tx(self._send_steer_msg(0x1000))) def test_rx_hook(self): + # TODO: move this test to common # checksum checks - SET_BTN = 3 - for msg in ["btn1", "btn2", "gas", "speed"]: + for msg in ["btn", "gas", "speed"]: self.safety.set_controls_allowed(1) - if msg == "btn1": - if self.safety.get_honda_hw() == HONDA_N_HW: - to_push = self._button_msg(SET_BTN, 0x1A6) # only in Honda_NIDEC - else: - continue - if msg == "btn2": - to_push = self._button_msg(SET_BTN, 0x296) + # TODO: add this coverage back by re-running all tests with the acura dbc + # to_push = self._button_msg(Btn.SET, 0x1A6) # only in Honda_NIDEC + if msg == "btn": + to_push = self._button_msg(Btn.SET) if msg == "gas": to_push = self._gas_msg(0) if msg == "speed": to_push = self._speed_msg(0) - self.assertTrue(self.safety.safety_rx_hook(to_push)) - to_push[0].RDHR = 0 # invalidate checksum - self.assertFalse(self.safety.safety_rx_hook(to_push)) - self.assertFalse(self.safety.get_controls_allowed()) + self.assertTrue(self._rx(to_push)) + if msg != "btn": + to_push[0].RDHR = 0 # invalidate checksum + self.assertFalse(self._rx(to_push)) + self.assertFalse(self.safety.get_controls_allowed()) # counter # reset wrong_counters to zero by sending valid messages @@ -311,161 +172,194 @@ def test_rx_hook(self): self.__class__.cnt_button += 1 if i < MAX_WRONG_COUNTERS: self.safety.set_controls_allowed(1) - self.safety.safety_rx_hook(self._button_msg(SET_BTN, 0x296)) - self.safety.safety_rx_hook(self._speed_msg(0)) - self.safety.safety_rx_hook(self._gas_msg(0)) + self._rx(self._button_msg(Btn.SET)) + self._rx(self._speed_msg(0)) + self._rx(self._gas_msg(0)) else: - self.assertFalse(self.safety.safety_rx_hook(self._button_msg(SET_BTN, 0x296))) - self.assertFalse(self.safety.safety_rx_hook(self._speed_msg(0))) - self.assertFalse(self.safety.safety_rx_hook(self._gas_msg(0))) + self.assertFalse(self._rx(self._button_msg(Btn.SET))) + self.assertFalse(self._rx(self._speed_msg(0))) + self.assertFalse(self._rx(self._gas_msg(0))) self.assertFalse(self.safety.get_controls_allowed()) # restore counters for future tests with a couple of good messages for i in range(2): self.safety.set_controls_allowed(1) - self.safety.safety_rx_hook(self._button_msg(SET_BTN, 0x296)) - self.safety.safety_rx_hook(self._speed_msg(0)) - self.safety.safety_rx_hook(self._gas_msg(0)) - self.safety.safety_rx_hook(self._button_msg(SET_BTN, 0x296)) + self._rx(self._button_msg(Btn.SET)) + self._rx(self._speed_msg(0)) + self._rx(self._gas_msg(0)) + self._rx(self._button_msg(Btn.SET)) self.assertTrue(self.safety.get_controls_allowed()) + def test_tx_hook_on_pedal_pressed(self): + for mode in [UNSAFE_MODE.DEFAULT, UNSAFE_MODE.DISABLE_DISENGAGE_ON_GAS]: + for pedal in ['brake', 'gas']: + self.safety.set_unsafe_mode(mode) + allow_ctrl = False + if pedal == 'brake': + # brake_pressed_prev and honda_moving + self._rx(self._speed_msg(100)) + self._rx(self._brake_msg(1)) + elif pedal == 'gas': + # gas_pressed_prev + self._rx(self._gas_msg(1)) + allow_ctrl = mode == UNSAFE_MODE.DISABLE_DISENGAGE_ON_GAS - def test_fwd_hook(self): - buss = list(range(0x0, 0x3)) - msgs = list(range(0x1, 0x800)) - fwd_brake = [False, True] - - for f in fwd_brake: - self.safety.set_honda_fwd_brake(f) - blocked_msgs = [0xE4, 0x194, 0x33D] - blocked_msgs += [0x30C] - if not f: - blocked_msgs += [0x1FA] - for b in buss: - for m in msgs: - if b == 0: - fwd_bus = 2 - elif b == 1: - fwd_bus = -1 - elif b == 2: - fwd_bus = -1 if m in blocked_msgs else 0 - - # assume len 8 - self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, make_msg(b, m, 8))) + self.safety.set_controls_allowed(1) + hw = self.safety.get_honda_hw() + if hw == HONDA_N_HW: + self.safety.set_honda_fwd_brake(False) + self.assertEqual(allow_ctrl, self._tx(self._send_brake_msg(MAX_BRAKE))) + self.assertEqual(allow_ctrl, self._tx(self._send_steer_msg(0x1000))) + + # reset status + self.safety.set_controls_allowed(0) + self.safety.set_unsafe_mode(UNSAFE_MODE.DEFAULT) + self._tx(self._send_brake_msg(0)) + self._tx(self._send_steer_msg(0)) + if pedal == 'brake': + self._rx(self._speed_msg(0)) + self._rx(self._brake_msg(0)) + elif pedal == 'gas': + self._rx(self._gas_msg(0)) + + +class TestHondaNidecSafety(TestHondaSafety): + + TX_MSGS = [[0xE4, 0], [0x194, 0], [0x1FA, 0], [0x200, 0], [0x30C, 0], [0x33D, 0]] + STANDSTILL_THRESHOLD = 0 + RELAY_MALFUNCTION_ADDR = 0xE4 + RELAY_MALFUNCTION_BUS = 0 + FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0x194, 0x33D, 0x30C]} + FWD_BUS_LOOKUP = {0: 2, 2: 0} + + def setUp(self): + self.packer = CANPackerPanda("honda_civic_touring_2016_can_generated") + self.safety = libpandasafety_py.libpandasafety + self.safety.set_safety_hooks(Panda.SAFETY_HONDA_NIDEC, 0) + self.safety.init_tests_honda() + def test_fwd_hook(self): + # normal operation, not forwarding AEB + self.FWD_BLACKLISTED_ADDRS[2].append(0x1FA) self.safety.set_honda_fwd_brake(False) + super().test_fwd_hook() - def test_tx_hook_on_pedal_pressed(self): - for pedal in ['brake', 'gas', 'interceptor']: - if pedal == 'brake': - # brake_pressed_prev and honda_moving - self.safety.safety_rx_hook(self._speed_msg(100)) - self.safety.safety_rx_hook(self._brake_msg(1)) - elif pedal == 'gas': - # gas_pressed_prev - self.safety.safety_rx_hook(self._gas_msg(1)) - elif pedal == 'interceptor': - # gas_interceptor_prev > INTERCEPTOR_THRESHOLD - self.safety.safety_rx_hook(honda_interceptor_msg(INTERCEPTOR_THRESHOLD+1, 0x201)) - self.safety.safety_rx_hook(honda_interceptor_msg(INTERCEPTOR_THRESHOLD+1, 0x201)) + # TODO: test latching until AEB event is over? + # forwarding AEB brake signal + self.FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0x194, 0x33D, 0x30C]} + self.safety.set_honda_fwd_brake(True) + super().test_fwd_hook() - self.safety.set_controls_allowed(1) - hw = self.safety.get_honda_hw() - if hw == HONDA_N_HW: - self.safety.set_honda_fwd_brake(False) - self.assertFalse(self.safety.safety_tx_hook(self._send_brake_msg(MAX_BRAKE))) - self.assertFalse(self.safety.safety_tx_hook(honda_interceptor_msg(INTERCEPTOR_THRESHOLD, 0x200))) - self.assertFalse(self.safety.safety_tx_hook(self._send_steer_msg(0x1000))) + def test_brake_safety_check(self): + for fwd_brake in [False, True]: + self.safety.set_honda_fwd_brake(fwd_brake) + for brake in np.arange(0, MAX_BRAKE + 10, 1): + for controls_allowed in [True, False]: + self.safety.set_controls_allowed(controls_allowed) + if fwd_brake: + send = False # block openpilot brake msg when fwd'ing stock msg + elif controls_allowed: + send = MAX_BRAKE >= brake >= 0 + else: + send = brake == 0 + self.assertEqual(send, self._tx(self._send_brake_msg(brake))) + self.safety.set_honda_fwd_brake(False) - # reset status - self.safety.set_controls_allowed(0) - self.safety.safety_tx_hook(self._send_brake_msg(0)) - self.safety.safety_tx_hook(self._send_steer_msg(0)) - self.safety.safety_tx_hook(honda_interceptor_msg(0, 0x200)) - if pedal == 'brake': - self.safety.safety_rx_hook(self._speed_msg(0)) - self.safety.safety_rx_hook(self._brake_msg(0)) - elif pedal == 'gas': - self.safety.safety_rx_hook(self._gas_msg(0)) - elif pedal == 'interceptor': - self.safety.set_gas_interceptor_detected(False) - - def test_tx_hook_on_pedal_pressed_on_unsafe_gas_mode(self): - for pedal in ['brake', 'gas', 'interceptor']: - self.safety.set_unsafe_mode(UNSAFE_MODE.DISABLE_DISENGAGE_ON_GAS) - if pedal == 'brake': - # brake_pressed_prev and honda_moving - self.safety.safety_rx_hook(self._speed_msg(100)) - self.safety.safety_rx_hook(self._brake_msg(1)) - allow_ctrl = False - elif pedal == 'gas': - # gas_pressed_prev - self.safety.safety_rx_hook(self._gas_msg(1)) - allow_ctrl = True - elif pedal == 'interceptor': - # gas_interceptor_prev > INTERCEPTOR_THRESHOLD - self.safety.safety_rx_hook(honda_interceptor_msg(INTERCEPTOR_THRESHOLD+1, 0x201)) - self.safety.safety_rx_hook(honda_interceptor_msg(INTERCEPTOR_THRESHOLD+1, 0x201)) - allow_ctrl = True + def test_gas_interceptor_safety_check(self): + for gas in np.arange(0, 4000, 100): + for controls_allowed in [True, False]: + self.safety.set_controls_allowed(controls_allowed) + if controls_allowed: + send = True + else: + send = gas == 0 + self.assertEqual(send, self._tx(honda_interceptor_msg(gas, 0x200))) + + def test_tx_hook_on_interceptor_pressed(self): + for mode in [UNSAFE_MODE.DEFAULT, UNSAFE_MODE.DISABLE_DISENGAGE_ON_GAS]: + self.safety.set_unsafe_mode(mode) + # gas_interceptor_prev > INTERCEPTOR_THRESHOLD + self._rx(honda_interceptor_msg(INTERCEPTOR_THRESHOLD+1, 0x201)) + self._rx(honda_interceptor_msg(INTERCEPTOR_THRESHOLD+1, 0x201)) + allow_ctrl = mode == UNSAFE_MODE.DISABLE_DISENGAGE_ON_GAS self.safety.set_controls_allowed(1) - hw = self.safety.get_honda_hw() - if hw == HONDA_N_HW: - self.safety.set_honda_fwd_brake(False) - self.assertEqual(allow_ctrl, self.safety.safety_tx_hook(self._send_brake_msg(MAX_BRAKE))) - self.assertEqual(allow_ctrl, self.safety.safety_tx_hook(honda_interceptor_msg(INTERCEPTOR_THRESHOLD, 0x200))) - self.assertEqual(allow_ctrl, self.safety.safety_tx_hook(self._send_steer_msg(0x1000))) + self.safety.set_honda_fwd_brake(False) + self.assertEqual(allow_ctrl, self._tx(self._send_brake_msg(MAX_BRAKE))) + self.assertEqual(allow_ctrl, self._tx(honda_interceptor_msg(INTERCEPTOR_THRESHOLD, 0x200))) + self.assertEqual(allow_ctrl, self._tx(self._send_steer_msg(0x1000))) + # reset status self.safety.set_controls_allowed(0) self.safety.set_unsafe_mode(UNSAFE_MODE.DEFAULT) - self.safety.safety_tx_hook(self._send_brake_msg(0)) - self.safety.safety_tx_hook(self._send_steer_msg(0)) - self.safety.safety_tx_hook(honda_interceptor_msg(0, 0x200)) - if pedal == 'brake': - self.safety.safety_rx_hook(self._speed_msg(0)) - self.safety.safety_rx_hook(self._brake_msg(0)) - elif pedal == 'gas': - self.safety.safety_rx_hook(self._gas_msg(0)) - elif pedal == 'interceptor': - self.safety.set_gas_interceptor_detected(False) - -class TestHondaBoschGiraffeSafety(TestHondaSafety): - @classmethod - def setUp(cls): - TestHondaSafety.setUp() - cls.safety = libpandasafety_py.libpandasafety - cls.safety.set_safety_hooks(Panda.SAFETY_HONDA_BOSCH_GIRAFFE, 0) - cls.safety.init_tests_honda() + self._tx(self._send_brake_msg(0)) + self._tx(self._send_steer_msg(0)) + self._tx(honda_interceptor_msg(0, 0x200)) + self.safety.set_gas_interceptor_detected(False) + + +class TestHondaBoschHarnessSafety(TestHondaSafety): + TX_MSGS = [[0xE4, 0], [0x296, 1], [0x33D, 0]] # Bosch Harness + STANDSTILL_THRESHOLD = 0 + RELAY_MALFUNCTION_ADDR = 0xE4 + RELAY_MALFUNCTION_BUS = 0 + FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0x33D]} + FWD_BUS_LOOKUP = {0: 2, 2: 0} + + PT_BUS = 1 + + def setUp(self): + self.packer = CANPackerPanda("honda_accord_s2t_2018_can_generated") + self.safety = libpandasafety_py.libpandasafety + self.safety.set_safety_hooks(Panda.SAFETY_HONDA_BOSCH_HARNESS, 0) + self.safety.init_tests_honda() + + def _alt_brake_msg(self, brake): + to_send = make_msg(0, 0x1BE) + to_send[0].RDLR = 0x10 if brake else 0 + return to_send + + def test_spam_cancel_safety_check(self): + self.safety.set_controls_allowed(0) + self.assertTrue(self._tx(self._button_msg(Btn.CANCEL))) + self.assertFalse(self._tx(self._button_msg(Btn.RESUME))) + self.assertFalse(self._tx(self._button_msg(Btn.SET))) + # do not block resume if we are engaged already + self.safety.set_controls_allowed(1) + self.assertTrue(self._tx(self._button_msg(Btn.RESUME))) + + def test_alt_disengage_on_brake(self): + self.safety.set_honda_alt_brake_msg(1) + self.safety.set_controls_allowed(1) + self._rx(self._alt_brake_msg(1)) + self.assertFalse(self.safety.get_controls_allowed()) + + self.safety.set_honda_alt_brake_msg(0) + self.safety.set_controls_allowed(1) + self._rx(self._alt_brake_msg(1)) + self.assertTrue(self.safety.get_controls_allowed()) + + +class TestHondaBoschGiraffeSafety(TestHondaBoschHarnessSafety): + TX_MSGS = [[0xE4, 2], [0x296, 0], [0x33D, 2]] # Bosch Giraffe + STANDSTILL_THRESHOLD = 0 + RELAY_MALFUNCTION_ADDR = 0xE4 + RELAY_MALFUNCTION_BUS = 2 + FWD_BLACKLISTED_ADDRS = {1: [0xE4, 0x33D]} + FWD_BUS_LOOKUP = {1: 2, 2: 1} + + PT_BUS = 0 + + def setUp(self): + super().setUp() + self.safety = libpandasafety_py.libpandasafety + self.safety.set_safety_hooks(Panda.SAFETY_HONDA_BOSCH_GIRAFFE, 0) + self.safety.init_tests_honda() + + def _send_steer_msg(self, steer): + values = {"STEER_TORQUE": steer} + return self.packer.make_can_msg_panda("STEERING_CONTROL", 2, values) - def test_fwd_hook(self): - buss = range(0x0, 0x3) - msgs = range(0x1, 0x800) - hw = self.safety.get_honda_hw() - bus_rdr_cam = 2 if hw == HONDA_BH_HW else 1 - bus_rdr_car = 0 if hw == HONDA_BH_HW else 2 - bus_pt = 1 if hw == HONDA_BH_HW else 0 - - blocked_msgs = [0xE4, 0x33D] - for b in buss: - for m in msgs: - if b == bus_pt: - fwd_bus = -1 - elif b == bus_rdr_cam: - fwd_bus = -1 if m in blocked_msgs else bus_rdr_car - elif b == bus_rdr_car: - fwd_bus = bus_rdr_cam - - # assume len 8 - self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, make_msg(b, m, 8))) - - -class TestHondaBoschHarnessSafety(TestHondaBoschGiraffeSafety): - @classmethod - def setUp(cls): - TestHondaBoschGiraffeSafety.setUp() - cls.safety = libpandasafety_py.libpandasafety - cls.safety.set_safety_hooks(Panda.SAFETY_HONDA_BOSCH_HARNESS, 0) - cls.safety.init_tests_honda() if __name__ == "__main__": unittest.main()