mirror of
https://github.com/infiniteCable2/panda.git
synced 2026-02-18 17:23:52 +08:00
* alternative experience * safety init * fix * more update * not really * misra * Add Custom MIT License (#38) * brake check was not handled * revert * alt -> lkas * explicit checks * support toyota and ford * rename * hyundai can-fd support * only allow lkas if enabled * hyundai: main button handling * revert * hyundai: main button heartbeat * add logging for controls allowed lateral * fix panda safety * ford btn * toyota btn * fca btn * honda btn * mads safety tests * more tests * safety misra * safety mutation * misra * mutation experiment * fix * ford test main button * ford test lkas button * more ford test * hyundai lkas and main * more ford * hyundai canfd * rename * rename * cleaner * more fixes * more hyundai tests * no longer needed * thanks for tests! * more tests for lat * more explicit * make sure to reset * try this out * probably needed * move * misra * not needed * move to safety_mads * not really needed * remove * MADS: Refactor MADS safety with improved state management (pull request #46) Refactor MADS safety with improved state management This commit introduces a major refactoring of the MADS safety module, improving state management and control flow. Key changes include: Core Changes: - Introduced a MADSState struct to centralize state management - Removed global state variables in favor of structured state - Implemented button transition handling with explicit state tracking (PRESSED/RELEASED/NO_CHANGE) - Added state flags for button availability detection - Simplified lateral control permission logic Button Handling: - Separated main button and LKAS button state tracking - Added independent engagement states for each button - Improved button press detection across multiple platforms - Added support for main and LKAS buttons on Hyundai platforms - Modified ACC main state handling Testing: - Added comprehensive test coverage for MADS state transitions - Added new MADS-specific test base class for consistent testing across platforms - Added mutation testing for state management - Extended timeout for mutation tests from 5 to 8 minutes - Added extensive button press validation tests - Enhanced debugging output in replay drive tests The refactored code provides a more organized implementation of MADS safety features while maintaining compatibility with existing safety checks. * adding note * adding ford (WIP) * adding honda (WIP) * adding toyota (WIP) * adding chrysler (WIP) * Standardize Button State Handling Across Platforms Refactor button state handling by replacing integer constants with an enumerated `ButtonState` type and updating logic to improve readability and maintainability. This change affects button press detection in Ford, Honda, Hyundai, and Toyota safety modules and aligns them with a unified MADS button state approach. Enums provide a clearer understanding of button states and transitions, facilitating easier maintenance and future enhancements. * Disable LKAS button press logic in Honda and Toyota safety. The code for processing LKAS button presses has been commented out in both Honda and Toyota safety implementations. This change aims to investigate or temporarily halt the button press effects without removing the logic altogether. It will be important to test for any impacts this may have on vehicle control functionality. * Remove commented out code in toyota_rx_hook function This commit cleans up the toyota_rx_hook function by removing unnecessary commented-out code that checks for LKAS button presses on bus 2. This helps improve code readability and maintainability without altering the existing functionality. * GM, mazda, nissan, subaru (global & preglobal) * Honda LKAS * Revert "Remove commented out code in toyota_rx_hook function" This reverts commit d6b012c01a08118d91fad56c9f6ac2f92b671968. * Toyota, Subaru Global LKAS * nissan fix * gm fix * use speed msg to force rx * im bored * misra * subaru/toyota/honda * nope * attempt * go through all buttons * try nissan * more nissan * nissan tests passed! * subaru lkas test (not sure why it's not passing 2 and 3 values) * Improved code organization in safety_subaru.h and test_subaru.py This commit includes a minor restructuring in safety_subaru.h and test_subaru.py for better readability and flow. The condition check in safety_subaru.h for lkas_hud now has explicit parentheses. With regard to test_subaru.py, an unnecessary import was removed, and the sequence of steps in the test was reordered - now enabling mads and cleaning up mads_states happens before each subtest. * Refactor tests to use _speed_msg instead of _user_brake_msg. Updated the MADS safety tests to utilize the _speed_msg(0) function call in place of _user_brake_msg(False). * Reworking the tests a little for clarity * disabling lkas again on toyota temporarily * fix mads condition to engage * hyundai and honda good with new tests * Redoing more tests * update for safety tick ensuring mads control is exited while lagging * Updating tests for toyota * cleaning up tests on hkg * commenting out temp_debug for future use * revert * constants * cleanup * format! * match yota * Apply suggestions from code review * force * explicit checks * revert --------- Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
239 lines
12 KiB
Python
239 lines
12 KiB
Python
import unittest
|
|
from abc import abstractmethod
|
|
from enum import IntFlag
|
|
|
|
|
|
class MadsStates(IntFlag):
|
|
DEFAULT = 0
|
|
RESERVED = 1
|
|
MAIN_BUTTON_AVAILABLE = 2
|
|
LKAS_BUTTON_AVAILABLE = 4
|
|
|
|
|
|
class MadsCommonBase(unittest.TestCase):
|
|
@abstractmethod
|
|
def _lkas_button_msg(self, enabled):
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
def _main_cruise_button_msg(self, enabled):
|
|
try:
|
|
self._button_msg(enabled)
|
|
except (NotImplementedError, AttributeError):
|
|
raise unittest.SkipTest("Skipping test because _button_msg is not implemented for this car. If you know it, please implement it.")
|
|
|
|
raise NotImplementedError("Since _button_msg is implemented, _main_cruise_button_msg should be implemented as well to signal the main cruise button press")
|
|
|
|
@abstractmethod
|
|
def _acc_state_msg(self, enabled):
|
|
raise NotImplementedError
|
|
|
|
def test_enable_control_from_cruise_button_press(self):
|
|
for enable_mads in (True, False):
|
|
with self.subTest("enable_mads", mads_enabled=enable_mads):
|
|
for cruise_button_press in [True, False]:
|
|
self.safety.set_enable_mads(enable_mads, False)
|
|
with self.subTest("cruise_button_press", cruise_button_press=cruise_button_press):
|
|
self._mads_states_cleanup()
|
|
self._rx(self._main_cruise_button_msg(cruise_button_press))
|
|
self.assertEqual(enable_mads and cruise_button_press, self.safety.get_controls_allowed_lat())
|
|
self._mads_states_cleanup()
|
|
|
|
def test_enable_control_from_lkas_button_press(self):
|
|
try:
|
|
self._lkas_button_msg(False)
|
|
except NotImplementedError:
|
|
raise unittest.SkipTest("Skipping test because _lkas_button_msg is not implemented for this car")
|
|
|
|
for enable_mads in (True, False):
|
|
with self.subTest("enable_mads", mads_enabled=enable_mads):
|
|
for lkas_button_press in [True, False]:
|
|
self.safety.set_enable_mads(enable_mads, False)
|
|
with self.subTest("lkas_button_press", button_state=lkas_button_press):
|
|
self._mads_states_cleanup()
|
|
self._rx(self._lkas_button_msg(lkas_button_press))
|
|
self.assertEqual(enable_mads and lkas_button_press, self.safety.get_controls_allowed_lat())
|
|
self._mads_states_cleanup()
|
|
|
|
def _mads_states_cleanup(self):
|
|
self.safety.set_main_button_press(-1)
|
|
self.safety.set_lkas_button_press(-1)
|
|
self.safety.set_controls_allowed_lat(False)
|
|
self.safety.set_main_button_engaged(False)
|
|
self.safety.set_lkas_button_engaged(False)
|
|
self.safety.set_mads_state_flags(0)
|
|
self.safety.set_acc_main_on(False)
|
|
|
|
def test_enable_control_from_setting_main_state_manually(self):
|
|
for enable_mads in (True, False):
|
|
with self.subTest("enable_mads", mads_enabled=enable_mads):
|
|
self.safety.set_enable_mads(enable_mads, False)
|
|
for main_button_press in (-1, 0, 1):
|
|
with self.subTest("main_button_press", button_state=main_button_press):
|
|
self._mads_states_cleanup()
|
|
self.safety.set_main_button_press(main_button_press)
|
|
self._rx(self._speed_msg(0))
|
|
self.assertEqual(enable_mads and main_button_press == 1, self.safety.get_controls_allowed_lat())
|
|
self._mads_states_cleanup()
|
|
|
|
def test_enable_control_from_setting_lkas_state_manually(self):
|
|
for enable_mads in (True, False):
|
|
with self.subTest("enable_mads", mads_enabled=enable_mads):
|
|
self.safety.set_enable_mads(enable_mads, False)
|
|
for lkas_button_press in (-1, 0, 1):
|
|
with self.subTest("lkas_button_press", button_state=lkas_button_press):
|
|
self._mads_states_cleanup()
|
|
self.safety.set_lkas_button_press(lkas_button_press)
|
|
self._rx(self._speed_msg(0))
|
|
self.assertEqual(enable_mads and lkas_button_press == 1, self.safety.get_controls_allowed_lat())
|
|
self._mads_states_cleanup()
|
|
|
|
def test_mads_state_flags(self):
|
|
for enable_mads in (True, False):
|
|
with self.subTest("enable_mads", mads_enabled=enable_mads):
|
|
self.safety.set_enable_mads(enable_mads, False)
|
|
self._mads_states_cleanup()
|
|
self.safety.set_main_button_press(0) # Meaning a message with those buttons was seen and the _prev inside is no longer -1
|
|
self.safety.set_lkas_button_press(0) # Meaning a message with those buttons was seen and the _prev inside is no longer -1
|
|
self._rx(self._speed_msg(0))
|
|
self.assertTrue(self.safety.get_mads_state_flags() & MadsStates.MAIN_BUTTON_AVAILABLE)
|
|
self.assertTrue(self.safety.get_mads_state_flags() & MadsStates.LKAS_BUTTON_AVAILABLE)
|
|
self._mads_states_cleanup()
|
|
|
|
def test_enable_control_from_acc_main_on(self):
|
|
"""Test that lateral controls are allowed when ACC main is enabled"""
|
|
for enable_mads in (True, False):
|
|
with self.subTest("enable_mads", mads_enabled=enable_mads):
|
|
self.safety.set_enable_mads(enable_mads, False)
|
|
for acc_main_on in (True, False):
|
|
with self.subTest("acc_main_on", acc_main_on=acc_main_on):
|
|
self._mads_states_cleanup()
|
|
self.safety.set_acc_main_on(acc_main_on)
|
|
self._rx(self._speed_msg(0))
|
|
self.assertEqual(enable_mads and acc_main_on, self.safety.get_controls_allowed_lat())
|
|
self._mads_states_cleanup()
|
|
|
|
def test_controls_allowed_must_always_enable_lat(self):
|
|
for mads_enabled in [True, False]:
|
|
with self.subTest("mads enabled", mads_enabled=mads_enabled):
|
|
self.safety.set_enable_mads(mads_enabled, False)
|
|
for controls_allowed in [True, False]:
|
|
with self.subTest("controls allowed", controls_allowed=controls_allowed):
|
|
self.safety.set_controls_allowed(controls_allowed)
|
|
self.assertEqual(self.safety.get_controls_allowed(), self.safety.get_lat_active())
|
|
|
|
def test_mads_disengage_lat_on_brake_setup(self):
|
|
for mads_enabled in [True, False]:
|
|
with self.subTest("mads enabled", mads_enabled=mads_enabled):
|
|
for disengage_on_brake in [True, False]:
|
|
with self.subTest("disengage on brake", disengage_on_brake=disengage_on_brake):
|
|
self.safety.set_enable_mads(mads_enabled, disengage_on_brake)
|
|
self.assertEqual(disengage_on_brake, self.safety.get_disengage_lat_on_brake())
|
|
|
|
def test_mads_state_flags_mutation(self):
|
|
"""Test to catch mutations in bitwise operations for state flags.
|
|
Specifically targets the mutation of & to | in flag checking operations.
|
|
Tests both setting and clearing of flags to catch potential bitwise operation mutations."""
|
|
|
|
# Test both MADS enabled and disabled states
|
|
for enable_mads in (True, False):
|
|
with self.subTest("enable_mads", mads_enabled=enable_mads):
|
|
self.safety.set_enable_mads(enable_mads, False)
|
|
self._mads_states_cleanup()
|
|
|
|
# Initial state - both flags should be unset
|
|
self._rx(self._speed_msg(0))
|
|
initial_flags = self.safety.get_mads_state_flags()
|
|
self.assertEqual(initial_flags & MadsStates.MAIN_BUTTON_AVAILABLE, MadsStates.DEFAULT) # Main button flag
|
|
self.assertEqual(initial_flags & MadsStates.LKAS_BUTTON_AVAILABLE, MadsStates.DEFAULT) # LKAS button flag
|
|
|
|
# Set only main button
|
|
self.safety.set_main_button_press(0)
|
|
self._rx(self._speed_msg(0))
|
|
main_only_flags = self.safety.get_mads_state_flags()
|
|
self.assertEqual(main_only_flags & MadsStates.MAIN_BUTTON_AVAILABLE, MadsStates.MAIN_BUTTON_AVAILABLE) # Main button flag should be set
|
|
self.assertEqual(main_only_flags & MadsStates.LKAS_BUTTON_AVAILABLE, MadsStates.DEFAULT) # LKAS button flag should still be unset
|
|
|
|
# Set LKAS button and verify both flags
|
|
self.safety.set_lkas_button_press(0)
|
|
self._rx(self._speed_msg(0))
|
|
both_flags = self.safety.get_mads_state_flags()
|
|
self.assertEqual(both_flags & MadsStates.MAIN_BUTTON_AVAILABLE, MadsStates.MAIN_BUTTON_AVAILABLE) # Main button flag should remain set
|
|
self.assertEqual(both_flags & MadsStates.LKAS_BUTTON_AVAILABLE, MadsStates.LKAS_BUTTON_AVAILABLE) # LKAS button flag should be set
|
|
|
|
# Verify that using | instead of & would give different results
|
|
self.assertNotEqual(both_flags & MadsStates.MAIN_BUTTON_AVAILABLE, both_flags | MadsStates.MAIN_BUTTON_AVAILABLE)
|
|
self.assertNotEqual(both_flags & MadsStates.LKAS_BUTTON_AVAILABLE, both_flags | MadsStates.LKAS_BUTTON_AVAILABLE)
|
|
|
|
# Reset flags and verify they're cleared
|
|
self._mads_states_cleanup()
|
|
self._rx(self._speed_msg(0))
|
|
cleared_flags = self.safety.get_mads_state_flags()
|
|
self.assertEqual(cleared_flags & MadsStates.MAIN_BUTTON_AVAILABLE, MadsStates.DEFAULT)
|
|
self.assertEqual(cleared_flags & MadsStates.LKAS_BUTTON_AVAILABLE, MadsStates.DEFAULT)
|
|
|
|
def test_mads_state_flags_persistence(self):
|
|
"""Test to verify that state flags remain set once buttons are seen"""
|
|
|
|
for enable_mads in (True, False):
|
|
with self.subTest("enable_mads", mads_enabled=enable_mads):
|
|
self.safety.set_enable_mads(enable_mads, False)
|
|
self._mads_states_cleanup()
|
|
|
|
# Set main button and verify flag
|
|
self.safety.set_main_button_press(0)
|
|
self._rx(self._speed_msg(0))
|
|
self.assertEqual(self.safety.get_mads_state_flags() & MadsStates.MAIN_BUTTON_AVAILABLE, MadsStates.MAIN_BUTTON_AVAILABLE)
|
|
|
|
# Reset main button to -1, flag should persist
|
|
self.safety.set_main_button_press(-1)
|
|
self._rx(self._speed_msg(0))
|
|
self.assertEqual(self.safety.get_mads_state_flags() & MadsStates.MAIN_BUTTON_AVAILABLE, MadsStates.MAIN_BUTTON_AVAILABLE)
|
|
|
|
# Set LKAS button and verify both flags
|
|
self.safety.set_lkas_button_press(0)
|
|
self._rx(self._speed_msg(0))
|
|
flags = self.safety.get_mads_state_flags()
|
|
self.assertEqual(flags & MadsStates.MAIN_BUTTON_AVAILABLE, MadsStates.MAIN_BUTTON_AVAILABLE)
|
|
self.assertEqual(flags & MadsStates.LKAS_BUTTON_AVAILABLE, MadsStates.LKAS_BUTTON_AVAILABLE)
|
|
|
|
def test_mads_state_flags_individual_control(self):
|
|
"""Test the ability to individually control state flags.
|
|
Verifies that flags can be set and cleared independently."""
|
|
|
|
for enable_mads in (True, False):
|
|
with self.subTest("enable_mads", mads_enabled=enable_mads):
|
|
self.safety.set_enable_mads(enable_mads, False)
|
|
self._mads_states_cleanup()
|
|
|
|
# Set main button flag only
|
|
self.safety.set_main_button_press(0)
|
|
self._rx(self._speed_msg(0))
|
|
main_flags = self.safety.get_mads_state_flags()
|
|
self.assertEqual(main_flags & MadsStates.MAIN_BUTTON_AVAILABLE, MadsStates.MAIN_BUTTON_AVAILABLE)
|
|
self.assertEqual(main_flags & MadsStates.LKAS_BUTTON_AVAILABLE, MadsStates.DEFAULT)
|
|
|
|
# Reset flags and set LKAS only
|
|
self._mads_states_cleanup()
|
|
self.safety.set_lkas_button_press(0)
|
|
self._rx(self._speed_msg(0))
|
|
lkas_flags = self.safety.get_mads_state_flags()
|
|
self.assertEqual(lkas_flags & MadsStates.MAIN_BUTTON_AVAILABLE, MadsStates.DEFAULT)
|
|
self.assertEqual(lkas_flags & MadsStates.LKAS_BUTTON_AVAILABLE, MadsStates.LKAS_BUTTON_AVAILABLE)
|
|
|
|
# Set both flags
|
|
self._mads_states_cleanup()
|
|
self.safety.set_main_button_press(0)
|
|
self.safety.set_lkas_button_press(0)
|
|
self._rx(self._speed_msg(0))
|
|
both_flags = self.safety.get_mads_state_flags()
|
|
self.assertEqual(both_flags & MadsStates.MAIN_BUTTON_AVAILABLE, MadsStates.MAIN_BUTTON_AVAILABLE)
|
|
self.assertEqual(both_flags & MadsStates.LKAS_BUTTON_AVAILABLE, MadsStates.LKAS_BUTTON_AVAILABLE)
|
|
|
|
# Clear all flags and verify
|
|
self._mads_states_cleanup()
|
|
self._rx(self._speed_msg(0))
|
|
final_flags = self.safety.get_mads_state_flags()
|
|
self.assertEqual(final_flags & MadsStates.MAIN_BUTTON_AVAILABLE, MadsStates.DEFAULT)
|
|
self.assertEqual(final_flags & MadsStates.LKAS_BUTTON_AVAILABLE, MadsStates.DEFAULT)
|