mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 05:23:41 +08:00
126 lines
6.8 KiB
Python
126 lines
6.8 KiB
Python
"""Expired command proofs may retain an existing intent, never native authority."""
|
|
from dataclasses import replace
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
from opendbc.car import structs
|
|
from opendbc.car.chrysler.values import CAR as Chrysler
|
|
from opendbc.car.ford.values import CAR as Ford
|
|
from opendbc.car.hyundai.values import CAR as Hyundai
|
|
from opendbc.safety.tests.test_ioniq6_native_pedal_order import profile
|
|
from openpilot.starpilot.aol.tests import test_stock_location_recovery as stock
|
|
from openpilot.starpilot.aol.runtime import decide_axes
|
|
from openpilot.starpilot.aol.transport_pause import TransportPauseFeedback
|
|
from openpilot.starpilot.tests.test_ford_reverse_intent import NOW, PRODUCER, change, native_change, sample
|
|
|
|
|
|
def exact_sample(cp, source, observed, sequence, **kwargs):
|
|
sm, native = sample(cp, source, observed, sequence, **kwargs)
|
|
if cp.alternativeExperience != 32:
|
|
native = native_change(sm, native, pandaInventory=(replace(native.pandaInventory[0], alternativeExperience=cp.alternativeExperience),))
|
|
change(sm, 'pandaStates', lambda pandas: setattr(pandas[0], 'alternativeExperience', cp.alternativeExperience))
|
|
return sm, native
|
|
|
|
|
|
@pytest.fixture(params=['ioniq6_std', 'ioniq6_alt', Chrysler.CHRYSLER_PACIFICA_2018, Chrysler.RAM_1500_5TH_GEN,
|
|
Ford.FORD_MUSTANG_MACH_E_MK1, Ford.FORD_ESCAPE_MK4, Hyundai.HYUNDAI_SONATA_HYBRID])
|
|
def established(request):
|
|
if request.param not in ('ioniq6_std', 'ioniq6_alt'):
|
|
return stock.established_stock.__wrapped__(request)
|
|
cp = profile(request.param == 'ioniq6_alt')
|
|
state = structs.CarState(canValid=True, gearShifter='drive', vEgo=15.)
|
|
state.cruiseState.available = True
|
|
feedback = TransportPauseFeedback(cp)
|
|
sm, _ = exact_sample(cp, NOW, NOW, 1)
|
|
assert not feedback.observe(sm, cp, state, NOW, latched=True, intent_producer_session=PRODUCER)
|
|
assert feedback.lateral_only_baseline
|
|
return cp, state, feedback
|
|
|
|
|
|
def clocks(feedback):
|
|
return (feedback.healthy_feedback_ns, feedback.healthy_native_ns, feedback.lateral_baseline_ns,
|
|
feedback.lateral_source_ns, feedback.lateral_native_ns)
|
|
|
|
|
|
def test_zero_request_retains_only_prior_intent_until_original_source_bound(established):
|
|
cp, state, feedback = established
|
|
original = clocks(feedback)
|
|
for age in (40_000_000, 190_000_000):
|
|
now = NOW + age
|
|
sm, native = exact_sample(cp, NOW, now, 1, requested=False, allowed=False)
|
|
assert not feedback.observe(sm, cp, state, now, latched=True, intent_producer_session=PRODUCER)
|
|
assert clocks(feedback) == original
|
|
assert feedback.source_retention_started_ns in (0, NOW + 40_000_000)
|
|
assert sm['aolAxisState'].validUntilMonoTime == NOW + 30_000_000 < now
|
|
axes = decide_axes(standard_lateral=False, standard_longitudinal=False,
|
|
intent=SimpleNamespace(allowedLatch=True, pauseLateral=False, pauseLongitudinal=False),
|
|
native=native, car_state=state, initialized=True, model_ready=True, no_entry=False,
|
|
immediate_disable=False, dm_lockout=False, pause_brake_mps=0.)
|
|
assert not axes.lateral_active and not axes.longitudinal_active
|
|
now = NOW + 200_000_001
|
|
sm, _ = exact_sample(cp, NOW, now, 1, requested=False, allowed=False)
|
|
assert feedback.observe(sm, cp, state, now, latched=True, intent_producer_session=PRODUCER)
|
|
assert clocks(feedback) == original
|
|
|
|
|
|
@pytest.mark.parametrize('fault', ['invalid_can', 'timeout', 'permanent_eps', 'temporary_eps', 'requested_denied',
|
|
'unexpected_long', 'native_grant', 'native_invalid', 'native_expired', 'native_future', 'native_old',
|
|
'native_serial', 'native_fault', 'native_rx', 'native_heartbeat', 'wrong_session', 'wrong_epoch',
|
|
'wrong_producer', 'cp_changed', 'no_baseline', 'immediate', 'other_soft'])
|
|
def test_zero_request_keeps_all_existing_source_and_native_vetoes(established, fault):
|
|
cp, state, feedback = established
|
|
now = NOW + 40_000_000
|
|
sm, native = exact_sample(cp, NOW, now, 1, requested=False, allowed=False)
|
|
producer = PRODUCER
|
|
if fault in ('invalid_can', 'timeout', 'permanent_eps', 'temporary_eps'):
|
|
field = {'invalid_can': 'canValid', 'timeout': 'canTimeout', 'permanent_eps': 'steerFaultPermanent', 'temporary_eps': 'steerFaultTemporary'}[fault]
|
|
setattr(state, field, fault != 'invalid_can')
|
|
elif fault == 'requested_denied':
|
|
native_change(sm, native, requestedLateral=True)
|
|
elif fault == 'unexpected_long':
|
|
native_change(sm, native, requestedLongitudinal=True)
|
|
elif fault == 'native_grant':
|
|
native_change(sm, native, lateralAllowed=True)
|
|
elif fault == 'native_invalid':
|
|
sm.valid['aolSafetyWire'] = False
|
|
elif fault == 'native_expired':
|
|
native_change(sm, native, validUntilMonoTime=now - 1)
|
|
elif fault == 'native_future':
|
|
native_change(sm, native, observedMonoTime=now + 1)
|
|
elif fault == 'native_old':
|
|
native_change(sm, native, observedMonoTime=NOW - 1)
|
|
elif fault == 'native_serial':
|
|
native_change(sm, native, pandaSerial='other', pandaInventory=(replace(native.pandaInventory[0], hardwareSerial='other'),))
|
|
elif fault in ('native_fault', 'native_rx', 'native_heartbeat'):
|
|
field = {'native_fault': 'faults', 'native_rx': 'safetyRxChecksInvalid', 'native_heartbeat': 'heartbeatLost'}[fault]
|
|
native_change(sm, native, pandaInventory=(replace(native.pandaInventory[0], **{field: 1 if field == 'faults' else True}),))
|
|
elif fault == 'wrong_session':
|
|
change(sm, 'aolAxisState', lambda axis: setattr(axis, 'sessionId', 'other'))
|
|
elif fault == 'wrong_epoch':
|
|
change(sm, 'aolAxisState', lambda axis: setattr(axis, 'intentIntegrityEpoch', 2))
|
|
elif fault == 'wrong_producer':
|
|
producer = 'other'
|
|
elif fault == 'cp_changed':
|
|
cp.flags ^= 1
|
|
elif fault == 'no_baseline':
|
|
feedback.lateral_baseline_ns = 0
|
|
elif fault in ('immediate', 'other_soft'):
|
|
event = sm.messages['onroadEvents'].as_builder()
|
|
event.onroadEvents = ([{'name': 'relayMalfunction', 'immediateDisable': True}] if fault == 'immediate' else
|
|
[{'name': 'lowMemory', 'softDisable': True}])
|
|
sm.messages['onroadEvents'] = event.as_reader()
|
|
assert feedback.observe(sm, cp, state, now, latched=True, intent_producer_session=producer)
|
|
|
|
|
|
def test_an_explicit_off_intent_does_not_enter_retention(established):
|
|
cp, state, feedback = established
|
|
now = NOW + 40_000_000
|
|
sm, native = exact_sample(cp, NOW, now, 1, requested=False, allowed=False)
|
|
assert not feedback.observe(sm, cp, state, now, latched=False, intent_producer_session=PRODUCER)
|
|
assert not feedback.source_retention_started_ns
|
|
axes = decide_axes(standard_lateral=False, standard_longitudinal=False,
|
|
intent=SimpleNamespace(allowedLatch=False, pauseLateral=False, pauseLongitudinal=False),
|
|
native=native, car_state=state, initialized=True, model_ready=True, no_entry=False,
|
|
immediate_disable=False, dm_lockout=False, pause_brake_mps=0.)
|
|
assert not axes.desired_lateral and not axes.lateral_active and not axes.longitudinal_active
|