mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-12 07:23:45 +08:00
Restore Tesla AOL for standard and screen harnesses with matching firmware
This commit is contained in:
@@ -3,24 +3,30 @@ from opendbc.car.tesla.values import CAR, TeslaFlags
|
||||
|
||||
SCREEN_WORDS = frozenset((512, 513, 1536, 1537, 768, 769, 1792, 1793))
|
||||
BASE_WORDS = frozenset((0, 1, 256, 257))
|
||||
ORDINARY_WORDS = frozenset((2048, 2049, 2304, 2305, 3072, 3073, 3328, 3329))
|
||||
AOL_WORDS = SCREEN_WORDS | ORDINARY_WORDS
|
||||
|
||||
|
||||
def screen_tap_supported(cp):
|
||||
def modern_aol_supported(cp):
|
||||
try:
|
||||
if (cp.brand != 'tesla' or cp.carFingerprint not in (CAR.TESLA_MODEL_3, CAR.TESLA_MODEL_Y) or
|
||||
cp.passive or cp.dashcamOnly or cp.notCar or cp.alternativeExperience not in (0, 32) or len(cp.safetyConfigs) != 1 or
|
||||
int(cp.safetyConfigs[0].safetyModel.raw) != 10 or
|
||||
not cp.flags & TeslaFlags.HAS_VEHICLE_BUS or
|
||||
cp.flags & ~int(TeslaFlags.MISSING_DAS_SETTINGS | TeslaFlags.HAS_VEHICLE_BUS | TeslaFlags.AOL_SCREEN_BUTTON)):
|
||||
return False
|
||||
word = int(cp.safetyConfigs[0].safetyParam)
|
||||
return (word in BASE_WORDS | SCREEN_WORDS and (not word & 256 or cp.carFingerprint == CAR.TESLA_MODEL_3) and
|
||||
return (word in BASE_WORDS | AOL_WORDS and (not word & 256 or cp.carFingerprint == CAR.TESLA_MODEL_3) and
|
||||
bool(word & 1) == bool(cp.openpilotLongitudinalControl) and
|
||||
bool(word & 512) == bool(cp.flags & TeslaFlags.AOL_SCREEN_BUTTON))
|
||||
bool(word & 512) == bool(cp.flags & TeslaFlags.AOL_SCREEN_BUTTON) and
|
||||
(not word & 512 or bool(cp.flags & TeslaFlags.HAS_VEHICLE_BUS)))
|
||||
except (AttributeError, ValueError, TypeError, IndexError):
|
||||
return False
|
||||
|
||||
|
||||
def screen_tap_supported(cp):
|
||||
return bool(modern_aol_supported(cp) and cp.flags & TeslaFlags.HAS_VEHICLE_BUS)
|
||||
|
||||
|
||||
modern_screen_supported = screen_tap_supported
|
||||
|
||||
|
||||
@@ -29,6 +35,24 @@ def screen_qualified(cp, *, marked_only=False):
|
||||
cp.alternativeExperience in ((32,) if marked_only else (0, 32)))
|
||||
|
||||
|
||||
def modern_aol_qualified(cp, *, marked_only=False):
|
||||
return bool(modern_aol_supported(cp) and int(cp.safetyConfigs[0].safetyParam) in AOL_WORDS and
|
||||
cp.alternativeExperience in ((32,) if marked_only else (0, 32)))
|
||||
|
||||
|
||||
def apply_modern_aol(cp, requested, screen, brake_disengage):
|
||||
if not modern_aol_supported(cp):
|
||||
return
|
||||
use_screen = requested and screen and screen_tap_supported(cp)
|
||||
base = int(cp.safetyConfigs[0].safetyParam) & 257
|
||||
cp.flags &= ~int(TeslaFlags.AOL_SCREEN_BUTTON)
|
||||
cp.safetyConfigs[0].safetyParam = base
|
||||
if requested:
|
||||
cp.safetyConfigs[0].safetyParam = base | (512 if use_screen else 2048) | (1024 if brake_disengage else 0)
|
||||
if use_screen:
|
||||
cp.flags |= TeslaFlags.AOL_SCREEN_BUTTON.value
|
||||
|
||||
|
||||
def apply_screen_button(cp, requested, brake_disengage):
|
||||
if not screen_tap_supported(cp):
|
||||
return
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
static bool tesla_screen_enabled = false;
|
||||
static bool tesla_screen_tap_enabled = false;
|
||||
static bool tesla_screen_brake_disengage = false;
|
||||
static bool tesla_screen_longitudinal = false;
|
||||
static bool tesla_screen_initialized = false;
|
||||
@@ -75,7 +76,7 @@ static uint8_t tesla_screen_permission(void) {
|
||||
}
|
||||
|
||||
static void tesla_screen_optional_rx(const CANPacket_t *msg) {
|
||||
if (tesla_screen_enabled && msg_matches(msg, 0x3DFU, 1U, 8U)) {
|
||||
if (tesla_screen_tap_enabled && msg_matches(msg, 0x3DFU, 1U, 8U)) {
|
||||
const bool pressed = msg->data[3] == 3U;
|
||||
if (tesla_screen_initialized && pressed && !tesla_screen_pressed && aol_rx_healthy() &&
|
||||
tesla_screen_physical_ready() && !(tesla_screen_brake_disengage && brake_pressed)) {
|
||||
@@ -132,9 +133,11 @@ static void tesla_screen_rx(const CANPacket_t *msg, bool autopark) {
|
||||
|
||||
static bool tesla_screen_param_valid(uint16_t param) {
|
||||
const uint16_t screen_param = (uint16_t)(param & ~256U);
|
||||
bool valid = (screen_param == 512U) || (screen_param == 1536U);
|
||||
bool valid = (screen_param == 512U) || (screen_param == 1536U) ||
|
||||
(screen_param == 2048U) || (screen_param == 3072U);
|
||||
#ifdef ALLOW_DEBUG
|
||||
valid = valid || (screen_param == 513U) || (screen_param == 1537U);
|
||||
valid = valid || (screen_param == 513U) || (screen_param == 1537U) ||
|
||||
(screen_param == 2049U) || (screen_param == 3073U);
|
||||
#endif
|
||||
return valid;
|
||||
}
|
||||
@@ -144,8 +147,9 @@ static void tesla_screen_configure(uint16_t param) {
|
||||
tesla_screen_reset();
|
||||
tesla_screen_enabled = tesla_screen_param_valid(screen_param);
|
||||
tesla_screen_enabled = tesla_screen_enabled && ((unsigned int)alternative_experience == 32U);
|
||||
tesla_screen_brake_disengage = (screen_param == 1536U) || (screen_param == 1537U);
|
||||
tesla_screen_longitudinal = (screen_param == 513U) || (screen_param == 1537U);
|
||||
tesla_screen_tap_enabled = tesla_screen_enabled && GET_FLAG(screen_param, 512U);
|
||||
tesla_screen_brake_disengage = tesla_screen_enabled && GET_FLAG(screen_param, 1024U);
|
||||
tesla_screen_longitudinal = tesla_screen_enabled && GET_FLAG(screen_param, 1U);
|
||||
if (tesla_screen_enabled) {
|
||||
static const AolSafetyPolicy policy = {
|
||||
.reset = tesla_screen_reset,
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import os
|
||||
import unittest
|
||||
from opendbc.safety.tests.common import CANPackerSafety
|
||||
from opendbc.safety.tests.libsafety import libsafety_py
|
||||
from opendbc.safety.tests import test_tesla_screen_button as screen
|
||||
|
||||
|
||||
class TestTeslaOrdinaryAol(unittest.TestCase):
|
||||
start = screen.TestTeslaScreenButton.start
|
||||
feed = screen.TestTeslaScreenButton.feed
|
||||
permission = screen.TestTeslaScreenButton.permission
|
||||
touch = screen.TestTeslaScreenButton.touch
|
||||
tearDown = screen.TestTeslaScreenButton.tearDown
|
||||
|
||||
def setUp(self):
|
||||
self.lib = libsafety_py.libsafety
|
||||
self.packer = CANPackerSafety('tesla_model3_party')
|
||||
self.start(2048)
|
||||
|
||||
def test_standard_harness_brake_choice_and_real_cruise_rearm(self):
|
||||
for word in (2048, 2304, 3072, 3328):
|
||||
with self.subTest(word=word):
|
||||
self.start(word)
|
||||
self.touch(0)
|
||||
self.touch(3)
|
||||
self.assertEqual(self.permission(), 0, 'optional bus1 gestures cannot arm ordinary AOL')
|
||||
self.feed(state=1)
|
||||
self.assertEqual(self.permission(3), 1)
|
||||
self.touch(0)
|
||||
self.touch(3)
|
||||
self.assertEqual(self.permission(), 1, 'optional bus1 gestures cannot cancel ordinary AOL')
|
||||
self.feed(state=1, brake=True)
|
||||
self.assertEqual(self.permission(), 0 if word & 1024 else 1)
|
||||
self.feed(state=1)
|
||||
self.assertEqual(self.permission(), 0 if word & 1024 else 1)
|
||||
self.feed(state=2)
|
||||
self.assertEqual(self.permission(), 1)
|
||||
self.feed(state=0)
|
||||
self.assertEqual(self.permission(), 0)
|
||||
self.feed(state=1)
|
||||
self.assertEqual(self.permission(), 0, 'availability cannot undo a cancellation')
|
||||
|
||||
def test_physical_faults_heartbeat_and_request_expiry_still_withdraw(self):
|
||||
for fault in ({'hands': 3}, {'gear': 0}, {'door': True}, {'eps': 3}, {'autopark': 3}, {'state': 5}):
|
||||
with self.subTest(fault=fault):
|
||||
self.start(2048)
|
||||
self.feed(state=1)
|
||||
self.assertEqual(self.permission(), 1)
|
||||
self.feed(**({'state': 1} | fault))
|
||||
self.assertEqual(self.permission(), 0)
|
||||
self.feed(state=1)
|
||||
self.assertEqual(self.permission(), 0)
|
||||
self.start(2048)
|
||||
self.feed(state=1)
|
||||
self.assertEqual(self.permission(), 1)
|
||||
self.lib.set_aol_test_heartbeat(False)
|
||||
self.assertEqual(self.permission(), 0)
|
||||
self.lib.set_aol_test_heartbeat(True)
|
||||
self.assertEqual(self.permission(), 0)
|
||||
self.feed(state=2)
|
||||
self.assertEqual(self.permission(), 1)
|
||||
self.time += 1_100_000
|
||||
self.lib.set_timer(self.time)
|
||||
self.assertEqual(self.lib.aol_get_permission_mask(), 0)
|
||||
|
||||
def test_longitudinal_stays_independent_and_release_debug_limits_remain(self):
|
||||
positive = self.packer.make_can_msg_safety('DAS_control', 0, {'DAS_accelMin': 1, 'DAS_accelMax': 1})
|
||||
for word in (2048, 3072, 2049, 3073):
|
||||
if word & 1 and os.environ.get('TESLA_SCREEN_NATIVE_RELEASE') == '1':
|
||||
self.assertEqual(self.lib.set_safety_hooks(10, word), -1)
|
||||
continue
|
||||
self.start(word)
|
||||
self.feed(state=1)
|
||||
self.assertEqual(self.permission(3), 1)
|
||||
self.assertFalse(self.lib.safety_tx_hook(positive))
|
||||
self.feed(state=2)
|
||||
self.assertEqual(self.permission(3), 3 if word & 1 else 1)
|
||||
if word & 1:
|
||||
self.assertTrue(self.lib.safety_tx_hook(positive))
|
||||
self.feed(state=2, brake=True)
|
||||
self.assertFalse(self.lib.safety_tx_hook(positive))
|
||||
|
||||
def test_profile_must_be_exact_and_saved_aol_opt_in_is_required(self):
|
||||
for word in (2048 | 512, 2048 | 16, 2048 | 2, 2048 | 4096, 3072 | 512):
|
||||
self.assertEqual(self.lib.set_safety_hooks(10, word), -1)
|
||||
self.lib.set_alternative_experience(0)
|
||||
self.assertEqual(self.lib.set_safety_hooks(10, 2048), 0)
|
||||
self.feed(state=1)
|
||||
self.assertEqual(self.permission(), 0)
|
||||
Binary file not shown.
@@ -260,7 +260,8 @@ int main() {
|
||||
ford.safety_param = static_cast<uint16_t>(word);
|
||||
assert(aol_capable(ford, 6U, VEHICLE_REGISTRY) == expected);
|
||||
}
|
||||
constexpr uint16_t tesla_screen_words[] = {512U, 513U, 768U, 769U, 1536U, 1537U, 1792U, 1793U};
|
||||
constexpr uint16_t tesla_screen_words[] = {512U, 513U, 768U, 769U, 1536U, 1537U, 1792U, 1793U,
|
||||
2048U, 2049U, 2304U, 2305U, 3072U, 3073U, 3328U, 3329U};
|
||||
for (uint32_t word = 0U; word <= 65535U; ++word) {
|
||||
bool expected = false;
|
||||
for (const auto exact : tesla_screen_words) expected |= word == exact;
|
||||
|
||||
@@ -2,18 +2,18 @@ from opendbc.car import structs
|
||||
from opendbc.car.tesla.preap.aol import qualified, temporary_restriction as preap_temporary_restriction
|
||||
from openpilot.starpilot.aol.policy import AolVehiclePolicy
|
||||
from openpilot.starpilot.car.tesla.stock_intent import TeslaPreapCardIntent
|
||||
from opendbc.car.tesla.screen_button import screen_tap_supported, screen_qualified, SCREEN_WORDS
|
||||
from opendbc.car.tesla.screen_button import modern_aol_supported, modern_aol_qualified, AOL_WORDS
|
||||
from openpilot.starpilot.car.tesla.screen_intent import TeslaScreenCardIntent
|
||||
|
||||
|
||||
def policy_for(cp):
|
||||
if screen_tap_supported(cp):
|
||||
admitted = screen_qualified(cp)
|
||||
if modern_aol_supported(cp):
|
||||
admitted = modern_aol_qualified(cp)
|
||||
return AolVehiclePolicy(intent_supported=admitted, settings_supported=True,
|
||||
runtime_supported=admitted, normal_runtime_supported=admitted,
|
||||
ordinary_axis_ack_required=screen_qualified(cp, marked_only=True),
|
||||
ordinary_axis_ack_required=modern_aol_qualified(cp, marked_only=True),
|
||||
full_axis_runtime_required=admitted,
|
||||
cruise_main_required=not screen_qualified(cp, marked_only=True),
|
||||
cruise_main_required=not modern_aol_qualified(cp, marked_only=True),
|
||||
explicit_latch=admitted, alternative_experience_addition=32 if admitted else 0)
|
||||
admitted = qualified(cp)
|
||||
return AolVehiclePolicy(intent_supported=admitted, settings_supported=admitted,
|
||||
@@ -25,31 +25,31 @@ def policy_for(cp):
|
||||
|
||||
|
||||
def native_profile_supported(model, param):
|
||||
return (model == 39 and param in (0, 1, 2, 3, 6, 7)) or (model == 10 and param in SCREEN_WORDS)
|
||||
return (model == 39 and param in (0, 1, 2, 3, 6, 7)) or (model == 10 and param in AOL_WORDS)
|
||||
|
||||
|
||||
def native_accepts_cp(cp, model, param):
|
||||
return bool((qualified(cp, marked_only=True) or screen_qualified(cp, marked_only=True)) and
|
||||
return bool((qualified(cp, marked_only=True) or modern_aol_qualified(cp, marked_only=True)) and
|
||||
native_profile_supported(model, param) and len(cp.safetyConfigs) == 1 and
|
||||
int(cp.safetyConfigs[0].safetyModel.raw) == model and int(cp.safetyConfigs[0].safetyParam) == param)
|
||||
|
||||
|
||||
def native_latch_rejected(cp, native):
|
||||
return (qualified(cp, marked_only=True) or screen_qualified(cp, marked_only=True)) and native.requestedLateral and not native.lateralAllowed
|
||||
return (qualified(cp, marked_only=True) or modern_aol_qualified(cp, marked_only=True)) and native.requestedLateral and not native.lateralAllowed
|
||||
|
||||
|
||||
def ordinary_axis_request_allowed(cp, state):
|
||||
return bool((qualified(cp, marked_only=True) or screen_qualified(cp, marked_only=True)) and
|
||||
return bool((qualified(cp, marked_only=True) or modern_aol_qualified(cp, marked_only=True)) and
|
||||
state.canValid and not state.canTimeout and not state.steerFaultPermanent)
|
||||
|
||||
|
||||
def retain_on_native_denial(cp, native, state):
|
||||
return preap_temporary_restriction(cp, state) or bool(
|
||||
screen_qualified(cp, marked_only=True) and state is not None and state.canValid and not state.canTimeout and
|
||||
modern_aol_qualified(cp, marked_only=True) and state is not None and state.canValid and not state.canTimeout and
|
||||
not state.steerFaultPermanent and not state.steeringDisengage and not state.doorOpen and not state.accFaulted and
|
||||
(state.gearShifter in (structs.CarState.GearShifter.reverse, structs.CarState.GearShifter.neutral) or
|
||||
state.gearShifter == structs.CarState.GearShifter.drive and state.steerFaultTemporary))
|
||||
|
||||
|
||||
def create_intent(cp, settings):
|
||||
return TeslaScreenCardIntent(cp, settings) if screen_qualified(cp) else TeslaPreapCardIntent(cp, settings)
|
||||
return TeslaScreenCardIntent(cp, settings) if modern_aol_qualified(cp) else TeslaPreapCardIntent(cp, settings)
|
||||
|
||||
@@ -10,7 +10,8 @@ inline constexpr AolSafetyProfile TESLA_PREAP_AOL_PROFILE{39U, tesla_preap_aol_p
|
||||
|
||||
inline bool tesla_screen_aol_param(uint16_t param) {
|
||||
param &= ~256U;
|
||||
return param == 512U || param == 513U || param == 1536U || param == 1537U;
|
||||
return param == 512U || param == 513U || param == 1536U || param == 1537U ||
|
||||
param == 2048U || param == 2049U || param == 3072U || param == 3073U;
|
||||
}
|
||||
|
||||
inline constexpr AolSafetyProfile TESLA_SCREEN_AOL_PROFILE{10U, tesla_screen_aol_param, true};
|
||||
|
||||
@@ -9,7 +9,8 @@ def supported(cp):
|
||||
cp.flags & ~int(TeslaFlags.MISSING_DAS_SETTINGS | TeslaFlags.HAS_VEHICLE_BUS | TeslaFlags.AOL_SCREEN_BUTTON)):
|
||||
return False
|
||||
word = int(cp.safetyConfigs[0].safetyParam)
|
||||
return word in (0, 1, 256, 257, 512, 513, 768, 769, 1536, 1537, 1792, 1793) and bool(word & 1) == bool(cp.openpilotLongitudinalControl)
|
||||
return (word in (0, 1, 256, 257, 512, 513, 768, 769, 1536, 1537, 1792, 1793, 2048, 2049, 2304, 2305, 3072, 3073, 3328, 3329) and
|
||||
bool(word & 1) == bool(cp.openpilotLongitudinalControl))
|
||||
except (AttributeError, TypeError, ValueError, IndexError):
|
||||
return False
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ from openpilot.starpilot.aol.intent import AolCardIntent, ButtonType
|
||||
class TeslaScreenCardIntent(AolCardIntent):
|
||||
def __init__(self, cp, settings):
|
||||
super().__init__(settings, explicit_latch=True)
|
||||
self.screen_enabled = bool(cp.safetyConfigs[0].safetyParam & 512)
|
||||
self.screen_override = None
|
||||
self.brake_disengage = bool(cp.safetyConfigs[0].safetyParam & 1024)
|
||||
self.previous_enabled = False
|
||||
@@ -23,7 +24,7 @@ class TeslaScreenCardIntent(AolCardIntent):
|
||||
available = bool(CS.cruiseState.available)
|
||||
# Stock cruise availability does not revoke an explicit screen selection.
|
||||
if (engaged and not self.previous_enabled) or (self.previous_available and not available and not CS.brakePressed and
|
||||
self.screen_override is not True):
|
||||
(not self.screen_enabled or self.screen_override is not True)):
|
||||
self.screen_override = None if engaged and not self.previous_enabled else False
|
||||
self.brake_blocked = False
|
||||
self.pause_lateral = False
|
||||
@@ -40,7 +41,7 @@ class TeslaScreenCardIntent(AolCardIntent):
|
||||
if fault or cancel or not self.settings.enabled:
|
||||
self.screen_override = False
|
||||
for event in CS.buttonEvents:
|
||||
if event.type != ButtonType.lkas:
|
||||
if event.type != ButtonType.lkas or not self.screen_enabled:
|
||||
continue
|
||||
fresh_press = bool(event.pressed and not self.screen_held)
|
||||
self.screen_held = bool(event.pressed)
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
"""Standard and screen harnesses select independent, exact AOL profiles."""
|
||||
from dataclasses import replace
|
||||
import pytest
|
||||
from opendbc.car import structs
|
||||
from opendbc.car.tesla.values import CAR
|
||||
from opendbc.car.tesla.screen_button import apply_modern_aol, modern_aol_supported, modern_aol_qualified, screen_tap_supported
|
||||
from opendbc.car.tesla.tests.test_screen_button import screen_params
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.starpilot.aol.intent import AolSettings, ButtonType
|
||||
from openpilot.starpilot.car.tesla.aol import policy_for, native_accepts_cp
|
||||
from openpilot.starpilot.car.tesla.screen_intent import TeslaScreenCardIntent
|
||||
from openpilot.starpilot.car.tesla.tests.test_screen_intent import state
|
||||
from openpilot.starpilot.vehicle_preferences import VehicleStartupPreferences
|
||||
from openpilot.starpilot.galaxy.tests.test_tesla_screen_settings import vehicle
|
||||
from openpilot.starpilot.galaxy.settings import AuthorityContext, SettingsGateway
|
||||
|
||||
|
||||
@pytest.mark.parametrize('candidate', (CAR.TESLA_MODEL_3, CAR.TESLA_MODEL_Y))
|
||||
@pytest.mark.parametrize('harness', (False, True))
|
||||
@pytest.mark.parametrize('screen', (False, True))
|
||||
@pytest.mark.parametrize('long', (False, True))
|
||||
@pytest.mark.parametrize('brake', (False, True))
|
||||
def test_profiles_settings_and_exact_native_identity(candidate, harness, screen, long, brake):
|
||||
cp = screen_params(candidate, harness=harness, long=long)
|
||||
assert modern_aol_supported(cp) and policy_for(cp).settings_supported
|
||||
assert screen_tap_supported(cp) is harness
|
||||
apply_modern_aol(cp, True, screen, brake)
|
||||
expected = (512 if harness and screen else 2048) + int(long) + 1024 * brake
|
||||
assert cp.safetyConfigs[0].safetyParam == expected
|
||||
assert modern_aol_qualified(cp) and policy_for(cp).runtime_supported
|
||||
cp.alternativeExperience = 32
|
||||
assert native_accepts_cp(cp, 10, expected)
|
||||
assert not native_accepts_cp(cp, 10, expected ^ 1024)
|
||||
apply_modern_aol(cp, False, screen, brake)
|
||||
assert cp.safetyConfigs[0].safetyParam == int(long)
|
||||
assert not modern_aol_qualified(cp)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('brake', (False, True))
|
||||
@pytest.mark.parametrize('cooperative', (False, True))
|
||||
def test_actual_startup_selection_is_frozen_and_master_off_clears(tmp_path, brake, cooperative):
|
||||
params = Params(str(tmp_path))
|
||||
for key, value in (('AlwaysOnLateral', True), ('TeslaAOLDisengageOnBrake', brake), ('TeslaCoopSteering', cooperative)):
|
||||
params.put_bool(key, value, block=True)
|
||||
saved = VehicleStartupPreferences.read(params, enabled=True)
|
||||
cp = screen_params(harness=False)
|
||||
saved.prepare(cp)
|
||||
word = 2048 + 1024 * brake + 256 * cooperative
|
||||
assert cp.safetyConfigs[0].safetyParam == word
|
||||
params.put_bool('TeslaAOLDisengageOnBrake', not brake, block=True)
|
||||
saved.finalize(cp)
|
||||
assert cp.safetyConfigs[0].safetyParam == word
|
||||
params.put_bool('AlwaysOnLateral', False, block=True)
|
||||
VehicleStartupPreferences.read(params, enabled=True).prepare(cp)
|
||||
assert cp.safetyConfigs[0].safetyParam == 256 * cooperative
|
||||
assert not policy_for(cp).runtime_supported
|
||||
|
||||
|
||||
@pytest.mark.parametrize('brake', (False, True))
|
||||
def test_ordinary_intent_brake_rearm_and_screen_input_refusal(brake):
|
||||
cp = screen_params(harness=False)
|
||||
apply_modern_aol(cp, True, False, brake)
|
||||
owner = TeslaScreenCardIntent(cp, AolSettings(True, 0, 9, 0, (0, 0, 0), (0, 0, 0)))
|
||||
owner.update(state().as_reader(), now_ns=1)
|
||||
assert owner.allowed_latch
|
||||
owner.update(state(press=True).as_reader(), now_ns=2)
|
||||
assert owner.allowed_latch, 'screen input must have no authority on the standard harness'
|
||||
owner.update(state(brake=True).as_reader(), now_ns=3)
|
||||
assert owner.allowed_latch is not brake
|
||||
owner.update(state().as_reader(), now_ns=4)
|
||||
assert owner.allowed_latch is not brake
|
||||
owner.update(state(enabled=True).as_reader(), now_ns=5)
|
||||
assert owner.allowed_latch
|
||||
cs = state(enabled=True)
|
||||
cs.buttonEvents = [structs.CarState.ButtonEvent(type=ButtonType.cancel, pressed=True)]
|
||||
owner.update(cs.as_reader(), now_ns=6)
|
||||
assert not owner.allowed_latch
|
||||
owner.settings = replace(owner.settings, enabled=False)
|
||||
owner.update(state(enabled=True).as_reader(), now_ns=7)
|
||||
assert not owner.allowed_latch
|
||||
|
||||
|
||||
@pytest.mark.parametrize('harness', (False, True))
|
||||
def test_actual_ui_master_and_brake_toggle_with_either_harness(tmp_path, harness):
|
||||
params = Params(str(tmp_path))
|
||||
cp = vehicle(harness=harness)
|
||||
class Owner:
|
||||
context = AuthorityContext(True, cp, cp.to_bytes())
|
||||
def sample(self):
|
||||
return self.context
|
||||
owner = Owner()
|
||||
gateway = SettingsGateway(params, owner, clock=lambda: 100.0)
|
||||
page = gateway.page('vehicle', 'session', b'generation')
|
||||
labels = {row['label'] for row in page['rows']}
|
||||
assert 'Disengage AOL on Brake' in labels
|
||||
assert ('Three-Finger AOL Tap' in labels) is harness
|
||||
aol = gateway.page('aol', 'session', b'generation')
|
||||
assert next(row for row in aol['rows'] if row['label'] == 'Enable Always On Lateral')['available']
|
||||
index = next(i for i, row in enumerate(page['rows']) if row['label'] == 'Disengage AOL on Brake')
|
||||
intent = gateway.preview(page['view'], index, 0, 'session', b'generation', value='On')
|
||||
assert gateway.confirm(intent['intent'], 'session', b'generation')
|
||||
assert params.get_bool('TeslaAOLDisengageOnBrake')
|
||||
assert cp.safetyConfigs[0].safetyParam == 0
|
||||
@@ -0,0 +1,99 @@
|
||||
"""Ordinary harness AOL through real parser, intent, native and axis callers."""
|
||||
from types import SimpleNamespace
|
||||
from typing import cast
|
||||
|
||||
import pytest
|
||||
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car.tesla.interface import CarInterface
|
||||
from opendbc.car.tesla.screen_button import apply_modern_aol
|
||||
from opendbc.car.tesla.tests.test_screen_button import screen_params
|
||||
from opendbc.safety.tests.libsafety import libsafety_py
|
||||
from openpilot.cereal import log
|
||||
from openpilot.selfdrive.selfdrived.events import Events
|
||||
from openpilot.selfdrive.selfdrived.selfdrived import SelfdriveD
|
||||
from openpilot.starpilot.aol.intent import AolSettings
|
||||
from openpilot.starpilot.aol.runtime import AxisDecision
|
||||
from openpilot.starpilot.aol.wire import IntentState, PandaSlot, SafetyState
|
||||
from openpilot.starpilot.car.tesla.screen_intent import TeslaScreenCardIntent
|
||||
|
||||
|
||||
class HealthyPeers(dict):
|
||||
def all_checks(self, services):
|
||||
return all(service in self for service in services)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('cooperative', [False, True])
|
||||
@pytest.mark.parametrize('brake_disengage', [False, True])
|
||||
def test_ordinary_axis_actual_parser_native_and_selfdrived(cooperative, brake_disengage):
|
||||
cp = screen_params(harness=False)
|
||||
apply_modern_aol(cp, True, False, brake_disengage)
|
||||
cp.safetyConfigs[0].safetyParam |= 256 * cooperative
|
||||
cp.alternativeExperience = 32
|
||||
ci = CarInterface(cp)
|
||||
owner = TeslaScreenCardIntent(cp, AolSettings(True, 0., 0, 0, (0, 0, 0), (0, 0, 0)))
|
||||
packer = CANPacker('tesla_model3_party')
|
||||
peers = HealthyPeers(modelV2=SimpleNamespace(),
|
||||
extrinsicsCalibration=SimpleNamespace(calStatus=log.ExtrinsicsCalibration.Status.calibrated),
|
||||
driverMonitoringState=SimpleNamespace(lockout=False, alwaysOnLockout=False, alertLevel=0))
|
||||
sd = SimpleNamespace(CP=cp, active=False, enabled=False, initialized=True, sm=peers, aol_settings=owner.settings,
|
||||
aol_axis_decision=AxisDecision(), aol_car_state_log_ns=0, aol_session_id='screen-test',
|
||||
aol_dm_lateral_inhibit=False, nostalgia_paddle_cancel=False, aol_cruise_main_required=False,
|
||||
events=Events())
|
||||
native = libsafety_py.libsafety
|
||||
native.init_tests()
|
||||
native.set_alternative_experience(32)
|
||||
native.set_safety_hooks(10, cp.safetyConfigs[0].safetyParam)
|
||||
native.set_aol_test_heartbeat(True)
|
||||
try:
|
||||
for tick in range(250):
|
||||
now = 1_000_000_000 + tick * 10_000_000
|
||||
native.set_timer(now // 1000)
|
||||
stock_state = 0 if tick < 20 else 1 if 80 <= tick < 140 else 2
|
||||
messages: tuple[tuple[str, int, dict[str, float]], ...] = (
|
||||
('DAS_control', 2, {}), ('DAS_steeringControl', 2, {}),
|
||||
('SCCM_steeringAngleSensor', 2, {}), ('DAS_status', 2, {}), ('DAS_settings', 2, {}),
|
||||
('DI_speed', 0, {'DI_vehicleSpeed': 0}),
|
||||
('ESP_B', 0, {'ESP_vehicleSpeed': 0, 'ESP_wheelSpeedsQF': 1, 'ESP_vehicleStandstillSts': 1}),
|
||||
('EPAS3S_sysStatus', 0, {'EPAS3S_internalSAS': 0, 'EPAS3S_eacStatus': 1,
|
||||
'EPAS3S_handsOnLevel': 3 if tick >= 240 else 0, 'EPAS3S_torsionBarTorque': 0}),
|
||||
('DI_systemStatus', 0, {'DI_gear': 4, 'DI_accelPedalPos': 20 if tick % 30 < 15 else 0}),
|
||||
('ESP_status', 0, {'ESP_driverBrakeApply': 2 if 100 <= tick < 120 else 1}),
|
||||
('UI_warning', 0, {'buckleStatus': 1}),
|
||||
('DI_state', 0, {'DI_cruiseState': stock_state}),
|
||||
)
|
||||
frames = [packer.make_can_msg(name, bus, values) for name, bus, values in messages]
|
||||
for address, data, bus in frames:
|
||||
assert native.safety_rx_hook(libsafety_py.make_CANPacket(address, bus, data))
|
||||
native.safety_tick()
|
||||
cs = ci.update([(now, frames)]).as_reader()
|
||||
if tick > 1:
|
||||
assert cs.canValid and not cs.canTimeout
|
||||
owner.update(cs, now_ns=now)
|
||||
allowed, pause, long_pause = owner.output(cs)
|
||||
intention = IntentState('screen-card', tick + 1, now, now, now + 30_000_000, allowed, pause, long_pause, True, allowed)
|
||||
permission = native.aol_get_permission_mask()
|
||||
request = native.aol_get_request_mask()
|
||||
safety = SafetyState(1, True, now, now + 200_000_000, 10, cp.safetyConfigs[0].safetyParam,
|
||||
bool(permission & 1), bool(permission & 2), bool(request & 1), bool(request & 2),
|
||||
'test-panda', 'screen-test', now,
|
||||
(PandaSlot(0, 'test-panda', 10, cp.safetyConfigs[0].safetyParam, 32, bool(native.get_controls_allowed())),))
|
||||
sd.aol_car_state_log_ns = now
|
||||
sd.events = Events()
|
||||
sd.events.add(log.OnroadEvent.EventName.pcmDisable)
|
||||
if not cs.cruiseState.available:
|
||||
sd.events.add(log.OnroadEvent.EventName.wrongCarMode)
|
||||
decision = SelfdriveD._decide_aol_axes(cast(SelfdriveD, sd), cs, intention, safety, now_ns=now)
|
||||
sd.aol_axis_decision = decision
|
||||
native.aol_set_host_request(int(decision.desired_lateral))
|
||||
assert not decision.longitudinal_active
|
||||
if brake_disengage and tick == 140:
|
||||
assert decision.desired_lateral and not decision.lateral_active
|
||||
elif 22 <= tick < 240:
|
||||
assert decision.lateral_active is not (brake_disengage and 100 <= tick < 140)
|
||||
elif tick >= 240:
|
||||
assert not decision.lateral_active
|
||||
finally:
|
||||
native.init_tests()
|
||||
native.set_alternative_experience(0)
|
||||
native.set_safety_hooks(0, 0)
|
||||
@@ -80,8 +80,8 @@ def test_actual_startup_preferences_preserve_off_and_freeze_brake(tmp_path):
|
||||
cp = screen_params()
|
||||
saved = VehicleStartupPreferences.read(params, enabled=True)
|
||||
saved.prepare(cp)
|
||||
assert cp.safetyConfigs[0].safetyParam == 0
|
||||
assert policy_for(cp).settings_supported and not policy_for(cp).runtime_supported
|
||||
assert cp.safetyConfigs[0].safetyParam == 2048
|
||||
assert policy_for(cp).settings_supported and policy_for(cp).runtime_supported
|
||||
params.put_bool('TeslaAOLScreenTap', True, block=True)
|
||||
params.put_bool('TeslaAOLDisengageOnBrake', True, block=True)
|
||||
saved = VehicleStartupPreferences.read(params, enabled=True)
|
||||
|
||||
@@ -101,7 +101,7 @@ DESCRIPTIONS = {
|
||||
'TruckTuning': 'Softens small acceleration requests, reduces grade correction and smooths light braking on supported GM trucks.',
|
||||
'EVTuning': 'Selects EV acceleration curves for inherited acceleration presets.',
|
||||
'TeslaAOLScreenTap': 'Lets a three-finger tap on a supported Tesla screen toggle Always On Lateral.',
|
||||
'TeslaAOLDisengageOnBrake': 'Disarms screen-tap Always On Lateral when you press the brake.',
|
||||
'TeslaAOLDisengageOnBrake': 'Disarms Tesla Always On Lateral when you press the brake, with either harness.',
|
||||
'RemoteStartBootsComma': 'Wakes comma during GM remote start.',
|
||||
'HKGRemoteStartBootsComma': 'Wakes comma during Hyundai, Kia or Genesis remote climate operation.',
|
||||
'IgnoreIgnitionLine': 'Uses GM CAN ignition instead of the harness ignition signal.',
|
||||
@@ -194,7 +194,7 @@ INITIALIZATION = {
|
||||
'permission. An already admitted mode can be disabled live.'),
|
||||
'VoltSNG': 'Applies when the vehicle controller next starts: its fresh resume-plan input is created for the selected stop-and-go mode.',
|
||||
'TeslaAOLScreenTap': 'Requires the next vehicle startup to configure the screen-button CAN source and matching safety permission.',
|
||||
'TeslaAOLDisengageOnBrake': 'Requires the next vehicle startup to configure the screen-button brake/disarm safety permission.',
|
||||
'TeslaAOLDisengageOnBrake': 'Requires the next vehicle startup to configure the Tesla brake/disarm safety permission.',
|
||||
'RemoteStartBootsComma': 'Requires a device restart to load the matching Panda ignition firmware.',
|
||||
'HKGRemoteStartBootsComma': 'Requires a device restart to load the matching Panda ignition firmware.',
|
||||
'IgnoreIgnitionLine': 'Requires a device restart to load the matching Panda ignition firmware.',
|
||||
|
||||
@@ -66,7 +66,9 @@ class TestTeslaScreenSettings(unittest.TestCase):
|
||||
with self.subTest(options=options):
|
||||
cp = vehicle(**options)
|
||||
self.context = AuthorityContext(True, cp, cp.to_bytes())
|
||||
self.assertFalse(any('AOL' in row['label'] for row in self.page()['rows']))
|
||||
labels = {row['label'] for row in self.page()['rows']}
|
||||
self.assertNotIn('Three-Finger AOL Tap', labels)
|
||||
self.assertEqual('Disengage AOL on Brake' in labels, options != {'marker': False})
|
||||
|
||||
def test_stale_vehicle_or_onroad_change_rejects_saved_intent(self):
|
||||
intent = self.intent()
|
||||
|
||||
@@ -10,7 +10,7 @@ ROWS = (
|
||||
'Touch the Tesla screen with three fingers to toggle Always On Lateral. Requires Always On Lateral and the detected Vehicle CAN add-on cable. ' +
|
||||
'Does not engage or cancel cruise control. Applies after the next startup.'),
|
||||
('TeslaAOLDisengageOnBrake', 'Disengage AOL on Brake',
|
||||
'With screen-tap steering enabled, pressing the brake disarms Always On Lateral. ' +
|
||||
'Pressing the brake disarms Always On Lateral. ' +
|
||||
'Turn off to keep steering armed during braking. Applies after the next startup.'),
|
||||
)
|
||||
|
||||
@@ -23,9 +23,9 @@ class TeslaScreenFeature:
|
||||
self.vehicle_fingerprint = owner.vehicle_fingerprint
|
||||
|
||||
def capability(self):
|
||||
from opendbc.car.tesla.screen_button import screen_tap_supported
|
||||
from opendbc.car.tesla.screen_button import modern_aol_supported
|
||||
cp = self.vehicle_params()
|
||||
if not screen_tap_supported(cp):
|
||||
if not modern_aol_supported(cp):
|
||||
return None
|
||||
return (str(cp.carFingerprint), str(cp.brand), int(cp.flags), bool(cp.openpilotLongitudinalControl),
|
||||
bool(cp.pcmCruise), int(cp.alternativeExperience),
|
||||
@@ -36,7 +36,10 @@ class TeslaScreenFeature:
|
||||
if capability is None:
|
||||
return ()
|
||||
rows = []
|
||||
from opendbc.car.tesla.screen_button import screen_tap_supported
|
||||
for key, label, reason in ROWS:
|
||||
if key == 'TeslaAOLScreenTap' and not screen_tap_supported(self.vehicle_params()):
|
||||
continue
|
||||
raw, readable = read_saved(self.params, key, 8)
|
||||
valid = readable and raw in (None, b'0', b'1')
|
||||
rows.append(FeatureRow(key, label, ('On' if raw == b'1' else 'Off') if valid else 'Invalid saved choice',
|
||||
@@ -54,7 +57,8 @@ class TeslaScreenFeature:
|
||||
def authorized():
|
||||
return (self.authority('parked_preferences') and bool(request.vehicle_fingerprint) and
|
||||
self.vehicle_fingerprint() == request.vehicle_fingerprint and request.capability is not None and
|
||||
self.capability() == request.capability)
|
||||
self.capability() == request.capability and
|
||||
(request.key != 'TeslaAOLScreenTap' or any(row.key == request.key for row in self.rows(True))))
|
||||
|
||||
result = commit_exact(self.params, key=request.key, max_bytes=8, expected=request.expected,
|
||||
raw=b'1' if request.value == 'On' else b'0', authorized=authorized, temp_prefix='.tesla-screen-')
|
||||
|
||||
@@ -47,5 +47,5 @@ def confirmation_question(request):
|
||||
if request.key == "TeslaAOLScreenTap":
|
||||
return f"{verb} three-finger screen taps for Always On Lateral after the next startup? Cruise control stays independent."
|
||||
if request.key == "TeslaAOLDisengageOnBrake":
|
||||
return f"{verb} disarming Always On Lateral with the brake after the next startup? This applies to screen-tap steering."
|
||||
return f"{verb} disarming Always On Lateral with the brake after the next startup? This applies with either Tesla harness."
|
||||
raise ValueError("Unsupported vehicle preference")
|
||||
|
||||
@@ -38,6 +38,7 @@ class VehicleStartupPreferences:
|
||||
gm_auto_hold: bool = False
|
||||
volt_one_pedal: bool = False
|
||||
tesla_cooperative: bool = field(default=False, kw_only=True)
|
||||
tesla_aol: bool = field(default=False, kw_only=True)
|
||||
tesla_screen: bool = False
|
||||
tesla_screen_brake: bool = False
|
||||
gm_camera_pedal: bool = True
|
||||
@@ -99,9 +100,9 @@ class VehicleStartupPreferences:
|
||||
assist = assist_readable and assist_raw in (None, b"1")
|
||||
except (OSError, TypeError, ValueError):
|
||||
assist = False
|
||||
screen = bool(enabled and readable and safe in (None, b"0") and
|
||||
read_saved(params, 'AlwaysOnLateral', 8) == (b'1', True) and
|
||||
read_saved(params, 'TeslaAOLScreenTap', 8) == (b'1', True))
|
||||
tesla_aol = bool(enabled and readable and safe in (None, b"0") and
|
||||
read_saved(params, 'AlwaysOnLateral', 8) == (b'1', True))
|
||||
screen = tesla_aol and read_saved(params, 'TeslaAOLScreenTap', 8) == (b'1', True)
|
||||
cooperative = bool(enabled and readable and safe in (None, b"0") and
|
||||
read_saved(params, "TeslaCoopSteering", 8) == (b"1", True))
|
||||
screen_brake = read_saved(params, 'TeslaAOLDisengageOnBrake', 8) == (b'1', True)
|
||||
@@ -114,7 +115,7 @@ class VehicleStartupPreferences:
|
||||
subaru_sng=bool(enabled and readable and safe in (None, b"0") and read_saved(params, "SubaruSNG", 8) == (b"1", True)),
|
||||
subaru_software_cruise=bool(enabled and readable and safe in (None, b"0") and
|
||||
read_saved(params, "SubaruRedneckCruise", 8) == (b"1", True)),
|
||||
ford_bluepilot=tuple(ford_bluepilot(params, enabled=enabled).items()), tesla_screen=screen,
|
||||
ford_bluepilot=tuple(ford_bluepilot(params, enabled=enabled).items()), tesla_aol=tesla_aol, tesla_screen=screen,
|
||||
tesla_screen_brake=screen_brake, tesla_cooperative=cooperative, toyota_auto_hold=toyota,
|
||||
toyota_sng=bool(enabled and readable and safe in (None, b"0") and
|
||||
read_saved(params, "SNGHack", 8) == (b"1", True)), jeep_brake_hold=jeep_hold,
|
||||
@@ -192,8 +193,8 @@ class VehicleStartupPreferences:
|
||||
from opendbc.car.gm.values import apply_gm_auto_hold, apply_volt_one_pedal
|
||||
from openpilot.starpilot.car.tesla.preap_preferences import prepare_stock
|
||||
prepare_stock(cp, self.tesla_preap_stock, self.tesla_preap_radar, self.tesla_preap_pedal)
|
||||
from opendbc.car.tesla.screen_button import apply_screen_button
|
||||
apply_screen_button(cp, self.tesla_screen, self.tesla_screen_brake)
|
||||
from opendbc.car.tesla.screen_button import apply_modern_aol
|
||||
apply_modern_aol(cp, self.tesla_aol, self.tesla_screen, self.tesla_screen_brake)
|
||||
from openpilot.starpilot.car.tesla.coop_preferences import prepare
|
||||
prepare(cp, self.tesla_cooperative)
|
||||
self._prepare_honda_radar(cp)
|
||||
@@ -237,8 +238,8 @@ class VehicleStartupPreferences:
|
||||
admitted_hold = is_gm_auto_hold(cp)
|
||||
from openpilot.starpilot.car.tesla.preap_preferences import prepare_stock
|
||||
prepare_stock(cp, self.tesla_preap_stock, self.tesla_preap_radar, self.tesla_preap_pedal)
|
||||
from opendbc.car.tesla.screen_button import apply_screen_button
|
||||
apply_screen_button(cp, self.tesla_screen, self.tesla_screen_brake)
|
||||
from opendbc.car.tesla.screen_button import apply_modern_aol
|
||||
apply_modern_aol(cp, self.tesla_aol, self.tesla_screen, self.tesla_screen_brake)
|
||||
from openpilot.starpilot.car.tesla.coop_preferences import prepare
|
||||
prepare(cp, self.tesla_cooperative)
|
||||
self._prepare_honda_radar(cp)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
extern const uint8_t gitversion[29];
|
||||
const uint8_t gitversion[29] = "DEV-b6d2afbc-Pf38f504f-DEBUG";
|
||||
const uint8_t gitversion[29] = "DEV-dd532d60-Pf38f504f-DEBUG";
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
DEV-b6d2afbc-Pf38f504f-DEBUG
|
||||
DEV-dd532d60-Pf38f504f-DEBUG
|
||||
Binary file not shown.
+1
-1
@@ -1 +1 @@
|
||||
{"protected_inventory_sha256": "5708a3cd319cd5c96ad0fa1679a47712342bb2b70ff983b410bc308aad184d47", "schema_version": 1}
|
||||
{"protected_inventory_sha256": "689c16ce9b83e3e452ebc990e5feb7451c0c68de933c5e0e053439ac40f83292", "schema_version": 1}
|
||||
|
||||
Reference in New Issue
Block a user