mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 13:33:42 +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>
113 lines
6.3 KiB
Python
113 lines
6.3 KiB
Python
"""Exact Ioniq admission avoids unrelated profiles, without caching authority."""
|
|
import unittest
|
|
from contextlib import ExitStack
|
|
from dataclasses import replace
|
|
from unittest import mock
|
|
|
|
from openpilot.cereal import messaging
|
|
from openpilot.selfdrive.car.tests.test_hyundai_aol import candidate
|
|
from openpilot.starpilot.aol.runtime import current_native
|
|
from openpilot.starpilot.aol.vehicle import native_matches_cp
|
|
from openpilot.starpilot.aol.wire import PandaSlot, SafetyState, encode_safety
|
|
from openpilot.starpilot.car.hyundai import aol
|
|
from opendbc.car.hyundai.values import HyundaiFlags
|
|
from opendbc.car.structs import car
|
|
|
|
|
|
class TestIoniqNativeDispatch(unittest.TestCase):
|
|
def profiles(self):
|
|
for alternate in (False, True):
|
|
for cp in candidate(alternate):
|
|
cp.safetyConfigs[0].safetyParam |= 0x0800
|
|
yield cp
|
|
|
|
def test_exact_profile_revalidates_without_visiting_unrelated_owners(self):
|
|
unrelated = ('qualified_blended_alpha', 'qualified_blended_stock', 'qualified_angle_aol',
|
|
'qualified_classic_long', 'qualified_classic_scc', 'qualified_canfd_long',
|
|
'qualified_canfd_stock', 'qualified_non_scc', 'qualified_ccnc_ev_stock')
|
|
for cp in self.profiles():
|
|
with self.subTest(word=cp.safetyConfigs[0].safetyParam), ExitStack() as stack:
|
|
for name in unrelated:
|
|
stack.enter_context(mock.patch.object(aol, name, side_effect=AssertionError(name)))
|
|
qualifier = stack.enter_context(mock.patch.object(aol, 'qualified_ioniq6', wraps=aol.qualified_ioniq6))
|
|
model, param = int(cp.safetyConfigs[0].safetyModel.raw), cp.safetyConfigs[0].safetyParam
|
|
self.assertTrue(native_matches_cp(cp, model, param))
|
|
cp.radarUnavailable = True
|
|
self.assertFalse(native_matches_cp(cp, model, param))
|
|
cp.radarUnavailable = False
|
|
self.assertTrue(native_matches_cp(cp, model, param))
|
|
self.assertEqual(qualifier.call_count, 3)
|
|
|
|
def test_exact_qualifier_contract_and_outer_native_word_check_are_preserved(self):
|
|
changes = (('brand', 'mock'), ('radarUnavailable', True), ('passive', True),
|
|
('dashcamOnly', True), ('notCar', True), ('openpilotLongitudinalControl', None),
|
|
('pcmCruise', None), ('alternativeExperience', 32),
|
|
('steerControlType', car.CarParams.SteerControlType.angle))
|
|
for original in self.profiles():
|
|
mutations = [original]
|
|
for field, value in changes:
|
|
cp = original.as_reader().as_builder()
|
|
setattr(cp, field, not getattr(cp, field) if value is None else value)
|
|
mutations.append(cp)
|
|
for flag in (HyundaiFlags.CANFD, HyundaiFlags.EV, HyundaiFlags.CANFD_LKA_STEER_MSG,
|
|
HyundaiFlags.CANFD_LKA_STEER_MSG_ALT, HyundaiFlags.CANFD_ALT_BUTTONS,
|
|
HyundaiFlags.CANFD_ANGLE_STEERING, HyundaiFlags.CANFD_CAMERA_SCC):
|
|
cp = original.as_reader().as_builder()
|
|
cp.flags ^= int(flag)
|
|
mutations.append(cp)
|
|
for cp in mutations:
|
|
model, param = int(cp.safetyConfigs[0].safetyModel.raw), cp.safetyConfigs[0].safetyParam
|
|
with self.subTest(word=param, flags=cp.flags, profile=cp.to_dict()):
|
|
expected = aol.qualified_ioniq6(cp)
|
|
self.assertEqual(aol.native_accepts_cp(cp, model, param), expected)
|
|
# Preserve the direct port API; exact native words are checked by dispatch.
|
|
self.assertEqual(aol.native_accepts_cp(cp, model + 1, param ^ 1), expected)
|
|
self.assertEqual(native_matches_cp(cp, model, param), expected)
|
|
self.assertFalse(native_matches_cp(cp, model + 1, param))
|
|
self.assertFalse(native_matches_cp(cp, model, param ^ 1))
|
|
for configs in ([], [original.safetyConfigs[0], original.safetyConfigs[0]]):
|
|
cp = original.as_reader().as_builder()
|
|
cp.safetyConfigs = configs
|
|
self.assertFalse(native_matches_cp(cp, int(car.CarParams.SafetyModel.hyundaiCanfd), 0x8895))
|
|
|
|
def test_native_payload_health_session_validity_and_original_lease_still_apply(self):
|
|
_, cp = candidate(True)
|
|
cp.safetyConfigs[0].safetyParam |= 0x0800
|
|
now = 1_000_000_000
|
|
model, param = int(cp.safetyConfigs[0].safetyModel.raw), cp.safetyConfigs[0].safetyParam
|
|
slot = PandaSlot(0, 'panda', model, param, cp.alternativeExperience, True)
|
|
good = SafetyState(1, True, now, now + 200_000_000, model, param,
|
|
True, True, True, True, 'panda', 'axis', now, (slot,))
|
|
sm = messaging.SubMaster(['aolSafetyWire'])
|
|
|
|
def receive(value, *, valid=True, stamp=now):
|
|
nonlocal sm
|
|
sm = messaging.SubMaster(['aolSafetyWire'])
|
|
envelope = messaging.new_message('aolSafetyWire', 0, valid=valid, logMonoTime=stamp)
|
|
envelope.aolSafetyWire = encode_safety(value)
|
|
sm.update_msgs(now / 1e9, [envelope.as_reader()])
|
|
|
|
receive(good)
|
|
self.assertIsNotNone(current_native(sm, cp, now_ns=now, axis_session_id='axis'))
|
|
self.assertIsNotNone(current_native(sm, cp, now_ns=now + 200_000_000, axis_session_id='axis'))
|
|
self.assertIsNone(current_native(sm, cp, now_ns=now + 200_000_001, axis_session_id='axis'))
|
|
self.assertIsNone(current_native(sm, cp, now_ns=now, axis_session_id='different'))
|
|
receive(good, valid=False)
|
|
self.assertIsNone(current_native(sm, cp, now_ns=now, axis_session_id='axis'))
|
|
for bad in (replace(good, compatible=False), replace(good, protocolVersion=2),
|
|
replace(good, safetyParam=param ^ 1), replace(good, safetyModel=model + 1),
|
|
replace(good, observedMonoTime=now + 1),
|
|
replace(good, pandaInventory=(replace(slot, heartbeatLost=True),)),
|
|
replace(good, pandaInventory=(replace(slot, safetyRxChecksInvalid=True),)),
|
|
replace(good, pandaInventory=(replace(slot, faults=1),)),
|
|
replace(good, pandaInventory=(replace(slot, alternativeExperience=32),))):
|
|
with self.subTest(native=bad):
|
|
receive(bad)
|
|
self.assertIsNone(current_native(sm, cp, now_ns=now, axis_session_id='axis'))
|
|
# A changed permission is observed immediately; no previous grant is reused.
|
|
receive(replace(good, lateralAllowed=False, longitudinalAllowed=False))
|
|
denied = current_native(sm, cp, now_ns=now, axis_session_id='axis')
|
|
self.assertIsNotNone(denied)
|
|
assert denied is not None
|
|
self.assertFalse(denied.lateralAllowed or denied.longitudinalAllowed)
|