#!/usr/bin/env python3
from parameterized import parameterized_class
import unittest
from opendbc . car . hyundai . values import HyundaiSafetyFlags
from opendbc . car . structs import CarParams
from opendbc . safety . tests . libsafety import libsafety_py
import opendbc . safety . tests . common as common
from opendbc . safety . tests . common import CANPackerPanda
from opendbc . safety . tests . hyundai_common import HyundaiButtonBase , HyundaiLongitudinalBase
# All combinations of radar/camera-SCC and gas/hybrid/EV cars
ALL_GAS_EV_HYBRID_COMBOS = [
# Radar SCC
{ " GAS_MSG " : ( " ACCELERATOR_BRAKE_ALT " , " ACCELERATOR_PEDAL_PRESSED " ) , " SCC_BUS " : 0 , " SAFETY_PARAM " : 0 } ,
{ " GAS_MSG " : ( " ACCELERATOR " , " ACCELERATOR_PEDAL " ) , " SCC_BUS " : 0 , " SAFETY_PARAM " : HyundaiSafetyFlags . EV_GAS } ,
{ " GAS_MSG " : ( " ACCELERATOR_ALT " , " ACCELERATOR_PEDAL " ) , " SCC_BUS " : 0 , " SAFETY_PARAM " : HyundaiSafetyFlags . HYBRID_GAS } ,
# Camera SCC
{ " GAS_MSG " : ( " ACCELERATOR_BRAKE_ALT " , " ACCELERATOR_PEDAL_PRESSED " ) , " SCC_BUS " : 2 , " SAFETY_PARAM " : HyundaiSafetyFlags . CAMERA_SCC } ,
{ " GAS_MSG " : ( " ACCELERATOR " , " ACCELERATOR_PEDAL " ) , " SCC_BUS " : 2 , " SAFETY_PARAM " : HyundaiSafetyFlags . EV_GAS | HyundaiSafetyFlags . CAMERA_SCC } ,
{ " GAS_MSG " : ( " ACCELERATOR_ALT " , " ACCELERATOR_PEDAL " ) , " SCC_BUS " : 2 , " SAFETY_PARAM " : HyundaiSafetyFlags . HYBRID_GAS | HyundaiSafetyFlags . CAMERA_SCC } ,
]
class TestHyundaiCanfdBase ( HyundaiButtonBase , common . PandaCarSafetyTest , common . DriverTorqueSteeringSafetyTest , common . SteerRequestCutSafetyTest ) :
TX_MSGS = [ [ 0x50 , 0 ] , [ 0x1CF , 1 ] , [ 0x2A4 , 0 ] ]
STANDSTILL_THRESHOLD = 12 # 0.375 kph
FWD_BLACKLISTED_ADDRS = { 2 : [ 0x50 , 0x2a4 ] }
MAX_RATE_UP = 2
MAX_RATE_DOWN = 3
MAX_TORQUE_LOOKUP = [ 0 ] , [ 270 ]
MAX_RT_DELTA = 112
DRIVER_TORQUE_ALLOWANCE = 250
DRIVER_TORQUE_FACTOR = 2
# Safety around steering req bit
MIN_VALID_STEERING_FRAMES = 89
MAX_INVALID_STEERING_FRAMES = 2
PT_BUS = 0
SCC_BUS = 2
STEER_BUS = 0
STEER_MSG = " "
GAS_MSG = ( " " , " " )
BUTTONS_TX_BUS = 1
def _torque_driver_msg ( self , torque ) :
values = { " STEERING_COL_TORQUE " : torque }
return self . packer . make_can_msg_panda ( " MDPS " , self . PT_BUS , values )
def _torque_cmd_msg ( self , torque , steer_req = 1 ) :
values = { " TORQUE_REQUEST " : torque , " STEER_REQ " : steer_req }
return self . packer . make_can_msg_panda ( self . STEER_MSG , self . STEER_BUS , values )
def _speed_msg ( self , speed ) :
values = { f " WHL_Spd { pos } Val " : speed * 0.03125 for pos in [ " FL " , " FR " , " RL " , " RR " ] }
return self . packer . make_can_msg_panda ( " WHEEL_SPEEDS " , self . PT_BUS , values )
def _user_brake_msg ( self , brake ) :
values = { " DriverBraking " : brake }
return self . packer . make_can_msg_panda ( " TCS " , self . PT_BUS , values )
def _user_gas_msg ( self , gas ) :
values = { self . GAS_MSG [ 1 ] : gas }
return self . packer . make_can_msg_panda ( self . GAS_MSG [ 0 ] , self . PT_BUS , values )
def _pcm_status_msg ( self , enable ) :
values = { " ACCMode " : 1 if enable else 0 }
return self . packer . make_can_msg_panda ( " SCC_CONTROL " , self . SCC_BUS , values )
def _button_msg ( self , buttons , main_button = 0 , bus = None ) :
if bus is None :
bus = self . PT_BUS
values = {
" CRUISE_BUTTONS " : buttons ,
" ADAPTIVE_CRUISE_MAIN_BTN " : main_button ,
}
return self . packer . make_can_msg_panda ( " CRUISE_BUTTONS " , bus , values )
class TestHyundaiCanfdLFASteeringBase ( TestHyundaiCanfdBase ) :
TX_MSGS = [ [ 0x12A , 0 ] , [ 0x1A0 , 1 ] , [ 0x1CF , 0 ] , [ 0x1E0 , 0 ] ]
RELAY_MALFUNCTION_ADDRS = { 0 : ( 0x12A , 0x1E0 ) } # LFA, LFAHDA_CLUSTER
FWD_BLACKLISTED_ADDRS = { 2 : [ 0x12A , 0x1E0 ] }
STEER_MSG = " LFA "
BUTTONS_TX_BUS = 2
SAFETY_PARAM : int
@classmethod
def setUpClass ( cls ) :
super ( ) . setUpClass ( )
if cls . __name__ in ( " TestHyundaiCanfdLFASteering " , " TestHyundaiCanfdLFASteeringAltButtons " ) :
cls . packer = None
cls . safety = None
raise unittest . SkipTest
def setUp ( self ) :
self . packer = CANPackerPanda ( " hyundai_canfd_generated " )
self . safety = libsafety_py . libsafety
self . safety . set_safety_hooks ( CarParams . SafetyModel . hyundaiCanfd , self . SAFETY_PARAM )
self . safety . init_tests ( )
@parameterized_class ( ALL_GAS_EV_HYBRID_COMBOS )
class TestHyundaiCanfdLFASteering ( TestHyundaiCanfdLFASteeringBase ) :
pass
class TestHyundaiCanfdLFASteeringAltButtonsBase ( TestHyundaiCanfdLFASteeringBase ) :
SAFETY_PARAM : int
def setUp ( self ) :
self . packer = CANPackerPanda ( " hyundai_canfd_generated " )
self . safety = libsafety_py . libsafety
self . safety . set_safety_hooks ( CarParams . SafetyModel . hyundaiCanfd , HyundaiSafetyFlags . CANFD_ALT_BUTTONS | self . SAFETY_PARAM )
self . safety . init_tests ( )
def _button_msg ( self , buttons , main_button = 0 , bus = 1 ) :
values = {
" CRUISE_BUTTONS " : buttons ,
" ADAPTIVE_CRUISE_MAIN_BTN " : main_button ,
}
return self . packer . make_can_msg_panda ( " CRUISE_BUTTONS_ALT " , self . PT_BUS , values )
def _acc_cancel_msg ( self , cancel , accel = 0 ) :
values = { " ACCMode " : 4 if cancel else 0 , " aReqRaw " : accel , " aReqValue " : accel }
return self . packer . make_can_msg_panda ( " SCC_CONTROL " , self . PT_BUS , values )
def test_button_sends ( self ) :
"""
No button send allowed with alt buttons .
"""
for enabled in ( True , False ) :
for btn in range ( 8 ) :
self . safety . set_controls_allowed ( enabled )
self . assertFalse ( self . _tx ( self . _button_msg ( btn ) ) )
def test_acc_cancel ( self ) :
# FIXME: the CANFD_ALT_BUTTONS cars are the only ones that use SCC_CONTROL to cancel, why can't we use buttons?
for enabled in ( True , False ) :
self . safety . set_controls_allowed ( enabled )
self . assertTrue ( self . _tx ( self . _acc_cancel_msg ( True ) ) )
self . assertFalse ( self . _tx ( self . _acc_cancel_msg ( True , accel = 1 ) ) )
self . assertFalse ( self . _tx ( self . _acc_cancel_msg ( False ) ) )
@parameterized_class ( ALL_GAS_EV_HYBRID_COMBOS )
class TestHyundaiCanfdLFASteeringAltButtons ( TestHyundaiCanfdLFASteeringAltButtonsBase ) :
pass
class TestHyundaiCanfdLKASteeringEV ( TestHyundaiCanfdBase ) :
TX_MSGS = [ [ 0x50 , 0 ] , [ 0x1CF , 1 ] , [ 0x2A4 , 0 ] ]
RELAY_MALFUNCTION_ADDRS = { 0 : ( 0x50 , 0x2a4 ) } # LKAS, CAM_0x2A4
FWD_BLACKLISTED_ADDRS = { 2 : [ 0x50 , 0x2a4 ] }
PT_BUS = 1
SCC_BUS = 1
STEER_MSG = " LKAS "
GAS_MSG = ( " ACCELERATOR " , " ACCELERATOR_PEDAL " )
def setUp ( self ) :
self . packer = CANPackerPanda ( " hyundai_canfd_generated " )
self . safety = libsafety_py . libsafety
self . safety . set_safety_hooks ( CarParams . SafetyModel . hyundaiCanfd , HyundaiSafetyFlags . CANFD_LKA_STEERING | HyundaiSafetyFlags . EV_GAS )
self . safety . init_tests ( )
# TODO: Handle ICE and HEV configurations once we see cars that use the new messages
class TestHyundaiCanfdLKASteeringAltEV ( TestHyundaiCanfdBase ) :
TX_MSGS = [ [ 0x110 , 0 ] , [ 0x1CF , 1 ] , [ 0x362 , 0 ] ]
RELAY_MALFUNCTION_ADDRS = { 0 : ( 0x110 , 0x362 ) } # LKAS_ALT, CAM_0x362
FWD_BLACKLISTED_ADDRS = { 2 : [ 0x110 , 0x362 ] }
PT_BUS = 1
SCC_BUS = 1
STEER_MSG = " LKAS_ALT "
GAS_MSG = ( " ACCELERATOR " , " ACCELERATOR_PEDAL " )
def setUp ( self ) :
self . packer = CANPackerPanda ( " hyundai_canfd_generated " )
self . safety = libsafety_py . libsafety
self . safety . set_safety_hooks ( CarParams . SafetyModel . hyundaiCanfd , HyundaiSafetyFlags . CANFD_LKA_STEERING | HyundaiSafetyFlags . EV_GAS |
HyundaiSafetyFlags . CANFD_LKA_STEERING_ALT )
self . safety . init_tests ( )
class TestHyundaiCanfdLKASteeringLongEV ( HyundaiLongitudinalBase , TestHyundaiCanfdLKASteeringEV ) :
TX_MSGS = [ [ 0x50 , 0 ] , [ 0x1CF , 1 ] , [ 0x2A4 , 0 ] , [ 0x51 , 0 ] , [ 0x730 , 1 ] , [ 0x12a , 1 ] , [ 0x160 , 1 ] ,
[ 0x1e0 , 1 ] , [ 0x1a0 , 1 ] , [ 0x1ea , 1 ] , [ 0x200 , 1 ] , [ 0x345 , 1 ] , [ 0x1da , 1 ] ]
RELAY_MALFUNCTION_ADDRS = { 0 : ( 0x50 , 0x2a4 ) , 1 : ( 0x1a0 , ) } # LKAS, CAM_0x2A4, SCC_CONTROL
DISABLED_ECU_UDS_MSG = ( 0x730 , 1 )
DISABLED_ECU_ACTUATION_MSG = ( 0x1a0 , 1 )
STEER_MSG = " LFA "
GAS_MSG = ( " ACCELERATOR " , " ACCELERATOR_PEDAL " )
STEER_BUS = 1
def setUp ( self ) :
self . packer = CANPackerPanda ( " hyundai_canfd_generated " )
self . safety = libsafety_py . libsafety
self . safety . set_safety_hooks ( CarParams . SafetyModel . hyundaiCanfd , HyundaiSafetyFlags . CANFD_LKA_STEERING |
HyundaiSafetyFlags . LONG | HyundaiSafetyFlags . EV_GAS )
self . safety . init_tests ( )
def _accel_msg ( self , accel , aeb_req = False , aeb_decel = 0 ) :
values = {
" aReqRaw " : accel ,
" aReqValue " : accel ,
}
return self . packer . make_can_msg_panda ( " SCC_CONTROL " , 1 , values )
# Tests longitudinal for ICE, hybrid, EV cars with LFA steering
class TestHyundaiCanfdLFASteeringLongBase ( HyundaiLongitudinalBase , TestHyundaiCanfdLFASteeringBase ) :
FWD_BLACKLISTED_ADDRS = { 2 : [ 0x12a , 0x1e0 , 0x1a0 , 0x160 ] }
RELAY_MALFUNCTION_ADDRS = { 0 : ( 0x12A , 0x1E0 , 0x1a0 , 0x160 ) } # LFA, LFAHDA_CLUSTER, SCC_CONTROL, ADRV_0x160
DISABLED_ECU_UDS_MSG = ( 0x7D0 , 0 )
DISABLED_ECU_ACTUATION_MSG = ( 0x1a0 , 0 )
@classmethod
def setUpClass ( cls ) :
if cls . __name__ == " TestHyundaiCanfdLFASteeringLongBase " :
cls . safety = None
raise unittest . SkipTest
def setUp ( self ) :
self . packer = CANPackerPanda ( " hyundai_canfd_generated " )
self . safety = libsafety_py . libsafety
self . safety . set_safety_hooks ( CarParams . SafetyModel . hyundaiCanfd , HyundaiSafetyFlags . LONG | self . SAFETY_PARAM )
self . safety . init_tests ( )
def _accel_msg ( self , accel , aeb_req = False , aeb_decel = 0 ) :
values = {
" aReqRaw " : accel ,
" aReqValue " : accel ,
}
return self . packer . make_can_msg_panda ( " SCC_CONTROL " , 0 , values )
def test_tester_present_allowed ( self , ecu_disable : bool = True ) :
super ( ) . test_tester_present_allowed ( ecu_disable = not self . SAFETY_PARAM & HyundaiSafetyFlags . CAMERA_SCC )
@parameterized_class ( ALL_GAS_EV_HYBRID_COMBOS )
class TestHyundaiCanfdLFASteeringLong ( TestHyundaiCanfdLFASteeringLongBase ) :
@classmethod
def setUpClass ( cls ) :
if cls . __name__ == " TestHyundaiCanfdLFASteeringLong " :
cls . safety = None
raise unittest . SkipTest
@parameterized_class ( ALL_GAS_EV_HYBRID_COMBOS )
class TestHyundaiCanfdLFASteeringLongAltButtons ( TestHyundaiCanfdLFASteeringLongBase , TestHyundaiCanfdLFASteeringAltButtonsBase ) :
@classmethod
def setUpClass ( cls ) :
if cls . __name__ == " TestHyundaiCanfdLFASteeringLongAltButtons " :
cls . safety = None
raise unittest . SkipTest
def setUp ( self ) :
self . packer = CANPackerPanda ( " hyundai_canfd_generated " )
self . safety = libsafety_py . libsafety
self . safety . set_safety_hooks ( CarParams . SafetyModel . hyundaiCanfd , HyundaiSafetyFlags . LONG | HyundaiSafetyFlags . CANFD_ALT_BUTTONS | self . SAFETY_PARAM )
self . safety . init_tests ( )
def test_acc_cancel ( self ) :
# Alt buttons does not use SCC_CONTROL to cancel if longitudinal
pass
if __name__ == " __main__ " :
unittest . main ( )