Files
onepilot/starpilot/common/safe_mode.py
T
firestar5683 876003da36 patches
2026-04-04 19:50:14 -05:00

321 lines
8.1 KiB
Python

#!/usr/bin/env python3
from __future__ import annotations
from cereal import log
from openpilot.common.params import Params
SAFE_MODE_PARAM = "SafeMode"
SAFE_MODE_BACKUP_PARAM = "SafeModeBackup"
SAFE_MODE_ENFORCE_FRAMES = 20
# Driving-affecting settings that Safe Mode forces back to safe branch/stock behavior.
SAFE_MODE_MANAGED_KEYS = (
"ExperimentalMode",
"LongitudinalPersonality",
"Model",
"DrivingModel",
"DrivingModelName",
"ModelVersion",
"DrivingModelVersion",
"ModelRandomizer",
"DisableOpenpilotLongitudinal",
"ForceFingerprint",
"ClusterOffset",
"LateralTune",
"AdvancedLateralTune",
"ForceAutoTune",
"ForceAutoTuneOff",
"ForceTorqueController",
"SteerDelay",
"SteerFriction",
"SteerKP",
"SteerLatAccel",
"SteerRatio",
"LaneChanges",
"LaneChangeTime",
"LaneDetectionWidth",
"MinimumLaneChangeSpeed",
"NudgelessLaneChange",
"OneLaneChange",
"NNFF",
"NNFFLite",
"TurnDesires",
"QOLLateral",
"PauseLateralSpeed",
"PauseLateralOnSignal",
"LongitudinalTune",
"AdvancedLongitudinalTune",
"EVTuning",
"TruckTuning",
"CustomAccelProfile",
"CustomAccelProfileInitialized",
"CustomAccelProfile0MPH",
"CustomAccelProfile11MPH",
"CustomAccelProfile22MPH",
"CustomAccelProfile34MPH",
"CustomAccelProfile45MPH",
"CustomAccelProfile56MPH",
"CustomAccelProfile89MPH",
"LongitudinalActuatorDelay",
"MaxDesiredAcceleration",
"StartAccel",
"StopAccel",
"StoppingDecelRate",
"VEgoStarting",
"VEgoStopping",
"AccelerationProfile",
"DecelerationProfile",
"HumanAcceleration",
"HumanFollowing",
"HumanLaneChanges",
"LeadDetectionThreshold",
"RecoveryPower",
"StopDistance",
"TacoTune",
"QOLLongitudinal",
"ForceStops",
"ForceStandstill",
"IncreasedStoppedDistance",
"MapGears",
"MapAcceleration",
"MapDeceleration",
"ReverseCruise",
"SetSpeedOffset",
"WeatherPresets",
"IncreaseFollowingLowVisibility",
"IncreaseFollowingRain",
"IncreaseFollowingRainStorm",
"IncreaseFollowingSnow",
"IncreasedStoppedDistanceLowVisibility",
"IncreasedStoppedDistanceRain",
"IncreasedStoppedDistanceRainStorm",
"IncreasedStoppedDistanceSnow",
"ReduceAccelerationLowVisibility",
"ReduceAccelerationRain",
"ReduceAccelerationRainStorm",
"ReduceAccelerationSnow",
"ReduceLateralAccelerationLowVisibility",
"ReduceLateralAccelerationRain",
"ReduceLateralAccelerationRainStorm",
"ReduceLateralAccelerationSnow",
"ConditionalExperimental",
"CECurves",
"CECurvesLead",
"CELead",
"CESlowerLead",
"CEStoppedLead",
"CESpeed",
"CESpeedLead",
"CEModelStopTime",
"CEStopLights",
"CESignalSpeed",
"CESignalLaneDetection",
"CurveSpeedController",
"SpeedLimitController",
"SetSpeedLimit",
"SLCFallback",
"SLCMapboxFiller",
"SLCOverride",
"SLCConfirmation",
"SLCConfirmationHigher",
"SLCConfirmationLower",
"SLCLookaheadHigher",
"SLCLookaheadLower",
"SLCPriority1",
"SLCPriority2",
"Offset1",
"Offset2",
"Offset3",
"Offset4",
"Offset5",
"Offset6",
"Offset7",
"SpeedLimitFiller",
"VisionSpeedLimitDetection",
"CustomPersonalities",
"TrafficPersonalityProfile",
"AggressivePersonalityProfile",
"StandardPersonalityProfile",
"RelaxedPersonalityProfile",
"TrafficFollow",
"TrafficJerkAcceleration",
"TrafficJerkDeceleration",
"TrafficJerkDanger",
"TrafficJerkSpeed",
"TrafficJerkSpeedDecrease",
"AggressiveFollow",
"AggressiveFollowHigh",
"AggressiveJerkAcceleration",
"AggressiveJerkDeceleration",
"AggressiveJerkDanger",
"AggressiveJerkSpeed",
"AggressiveJerkSpeedDecrease",
"StandardFollow",
"StandardFollowHigh",
"StandardJerkAcceleration",
"StandardJerkDeceleration",
"StandardJerkDanger",
"StandardJerkSpeed",
"StandardJerkSpeedDecrease",
"RelaxedFollow",
"RelaxedFollowHigh",
"RelaxedJerkAcceleration",
"RelaxedJerkDeceleration",
"RelaxedJerkDanger",
"RelaxedJerkSpeed",
"RelaxedJerkSpeedDecrease",
"FrogsGoMoosTweak",
"SNGHack",
"SubaruSNG",
"VoltSNG",
"GMPedalLongitudinal",
"LongPitch",
"TacoTuneHacks",
)
SAFE_MODE_FIXED_VALUES = {
"ExperimentalMode": False,
"LongitudinalPersonality": int(log.LongitudinalPersonality.relaxed),
}
SAFE_MODE_STOCK_PARAM_MAP = {
"SteerDelay": "SteerDelayStock",
"SteerFriction": "SteerFrictionStock",
"SteerKP": "SteerKPStock",
"SteerLatAccel": "SteerLatAccelStock",
"SteerRatio": "SteerRatioStock",
"LongitudinalActuatorDelay": "LongitudinalActuatorDelayStock",
"StartAccel": "StartAccelStock",
"StopAccel": "StopAccelStock",
"StoppingDecelRate": "StoppingDecelRateStock",
"VEgoStarting": "VEgoStartingStock",
"VEgoStopping": "VEgoStoppingStock",
}
SAFE_MODE_MEMORY_VALUES = {
"CEStatus": 0,
}
def safe_mode_enabled(params_raw: Params | None = None) -> bool:
params_raw = params_raw or Params()
return params_raw.get_bool(SAFE_MODE_PARAM)
def _load_backup(params_raw: Params) -> dict[str, dict]:
backup = params_raw.get(SAFE_MODE_BACKUP_PARAM)
return backup if isinstance(backup, dict) else {}
def _current_entry(params_raw: Params, key: str) -> dict[str, object]:
value = params_raw.get(key)
return {"present": value is not None, "value": value}
def _safe_value(params: Params, key: str):
if key in SAFE_MODE_FIXED_VALUES:
return SAFE_MODE_FIXED_VALUES[key]
stock_param = SAFE_MODE_STOCK_PARAM_MAP.get(key)
if stock_param is not None:
stock_value = params.get(stock_param)
if stock_value is not None:
return stock_value
return params.get_stock_value(key)
def _apply_value(params_raw: Params, key: str, value) -> bool:
current = params_raw.get(key)
if value is None:
if current is None:
return False
params_raw.remove(key)
return True
if current == value:
return False
params_raw.put(key, value)
return True
def _mark_toggle_update(params_memory: Params | None) -> None:
if params_memory is None:
return
params_memory.put_bool("StarPilotTogglesUpdated", True)
def apply_safe_mode(params: Params, params_raw: Params, params_memory: Params | None = None, *,
ensure_backup: bool = True) -> bool:
changed = False
if ensure_backup:
backup = _load_backup(params_raw)
missing_backup_keys = [key for key in SAFE_MODE_MANAGED_KEYS if key not in backup]
if missing_backup_keys:
backup = dict(backup)
for key in missing_backup_keys:
backup[key] = _current_entry(params_raw, key)
params_raw.put(SAFE_MODE_BACKUP_PARAM, backup)
changed = True
for key in SAFE_MODE_MANAGED_KEYS:
changed |= _apply_value(params_raw, key, _safe_value(params, key))
if params_memory is not None:
for key, value in SAFE_MODE_MEMORY_VALUES.items():
if params_memory.get(key) != value:
params_memory.put(key, value)
changed = True
if changed:
params_raw.put_bool("OnroadCycleRequested", True)
_mark_toggle_update(params_memory)
return changed
def restore_safe_mode(params_raw: Params, params_memory: Params | None = None) -> bool:
changed = False
backup = _load_backup(params_raw)
if not backup:
if params_raw.get(SAFE_MODE_BACKUP_PARAM) is not None:
params_raw.remove(SAFE_MODE_BACKUP_PARAM)
changed = True
if params_memory is not None:
for key, value in SAFE_MODE_MEMORY_VALUES.items():
if params_memory.get(key) != value:
params_memory.put(key, value)
changed = True
if changed:
params_raw.put_bool("OnroadCycleRequested", True)
_mark_toggle_update(params_memory)
return changed
restore_keys = dict.fromkeys((*SAFE_MODE_MANAGED_KEYS, *backup.keys()))
for key in restore_keys:
entry = backup.get(key, {"present": False, "value": None})
restore_value = entry.get("value") if entry.get("present") else None
changed |= _apply_value(params_raw, key, restore_value)
if params_raw.get(SAFE_MODE_BACKUP_PARAM) is not None:
params_raw.remove(SAFE_MODE_BACKUP_PARAM)
changed = True
if params_memory is not None:
for key, value in SAFE_MODE_MEMORY_VALUES.items():
if params_memory.get(key) != value:
params_memory.put(key, value)
changed = True
if changed:
params_raw.put_bool("OnroadCycleRequested", True)
_mark_toggle_update(params_memory)
return changed