diff --git a/openpilot/selfdrive/selfdrived/selfdrived.py b/openpilot/selfdrive/selfdrived/selfdrived.py index 789dd6f5f8..64ff6bd0f8 100755 --- a/openpilot/selfdrive/selfdrived/selfdrived.py +++ b/openpilot/selfdrive/selfdrived/selfdrived.py @@ -1391,6 +1391,10 @@ class SelfdriveD: not self.events.contains(ET.IMMEDIATE_DISABLE) and (not decision.longitudinal_active or self.enabled and self.active)) user_disable = self.events.contains(ET.USER_DISABLE) + if (user_disable and getattr(self, 'nostalgia_paddle_cancel', False) and fresh_successor and + decision.lateral_active and not decision.longitudinal_active and not self.enabled and not self.active and + intent.allowedLatch and intent.lateralArmed): + user_disable = any(event.userDisable and event.name != EventName.buttonCancel for event in self.events.to_msg()) if user_disable: from opendbc.car.hyundai.canfd_angle_aol import qualified as angle_qualified from opendbc.car.hyundai.canfd_stock_aol import qualified as stock_canfd_qualified diff --git a/openpilot/starpilot/aol/tests/test_hkg_cancel_joined.py b/openpilot/starpilot/aol/tests/test_hkg_cancel_joined.py index 3988379b33..430dd9d1bc 100644 --- a/openpilot/starpilot/aol/tests/test_hkg_cancel_joined.py +++ b/openpilot/starpilot/aol/tests/test_hkg_cancel_joined.py @@ -620,3 +620,73 @@ def test_gv70_single_panda_long_cancel_neighbor(withdrawal, monkeypatch): (CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN, False), (CAR.KIA_EV6, True)]) def test_default_aol_actual_cancel_join(identity, alternate, monkeypatch): _exercise_join(identity, alternate, 'cancel', 'toggle-off', monkeypatch, unmapped=True) + + +@pytest.mark.parametrize('family,alternate', [('ioniq6', False), ('ioniq6', True), ('ioniq5', False)]) +@pytest.mark.parametrize('expired', [False, True]) +def test_nostalgia_paddle_cancel_keeps_fresh_lateral_after_old_proof_expiry(family, alternate, expired): + from openpilot.selfdrive.car.tests.test_hyundai_aol import candidate + from openpilot.starpilot.aol.tests import test_ioniq_availability as fixture + from openpilot.starpilot.tests.test_nostalgia import _ioniq5_params + + with OpenpilotPrefix(), patch.dict(os.environ, {'SIMULATION': '0', 'AOL_REPLAY_RUNTIME': '1'}): + Params().put_bool('NostalgiaMode', True, block=True) + case = fixture.IoniqAvailabilityTests(methodName='runTest') + try: + cp = candidate(alternate)[1] if family == 'ioniq6' else _ioniq5_params(alternate=alternate) + if family == 'ioniq6': + cp.safetyConfigs[-1].safetyParam |= 0x0800 + sd, _, step, card, receive = case._ioniq_availability_callers(True, cp=cp) + else: + sd, _, step = case._authority_callers(True, cp=cp, genuine_events=True) + for tick in (0, 1): + axis, _, command = step(tick) + assert axis.lateralActive and axis.longitudinalActive and command.longActive + sd.aol_settings = read_settings(Params()) + prior = sd.aol_published_input_proof + observed = fixture.car_state() + observed.vEgo = observed.vEgoRaw = .75 + observed.buttonEvents = [structs.CarState.ButtonEvent(type='altButton2', pressed=True)] + tick, offset = (4, 1_516_362) if expired else (2, 0) + now = 10_000_000_000 + tick * 10_000_000 + offset + assert (now > prior[3]) == expired + axis, state, command = step(tick, offset_ns=offset, observed_state=observed, native_request_long=True) + assert sd.nostalgia_paddle_cancel + assert not state.enabled and not command.longActive and not axis.longitudinalActive + assert axis.lateralActive and command.latActive and str(axis.faultReason) == 'none' + assert axis.sourceCarStateMonoTime == now and axis.validUntilMonoTime == now + 30_000_000 + assert 'controlsMismatch' not in [str(event) for event in sd.events.names] + if family == 'ioniq6': + assert receive(now, state=observed, axis=sd.pm.messages['aolAxisState'], event=sd.pm.messages['onroadEvents']) + assert card.aol_card_intent.allowed_latch + axis, state, command = step(tick + 1, offset_ns=offset, native_request_long=False, long_allowed=False) + assert axis.lateralActive and command.latActive and not command.longActive and not state.enabled + finally: + case.doCleanups() + + +@pytest.mark.parametrize('fault', ['native_denied', 'native_invalid', 'can_invalid', 'permanent_eps']) +def test_nostalgia_paddle_expiry_cannot_restore_invalid_lateral(fault): + from openpilot.starpilot.aol.tests import test_ioniq_availability as fixture + + with OpenpilotPrefix(), patch.dict(os.environ, {'SIMULATION': '0', 'AOL_REPLAY_RUNTIME': '1'}): + Params().put_bool('NostalgiaMode', True, block=True) + case = fixture.IoniqAvailabilityTests(methodName='runTest') + try: + sd, _, step, _, _ = case._ioniq_availability_callers(True) + sd.aol_settings = read_settings(Params()) + observed = fixture.car_state() + observed.buttonEvents = [structs.CarState.ButtonEvent(type='altButton2', pressed=True)] + kwargs = {} + if fault == 'native_denied': + kwargs['allowed'] = False + elif fault == 'native_invalid': + kwargs['native_valid'] = False + elif fault == 'can_invalid': + observed.canValid = False + else: + observed.steerFaultPermanent = True + axis, _, command = step(5, observed_state=observed, **kwargs) + assert not axis.lateralActive and not command.latActive + finally: + case.doCleanups() diff --git a/prebuilt.json b/prebuilt.json index 7eb189fb8a..3a8bef29ea 100644 --- a/prebuilt.json +++ b/prebuilt.json @@ -1 +1 @@ -{"protected_inventory_sha256": "ae261c44ebe70588f8c89085c1c2ae27b086b0ac273aa31e93261378ed7c2125", "schema_version": 1} +{"protected_inventory_sha256": "abf414e89e892227c9c235b8dd5a7c8891726459b52617cd571428cfbafb3b38", "schema_version": 1}