#!/usr/bin/env python3 import unittest import panda.tests.safety.common as common from panda import Panda from panda.tests.safety import libpandasafety_py from panda.tests.safety.common import CANPackerPanda MSG_EngBrakeData = 0x165 # RX from PCM, for driver brake pedal and cruise state MSG_EngVehicleSpThrottle = 0x204 # RX from PCM, for driver throttle input MSG_Steering_Data_FD1 = 0x083 # TX by OP, various driver switches and LKAS/CC buttons MSG_ACCDATA_3 = 0x18A # TX by OP, ACC/TJA user interface MSG_Lane_Assist_Data1 = 0x3CA # TX by OP, Lane Keep Assist MSG_LateralMotionControl = 0x3D3 # TX by OP, Traffic Jam Assist MSG_IPMA_Data = 0x3D8 # TX by OP, IPMA and LKAS user interface class Buttons: CANCEL = 0 RESUME = 1 TJA_TOGGLE = 2 class TestFordSafety(common.PandaSafetyTest): STANDSTILL_THRESHOLD = 1 RELAY_MALFUNCTION_ADDR = MSG_IPMA_Data RELAY_MALFUNCTION_BUS = 0 TX_MSGS = [ [MSG_Steering_Data_FD1, 0], [MSG_Steering_Data_FD1, 2], [MSG_ACCDATA_3, 0], [MSG_Lane_Assist_Data1, 0], [MSG_LateralMotionControl, 0], [MSG_IPMA_Data, 0], ] FWD_BLACKLISTED_ADDRS = {2: [MSG_ACCDATA_3, MSG_Lane_Assist_Data1, MSG_LateralMotionControl, MSG_IPMA_Data]} FWD_BUS_LOOKUP = {0: 2, 2: 0} def setUp(self): self.packer = CANPackerPanda("ford_lincoln_base_pt") self.safety = libpandasafety_py.libpandasafety self.safety.set_safety_hooks(Panda.SAFETY_FORD, 0) self.safety.init_tests() # Driver brake pedal def _user_brake_msg(self, brake: bool): # brake pedal and cruise state share same message, so we have to send # the other signal too enable = self.safety.get_controls_allowed() values = { "BpedDrvAppl_D_Actl": 2 if brake else 1, "CcStat_D_Actl": 5 if enable else 0, } return self.packer.make_can_msg_panda("EngBrakeData", 0, values) # Standstill state def _speed_msg(self, speed: float): values = {"VehStop_D_Stat": 1 if speed <= self.STANDSTILL_THRESHOLD else 0} return self.packer.make_can_msg_panda("DesiredTorqBrk", 0, values) # Drive throttle input def _user_gas_msg(self, gas: float): values = {"ApedPos_Pc_ActlArb": gas} return self.packer.make_can_msg_panda("EngVehicleSpThrottle", 0, values) # Cruise status def _pcm_status_msg(self, enable: bool): # brake pedal and cruise state share same message, so we have to send # the other signal too brake = self.safety.get_brake_pressed_prev() values = { "BpedDrvAppl_D_Actl": 2 if brake else 1, "CcStat_D_Actl": 5 if enable else 0, } return self.packer.make_can_msg_panda("EngBrakeData", 0, values) # LKAS command def _lkas_command_msg(self, action: int): values = { "LkaActvStats_D2_Req": action, } return self.packer.make_can_msg_panda("Lane_Assist_Data1", 0, values) # TJA command def _tja_command_msg(self, enabled: bool): values = { "LatCtl_D_Rq": 1 if enabled else 0, } return self.packer.make_can_msg_panda("LateralMotionControl", 0, values) # Cruise control buttons def _acc_button_msg(self, button: int, bus: int): values = { "CcAslButtnCnclPress": 1 if button == Buttons.CANCEL else 0, "CcAsllButtnResPress": 1 if button == Buttons.RESUME else 0, "TjaButtnOnOffPress": 1 if button == Buttons.TJA_TOGGLE else 0, } return self.packer.make_can_msg_panda("Steering_Data_FD1", bus, values) def test_steer_allowed(self): self.safety.set_controls_allowed(1) self.assertTrue(self._tx(self._tja_command_msg(1))) self.assertTrue(self.safety.get_controls_allowed()) self.safety.set_controls_allowed(0) self.assertFalse(self._tx(self._tja_command_msg(1))) self.assertFalse(self.safety.get_controls_allowed()) def test_prevent_lkas_action(self): self.safety.set_controls_allowed(1) self.assertFalse(self._tx(self._lkas_command_msg(1))) self.safety.set_controls_allowed(0) self.assertFalse(self._tx(self._lkas_command_msg(1))) def test_acc_buttons(self): for allowed in (0, 1): self.safety.set_controls_allowed(allowed) for enabled in (True, False): self._rx(self._pcm_status_msg(enabled)) self.assertTrue(self._tx(self._acc_button_msg(Buttons.TJA_TOGGLE, 2))) for allowed in (0, 1): self.safety.set_controls_allowed(allowed) for bus in (0, 2): self.assertEqual(allowed, self._tx(self._acc_button_msg(Buttons.RESUME, bus))) for enabled in (True, False): self._rx(self._pcm_status_msg(enabled)) for bus in (0, 2): self.assertEqual(enabled, self._tx(self._acc_button_msg(Buttons.CANCEL, bus))) if __name__ == "__main__": unittest.main()