wip
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
# NOTE: Do NOT import anything here that needs be built (e.g. params)
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
@@ -29,7 +28,7 @@ def build(spinner: Spinner, dirty: bool = False, minimal: bool = False) -> None:
|
||||
|
||||
# building with all cores can result in using too
|
||||
# much memory, so retry with less parallelism
|
||||
compile_output: List[bytes] = []
|
||||
compile_output: list[bytes] = []
|
||||
for n in (nproc, nproc/2, 1):
|
||||
compile_output.clear()
|
||||
scons: subprocess.Popen = subprocess.Popen(["scons", f"-j{int(n)}", "--cache-populate", *extra_args], cwd=BASEDIR, env=env, stderr=subprocess.PIPE)
|
||||
|
||||
@@ -5,16 +5,15 @@ import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from typing import List, Tuple, Union
|
||||
|
||||
from cereal import log
|
||||
import cereal.messaging as messaging
|
||||
import openpilot.selfdrive.sentry as sentry
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.common.params import Params, ParamKeyType
|
||||
from openpilot.common.text_window import TextWindow
|
||||
from openpilot.common.time import system_time_valid
|
||||
from openpilot.system.hardware import HARDWARE, PC
|
||||
from openpilot.selfdrive.manager.helpers import unblock_stdout, write_onroad_params, save_bootlog
|
||||
from openpilot.selfdrive.manager.process import ensure_running
|
||||
@@ -25,63 +24,61 @@ from openpilot.system.version import is_dirty, get_commit, get_version, get_orig
|
||||
get_normalized_origin, terms_version, training_version, \
|
||||
is_tested_branch, is_release_branch, get_commit_date
|
||||
|
||||
from openpilot.selfdrive.frogpilot.functions.frogpilot_functions import DEFAULT_MODEL
|
||||
from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_functions import FrogPilotFunctions
|
||||
from openpilot.selfdrive.frogpilot.controls.lib.model_manager import DEFAULT_MODEL, DEFAULT_MODEL_NAME, delete_deprecated_models
|
||||
|
||||
|
||||
def manager_init() -> None:
|
||||
def frogpilot_boot_functions(frogpilot_functions):
|
||||
try:
|
||||
delete_deprecated_models()
|
||||
|
||||
while not system_time_valid():
|
||||
print("Waiting for system time to become valid...")
|
||||
time.sleep(1)
|
||||
|
||||
try:
|
||||
frogpilot_functions.backup_frogpilot()
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Failed to backup FrogPilot. Error: {e}")
|
||||
return
|
||||
|
||||
try:
|
||||
frogpilot_functions.backup_toggles()
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Failed to backup toggles. Error: {e}")
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
print(f"An unexpected error occurred: {e}")
|
||||
|
||||
def manager_init(frogpilot_functions) -> None:
|
||||
frogpilot_boot = threading.Thread(target=frogpilot_boot_functions, args=(frogpilot_functions,))
|
||||
frogpilot_boot.start()
|
||||
|
||||
save_bootlog()
|
||||
|
||||
params = Params()
|
||||
params_storage = Params("/persist/comma/params")
|
||||
params_storage = Params("/persist/params")
|
||||
params.clear_all(ParamKeyType.CLEAR_ON_MANAGER_START)
|
||||
params.clear_all(ParamKeyType.CLEAR_ON_ONROAD_TRANSITION)
|
||||
params.clear_all(ParamKeyType.CLEAR_ON_OFFROAD_TRANSITION)
|
||||
if is_release_branch():
|
||||
params.clear_all(ParamKeyType.DEVELOPMENT_ONLY)
|
||||
|
||||
############### Remove this after the April 26th update ###############
|
||||
|
||||
previous_speed_limit = params.get_float("PreviousSpeedLimit")
|
||||
if previous_speed_limit >= 50:
|
||||
params.put_float("PreviousSpeedLimit", previous_speed_limit / 100)
|
||||
|
||||
for priority_key in ["SLCPriority", "SLCPriority1", "SLCPriority2", "SLCPriority3"]:
|
||||
priority_value = params.get(priority_key)
|
||||
if isinstance(priority_value, int):
|
||||
params.remove(priority_key)
|
||||
|
||||
attributes = ["AggressiveFollow", "StandardFollow", "RelaxedFollow", "AggressiveJerk", "StandardJerk", "RelaxedJerk"]
|
||||
values = {attr: params.get_float(attr) for attr in attributes}
|
||||
if any(value > 5 for value in values.values()):
|
||||
for attr, value in values.items():
|
||||
params.put_float(attr, value / 10)
|
||||
|
||||
if params.get_bool("SilentMode"):
|
||||
attributes = ["DisengageVolume", "EngageVolume", "PromptVolume", "PromptDistractedVolume", "RefuseVolume", "WarningSoftVolume", "WarningImmediateVolume"]
|
||||
for attr in attributes:
|
||||
params.put_float(attr, 0)
|
||||
params.put_bool("SilentMode", False)
|
||||
|
||||
#######################################################################
|
||||
|
||||
# Check if the currently selected model still exists
|
||||
current_model = params.get("Model", encoding='utf-8')
|
||||
|
||||
if current_model != DEFAULT_MODEL:
|
||||
models_folder = os.path.join(BASEDIR, 'selfdrive/modeld/models/models')
|
||||
model_exists = current_model in [os.path.splitext(file)[0] for file in os.listdir(models_folder)]
|
||||
|
||||
if not model_exists:
|
||||
params.remove("Model")
|
||||
|
||||
default_params: List[Tuple[str, Union[str, bytes]]] = [
|
||||
default_params: list[tuple[str, str | bytes]] = [
|
||||
("CarParamsPersistent", ""),
|
||||
("CompletedTrainingVersion", "0"),
|
||||
("DisengageOnAccelerator", "0"),
|
||||
("ExperimentalLongitudinalEnabled", "1"),
|
||||
("GsmMetered", "1"),
|
||||
("HasAcceptedTerms", "0"),
|
||||
("IsLdwEnabled", "0"),
|
||||
("IsMetric", "0"),
|
||||
("LanguageSetting", "main_en"),
|
||||
("NavSettingLeftSide", "0"),
|
||||
("NavSettingTime24h", "0"),
|
||||
("OpenpilotEnabledToggle", "1"),
|
||||
("UpdaterAvailableBranches", ""),
|
||||
("RecordFront", "0"),
|
||||
("LongitudinalPersonality", str(log.LongitudinalPersonality.standard)),
|
||||
|
||||
# Default FrogPilot parameters
|
||||
@@ -89,15 +86,17 @@ def manager_init() -> None:
|
||||
("AccelerationProfile", "2"),
|
||||
("AdjacentPath", "0"),
|
||||
("AdjacentPathMetrics", "0"),
|
||||
("AdjustablePersonalities", "1"),
|
||||
("AggressiveAcceleration", "1"),
|
||||
("AggressiveFollow", "1.25"),
|
||||
("AggressiveJerk", "0.5"),
|
||||
("AlertVolumeControl", "0"),
|
||||
("AlwaysOnLateral", "1"),
|
||||
("AlwaysOnLateralMain", "0"),
|
||||
("AMapKey1", ""),
|
||||
("AMapKey2", ""),
|
||||
("AutomaticUpdates", "0"),
|
||||
("BlindSpotPath", "1"),
|
||||
("CameraView", "1"),
|
||||
("CameraView", "2"),
|
||||
("CarMake", ""),
|
||||
("CarModel", ""),
|
||||
("CECurves", "1"),
|
||||
@@ -106,18 +105,21 @@ def manager_init() -> None:
|
||||
("CENavigationLead", "1"),
|
||||
("CENavigationTurns", "1"),
|
||||
("CESignal", "1"),
|
||||
("CESlowerLead", "0"),
|
||||
("CESlowerLead", "1"),
|
||||
("CESpeed", "0"),
|
||||
("CESpeedLead", "0"),
|
||||
("CEStopLights", "1"),
|
||||
("CEStopLightsLead", "0"),
|
||||
("Compass", "0"),
|
||||
("Compass", "1"),
|
||||
("ConditionalExperimental", "1"),
|
||||
("CrosstrekTorque", "1"),
|
||||
("CurveSensitivity", "100"),
|
||||
("CustomAlerts", "1"),
|
||||
("CustomColors", "1"),
|
||||
("CustomCruise", "1"),
|
||||
("CustomCruiseLong", "5"),
|
||||
("CustomIcons", "1"),
|
||||
("CustomPaths", "1"),
|
||||
("CustomPersonalities", "1"),
|
||||
("CustomSignals", "1"),
|
||||
("CustomSounds", "1"),
|
||||
@@ -125,6 +127,8 @@ def manager_init() -> None:
|
||||
("CustomUI", "1"),
|
||||
("CydiaTune", "0"),
|
||||
("DecelerationProfile", "1"),
|
||||
("DeveloperUI", "0"),
|
||||
("DeviceManagement", "1"),
|
||||
("DeviceShutdown", "9"),
|
||||
("DisableMTSCSmoothing", "0"),
|
||||
("DisableOnroadUploads", "0"),
|
||||
@@ -133,26 +137,22 @@ def manager_init() -> None:
|
||||
("DisengageVolume", "100"),
|
||||
("DragonPilotTune", "0"),
|
||||
("DriverCamera", "0"),
|
||||
("DriveStats", "1"),
|
||||
("DynamicPathWidth", "0"),
|
||||
("EngageVolume", "100"),
|
||||
("EVTable", "1"),
|
||||
("ExperimentalModeActivation", "1"),
|
||||
("ExperimentalModeViaDistance", "0"),
|
||||
("ExperimentalModeViaLKAS", "0"),
|
||||
("ExperimentalModeViaScreen", "1"),
|
||||
("ExperimentalModeViaDistance", "1"),
|
||||
("ExperimentalModeViaLKAS", "1"),
|
||||
("ExperimentalModeViaTap", "0"),
|
||||
("Fahrenheit", "0"),
|
||||
("FireTheBabysitter", "0"),
|
||||
("ForceAutoTune", "0"),
|
||||
("ForceAutoTune", "1"),
|
||||
("ForceFingerprint", "0"),
|
||||
("ForceMPHDashboard", "0"),
|
||||
("FPSCounter", "0"),
|
||||
("FrogPilotDrives", "0"),
|
||||
("FrogPilotKilometers", "0"),
|
||||
("FrogPilotMinutes", "0"),
|
||||
("FrogsGoMooTune", "1"),
|
||||
("FullMap", "0"),
|
||||
("GasRegenCmd", "0"),
|
||||
("GMapKey", ""),
|
||||
("GoatScream", "1"),
|
||||
("GreenLightAlert", "0"),
|
||||
("HideAlerts", "0"),
|
||||
@@ -166,46 +166,56 @@ def manager_init() -> None:
|
||||
("HideUIElements", "0"),
|
||||
("HigherBitrate", "0"),
|
||||
("HolidayThemes", "1"),
|
||||
("IncreaseThermalLimits", "0"),
|
||||
("LaneChangeTime", "0"),
|
||||
("LaneDetection", "1"),
|
||||
("LaneDetectionWidth", "60"),
|
||||
("LaneLinesWidth", "4"),
|
||||
("LateralTune", "1"),
|
||||
("LeadDepartingAlert", "0"),
|
||||
("LeadDetectionThreshold", "25"),
|
||||
("LeadDetectionThreshold", "35"),
|
||||
("LeadInfo", "0"),
|
||||
("LockDoors", "0"),
|
||||
("LockDoors", "1"),
|
||||
("LongitudinalTune", "1"),
|
||||
("LongPitch", "1"),
|
||||
("LoudBlindspotAlert", "0"),
|
||||
("LowerVolt", "1"),
|
||||
("LowVoltageShutdown", "11.8"),
|
||||
("kiV1", "0.60"),
|
||||
("kiV2", "0.45"),
|
||||
("kiV3", "0.30"),
|
||||
("kiV4", "0.15"),
|
||||
("kpV1", "1.50"),
|
||||
("kpV2", "1.00"),
|
||||
("kpV3", "0.75"),
|
||||
("kpV4", "0.50"),
|
||||
("MapsSelected", ""),
|
||||
("MapboxPublicKey", ""),
|
||||
("MapboxSecretKey", ""),
|
||||
("MapStyle", "0"),
|
||||
("MTSCAggressiveness", "100"),
|
||||
("MTSCCurvatureCheck", "0"),
|
||||
("MTSCLimit", "0"),
|
||||
("Model", DEFAULT_MODEL),
|
||||
("ModelName", DEFAULT_MODEL_NAME),
|
||||
("ModelSelector", "1"),
|
||||
("ModelUI", "1"),
|
||||
("MTSCEnabled", "1"),
|
||||
("MuteOverheated", "0"),
|
||||
("NavChill", "0"),
|
||||
("NNFF", "1"),
|
||||
("NNFFLite", "1"),
|
||||
("NoLogging", "0"),
|
||||
("NoUploads", "0"),
|
||||
("NudgelessLaneChange", "1"),
|
||||
("NumericalTemp", "0"),
|
||||
("OfflineMode", "0"),
|
||||
("OfflineMode", "1"),
|
||||
("Offset1", "5"),
|
||||
("Offset2", "5"),
|
||||
("Offset3", "5"),
|
||||
("Offset4", "10"),
|
||||
("OneLaneChange", "1"),
|
||||
("OnroadDistanceButton", "0"),
|
||||
("PathEdgeWidth", "20"),
|
||||
("PathWidth", "61"),
|
||||
("PauseAOLOnBrake", "0"),
|
||||
("PauseLateralOnSignal", "0"),
|
||||
("PedalsOnUI", "1"),
|
||||
("PersonalitiesViaScreen", "1"),
|
||||
("PersonalitiesViaWheel", "1"),
|
||||
("PreferredSchedule", "0"),
|
||||
("PromptVolume", "100"),
|
||||
("PromptDistractedVolume", "100"),
|
||||
@@ -232,21 +242,27 @@ def manager_init() -> None:
|
||||
("ShowCPU", "0"),
|
||||
("ShowGPU", "0"),
|
||||
("ShowIP", "0"),
|
||||
("ShowJerk", "1"),
|
||||
("ShowMemoryUsage", "0"),
|
||||
("ShowSLCOffset", "0"),
|
||||
("ShowSLCOffset", "1"),
|
||||
("ShowSLCOffsetUI", "1"),
|
||||
("ShowStorageLeft", "0"),
|
||||
("ShowStorageUsed", "0"),
|
||||
("ShowTuning", "1"),
|
||||
("Sidebar", "0"),
|
||||
("SLCConfirmation", "1"),
|
||||
("SLCConfirmationLower", "1"),
|
||||
("SLCConfirmationHigher", "1"),
|
||||
("SLCFallback", "2"),
|
||||
("SLCLookaheadHigher", "5"),
|
||||
("SLCLookaheadLower", "5"),
|
||||
("SLCOverride", "1"),
|
||||
("SLCPriority1", "Dashboard"),
|
||||
("SLCPriority2", "Offline Maps"),
|
||||
("SLCPriority3", "Navigation"),
|
||||
("SmoothBraking", "1"),
|
||||
("SmoothBrakingFarLead", "0"),
|
||||
("SmoothBrakingJerk", "0"),
|
||||
("SNGHack", "1"),
|
||||
("SpeedLimitChangedAlert", "1"),
|
||||
("SpeedLimitController", "1"),
|
||||
@@ -256,13 +272,17 @@ def manager_init() -> None:
|
||||
("SteerRatio", "0"),
|
||||
("StockTune", "0"),
|
||||
("StoppingDistance", "0"),
|
||||
("TacoTune", "1"),
|
||||
("ToyotaDoors", "0"),
|
||||
("TrafficFollow", "0.5"),
|
||||
("TrafficJerk", "1"),
|
||||
("TrafficMode", "0"),
|
||||
("Tuning", "1"),
|
||||
("TurnAggressiveness", "100"),
|
||||
("TurnDesires", "0"),
|
||||
("UnlimitedLength", "1"),
|
||||
("UpdateSchedule", "0"),
|
||||
("UseLateralJerk", "0"),
|
||||
("UseSI", "0"),
|
||||
("UnlockDoors", "1"),
|
||||
("UseSI", "1"),
|
||||
("UseVienna", "0"),
|
||||
("VisionTurnControl", "1"),
|
||||
("WarningSoftVolume", "100"),
|
||||
@@ -347,23 +367,15 @@ def manager_cleanup() -> None:
|
||||
cloudlog.info("everything is dead")
|
||||
|
||||
|
||||
def update_frogpilot_params(params, params_memory):
|
||||
keys = ["DisableOnroadUploads", "FireTheBabysitter", "NoLogging", "NoUploads", "RoadNameUI", "SpeedLimitController"]
|
||||
for key in keys:
|
||||
params_memory.put_bool(key, params.get_bool(key))
|
||||
|
||||
def manager_thread() -> None:
|
||||
def manager_thread(frogpilot_functions) -> None:
|
||||
cloudlog.bind(daemon="manager")
|
||||
cloudlog.info("manager start")
|
||||
cloudlog.info({"environ": os.environ})
|
||||
|
||||
params = Params()
|
||||
params_memory = Params("/dev/shm/params")
|
||||
params_storage = Params("/persist/comma/params")
|
||||
|
||||
update_frogpilot_params(params, params_memory)
|
||||
|
||||
ignore: List[str] = []
|
||||
ignore: list[str] = []
|
||||
if params.get("DongleId", encoding='utf8') in (None, UNREGISTERED_DONGLE_ID):
|
||||
ignore += ["manage_athenad", "uploader"]
|
||||
if os.getenv("NOBOARD") is not None:
|
||||
@@ -374,20 +386,23 @@ def manager_thread() -> None:
|
||||
pm = messaging.PubMaster(['managerState'])
|
||||
|
||||
write_onroad_params(False, params)
|
||||
ensure_running(managed_processes.values(), False, params=params, params_memory=params_memory, CP=sm['carParams'], not_run=ignore)
|
||||
ensure_running(managed_processes.values(), False, params=params, CP=sm['carParams'], not_run=ignore)
|
||||
|
||||
started_prev = False
|
||||
|
||||
while True:
|
||||
sm.update(1000)
|
||||
|
||||
openpilot_crashed = os.path.isfile(os.path.join(sentry.CRASHES_DIR, 'error.txt'))
|
||||
if openpilot_crashed:
|
||||
frogpilot_functions.delete_logs()
|
||||
|
||||
started = sm['deviceState'].started
|
||||
|
||||
if started and not started_prev:
|
||||
params.clear_all(ParamKeyType.CLEAR_ON_ONROAD_TRANSITION)
|
||||
|
||||
# Clear the error log on offroad transition to prevent old errors from hanging around
|
||||
if os.path.isfile(os.path.join(sentry.CRASHES_DIR, 'error.txt')):
|
||||
if openpilot_crashed:
|
||||
os.remove(os.path.join(sentry.CRASHES_DIR, 'error.txt'))
|
||||
|
||||
elif not started and started_prev:
|
||||
@@ -400,9 +415,9 @@ def manager_thread() -> None:
|
||||
|
||||
started_prev = started
|
||||
|
||||
ensure_running(managed_processes.values(), started, params=params, params_memory=params_memory, CP=sm['carParams'], not_run=ignore)
|
||||
ensure_running(managed_processes.values(), started, params=params, CP=sm['carParams'], not_run=ignore)
|
||||
|
||||
running = ' '.join("%s%s\u001b[0m" % ("\u001b[32m" if p.proc.is_alive() else "\u001b[31m", p.name)
|
||||
running = ' '.join("{}{}\u001b[0m".format("\u001b[32m" if p.proc.is_alive() else "\u001b[31m", p.name)
|
||||
for p in managed_processes.values() if p.proc)
|
||||
print(running)
|
||||
cloudlog.debug(running)
|
||||
@@ -414,7 +429,7 @@ def manager_thread() -> None:
|
||||
|
||||
# Exit main loop when uninstall/shutdown/reboot is needed
|
||||
shutdown = False
|
||||
for param in ("DoUninstall", "DoShutdown", "DoReboot"):
|
||||
for param in ("DoUninstall", "DoShutdown", "DoReboot", "DoSoftReboot"):
|
||||
if params.get_bool(param):
|
||||
shutdown = True
|
||||
params.put("LastManagerExitReason", f"{param} {datetime.datetime.now()}")
|
||||
@@ -423,55 +438,17 @@ def manager_thread() -> None:
|
||||
if shutdown:
|
||||
break
|
||||
|
||||
if params_memory.get_bool("FrogPilotTogglesUpdated"):
|
||||
update_frogpilot_params(params, params_memory)
|
||||
|
||||
def backup_openpilot():
|
||||
# Configure the auto backup generator
|
||||
backup_dir_path = '/data/backups'
|
||||
Path(backup_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Sort backups by creation time and only keep the 5 newest auto generated ones
|
||||
auto_backups = sorted([f for f in os.listdir(backup_dir_path) if f.endswith("_auto")],
|
||||
key=lambda x: os.path.getmtime(os.path.join(backup_dir_path, x)))
|
||||
for old_backup in auto_backups:
|
||||
subprocess.run(['sudo', 'rm', '-rf', os.path.join(backup_dir_path, old_backup)], check=True)
|
||||
print(f"Deleted oldest backup to maintain limit: {old_backup}")
|
||||
|
||||
# Generate the backup folder name from the current git commit and branch name
|
||||
branch = get_short_branch()
|
||||
commit = get_commit_date()[12:-16]
|
||||
backup_folder_name = f"{branch}_{commit}_auto"
|
||||
|
||||
# Check if the backup folder already exists
|
||||
backup_path = os.path.join(backup_dir_path, backup_folder_name)
|
||||
|
||||
if os.path.exists(backup_path):
|
||||
print(f"Backup folder {backup_folder_name} already exists. Skipping backup.")
|
||||
return
|
||||
|
||||
# Create the backup directory and copy openpilot to it
|
||||
Path(backup_path).mkdir(parents=True, exist_ok=True)
|
||||
subprocess.run(['sudo', 'cp', '-a', '/data/openpilot/.', backup_path + '/'], check=True)
|
||||
print(f"Successfully backed up openpilot to {backup_folder_name}.")
|
||||
|
||||
def main() -> None:
|
||||
# Create the long term param storage folder
|
||||
try:
|
||||
# Attempt to remount /persist as read-write
|
||||
subprocess.run(['sudo', 'mount', '-o', 'remount,rw', '/persist'], check=True)
|
||||
print("Successfully remounted /persist as read-write.")
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Failed to remount /persist. Error: {e}")
|
||||
frogpilot_functions = FrogPilotFunctions()
|
||||
|
||||
# Backup the current version of openpilot
|
||||
try:
|
||||
backup_thread = threading.Thread(target=backup_openpilot)
|
||||
backup_thread.start()
|
||||
frogpilot_functions.setup_frogpilot()
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Failed to backup openpilot. Error: {e}")
|
||||
print(f"Failed to setup FrogPilot. Error: {e}")
|
||||
return
|
||||
|
||||
manager_init()
|
||||
manager_init(frogpilot_functions)
|
||||
if os.getenv("PREPAREONLY") is not None:
|
||||
return
|
||||
|
||||
@@ -479,7 +456,7 @@ def main() -> None:
|
||||
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit(1))
|
||||
|
||||
try:
|
||||
manager_thread()
|
||||
manager_thread(frogpilot_functions)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
sentry.capture_exception()
|
||||
@@ -489,10 +466,13 @@ def main() -> None:
|
||||
params = Params()
|
||||
if params.get_bool("DoUninstall"):
|
||||
cloudlog.warning("uninstalling")
|
||||
HARDWARE.uninstall()
|
||||
frogpilot_functions.uninstall_frogpilot()
|
||||
elif params.get_bool("DoReboot"):
|
||||
cloudlog.warning("reboot")
|
||||
HARDWARE.reboot()
|
||||
elif params.get_bool("DoSoftReboot"):
|
||||
cloudlog.warning("softreboot")
|
||||
HARDWARE.soft_reboot()
|
||||
elif params.get_bool("DoShutdown"):
|
||||
cloudlog.warning("shutdown")
|
||||
HARDWARE.shutdown()
|
||||
|
||||
@@ -4,7 +4,7 @@ import signal
|
||||
import struct
|
||||
import time
|
||||
import subprocess
|
||||
from typing import Optional, Callable, List, ValuesView
|
||||
from collections.abc import Callable, ValuesView
|
||||
from abc import ABC, abstractmethod
|
||||
from multiprocessing import Process
|
||||
|
||||
@@ -47,7 +47,7 @@ def launcher(proc: str, name: str) -> None:
|
||||
raise
|
||||
|
||||
|
||||
def nativelauncher(pargs: List[str], cwd: str, name: str) -> None:
|
||||
def nativelauncher(pargs: list[str], cwd: str, name: str) -> None:
|
||||
os.environ['MANAGER_DAEMON'] = name
|
||||
|
||||
# exec the process
|
||||
@@ -67,12 +67,12 @@ class ManagerProcess(ABC):
|
||||
daemon = False
|
||||
sigkill = False
|
||||
should_run: Callable[[bool, Params, car.CarParams], bool]
|
||||
proc: Optional[Process] = None
|
||||
proc: Process | None = None
|
||||
enabled = True
|
||||
name = ""
|
||||
|
||||
last_watchdog_time = 0
|
||||
watchdog_max_dt: Optional[int] = None
|
||||
watchdog_max_dt: int | None = None
|
||||
watchdog_seen = False
|
||||
shutting_down = False
|
||||
|
||||
@@ -109,7 +109,7 @@ class ManagerProcess(ABC):
|
||||
else:
|
||||
self.watchdog_seen = True
|
||||
|
||||
def stop(self, retry: bool = True, block: bool = True, sig: Optional[signal.Signals] = None) -> Optional[int]:
|
||||
def stop(self, retry: bool = True, block: bool = True, sig: signal.Signals = None) -> int | None:
|
||||
if self.proc is None:
|
||||
return None
|
||||
|
||||
@@ -239,7 +239,7 @@ class DaemonProcess(ManagerProcess):
|
||||
self.params = None
|
||||
|
||||
@staticmethod
|
||||
def should_run(started, params, params_memory, CP):
|
||||
def should_run(started, params, CP):
|
||||
return True
|
||||
|
||||
def prepare(self) -> None:
|
||||
@@ -274,14 +274,14 @@ class DaemonProcess(ManagerProcess):
|
||||
pass
|
||||
|
||||
|
||||
def ensure_running(procs: ValuesView[ManagerProcess], started: bool, params=None, params_memory=None, CP: car.CarParams=None,
|
||||
not_run: Optional[List[str]]=None) -> List[ManagerProcess]:
|
||||
def ensure_running(procs: ValuesView[ManagerProcess], started: bool, params=None, CP: car.CarParams=None,
|
||||
not_run: list[str] | None=None) -> list[ManagerProcess]:
|
||||
if not_run is None:
|
||||
not_run = []
|
||||
|
||||
running = []
|
||||
for p in procs:
|
||||
if p.enabled and p.name not in not_run and p.should_run(started, params, params_memory, CP):
|
||||
if p.enabled and p.name not in not_run and p.should_run(started, params, CP):
|
||||
running.append(p)
|
||||
else:
|
||||
p.stop(block=False)
|
||||
|
||||
@@ -5,51 +5,53 @@ from openpilot.common.params import Params
|
||||
from openpilot.system.hardware import PC, TICI
|
||||
from openpilot.selfdrive.manager.process import PythonProcess, NativeProcess, DaemonProcess
|
||||
|
||||
from openpilot.selfdrive.frogpilot.controls.lib.model_manager import RADARLESS_MODELS
|
||||
|
||||
RADARLESS = Params().get("Model", encoding='utf-8') in RADARLESS_MODELS
|
||||
WEBCAM = os.getenv("USE_WEBCAM") is not None
|
||||
|
||||
def driverview(started: bool, params: Params, params_memory: Params, CP: car.CarParams) -> bool:
|
||||
def driverview(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return started or params.get_bool("IsDriverViewEnabled")
|
||||
|
||||
def notcar(started: bool, params: Params, params_memory: Params, CP: car.CarParams) -> bool:
|
||||
def notcar(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return started and CP.notCar
|
||||
|
||||
def iscar(started: bool, params: Params, params_memory: Params, CP: car.CarParams) -> bool:
|
||||
def iscar(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return started and not CP.notCar
|
||||
|
||||
def logging(started, params, params_memory, CP: car.CarParams) -> bool:
|
||||
def logging(started, params, CP: car.CarParams) -> bool:
|
||||
run = (not CP.notCar) or not params.get_bool("DisableLogging")
|
||||
return started and run
|
||||
|
||||
def ublox_available() -> bool:
|
||||
return os.path.exists('/dev/ttyHS0') and not os.path.exists('/persist/comma/use-quectel-gps')
|
||||
|
||||
def ublox(started, params, params_memory, CP: car.CarParams) -> bool:
|
||||
def ublox(started, params, CP: car.CarParams) -> bool:
|
||||
use_ublox = ublox_available()
|
||||
if use_ublox != params.get_bool("UbloxAvailable"):
|
||||
params.put_bool("UbloxAvailable", use_ublox)
|
||||
return started and use_ublox
|
||||
|
||||
def qcomgps(started, params, params_memory, CP: car.CarParams) -> bool:
|
||||
def qcomgps(started, params, CP: car.CarParams) -> bool:
|
||||
return started and not ublox_available()
|
||||
|
||||
def always_run(started, params, params_memory, CP: car.CarParams) -> bool:
|
||||
def always_run(started, params, CP: car.CarParams) -> bool:
|
||||
return True
|
||||
|
||||
def only_onroad(started: bool, params, params_memory, CP: car.CarParams) -> bool:
|
||||
def only_onroad(started: bool, params, CP: car.CarParams) -> bool:
|
||||
return started
|
||||
|
||||
def only_offroad(started, params, params_memory, CP: car.CarParams) -> bool:
|
||||
def only_offroad(started, params, CP: car.CarParams) -> bool:
|
||||
return not started
|
||||
|
||||
# FrogPilot functions
|
||||
def allow_uploads(started, params, params_memory, CP: car.CarParams) -> bool:
|
||||
allow_uploads = not (params_memory.get_bool("FireTheBabysitter") and params_memory.get_bool("NoUploads"))
|
||||
at_home = not started or not params_memory.get_bool("DisableOnroadUploads")
|
||||
return allow_uploads and at_home
|
||||
def allow_logging(started, params, CP: car.CarParams) -> bool:
|
||||
allow_logging = not (params.get_bool("DeviceManagement") and params.get_bool("NoLogging"))
|
||||
return allow_logging and logging(started, params, CP)
|
||||
|
||||
def allow_logging(started, params, params_memory, CP: car.CarParams) -> bool:
|
||||
allow_logging = not (params_memory.get_bool("FireTheBabysitter") and params_memory.get_bool("NoLogging"))
|
||||
return allow_logging and logging(started, params, params_memory, CP)
|
||||
def allow_uploads(started, params, CP: car.CarParams) -> bool:
|
||||
allow_uploads = not (params.get_bool("DeviceManagement") and params.get_bool("NoUploads"))
|
||||
return allow_uploads
|
||||
|
||||
procs = [
|
||||
DaemonProcess("manage_athenad", "selfdrive.athena.manage_athenad", "AthenadPid"),
|
||||
@@ -79,16 +81,18 @@ procs = [
|
||||
PythonProcess("deleter", "system.loggerd.deleter", always_run),
|
||||
PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", driverview, enabled=(not PC or WEBCAM)),
|
||||
PythonProcess("qcomgpsd", "system.qcomgpsd.qcomgpsd", qcomgps, enabled=TICI),
|
||||
#PythonProcess("ugpsd", "system.ugpsd", only_onroad, enabled=TICI),
|
||||
PythonProcess("navd", "selfdrive.navd.navd", only_onroad),
|
||||
PythonProcess("pandad", "selfdrive.boardd.pandad", always_run),
|
||||
PythonProcess("paramsd", "selfdrive.locationd.paramsd", only_onroad),
|
||||
NativeProcess("ubloxd", "system/ubloxd", ["./ubloxd"], ublox, enabled=TICI),
|
||||
PythonProcess("pigeond", "system.sensord.pigeond", ublox, enabled=TICI),
|
||||
PythonProcess("pigeond", "system.ubloxd.pigeond", ublox, enabled=TICI),
|
||||
PythonProcess("plannerd", "selfdrive.controls.plannerd", only_onroad),
|
||||
PythonProcess("radard", "selfdrive.controls.radard", only_onroad),
|
||||
PythonProcess("radard", "selfdrive.controls.radard", only_onroad, enabled=not RADARLESS),
|
||||
PythonProcess("radardless", "selfdrive.controls.radardless", only_onroad, enabled=RADARLESS),
|
||||
PythonProcess("thermald", "selfdrive.thermald.thermald", always_run),
|
||||
PythonProcess("tombstoned", "selfdrive.tombstoned", allow_logging, enabled=not PC),
|
||||
PythonProcess("updated", "selfdrive.updated", always_run, enabled=not PC),
|
||||
PythonProcess("tombstoned", "selfdrive.tombstoned", always_run, enabled=not PC),
|
||||
PythonProcess("updated", "selfdrive.updated.updated", always_run, enabled=not PC),
|
||||
PythonProcess("uploader", "system.loggerd.uploader", allow_uploads),
|
||||
PythonProcess("statsd", "selfdrive.statsd", allow_logging),
|
||||
|
||||
@@ -99,8 +103,8 @@ procs = [
|
||||
|
||||
# FrogPilot processes
|
||||
PythonProcess("fleet_manager", "selfdrive.frogpilot.fleetmanager.fleet_manager", always_run),
|
||||
PythonProcess("frogpilot_process", "selfdrive.frogpilot.functions.frogpilot_process", always_run),
|
||||
PythonProcess("mapd", "selfdrive.frogpilot.functions.mapd", always_run),
|
||||
PythonProcess("frogpilot_process", "selfdrive.frogpilot.frogpilot_process", always_run),
|
||||
PythonProcess("mapd", "selfdrive.frogpilot.navigation.mapd", always_run),
|
||||
]
|
||||
|
||||
managed_processes = {p.name: p for p in procs}
|
||||
|
||||
0
selfdrive/manager/test/__init__.py
Normal file
0
selfdrive/manager/test/__init__.py
Normal file
82
selfdrive/manager/test/test_manager.py
Executable file
82
selfdrive/manager/test/test_manager.py
Executable file
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import pytest
|
||||
import signal
|
||||
import time
|
||||
import unittest
|
||||
|
||||
from parameterized import parameterized
|
||||
|
||||
from cereal import car
|
||||
from openpilot.common.params import Params
|
||||
import openpilot.selfdrive.manager.manager as manager
|
||||
from openpilot.selfdrive.manager.process import ensure_running
|
||||
from openpilot.selfdrive.manager.process_config import managed_processes
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
|
||||
os.environ['FAKEUPLOAD'] = "1"
|
||||
|
||||
MAX_STARTUP_TIME = 3
|
||||
BLACKLIST_PROCS = ['manage_athenad', 'pandad', 'pigeond']
|
||||
|
||||
|
||||
@pytest.mark.tici
|
||||
class TestManager(unittest.TestCase):
|
||||
def setUp(self):
|
||||
HARDWARE.set_power_save(False)
|
||||
|
||||
# ensure clean CarParams
|
||||
params = Params()
|
||||
params.clear_all()
|
||||
|
||||
def tearDown(self):
|
||||
manager.manager_cleanup()
|
||||
|
||||
def test_manager_prepare(self):
|
||||
os.environ['PREPAREONLY'] = '1'
|
||||
manager.main()
|
||||
|
||||
def test_blacklisted_procs(self):
|
||||
# TODO: ensure there are blacklisted procs until we have a dedicated test
|
||||
self.assertTrue(len(BLACKLIST_PROCS), "No blacklisted procs to test not_run")
|
||||
|
||||
@parameterized.expand([(i,) for i in range(10)])
|
||||
def test_startup_time(self, index):
|
||||
start = time.monotonic()
|
||||
os.environ['PREPAREONLY'] = '1'
|
||||
manager.main()
|
||||
t = time.monotonic() - start
|
||||
assert t < MAX_STARTUP_TIME, f"startup took {t}s, expected <{MAX_STARTUP_TIME}s"
|
||||
|
||||
@unittest.skip("this test is flaky the way it's currently written, should be moved to test_onroad")
|
||||
def test_clean_exit(self):
|
||||
"""
|
||||
Ensure all processes exit cleanly when stopped.
|
||||
"""
|
||||
HARDWARE.set_power_save(False)
|
||||
manager.manager_init()
|
||||
|
||||
CP = car.CarParams.new_message()
|
||||
procs = ensure_running(managed_processes.values(), True, Params(), CP, not_run=BLACKLIST_PROCS)
|
||||
|
||||
time.sleep(10)
|
||||
|
||||
for p in procs:
|
||||
with self.subTest(proc=p.name):
|
||||
state = p.get_process_state_msg()
|
||||
self.assertTrue(state.running, f"{p.name} not running")
|
||||
exit_code = p.stop(retry=False)
|
||||
|
||||
self.assertNotIn(p.name, BLACKLIST_PROCS, f"{p.name} was started")
|
||||
|
||||
self.assertTrue(exit_code is not None, f"{p.name} failed to exit")
|
||||
|
||||
# TODO: interrupted blocking read exits with 1 in cereal. use a more unique return code
|
||||
exit_codes = [0, 1]
|
||||
if p.sigkill:
|
||||
exit_codes = [-signal.SIGKILL]
|
||||
self.assertIn(exit_code, exit_codes, f"{p.name} died with {exit_code}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user