From 8ff493640d728f01d6e2ed73b43c56b090f80efb Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Mon, 2 Nov 2026 07:57:38 -0600 Subject: [PATCH] Preserve HKG AOL through temporary EPS during driver override --- openpilot/selfdrive/selfdrived/selfdrived.py | 6 +- .../tests/test_temporary_steering_sequence.py | 59 +++++++++++++++++++ prebuilt.json | 2 +- 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/openpilot/selfdrive/selfdrived/selfdrived.py b/openpilot/selfdrive/selfdrived/selfdrived.py index 7e672adf4b..fa20ded83d 100755 --- a/openpilot/selfdrive/selfdrived/selfdrived.py +++ b/openpilot/selfdrive/selfdrived/selfdrived.py @@ -2183,11 +2183,11 @@ class SelfdriveD: # Lateral-only operation keeps ordinary longitudinal faults visible. self.aol_soft_takeover_alert = True if (self.aol_replay and intent is not None and intent.lateralArmed and - CS.steerFaultTemporary and not CS.steerFaultPermanent and not CS.steeringPressed): + CS.steerFaultTemporary and not CS.steerFaultPermanent and (not CS.steeringPressed or self.CP.brand == 'hyundai')): # An armed AOL session still needs the ordinary temporary-steering warning # while actual steering is suspended and the stock state machine is off. - # Like CarSpecificEvents, skip it while the driver is steering: a hard - # override alone puts Honda EPS in NO_TORQUE_ALERT_1. + # HKG needs the event to retain intent through temporary EPS withdrawal. + # Other owners keep CarSpecificEvents' driver-override warning policy. if EventName.steerTempUnavailableSilent not in self.events.names: self.events.add(EventName.steerTempUnavailableSilent) if ET.WARNING not in self.state_machine.current_alert_types: diff --git a/openpilot/starpilot/aol/tests/test_temporary_steering_sequence.py b/openpilot/starpilot/aol/tests/test_temporary_steering_sequence.py index ae4d49fbe2..55caf85069 100644 --- a/openpilot/starpilot/aol/tests/test_temporary_steering_sequence.py +++ b/openpilot/starpilot/aol/tests/test_temporary_steering_sequence.py @@ -345,3 +345,62 @@ def test_axis_first_eps_join_uses_last_accepted_active_proof_without_renewing_le assert leases(feedback) == original assert receive(now + 100_000, event=event, state=temporary) assert card.aol_card_intent.allowed_latch and leases(feedback) == original + + +@pytest.mark.parametrize('profile', ('g70', 'sonata', 'ev6', 'mixed_hdai', 'mixed_hdaii')) +@pytest.mark.parametrize('fault', ('none', 'permanent_eps', 'invalid_can', 'invalid_native', 'relay_fault')) +def test_hkg_steering_override_eps_then_brake_retains_only_valid_intent(profile, fault): + from opendbc.car.hyundai.values import CAR + from openpilot.common.params import Params + from openpilot.starpilot.aol.tests.test_classic_scc_profiles import params as classic_params + from openpilot.starpilot.aol.tests.test_canfd_stock_profiles import params as canfd_params + from opendbc.car.hyundai.tests.test_palisade_2023 import params as mixed_params + + case = setup_case() + with ExitStack() as cleanup: + cleanup.callback(case.doCleanups) + cleanup.enter_context(OpenpilotPrefix()) + Params().put('MainCruiseButtonControl', 9, block=True) + if profile.startswith('mixed_'): + cp = mixed_params(profile.removeprefix('mixed_')) + elif profile == 'ev6': + cp = canfd_params(CAR.KIA_EV6) + else: + cp = classic_params(CAR.GENESIS_G70 if profile == 'g70' else CAR.HYUNDAI_SONATA) + sd, _, drive, card, receive = case._ioniq_availability_callers(False, cp=cp, initially_armed=False) + arm = fixture.car_state() + arm.cruiseState.available = True + arm.buttonEvents = [car.CarState.ButtonEvent(type='mainCruise', pressed=True)] + card.aol_card_intent.update(arm, now_ns=10_015_000_000) + assert card.aol_card_intent.allowed_latch + for tick in range(2, 7): + axis, _, command = drive(tick, steering=True, native_request_long=False) + assert axis.lateralActive and command.latActive + assert receive(10_000_000_000 + tick * 10_000_000, + axis=sd.pm.messages['aolAxisState'], event=sd.pm.messages['onroadEvents']) + temporary = fixture.car_state() + temporary.steerFaultTemporary = temporary.steeringPressed = True + if fault == 'permanent_eps': + temporary.steerFaultPermanent = True + elif fault == 'invalid_can': + temporary.canValid = False + for tick in range(7, 21): + temporary.brakePressed = tick >= 16 + axis, state, command = drive(tick, steering=True, brake=temporary.brakePressed, observed_state=temporary, + native_request_lat=tick == 7, allowed=tick == 7, native_request_long=False, + native_valid=fault != 'invalid_native', relay_fault=fault == 'relay_fault') + assert not axis.lateralActive and not command.latActive and not command.longActive + retained = receive(10_000_000_000 + tick * 10_000_000, + axis=sd.pm.messages['aolAxisState'], event=sd.pm.messages['onroadEvents'], state=temporary) + if fault != 'none': + assert not retained and not card.aol_card_intent.allowed_latch + break + assert retained and card.aol_card_intent.allowed_latch + assert state.alertType == 'steerTempUnavailableSilent/warning' + assert not state.enabled and not state.active + assert axis.validUntilMonoTime - axis.observedMonoTime == 30_000_000 + if fault == 'none': + axis, _, command = drive(21, steering=True, brake=True, native_request_lat=True, native_request_long=False) + assert axis.lateralActive and command.latActive and not command.longActive + assert receive(10_210_000_000, axis=sd.pm.messages['aolAxisState'], event=sd.pm.messages['onroadEvents']) + assert card.aol_card_intent.allowed_latch diff --git a/prebuilt.json b/prebuilt.json index 1a773574b2..17f4f44908 100644 --- a/prebuilt.json +++ b/prebuilt.json @@ -1 +1 @@ -{"protected_inventory_sha256": "7e75818ebebdd00f375c45016a48613f31cd7a9bf7794aba62fba5108d6a37bf", "schema_version": 1} +{"protected_inventory_sha256": "2c450b2ddd0952f89c7bc6666fb3f3e5636c013ba1c381acfe65913d63d8ad48", "schema_version": 1}