Align HKG location regressions with shared armed intent policy

This commit is contained in:
firestar5683
2026-10-23 02:28:18 -05:00
parent d049218089
commit 1ec8ca7abb
5 changed files with 57 additions and 17 deletions
@@ -23,7 +23,7 @@ from openpilot.starpilot.aol.runtime import current_native
from openpilot.starpilot.aol.tests import test_ioniq_availability as caller_fixture
from openpilot.starpilot.aol.tests.test_classic_location_stock_intent import host_heartbeat_bridge as host_heartbeat_bridge
from openpilot.starpilot.aol.tests.test_classic_location_stock_intent import (
assert_classic_location_independent_refusals as assert_stock_location_refusals,
assert_stock_location_warning_contract as assert_stock_location_refusals,
)
from openpilot.starpilot.aol.wire import PandaSlot, SafetyState, encode_safety
@@ -94,7 +94,7 @@ def _invalid_controls_emits_no_commands(received):
def assert_classic_location_independent_refusals(sd, card, state, now_ns):
def assert_stock_location_warning_contract(sd, card, state, now_ns):
from copy import copy
from openpilot.starpilot.aol.wire import decode_safety
@@ -178,10 +178,22 @@ def assert_classic_location_independent_refusals(sd, card, state, now_ns):
view.values['pandaStates'] = panda.as_reader().pandaStates
native = dataclasses.replace(native, pandaInventory=tuple(inventory))
view.values['aolSafetyWire'] = encode_safety(native)
# Production dispatches location SOFT withdrawals through observe() and
# the shared warning owner, rather than the older critical-only helper.
view.values['aolAxisState'] = proof.as_reader()
view.logMonoTime['aolAxisState'] = stamp
before = (current.lateral_baseline_ns, current.lateral_native_ns)
retained = current._temporary_host_intent_current(view, card.CP, cs, proof, stamp, now_ns,
latched=card.aol_card_intent.allowed_latch, producer=feedback.availability_producer)
assert retained == (fault == 'none'), fault
retained = not current.observe(view, card.CP, cs, now_ns,
latched=card.aol_card_intent.allowed_latch, intent_producer_session=feedback.availability_producer)
# Pedals, belt and temporary EPS conditions preserve only armed intent.
# A denied native LAT receipt while both host axes are withdrawn is also
# a pause; it supplies no native permission and never resumes commands.
# Classic independent AOL does not require the cruise MAIN level. This
# is not a new MAIN button edge and cannot replay the configured toggle.
expected = fault in ('none', 'seatbelt', 'brake', 'regen', 'gas', 'cancel', 'temporary_eps', 'native_denial') or (
fault == 'main_off' and qualified_classic(card.CP, marked_only=True))
assert retained == expected, (fault, current.__dict__)
assert not original.aolAxisState.lateralActive and not original.aolAxisState.longitudinalActive
assert (current.lateral_baseline_ns, current.lateral_native_ns) == before
@@ -633,7 +645,7 @@ def test_real_classic_stock_engagement_then_location_loss_keeps_intent(raw_fault
continue
fault_ticks += 1
if fault_ticks == 1:
assert_classic_location_independent_refusals(sd, card, state, now[0])
assert_stock_location_warning_contract(sd, card, state, now[0])
assert log.OnroadEvent.EventName.pcmDisable in sd.events.names
assert not axis.lateralActive and not axis.longitudinalActive
assert not command.latActive and not command.longActive
@@ -81,7 +81,7 @@ def test_stock_pcm_location_input_pause_retains_intent_without_output(identity,
@pytest.mark.parametrize('identity', [CAR.HYUNDAI_IONIQ_5_PE, CAR.KIA_EV9])
@pytest.mark.parametrize('fault', ['native_denial', 'native_invalid', 'panda_rx', 'relay', 'permanent_eps',
'invalid_can', 'can_timeout', 'door', 'seatbelt', 'brake', 'regen', 'physical_cancel', 'reverse', 'unknown_gear',
'extra_user', 'hard_event', 'epoch', 'session', 'expired', 'future_source', 'no_baseline'])
'extra_user', 'hard_event', 'epoch', 'session', 'expired', 'future_source', 'no_baseline', 'configured_cancel'])
def test_stock_location_input_retention_refuses_independent_faults(identity, fault):
case = fixture.IoniqAvailabilityTests(methodName='runTest')
with ExitStack() as cleanup:
@@ -107,7 +107,10 @@ def test_stock_location_input_retention_refuses_independent_faults(identity, fau
state.seatbeltUnlatched = fault == 'seatbelt'
state.brakePressed = fault == 'brake'
state.regenBraking = fault == 'regen'
if fault == 'physical_cancel':
if fault == 'configured_cancel':
from dataclasses import replace
card.aol_card_intent.settings = replace(card.aol_card_intent.settings, cancel_actions=(9, 0, 0))
if fault in ('physical_cancel', 'configured_cancel'):
state.buttonEvents = [structs.CarState.ButtonEvent(type='cancel', pressed=True)]
if fault in ('reverse', 'unknown_gear'):
state.gearShifter = 'reverse' if fault == 'reverse' else 'unknown'
@@ -124,7 +127,18 @@ def test_stock_location_input_retention_refuses_independent_faults(identity, fau
if fault == 'future_source':
proof.aolAxisState.sourceCarStateMonoTime = 10_030_000_000
if fault == 'no_baseline':
from openpilot.starpilot.aol.transport_pause import TransportPauseFeedback
card.aol_transport_feedback = TransportPauseFeedback(card.CP)
assert not receive(10_020_000_000, state=state, axis=proof.as_reader(), event=event.as_reader())
assert not card.aol_card_intent.allowed_latch
# Remove the established baseline without erasing its session/history.
card.aol_transport_feedback.lateral_baseline_ns = 0
# A physical pause is independent of the location SOFT warning. Current
# native proof and zero host commands remain mandatory; unassigned CANCEL
# preserves intent, whereas the explicitly configured CANCEL still clears.
baseline = (card.aol_transport_feedback.lateral_baseline_ns, card.aol_transport_feedback.lateral_native_ns)
retained = receive(10_020_000_000, state=state, axis=proof.as_reader(), event=event.as_reader())
expected = fault in ('seatbelt', 'brake', 'regen')
assert retained == expected, fault
assert card.aol_card_intent.allowed_latch == expected
assert not axis.lateralActive and not axis.longitudinalActive
assert not command.latActive and not command.longActive
if expected:
assert baseline == (card.aol_transport_feedback.lateral_baseline_ns, card.aol_transport_feedback.lateral_native_ns)
@@ -93,7 +93,7 @@ def test_existing_stock_hkg_location_and_nominal_pcm_off_keep_established_intent
@pytest.mark.parametrize('family', ['canfd_radar', 'canfd_lka', 'canfd_lka_alt', 'canfd_camera', 'canfd_ccnc_camera'])
@pytest.mark.parametrize('fault', ['native_denial', 'native_invalid', 'panda_rx', 'relay', 'permanent_eps',
'invalid_can', 'can_timeout', 'door', 'seatbelt', 'brake', 'regen', 'gas', 'physical_cancel', 'reverse', 'unknown_gear',
'extra_user', 'hard_event', 'epoch', 'session', 'expired', 'future_source', 'no_baseline'])
'extra_user', 'hard_event', 'epoch', 'session', 'expired', 'future_source', 'no_baseline', 'configured_cancel'])
def test_stock_location_never_retains_intent_through_independent_fault(family, fault):
from opendbc.car import structs
@@ -134,7 +134,10 @@ def test_stock_location_never_retains_intent_through_independent_fault(family, f
state.brakePressed = fault == 'brake'
state.regenBraking = fault == 'regen'
state.gasPressed = fault == 'gas'
if fault == 'physical_cancel':
if fault == 'configured_cancel':
from dataclasses import replace
card.aol_card_intent.settings = replace(card.aol_card_intent.settings, cancel_actions=(9, 0, 0))
if fault in ('physical_cancel', 'configured_cancel'):
state.buttonEvents = [structs.CarState.ButtonEvent(type='cancel', pressed=True)]
if fault in ('reverse', 'unknown_gear'):
state.gearShifter = 'reverse' if fault == 'reverse' else 'unknown'
@@ -152,5 +155,16 @@ def test_stock_location_never_retains_intent_through_independent_fault(family, f
proof.aolAxisState.sourceCarStateMonoTime = 10_060_000_000
if fault == 'no_baseline':
card.aol_transport_feedback.lateral_baseline_ns = 0
assert not receive(10_050_000_001, state=state, axis=proof.as_reader(), event=event.as_reader())
assert not card.aol_card_intent.allowed_latch
# A physical pause is independent of the location SOFT warning. Current
# native proof and zero host commands remain mandatory; unassigned CANCEL
# preserves intent, whereas the explicitly configured CANCEL still clears.
baseline = (card.aol_transport_feedback.lateral_baseline_ns, card.aol_transport_feedback.lateral_native_ns)
retained = receive(10_050_000_001, state=state, axis=proof.as_reader(), event=event.as_reader())
expected = fault in ('seatbelt', 'brake', 'regen', 'gas', 'physical_cancel')
assert retained == expected, fault
assert card.aol_card_intent.allowed_latch == expected
assert not axis.lateralActive and not axis.longitudinalActive
assert not command.latActive and not command.longActive
if expected:
assert baseline == (card.aol_transport_feedback.lateral_baseline_ns, card.aol_transport_feedback.lateral_native_ns)
+1 -1
View File
@@ -1 +1 @@
{"protected_inventory_sha256": "3a3c366dd4876659aa63861b1316f18d9db29ddd42b8b63f66b660f6faa97f77", "schema_version": 1}
{"protected_inventory_sha256": "a8cf3e523e217d01bce30adf58c7478a7ca9a4216a754d6729da51111304172a", "schema_version": 1}