mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-28 09:03:46 +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>
198 lines
11 KiB
Python
198 lines
11 KiB
Python
"""Saved speed choice retains exact Volt physical and lateral ownership."""
|
|
from pathlib import Path
|
|
import unittest
|
|
|
|
from opendbc.car import gen_empty_fingerprint
|
|
from opendbc.car.gm.interface import CarInterface
|
|
from opendbc.car.gm.lateral import lane_centering_supported
|
|
from opendbc.car.gm.startup_preferences import disable_long_supported
|
|
from opendbc.car.gm.tests.test_bolt_volt_configurations import ordinary_params
|
|
from opendbc.car.gm.values import CAR, is_volt_gateway_alternate_brake
|
|
from openpilot.common.params import Params
|
|
from openpilot.common.prefix import OpenpilotPrefix
|
|
from openpilot.starpilot.vehicle_preferences import VehicleStartupPreferences
|
|
from openpilot.starpilot.ui.feature_settings_owner import FeatureSettingsOwner
|
|
from openpilot.starpilot.ui.feature_settings_state import FeatureSettingsRequest
|
|
|
|
|
|
def volt_cc_params(*, release=False):
|
|
fingerprint = gen_empty_fingerprint()
|
|
fingerprint[0].update({0xBE: 6, 0x3D1: 8, 0xC9: 8, 0x1E1: 7, 0x1F5: 8, 0x34A: 5, 0x1C4: 8, 0xBD: 7})
|
|
return CarInterface.get_params(CAR.CHEVROLET_VOLT_CC, fingerprint, [], False, release, False)
|
|
|
|
|
|
def configured(variant):
|
|
if variant == 'cc':
|
|
return volt_cc_params()
|
|
return ordinary_params(CAR.CHEVROLET_VOLT, radar=True, accelerator=variant == 'be')
|
|
|
|
|
|
class TestVoltDisableLongitudinal(unittest.TestCase):
|
|
def test_actual_registered_preference_finalized_cp_and_idempotence(self):
|
|
for variant in ('be', 'f1', 'cc'):
|
|
for raw in (None, b'0', b'1', b'corrupt'):
|
|
with self.subTest(variant=variant, raw=raw), OpenpilotPrefix():
|
|
params = Params()
|
|
if raw is not None:
|
|
Path(params.get_param_path('DisableOpenpilotLongitudinal')).write_bytes(raw)
|
|
cp = configured(variant)
|
|
self.assertTrue(cp.openpilotLongitudinalControl)
|
|
before = (cp.pcmCruise, int(cp.flags), cp.safetyConfigs[0].safetyModel, cp.safetyConfigs[0].safetyParam)
|
|
preferences = VehicleStartupPreferences.read(params, enabled=True)
|
|
preferences.prepare(cp)
|
|
preferences.finalize(cp)
|
|
preferences.prepare(cp)
|
|
self.assertEqual(cp.openpilotLongitudinalControl, raw in (None, b'0'))
|
|
self.assertEqual((cp.pcmCruise, int(cp.flags), cp.safetyConfigs[0].safetyModel, cp.safetyConfigs[0].safetyParam), before)
|
|
self.assertFalse(cp.pcmCruise)
|
|
self.assertTrue(disable_long_supported(cp))
|
|
self.assertTrue(lane_centering_supported(cp))
|
|
self.assertEqual(is_volt_gateway_alternate_brake(cp), variant == 'f1')
|
|
|
|
def test_unqualified_and_sibling_configs_unchanged(self):
|
|
candidates = [volt_cc_params(release=True), ordinary_params(CAR.CHEVROLET_VOLT, radar=False),
|
|
ordinary_params(CAR.CHEVROLET_VOLT_CAMERA), ordinary_params(CAR.CHEVROLET_VOLT_2019),
|
|
ordinary_params(CAR.CHEVROLET_VOLT_ASCM, sascm=True),
|
|
ordinary_params(CAR.CHEVROLET_VOLT_ASCM, alpha=True)]
|
|
for cp in candidates:
|
|
before = cp.to_dict()
|
|
self.assertFalse(disable_long_supported(cp))
|
|
VehicleStartupPreferences(disable_bolt_long=True).prepare(cp)
|
|
self.assertEqual(cp.to_dict(), before)
|
|
for field in ('passive', 'dashcamOnly', 'notCar'):
|
|
cp = configured('be')
|
|
setattr(cp, field, True)
|
|
self.assertFalse(disable_long_supported(cp))
|
|
|
|
def test_qualified_ascm_withdraws_to_exact_stock_owner(self):
|
|
for accelerator in (True, False):
|
|
for radar in (False, True):
|
|
with self.subTest(accelerator=accelerator, radar=radar):
|
|
cp = ordinary_params(CAR.CHEVROLET_VOLT_ASCM, sascm=True, alpha=True,
|
|
radar=radar, accelerator=accelerator)
|
|
word = 0x4207 | (0 if accelerator else 0x400) | (0x800 if radar else 0)
|
|
self.assertEqual(cp.safetyConfigs[0].safetyParam, word)
|
|
self.assertTrue(disable_long_supported(cp))
|
|
before = cp.to_dict()
|
|
VehicleStartupPreferences(disable_bolt_long=True).prepare(cp)
|
|
expected = dict(before)
|
|
expected['openpilotLongitudinalControl'] = False
|
|
expected['pcmCruise'] = True
|
|
expected['safetyConfigs'] = [dict(before['safetyConfigs'][0], safetyParam=word & ~0x4002)]
|
|
self.assertEqual(cp.to_dict(), expected)
|
|
self.assertFalse(disable_long_supported(cp))
|
|
VehicleStartupPreferences(disable_bolt_long=True).prepare(cp)
|
|
self.assertEqual(cp.to_dict(), expected)
|
|
|
|
def test_parked_ui_recovery_changes_only_next_startup(self):
|
|
for variant in ('be', 'f1', 'cc'):
|
|
with self.subTest(variant=variant), OpenpilotPrefix():
|
|
params = Params()
|
|
cp = configured(variant)
|
|
owner = FeatureSettingsOwner(params, lambda group: True, vehicle_fingerprint=lambda cp=cp: cp.carFingerprint,
|
|
vehicle_params=lambda cp=cp: cp)
|
|
view = owner.snapshot('vehicle', parked=True, system_long=True, lateral_context=True, metric=False)
|
|
row = next(row for row in view.rows if row.key == 'DisableOpenpilotLongitudinal')
|
|
self.assertTrue(row.available)
|
|
capability = owner._bolt_disable_capability()
|
|
request = FeatureSettingsRequest('DisableOpenpilotLongitudinal', None, 'On', confirmation=True,
|
|
vehicle_fingerprint=cp.carFingerprint, capability=capability)
|
|
self.assertTrue(owner.apply(request))
|
|
self.assertTrue(cp.openpilotLongitudinalControl)
|
|
VehicleStartupPreferences.read(params, enabled=True).prepare(cp)
|
|
self.assertFalse(cp.openpilotLongitudinalControl)
|
|
recovery = owner._bolt_disable_capability()
|
|
request = FeatureSettingsRequest('DisableOpenpilotLongitudinal', b'1', 'Off', confirmation=True,
|
|
vehicle_fingerprint=cp.carFingerprint, capability=recovery)
|
|
self.assertTrue(owner.apply(request))
|
|
self.assertFalse(cp.openpilotLongitudinalControl)
|
|
next_cp = configured(variant)
|
|
VehicleStartupPreferences.read(params, enabled=True).prepare(next_cp)
|
|
self.assertTrue(next_cp.openpilotLongitudinalControl)
|
|
|
|
def test_actual_card_and_packed_sources_suppress_host_longitudinal(self):
|
|
import os
|
|
from unittest.mock import patch
|
|
from opendbc.can import CANPacker
|
|
from opendbc.car import Bus, structs
|
|
from opendbc.car.gm import gmcan
|
|
from opendbc.car.gm.radar_interface import RadarInterface
|
|
from opendbc.car.gm.tests.test_bolt_cc import control, feed, setup, native
|
|
from opendbc.car.gm.values import DBC, CruiseButtons
|
|
from opendbc.safety.tests.libsafety import libsafety_py
|
|
from openpilot.selfdrive.car.card import Car
|
|
|
|
for variant in ('be', 'f1', 'cc'):
|
|
with self.subTest(variant=variant), OpenpilotPrefix(), patch.dict(os.environ, {'SIMULATION': '1'}):
|
|
params = Params()
|
|
params.put_bool('OpenpilotEnabledToggle', True, block=True)
|
|
params.put_bool('DisableOpenpilotLongitudinal', True, block=True)
|
|
safety = libsafety_py.libsafety
|
|
release = safety.set_safety_hooks(structs.CarParams.SafetyModel.allOutput, 0) != 0
|
|
if variant == 'cc' and release:
|
|
cp = volt_cc_params(release=True)
|
|
self.assertTrue(cp.dashcamOnly)
|
|
self.assertFalse(disable_long_supported(cp))
|
|
continue
|
|
cp = configured(variant)
|
|
ci = CarInterface(cp)
|
|
word = cp.safetyConfigs[0].safetyParam
|
|
card = Car(CI=ci, RI=RadarInterface(cp))
|
|
self.assertFalse(card.CP.openpilotLongitudinalControl)
|
|
self.assertEqual(card.CP.safetyConfigs[0].safetyParam, word)
|
|
packer = CANPacker(DBC[cp.carFingerprint][Bus.pt])
|
|
setup(cp)
|
|
steering = []
|
|
command = control(long_active=True)
|
|
# Actual Card finalized the same CI after parser/controller construction.
|
|
# Every physical source is packed and observed; no fabricated canValid.
|
|
for tick in range(60):
|
|
now = 1_000_000_000 + tick * 10_000_000
|
|
_, sources = feed(ci, packer, now, counter=tick % 4)
|
|
selected = packer.make_can_msg('AcceleratorPedal2', 0, {'CruiseState': 2})
|
|
sources = [source for source in sources if source[0] != selected[0]] + [selected]
|
|
button = CruiseButtons.RES_ACCEL if tick == 5 else CruiseButtons.UNPRESS
|
|
selected = gmcan.create_buttons(packer, 0, tick % 4, button)
|
|
sources = [source for source in sources if source[0] != selected[0]] + [selected]
|
|
if variant == 'f1':
|
|
sources = [source for source in sources if source[0] != 0xBE]
|
|
sources.append(packer.make_can_msg('EBCMBrakePedalPosition', 0, {'BrakePedalPosition': 0}))
|
|
out = ci.update([(now + 1, sources)])
|
|
for source in sources:
|
|
native('rx', source, now // 1000)
|
|
if tick < 10:
|
|
continue
|
|
self.assertTrue(out.canValid)
|
|
self.assertFalse(out.brakePressed)
|
|
ci.CC.frame = tick
|
|
command.actuators.accel = -3. if tick % 2 else 2.
|
|
command.cruiseControl.cancel = tick >= 30
|
|
_, messages = ci.apply(command.as_reader(), now + 2)
|
|
self.assertFalse(any(message[0] in (0x2CB, 0x315, 0x370, 0x409, 0x40A, 0xA1, 0x306, 0x308, 0x310) for message in messages))
|
|
if variant != 'cc':
|
|
self.assertFalse(any(message[0] == 0x1E1 for message in messages))
|
|
for message in messages:
|
|
if message[0] == 0x180:
|
|
steering.append(message)
|
|
self.assertTrue(native('tx', message, now // 1000))
|
|
self.assertTrue(steering)
|
|
self.assertTrue(any(message[1][0] & 8 for message in steering))
|
|
# The positive complete feed above is required before this expiry check.
|
|
ci.CC.frame = 72
|
|
_, expired = ci.apply(command.as_reader(), now + 400_000_000)
|
|
expired_steering = [message for message in expired if message[0] == 0x180]
|
|
self.assertTrue(expired_steering)
|
|
self.assertFalse(any(message[1][0] & 8 for message in expired_steering))
|
|
# Physical brake is observed on the selected source, then native authority
|
|
# withdraws without relying on an updated Controls command.
|
|
now += 410_000_000
|
|
brake_name, brake_signal, pressed = (('EBCMBrakePedalPosition', 'BrakePedalPosition', 6)
|
|
if variant == 'f1' else ('ECMAcceleratorPos', 'BrakePedalPos', 12))
|
|
brake_message = packer.make_can_msg(brake_name, 0, {brake_signal: pressed})
|
|
sources = [source for source in sources if source[0] != brake_message[0]] + [brake_message]
|
|
out = ci.update([(now, sources)])
|
|
self.assertTrue(out.brakePressed)
|
|
for source in sources:
|
|
native('rx', source, now // 1000)
|
|
self.assertFalse(safety.get_controls_allowed())
|