mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 05:23:41 +08:00
80fbb4f6db
Register vehicle controls, planner modes, UI actions and Galaxy services after their providers exist. Wire the shared runtime entrypoints and cover their combined behavior. Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com> Co-authored-by: Daniel Koepping <elkoled@gmail.com> Co-authored-by: Danny <dannyirwin31@yahoo.com> Co-authored-by: DimitriChrysafis <Dimitri.chrysafis@gmail.com> Co-authored-by: Dom <168790843+firestar5683@users.noreply.github.com> Co-authored-by: Harald Schäfer <harald.the.engineer@gmail.com> Co-authored-by: Kacper Rączy <gfw.kra@gmail.com> Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu> Co-authored-by: dirwin31 <83434411+dirwin31@users.noreply.github.com> Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com> Co-authored-by: stef <19478336+stefpi@users.noreply.github.com> Co-authored-by: whoisdomi <dcantu83@gmail.com> Co-authored-by: Intelli <contact@intelli.software> Co-authored-by: haraschax <6804392+haraschax@users.noreply.github.com> Co-authored-by: prabhaavp <prabhaav@msu.edu>
482 lines
21 KiB
Python
482 lines
21 KiB
Python
import unittest
|
|
from types import SimpleNamespace
|
|
from unittest.mock import Mock, patch
|
|
|
|
from opendbc.car import gen_empty_fingerprint
|
|
from opendbc.car.can_definitions import CanData
|
|
from opendbc.car.gm.interface import CarInterface as GMInterface
|
|
from opendbc.car.gm.values import CAR as GMCar, GMFlags
|
|
from openpilot.cereal import messaging
|
|
from openpilot.selfdrive.car.card import Car, EventName
|
|
from openpilot.selfdrive.pandad import can_capnp_to_list
|
|
from openpilot.starpilot.vehicle_startup import VehicleStartupOwner
|
|
|
|
|
|
class CardInitLifecycleTest(unittest.TestCase):
|
|
def defer_radar_fixture(self, card):
|
|
eager = card.state_update
|
|
batch = []
|
|
|
|
def receive(*, defer_radar=False):
|
|
self.assertTrue(defer_radar)
|
|
card._deferred_radar_can = batch
|
|
return eager()
|
|
|
|
def radar_update(value):
|
|
self.assertIs(value, batch)
|
|
return None
|
|
|
|
card.state_update = Mock(side_effect=receive)
|
|
card.RI = SimpleNamespace(update=Mock(side_effect=radar_update))
|
|
self.addCleanup(lambda: self.assertEqual(card.RI.update.call_count, card.state_update.call_count))
|
|
|
|
def test_bolt_pedal_waits_for_control_then_initializes_once_before_output(self):
|
|
fingerprint = gen_empty_fingerprint()
|
|
fingerprint[0][0x201] = 6
|
|
cp = GMInterface.get_params(GMCar.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
|
fingerprint, [], False, False, False)
|
|
self.assertTrue(cp.flags & GMFlags.PEDAL_LONG.value)
|
|
|
|
order = []
|
|
card = Car.__new__(Car)
|
|
card.CP = cp
|
|
card.vehicle_startup = VehicleStartupOwner()
|
|
card.ci_initialized = False
|
|
card.initialized_prev = False
|
|
card.aol_replay = False
|
|
card.ioniq6_long_prearmed = False
|
|
card.ioniq6_long_selected = False
|
|
card.can_callbacks = (lambda wait_for_one=False: [], lambda frames: None)
|
|
|
|
def apply(*_):
|
|
order.append('apply')
|
|
return object(), [CanData(0x200, b'\x00' * 8, 0)]
|
|
|
|
fake_ci = SimpleNamespace(
|
|
CS=GMInterface(cp).CS,
|
|
CC=SimpleNamespace(),
|
|
init=Mock(side_effect=lambda *_: order.append('init')),
|
|
apply=Mock(side_effect=apply),
|
|
)
|
|
self.enterContext(patch.object(card, 'CI', fake_ci, create=True))
|
|
self.enterContext(patch.object(card, 'params', SimpleNamespace(put_bool=lambda key, value: order.append((key, value))), create=True))
|
|
sent = []
|
|
|
|
def publish(service, packet):
|
|
assert service == 'sendcan'
|
|
sent.append(packet)
|
|
order.append(('sendcan', tuple(can_capnp_to_list([packet], msgtype='sendcan')[0][1]),
|
|
bool(messaging.log_from_bytes(packet).valid)))
|
|
|
|
self.enterContext(patch.object(card, 'pm', SimpleNamespace(send=publish), create=True))
|
|
card.state_publish = Mock(side_effect=lambda *_: order.append('carState'))
|
|
cs = SimpleNamespace(canValid=True, canTimeout=False)
|
|
card.state_update = Mock(return_value=(cs, None))
|
|
self.defer_radar_fixture(card)
|
|
control = object()
|
|
|
|
class SubMaster:
|
|
valid = {'carControl': False}
|
|
alive = {'carControl': False}
|
|
seen = {'onroadEvents': False, 'carControl': True}
|
|
logMonoTime = {}
|
|
recv_time = {}
|
|
events = []
|
|
|
|
def __getitem__(self, key):
|
|
return self.events if key == 'onroadEvents' else control
|
|
|
|
def all_alive(self, services):
|
|
return all(self.alive[name] for name in services)
|
|
|
|
sm = SubMaster()
|
|
self.enterContext(patch.object(card, 'sm', sm, create=True))
|
|
for _ in range(30):
|
|
card.step()
|
|
self.assertNotIn('init', order)
|
|
sm.seen['onroadEvents'] = True
|
|
sm.events = [SimpleNamespace(name=EventName.selfdriveInitializing)]
|
|
card.step()
|
|
self.assertNotIn('init', order)
|
|
|
|
sm.events = []
|
|
card.step() # Controls still have no live producer.
|
|
self.assertNotIn('init', order)
|
|
sm.valid['carControl'] = True
|
|
sm.alive['carControl'] = True
|
|
import time
|
|
sm.logMonoTime['carControl'] = time.monotonic_ns()
|
|
sm.recv_time['carControl'] = time.monotonic()
|
|
cs.canValid = False
|
|
card.step() # A live controller cannot initialize against invalid CAN.
|
|
self.assertNotIn('init', order)
|
|
|
|
cs.canValid = True
|
|
card.step()
|
|
self.assertEqual(order[-5:-1], ['carState', 'init', ('ControlsReady', True), 'apply'])
|
|
self.assertEqual(order[-1][0], 'sendcan')
|
|
self.assertEqual([(frame[0], frame[2]) for frame in order[-1][1]], [(0x200, 0)])
|
|
self.assertTrue(order[-1][2])
|
|
self.assertTrue(all(not can_capnp_to_list([packet], msgtype='sendcan')[0][1] for packet in sent[:-1]))
|
|
self.assertTrue(all(not messaging.log_from_bytes(packet).valid for packet in sent[:-1]))
|
|
card.step()
|
|
fake_ci.init.assert_called_once_with(cp, *card.can_callbacks)
|
|
self.assertEqual(order.count(('ControlsReady', True)), 1)
|
|
|
|
applied = fake_ci.apply.call_count
|
|
sent_before = len(sent)
|
|
sm.valid['carControl'] = False
|
|
card.step() # A live but invalid native command cannot renew CAN output.
|
|
self.assertEqual(fake_ci.apply.call_count, applied)
|
|
self.assertEqual(len(sent), sent_before)
|
|
sm.valid['carControl'] = True
|
|
card.step()
|
|
self.assertEqual(fake_ci.apply.call_count, applied + 1)
|
|
self.assertEqual(len(sent), sent_before + 1)
|
|
self.assertTrue(messaging.log_from_bytes(sent[-1]).valid)
|
|
fake_ci.init.assert_called_once_with(cp, *card.can_callbacks)
|
|
|
|
def test_volt_sdgm_inactive_startup_continues_until_initialized(self):
|
|
from opendbc.car import Bus, structs
|
|
from opendbc.can import CANPacker
|
|
from opendbc.car.gm.values import DBC
|
|
from opendbc.car.gm.tests.test_volt_camera_control import feed_camera
|
|
fingerprint = gen_empty_fingerprint()
|
|
fingerprint[2][0x180] = 4
|
|
fingerprint[0][0x2FF] = 8
|
|
fingerprint[0][0xBE] = 6
|
|
cp = GMInterface.get_params(GMCar.CHEVROLET_VOLT_2019, fingerprint, [], True, False, False)
|
|
self.assertEqual(cp.safetyConfigs[0].safetyParam, 0x5007)
|
|
cp.alternativeExperience = 32
|
|
card = Car.__new__(Car)
|
|
card.CP = cp
|
|
card.CI = GMInterface(cp)
|
|
card.vehicle_startup = VehicleStartupOwner()
|
|
card.ci_initialized = False
|
|
card.initialized_prev = False
|
|
card.aol_replay = True
|
|
card.ioniq6_long_prearmed = False
|
|
card.ioniq6_long_selected = False
|
|
card.can_callbacks = (lambda wait_for_one=False: [], lambda _: None)
|
|
params = SimpleNamespace(put_bool=Mock())
|
|
self.enterContext(patch.object(card, 'params', params, create=True))
|
|
card.state_publish = Mock()
|
|
packer = CANPacker(DBC[cp.carFingerprint][Bus.pt])
|
|
cs, _ = feed_camera(card.CI, packer, 22_000_000_000, active=False)
|
|
self.assertTrue(cs.canValid)
|
|
card.state_update = Mock(return_value=(cs, None))
|
|
self.defer_radar_fixture(card)
|
|
control = structs.CarControl.new_message().as_reader()
|
|
panda = SimpleNamespace(safetyModel=cp.safetyConfigs[0].safetyModel, safetyParam=0x5007,
|
|
alternativeExperience=cp.alternativeExperience, safetyRxChecksInvalid=True)
|
|
mono, boot = 20_000_000_000, 22_000_000_000
|
|
|
|
class SubMaster:
|
|
seen = dict.fromkeys(('carControl', 'pandaStates', 'onroadEvents'), True)
|
|
valid = dict.fromkeys(('carControl', 'pandaStates'), True)
|
|
alive = dict(valid)
|
|
logMonoTime = {'carControl': mono, 'pandaStates': boot}
|
|
recv_time = dict.fromkeys(('carControl', 'pandaStates'), mono / 1e9)
|
|
events = [SimpleNamespace(name=EventName.selfdriveInitializing)]
|
|
|
|
def __getitem__(self, key):
|
|
return {'carControl': control, 'pandaStates': [panda], 'onroadEvents': self.events}[key]
|
|
|
|
def all_alive(self, services):
|
|
return all(self.alive[name] for name in services)
|
|
|
|
sm = SubMaster()
|
|
self.enterContext(patch.object(card, 'sm', sm, create=True))
|
|
sent = []
|
|
self.enterContext(patch.object(card, 'publish_sendcan', lambda frames, valid=True: sent.extend(frames)))
|
|
card.learning_send_committed_mono_ns = 0
|
|
# These saved requests cannot acquire startup brake ownership.
|
|
card.CI.CC.gm_auto_hold = True
|
|
card.CI.CC.volt_one_pedal = True
|
|
with patch('openpilot.selfdrive.car.card.time.monotonic_ns', return_value=mono), \
|
|
patch('openpilot.selfdrive.car.card.time.clock_gettime_ns', return_value=boot), \
|
|
patch.object(card.CI, 'apply', wraps=card.CI.apply) as apply, \
|
|
patch.object(card.CI, 'init', wraps=card.CI.init) as init:
|
|
self.assertFalse(card.startup_panda_configured())
|
|
self.assertTrue(card.startup_panda_configured(inactive_keepalive=True))
|
|
for _ in range(30):
|
|
card.step()
|
|
self.assertEqual(apply.call_count, 30)
|
|
init.assert_called_once()
|
|
params.put_bool.assert_called_once_with('ControlsReady', True)
|
|
self.assertEqual(card.CI.CC.gm_auto_hold_state.drive_ns, 0)
|
|
self.assertEqual(card.CI.CC.apply_brake, 0)
|
|
for call in apply.call_args_list:
|
|
cc, timestamp = call.args
|
|
self.assertEqual(timestamp, boot)
|
|
self.assertTrue(call.kwargs['startup_keepalive'])
|
|
self.assertFalse(cc.enabled or cc.latActive or cc.longActive)
|
|
self.assertEqual((cc.actuators.torque, cc.actuators.accel), (0., 0.))
|
|
self.assertTrue(sent)
|
|
for address, data, _ in sent:
|
|
if address == 0x180:
|
|
self.assertEqual(data[0] & 15, 0)
|
|
self.assertEqual(data[1], 0)
|
|
elif address == 0x315:
|
|
self.assertEqual(((data[0] & 15) << 8) | data[1], 0)
|
|
elif address == 0x2CB:
|
|
self.assertEqual(data[0] & 1, 0)
|
|
panda.safetyParam = 0x5087
|
|
card.step()
|
|
self.assertEqual(apply.call_count, 30)
|
|
panda.safetyParam = 0x5007
|
|
sm.logMonoTime['pandaStates'] = boot - 300_000_001
|
|
card.step()
|
|
self.assertEqual(apply.call_count, 30)
|
|
sm.logMonoTime['pandaStates'] = boot
|
|
active = structs.CarControl.new_message(latActive=True)
|
|
control = active.as_reader()
|
|
card.step()
|
|
self.assertEqual(apply.call_count, 30)
|
|
control = structs.CarControl.new_message().as_reader()
|
|
sm.events = []
|
|
card.step()
|
|
self.assertEqual(apply.call_count, 31)
|
|
self.assertEqual(apply.call_args.args[1], boot)
|
|
self.assertFalse(apply.call_args.kwargs)
|
|
|
|
|
|
def test_volt_ascm_inactive_startup_has_no_six_second_handoff_gap(self):
|
|
self._assert_volt_ascm_inactive_startup(alpha=True)
|
|
|
|
def test_volt_stock_ascm_inactive_startup_has_no_six_second_steering_gap(self):
|
|
self._assert_volt_ascm_inactive_startup(alpha=False)
|
|
|
|
def _assert_volt_ascm_inactive_startup(self, *, alpha):
|
|
from opendbc.car import Bus, structs
|
|
from opendbc.can import CANPacker
|
|
from opendbc.car.gm.values import DBC
|
|
from opendbc.car.gm.tests.test_ascm_intercept import params as ascm_params
|
|
from opendbc.car.gm.tests.test_volt_camera_control import feed_camera
|
|
cp = ascm_params(GMCar.CHEVROLET_VOLT_ASCM, sascm=True, alpha=alpha, accelerator=True, radar=True)
|
|
word = 0xD114 if alpha else 0xA05
|
|
if alpha:
|
|
cp.safetyConfigs[0].safetyParam = word
|
|
self.assertEqual(cp.safetyConfigs[0].safetyParam, word)
|
|
cadence = ((0x180, 110_000_000), (0x2CB, 50_000_000), (0x315, 50_000_000)) if alpha else ((0x180, 110_000_000),)
|
|
cp.alternativeExperience = 32
|
|
card = Car.__new__(Car)
|
|
card.CP = cp
|
|
card.CI = GMInterface(cp)
|
|
card.vehicle_startup = VehicleStartupOwner()
|
|
card.ci_initialized = False
|
|
card.initialized_prev = False
|
|
card.aol_replay = True
|
|
card.ioniq6_long_prearmed = False
|
|
card.ioniq6_long_selected = False
|
|
card.can_callbacks = (lambda wait_for_one=False: [], lambda _: None)
|
|
params = SimpleNamespace(put_bool=Mock())
|
|
self.enterContext(patch.object(card, 'params', params, create=True))
|
|
card.state_publish = Mock()
|
|
packer = CANPacker(DBC[cp.carFingerprint][Bus.pt])
|
|
cs, _ = feed_camera(card.CI, packer, 22_000_000_000, active=False)
|
|
self.assertTrue(cs.canValid)
|
|
counter = [1]
|
|
|
|
def state_update():
|
|
state, _ = feed_camera(card.CI, packer, boot, counter=counter[0] % 4, speed=0., active=False)
|
|
counter[0] += 1
|
|
self.assertTrue(state.canValid)
|
|
return state, None
|
|
|
|
self.enterContext(patch.object(card, 'state_update', state_update))
|
|
self.defer_radar_fixture(card)
|
|
control = structs.CarControl.new_message().as_reader()
|
|
panda = SimpleNamespace(safetyModel=cp.safetyConfigs[0].safetyModel, safetyParam=word,
|
|
alternativeExperience=cp.alternativeExperience, safetyRxChecksInvalid=True)
|
|
mono, boot = 20_000_000_000, 22_000_000_000
|
|
|
|
class SubMaster:
|
|
seen = dict.fromkeys(('carControl', 'pandaStates', 'onroadEvents'), True)
|
|
valid = dict.fromkeys(('carControl', 'pandaStates'), True)
|
|
alive = dict(valid)
|
|
logMonoTime = {'carControl': mono, 'pandaStates': boot}
|
|
recv_time = dict.fromkeys(('carControl', 'pandaStates'), mono / 1e9)
|
|
events = [SimpleNamespace(name=EventName.selfdriveInitializing)]
|
|
|
|
def __getitem__(self, key):
|
|
return {'carControl': control, 'pandaStates': [panda], 'onroadEvents': self.events}[key]
|
|
|
|
def all_alive(self, services):
|
|
return all(self.alive[name] for name in services)
|
|
|
|
sm = SubMaster()
|
|
self.enterContext(patch.object(card, 'sm', sm, create=True))
|
|
sent = []
|
|
timestamps = {}
|
|
envelopes = []
|
|
|
|
def capture_packet(service, packet):
|
|
self.assertEqual(service, 'sendcan')
|
|
event = messaging.log_from_bytes(packet)
|
|
self.assertTrue(event.valid)
|
|
self.assertEqual(event.logMonoTime, boot)
|
|
# Match pandad's unsigned BOOTTIME subtraction and strict one-second bound.
|
|
self.assertLess((boot - int(event.logMonoTime)) & ((1 << 64) - 1), 1_000_000_000)
|
|
envelopes.append(event.logMonoTime)
|
|
|
|
self.enterContext(patch.object(card, 'pm', SimpleNamespace(send=capture_packet), create=True))
|
|
|
|
def publish(frames, valid=True):
|
|
Car.publish_sendcan(card, frames, valid=valid)
|
|
self.assertTrue(valid)
|
|
sent.extend(frames)
|
|
for address, _, _ in frames:
|
|
timestamps.setdefault(address, []).append(boot)
|
|
|
|
self.enterContext(patch.object(card, 'publish_sendcan', publish))
|
|
# These saved requests cannot acquire startup brake ownership.
|
|
card.CI.CC.gm_auto_hold = True
|
|
card.CI.CC.volt_one_pedal = True
|
|
with patch('openpilot.selfdrive.car.card.time.monotonic', side_effect=lambda: mono / 1e9), \
|
|
patch('openpilot.selfdrive.car.card.time.monotonic_ns', side_effect=lambda: mono), \
|
|
patch('openpilot.selfdrive.car.card.time.clock_gettime_ns', side_effect=lambda _clock: boot), \
|
|
patch.object(card.CI, 'apply', wraps=card.CI.apply) as apply, \
|
|
patch.object(card.CI, 'init', wraps=card.CI.init) as init:
|
|
self.assertFalse(card.startup_panda_configured())
|
|
self.assertTrue(card.startup_panda_configured(inactive_keepalive=True))
|
|
for _ in range(601):
|
|
sm.logMonoTime.update(carControl=mono, pandaStates=boot)
|
|
sm.recv_time.update(carControl=mono / 1e9, pandaStates=mono / 1e9)
|
|
card.step()
|
|
mono += 10_000_000
|
|
boot += 10_000_000
|
|
sm.logMonoTime.update(carControl=mono, pandaStates=boot)
|
|
sm.recv_time.update(carControl=mono / 1e9, pandaStates=mono / 1e9)
|
|
self.assertEqual(apply.call_count, 601)
|
|
init.assert_called_once()
|
|
params.put_bool.assert_called_once_with('ControlsReady', True)
|
|
self.assertEqual(card.CI.CC.gm_auto_hold_state.drive_ns, 0)
|
|
self.assertEqual(card.CI.CC.apply_brake, 0)
|
|
for call in apply.call_args_list:
|
|
cc, timestamp = call.args
|
|
self.assertGreaterEqual(timestamp, 22_000_000_000)
|
|
self.assertLessEqual(timestamp, 28_000_000_000)
|
|
self.assertTrue(call.kwargs['startup_keepalive'])
|
|
self.assertFalse(cc.enabled or cc.latActive or cc.longActive)
|
|
self.assertEqual((cc.actuators.torque, cc.actuators.accel), (0., 0.))
|
|
for address, max_gap in cadence:
|
|
times = timestamps[address]
|
|
self.assertLessEqual(times[0], 22_100_000_000)
|
|
self.assertGreaterEqual(times[-1], 27_900_000_000)
|
|
self.assertLessEqual(max(b - a for a, b in zip(times, times[1:], strict=False)), max_gap)
|
|
self.assertTrue(sent)
|
|
if not alpha:
|
|
self.assertFalse(any(address in (0x2CB, 0x315) for address, _, _ in sent))
|
|
self.assertTrue(card.CP.pcmCruise)
|
|
self.assertFalse(card.CP.openpilotLongitudinalControl)
|
|
for address, data, _ in sent:
|
|
if address == 0x180:
|
|
self.assertEqual(data[0] & 15, 0)
|
|
self.assertEqual(data[1], 0)
|
|
elif address == 0x315:
|
|
self.assertEqual(((data[0] & 15) << 8) | data[1], 0)
|
|
elif address == 0x2CB:
|
|
self.assertEqual(data[0] & 1, 0)
|
|
self.assertEqual(data[1] & 0x20, 0)
|
|
self.assertEqual((((data[1] & 7) << 16) | (data[2] << 8) | data[3]) * .125 - 22534, -650)
|
|
panda.safetyParam = word ^ 1
|
|
card.step()
|
|
self.assertEqual(apply.call_count, 601)
|
|
panda.safetyParam = word
|
|
sm.logMonoTime['pandaStates'] = boot - 300_000_001
|
|
card.step()
|
|
self.assertEqual(apply.call_count, 601)
|
|
sm.logMonoTime['pandaStates'] = boot
|
|
sm.valid['carControl'] = False
|
|
card.step()
|
|
self.assertEqual(apply.call_count, 601)
|
|
sm.valid['carControl'] = True
|
|
sm.logMonoTime['carControl'] = mono - 150_000_001
|
|
card.step()
|
|
self.assertEqual(apply.call_count, 601)
|
|
sm.logMonoTime['carControl'] = mono
|
|
active = structs.CarControl.new_message(latActive=True)
|
|
control = active.as_reader()
|
|
card.step()
|
|
self.assertEqual(apply.call_count, 601)
|
|
control = structs.CarControl.new_message().as_reader()
|
|
sm.events = []
|
|
# The physical CAN clock remains continuous after inactive startup.
|
|
for _ in range(10):
|
|
sm.logMonoTime.update(carControl=mono, pandaStates=boot)
|
|
sm.recv_time.update(carControl=mono / 1e9, pandaStates=mono / 1e9)
|
|
card.step()
|
|
self.assertEqual(apply.call_args.args[1], boot)
|
|
self.assertFalse(apply.call_args.kwargs)
|
|
mono += 10_000_000
|
|
boot += 10_000_000
|
|
self.assertEqual(apply.call_count, 611)
|
|
self.assertEqual(len(envelopes), 611)
|
|
self.assertEqual(envelopes, [call.args[1] for call in apply.call_args_list])
|
|
self.assertGreaterEqual((boot - mono) & ((1 << 64) - 1), 1_000_000_000)
|
|
for before, after in zip(apply.call_args_list, apply.call_args_list[1:], strict=False):
|
|
self.assertEqual(after.args[1] - before.args[1], 10_000_000)
|
|
for address, max_gap in cadence:
|
|
times = timestamps[address]
|
|
self.assertGreaterEqual(times[-1], 28_060_000_000)
|
|
self.assertLessEqual(max(b - a for a, b in zip(times, times[1:], strict=False)), max_gap)
|
|
|
|
|
|
def test_volt_startup_selector_denies_other_profiles_and_permissions(self):
|
|
from opendbc.car.gm.tests.test_ascm_intercept import params as ascm_params
|
|
from opendbc.car.gm.tests.test_volt_camera_control import camera_params
|
|
card = Car.__new__(Car)
|
|
self.assertFalse(card.volt_startup_keepalive())
|
|
self.assertFalse(card.volt_sdgm_startup_keepalive())
|
|
for cp in (None, SimpleNamespace(safetyConfigs=[]), SimpleNamespace(brand='hyundai'),
|
|
SimpleNamespace(brand='toyota', alternativeExperience=32, flags=0,
|
|
carFingerprint=GMCar.CHEVROLET_VOLT_ASCM)):
|
|
with self.subTest(cp=cp):
|
|
card.CP = cp
|
|
self.assertFalse(card.volt_startup_keepalive())
|
|
self.assertFalse(card.volt_sdgm_startup_keepalive())
|
|
for radar in (False, True):
|
|
for accelerator in (False, True):
|
|
cp = ascm_params(GMCar.CHEVROLET_VOLT_ASCM, sascm=True, alpha=True,
|
|
accelerator=accelerator, radar=radar)
|
|
cp.alternativeExperience = 32
|
|
card.CP = cp
|
|
self.assertTrue(card.volt_startup_keepalive())
|
|
for field, value in (('alternativeExperience', 0), ('openpilotLongitudinalControl', False),
|
|
('pcmCruise', True), ('passive', True), ('dashcamOnly', True),
|
|
('flags', int(cp.flags) | int(GMFlags.PEDAL_LONG))):
|
|
saved = getattr(cp, field)
|
|
setattr(cp, field, value)
|
|
self.assertFalse(card.volt_startup_keepalive())
|
|
setattr(cp, field, saved)
|
|
for radar in (False, True):
|
|
for accelerator in (False, True):
|
|
cp = ascm_params(GMCar.CHEVROLET_VOLT_ASCM, sascm=True, alpha=False,
|
|
accelerator=accelerator, radar=radar)
|
|
cp.alternativeExperience = 32
|
|
card.CP = cp
|
|
self.assertTrue(card.volt_startup_keepalive())
|
|
for field, value in (('alternativeExperience', 0), ('openpilotLongitudinalControl', True),
|
|
('pcmCruise', False), ('passive', True), ('dashcamOnly', True),
|
|
('radarUnavailable', not cp.radarUnavailable),
|
|
('flags', int(cp.flags) | int(GMFlags.PEDAL_LONG))):
|
|
saved = getattr(cp, field)
|
|
setattr(cp, field, value)
|
|
self.assertFalse(card.volt_startup_keepalive())
|
|
setattr(cp, field, saved)
|
|
word = cp.safetyConfigs[0].safetyParam
|
|
cp.safetyConfigs[0].safetyParam = word | 0x4000
|
|
self.assertFalse(card.volt_startup_keepalive())
|
|
cp.safetyConfigs[0].safetyParam = word
|
|
cc = GMInterface.get_params(GMCar.CHEVROLET_VOLT_CC, gen_empty_fingerprint(), [], True, False, False)
|
|
for cp in (cc, camera_params(), GMInterface.get_params(GMCar.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
|
gen_empty_fingerprint(), [], True, False, False)):
|
|
cp.alternativeExperience = 32
|
|
card.CP = cp
|
|
self.assertFalse(card.volt_startup_keepalive())
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|