I AM SCREAMING

This commit is contained in:
firestar5683
2026-07-02 16:38:13 -05:00
parent 5bbe5f1daf
commit ea2aa532af
18 changed files with 173 additions and 1187 deletions
+21 -27
View File
@@ -1,11 +1,18 @@
import errno
import fcntl
import os
import sys
import pathlib
import shutil
import signal
import subprocess
import tempfile
import threading
from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params
def unblock_stdout() -> None:
import errno
import fcntl
import signal
import sys
# get a non-blocking stdout
child_pid, child_pty = os.forkpty()
if child_pid != 0: # parent
@@ -44,30 +51,17 @@ def write_onroad_params(started, params):
def save_bootlog():
import threading
# copy current params
tmp = tempfile.mkdtemp()
params_dirname = pathlib.Path(Params().get_param_path()).name
params_dir = os.path.join(tmp, params_dirname)
shutil.copytree(Params().get_param_path(), params_dir, dirs_exist_ok=True)
def fn():
import pathlib
import shutil
import subprocess
import tempfile
from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params
tmpdir = tempfile.mkdtemp()
def fn(tmpdir):
env = os.environ.copy()
env['PARAMS_COPY_PATH'] = tmpdir
try:
params = Params()
params_dirname = pathlib.Path(params.get_param_path()).name
params_dir = os.path.join(tmpdir, params_dirname)
shutil.copytree(params.get_param_path(), params_dir, dirs_exist_ok=True)
subprocess.call("./bootlog", cwd=os.path.join(BASEDIR, "system/loggerd"), env=env)
finally:
shutil.rmtree(tmpdir, ignore_errors=True)
t = threading.Thread(target=fn)
subprocess.call("./bootlog", cwd=os.path.join(BASEDIR, "system/loggerd"), env=env)
shutil.rmtree(tmpdir)
t = threading.Thread(target=fn, args=(tmp, ))
t.daemon = True
t.start()
+40 -216
View File
@@ -2,13 +2,12 @@
import datetime
import json
import os
import shutil
from pathlib import Path
import signal
import sys
import threading
import time
import traceback
from types import SimpleNamespace
_MANAGER_IMPORT_START = time.monotonic()
_BOOT_TIMING_LOG_PATH = os.environ.get("SP_BOOT_TIMING_LOG", "/tmp/starpilot_boot_timing.log")
@@ -24,29 +23,37 @@ def _append_boot_timing_line(line: str) -> None:
from cereal import car, log
import cereal.messaging as messaging
import openpilot.system.sentry as sentry
from openpilot.common.utils import atomic_write
from openpilot.common.params import Params, ParamKeyFlag, ParamKeyType
from openpilot.common.text_window import TextWindow
from openpilot.system.hardware import HARDWARE
from openpilot.system.manager.helpers import unblock_stdout, write_onroad_params, save_bootlog
from openpilot.system.manager.process import ensure_running
from openpilot.system.manager.process_config import managed_processes
from openpilot.common.swaglog import cloudlog
from openpilot.system.athena.registration import register, UNREGISTERED_DONGLE_ID
from openpilot.common.swaglog import cloudlog, add_file_handler
from openpilot.system.version import get_build_metadata, terms_version, training_version
from openpilot.system.hardware.hw import Paths
_MANAGER_CORE_IMPORT_DONE = time.monotonic()
from openpilot.starpilot.common.starpilot_functions import starpilot_boot_functions, install_starpilot, uninstall_starpilot
from openpilot.starpilot.common.starpilot_variables import (
LEGACY_STARPILOT_PARAM_RENAMES,
LEGACY_STARPILOT_STATS_KEY_RENAMES,
get_starpilot_toggles,
)
_MANAGER_IMPORT_DONE = time.monotonic()
if __name__ == "__main__":
_manager_import_timing_line = (
"SP_BOOT_TIMING manager_import "
f"core={_MANAGER_CORE_IMPORT_DONE - _MANAGER_IMPORT_START:.3f}s "
f"starpilot={_MANAGER_IMPORT_DONE - _MANAGER_CORE_IMPORT_DONE:.3f}s "
f"total={_MANAGER_IMPORT_DONE - _MANAGER_IMPORT_START:.3f}s"
)
print(_manager_import_timing_line, flush=True)
_append_boot_timing_line(_manager_import_timing_line)
_manager_import_timing_line = (
"SP_BOOT_TIMING manager_import "
f"core={_MANAGER_CORE_IMPORT_DONE - _MANAGER_IMPORT_START:.3f}s "
f"starpilot={_MANAGER_IMPORT_DONE - _MANAGER_CORE_IMPORT_DONE:.3f}s "
f"total={_MANAGER_IMPORT_DONE - _MANAGER_IMPORT_START:.3f}s"
)
print(_manager_import_timing_line, flush=True)
_append_boot_timing_line(_manager_import_timing_line)
LEGACY_BOLT_FP_MIGRATION_FLAG = Path("/data") / "legacy_bolt_fp_migration_v1"
@@ -60,30 +67,9 @@ STARPILOT_PC_ROOT_MIGRATION_FLAG = Path("/data") / "starpilot_pc_root_v1"
STARPILOT_PARAMS_CACHE_MIGRATION_FLAG = Path("/data") / "starpilot_params_cache_v1"
STARPILOT_LEGACY_CACHE_MARKER_KEYS = ("RemapCancelToDistance",)
STARPILOT_REMOVED_PARAM_KEYS = ("HumanFollowing",)
PARAMS_CACHE_RESTORE_SKIP_FLAGS = (
ParamKeyFlag.CLEAR_ON_MANAGER_START
| ParamKeyFlag.CLEAR_ON_ONROAD_TRANSITION
| ParamKeyFlag.CLEAR_ON_OFFROAD_TRANSITION
| ParamKeyFlag.CLEAR_ON_IGNITION_ON
)
UNREGISTERED_DONGLE_ID = "UnregisteredDevice"
POWER_WATCHDOG_PATH = "/var/tmp/power_watchdog"
_PENDING_PARAMS_CACHE_SYNC: tuple[str, list[str]] | None = None
LEGACY_CARMODEL_MIGRATIONS = {
"CHEVROLET_BOLT_CC_2019_2021": "CHEVROLET_BOLT_CC_2018_2021",
}
LEGACY_STARPILOT_PARAM_RENAMES = {
"FrogPilotApiToken": "StarPilotApiToken",
"FrogPilotCarParams": "StarPilotCarParams",
"FrogPilotCarParamsPersistent": "StarPilotCarParamsPersistent",
"FrogPilotDongleId": "StarPilotDongleId",
"FrogPilotStats": "StarPilotStats",
}
LEGACY_STARPILOT_STATS_KEY_RENAMES = {
"FrogPilotDrives": "StarPilotDrives",
"FrogPilotMeters": "StarPilotMeters",
"FrogPilotSeconds": "StarPilotSeconds",
}
STARPILOT_STATS_DROP_KEYS = {"CurrentMonthsKilometers", "ResetStats"}
STARPILOT_STATS_MAX_KEYS = {"LongestDistanceWithoutOverride", "MaxAcceleration"}
@@ -97,154 +83,6 @@ def _log_boot_timing(scope: str, label: str, start: float, previous: float | Non
return now
def _sync_params_cache_keys_async(cache_params_path: str, keys: list[str]) -> None:
try:
params = Params()
params_cache = Params(cache_params_path, return_defaults=True)
for key in keys:
current_value = params.get(key)
if current_value is not None and params_cache.get(key) != current_value:
params_cache.put(key, current_value)
except Exception:
cloudlog.exception("failed to sync params cache")
def _schedule_params_cache_sync(cache_params_path: str, keys: list[str]) -> None:
timer = threading.Timer(5.0, _sync_params_cache_keys_async, args=(cache_params_path, keys))
timer.daemon = True
timer.start()
def _schedule_pending_params_cache_sync() -> None:
global _PENDING_PARAMS_CACHE_SYNC
pending_sync = _PENDING_PARAMS_CACHE_SYNC
_PENDING_PARAMS_CACHE_SYNC = None
if pending_sync is not None and pending_sync[1]:
_schedule_params_cache_sync(*pending_sync)
def _iter_param_store_keys(store_path: str | Path) -> set[str]:
try:
path = Path(store_path)
if not path.is_dir():
return set()
with os.scandir(path) as entries:
return {
entry.name
for entry in entries
if entry.is_file(follow_symlinks=False) and entry.name != ".lock" and not entry.name.startswith(".tmp_")
}
except Exception:
cloudlog.exception(f"failed to list params store: {store_path}")
return set()
def _param_key_to_text(key: bytes | str) -> str:
return key.decode("utf-8", errors="ignore") if isinstance(key, bytes) else str(key)
def _should_restore_param_from_cache(params: Params, key: bytes | str) -> bool:
try:
return not (params.get_key_flag(key) & PARAMS_CACHE_RESTORE_SKIP_FLAGS)
except Exception:
return False
def _restore_missing_params_from_cache(params: Params, params_cache: Params, active_keys: set[str] | None = None) -> list[str]:
active_keys = active_keys if active_keys is not None else _iter_param_store_keys(params.get_param_path())
restored_keys: list[str] = []
for raw_key in params.all_keys():
key = _param_key_to_text(raw_key)
if key in active_keys:
continue
if not _should_restore_param_from_cache(params, raw_key):
continue
cached_value = params_cache.get(raw_key)
if cached_value is None:
continue
try:
params.put(raw_key, cached_value)
restored_keys.append(key)
except Exception:
cloudlog.exception(f"failed to restore param from cache: {key}")
return restored_keys
def _init_sentry_async() -> None:
def fn() -> None:
try:
import openpilot.system.sentry as sentry
sentry.init(sentry.SentryProject.SELFDRIVE)
except Exception:
cloudlog.exception("failed to initialize sentry")
threading.Thread(target=fn, daemon=True).start()
def _capture_manager_exception() -> None:
try:
import openpilot.system.sentry as sentry
sentry.capture_exception()
except Exception:
cloudlog.exception("failed to capture manager exception")
def _write_power_watchdog(timestamp: float) -> None:
import tempfile
tmp_file_name = None
try:
with tempfile.NamedTemporaryFile("w", dir=os.path.dirname(POWER_WATCHDOG_PATH), delete=False) as f:
tmp_file_name = f.name
f.write(str(timestamp))
os.replace(tmp_file_name, POWER_WATCHDOG_PATH)
tmp_file_name = None
finally:
if tmp_file_name is not None:
try:
os.unlink(tmp_file_name)
except FileNotFoundError:
pass
def _get_starpilot_toggles(sm=None):
from openpilot.starpilot.common.starpilot_variables import get_starpilot_toggles
return get_starpilot_toggles(sm)
def _get_manager_startup_toggles(params: Params | None = None) -> SimpleNamespace:
params = params or Params()
force_onroad = params.get_bool("ForceOnroad")
device_management = params.get_bool("DeviceManagement")
vetting_branch = os.environ.get("GIT_BRANCH") == "StarPilot-Vetting"
no_logging = False
no_uploads = False
if device_management and not vetting_branch:
no_logging = params.get_bool("NoLogging")
no_uploads = params.get_bool("NoUploads")
return SimpleNamespace(
force_offroad=params.get_bool("ForceOffroad"),
force_onroad=force_onroad,
no_logging=no_logging or (force_onroad and HARDWARE.get_device_type() == "pc"),
no_uploads=no_uploads,
no_onroad_uploads=params.get_bool("DisableOnroadUploads") if no_uploads else False,
speed_limit_filler=params.get_bool("SpeedLimitFiller"),
vision_speed_limit_detection=params.get_bool("VisionSpeedLimitDetection"),
)
def _to_text(value):
if value is None:
return None
@@ -416,8 +254,6 @@ def _copy_param_store_without_overwrite(source: Path, destination: Path) -> int:
if not source.is_dir():
return 0
import shutil
destination.mkdir(parents=True, exist_ok=True)
copied_entries = 0
for path in source.iterdir():
@@ -535,8 +371,6 @@ def _merge_tree_without_overwrite(source: Path, destination: Path) -> int:
if not source.exists():
return moved_entries
import shutil
if not destination.exists():
destination.parent.mkdir(parents=True, exist_ok=True)
shutil.move(str(source), str(destination))
@@ -926,12 +760,11 @@ def migrate_legacy_experimental_longitudinal(params: Params, params_cache: Param
def manager_init() -> None:
global _PENDING_PARAMS_CACHE_SYNC
manager_init_start = time.monotonic()
last_timing = _log_boot_timing("manager_init", "start", manager_init_start, manager_init_start)
last_timing = _log_boot_timing("manager_init", "save_bootlog_deferred", manager_init_start, last_timing)
save_bootlog()
last_timing = _log_boot_timing("manager_init", "save_bootlog", manager_init_start, last_timing)
build_metadata = get_build_metadata()
last_timing = _log_boot_timing("manager_init", "build_metadata", manager_init_start, last_timing)
@@ -977,11 +810,14 @@ def manager_init() -> None:
last_timing = _log_boot_timing("manager_init", "starpilot_migrations", manager_init_start, last_timing)
# set unset params to their default value
active_param_keys = _iter_param_store_keys(params.get_param_path())
last_timing = _log_boot_timing("manager_init", f"params_store_snapshot_{len(active_param_keys)}", manager_init_start, last_timing)
restored_param_keys = _restore_missing_params_from_cache(params, params_cache, active_param_keys)
last_timing = _log_boot_timing("manager_init", f"params_restore_{len(restored_param_keys)}", manager_init_start, last_timing)
params_cache_update_keys = sorted(active_param_keys)
for k in params.all_keys():
current_value = params.get(k)
if current_value is None:
cached_value = params_cache.get(k)
if cached_value is not None:
params.put(k, cached_value)
else:
params_cache.put(k, current_value)
last_timing = _log_boot_timing("manager_init", "params_defaults_cache_sync", manager_init_start, last_timing)
# Create folders needed for msgq
@@ -1011,8 +847,6 @@ def manager_init() -> None:
last_timing = _log_boot_timing("manager_init", "version_params", manager_init_start, last_timing)
# set dongle id
from openpilot.system.athena.registration import register
reg_res = register(show_spinner=True)
if reg_res:
dongle_id = reg_res
@@ -1028,6 +862,7 @@ def manager_init() -> None:
os.environ['CLEAN'] = '1'
# init logging
sentry.init(sentry.SentryProject.SELFDRIVE)
cloudlog.bind_global(dongle_id=dongle_id,
version=build_metadata.openpilot.version,
origin=build_metadata.openpilot.git_normalized_origin,
@@ -1037,22 +872,16 @@ def manager_init() -> None:
device=HARDWARE.get_device_type())
last_timing = _log_boot_timing("manager_init", "logging_ready", manager_init_start, last_timing)
# Preimporting every process serializes a lot of import work before manager can
# start the always-on processes. Keep the old behavior available for debugging,
# but optimize normal boot by letting children import their modules in parallel.
if os.getenv("SP_MANAGER_PREIMPORT", "0").lower() in ("1", "true", "yes", "on"):
for p in managed_processes.values():
p.prepare()
last_timing = _log_boot_timing("manager_init", "preimport_processes", manager_init_start, last_timing)
else:
last_timing = _log_boot_timing("manager_init", "preimport_processes_skipped", manager_init_start, last_timing)
# preimport all processes
for p in managed_processes.values():
p.prepare()
last_timing = _log_boot_timing("manager_init", "preimport_processes", manager_init_start, last_timing)
# StarPilot variables
install_starpilot(build_metadata, params)
last_timing = _log_boot_timing("manager_init", "install_starpilot", manager_init_start, last_timing)
starpilot_boot_functions(build_metadata, params)
_log_boot_timing("manager_init", "starpilot_boot_functions", manager_init_start, last_timing)
_PENDING_PARAMS_CACHE_SYNC = (cache_params_path, params_cache_update_keys)
def manager_cleanup() -> None:
@@ -1090,7 +919,7 @@ def manager_thread() -> None:
last_timing = _log_boot_timing("manager_thread", "messaging", manager_thread_start, last_timing)
write_onroad_params(False, params)
initial_toggles = _get_manager_startup_toggles(params)
initial_toggles = get_starpilot_toggles()
last_timing = _log_boot_timing("manager_thread", "initial_toggles", manager_thread_start, last_timing)
ensure_running(managed_processes.values(), False, params=params, CP=sm['carParams'], not_run=ignore, starpilot_toggles=initial_toggles)
last_timing = _log_boot_timing("manager_thread", "initial_ensure_running", manager_thread_start, last_timing)
@@ -1106,12 +935,9 @@ def manager_thread() -> None:
params_memory = Params(memory=True)
starpilot_toggles = _get_manager_startup_toggles(params)
starpilot_toggles = get_starpilot_toggles()
last_timing = _log_boot_timing("manager_thread", "loop_toggles", manager_thread_start, last_timing)
_log_boot_timing("manager_thread", "loop_ready", manager_thread_start, last_timing)
save_bootlog()
_init_sentry_async()
_schedule_pending_params_cache_sync()
while True:
sm.update(1000)
@@ -1159,7 +985,8 @@ def manager_thread() -> None:
# kick AGNOS power monitoring watchdog
try:
if sm.all_checks(['deviceState']):
_write_power_watchdog(time.monotonic())
with atomic_write("/var/tmp/power_watchdog", "w", overwrite=True) as f:
f.write(str(time.monotonic()))
except Exception:
pass
@@ -1182,7 +1009,7 @@ def manager_thread() -> None:
break
# StarPilot variables
starpilot_toggles = _get_starpilot_toggles(sm)
starpilot_toggles = get_starpilot_toggles(sm)
def main() -> None:
@@ -1197,7 +1024,7 @@ def main() -> None:
manager_thread()
except Exception:
traceback.print_exc()
_capture_manager_exception()
sentry.capture_exception()
finally:
manager_cleanup()
@@ -1221,9 +1048,6 @@ if __name__ == "__main__":
except KeyboardInterrupt:
print("got CTRL-C, exiting")
except Exception:
from openpilot.common.swaglog import add_file_handler
from openpilot.common.text_window import TextWindow
add_file_handler(cloudlog)
cloudlog.exception("Manager failed to start")
+7 -48
View File
@@ -6,8 +6,6 @@ import struct
import threading
import time
import subprocess
import multiprocessing
import sys
from pathlib import Path
from collections.abc import Callable, ValuesView
from abc import ABC, abstractmethod
@@ -18,6 +16,7 @@ from setproctitle import setproctitle
from cereal import car, log
import cereal.messaging as messaging
import openpilot.system.sentry as sentry
from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
@@ -430,6 +429,7 @@ def launcher(proc: str, name: str, nice: int | None = None) -> None:
# add daemon name tag to logs
cloudlog.bind(daemon=name)
sentry.set_tag("daemon", name)
# exec the process
mod.main()
@@ -438,13 +438,7 @@ def launcher(proc: str, name: str, nice: int | None = None) -> None:
except Exception:
# can't install the crash handler because sys.excepthook doesn't play nice
# with threads, so catch it here.
try:
import openpilot.system.sentry as sentry
sentry.set_tag("daemon", name)
sentry.capture_exception()
except Exception:
cloudlog.exception(f"failed to capture exception for child {proc}")
sentry.capture_exception()
raise
@@ -467,33 +461,11 @@ def join_process(process: Process, timeout: float) -> None:
time.sleep(0.001)
class SubprocessProcess:
def __init__(self, proc: subprocess.Popen):
self._proc = proc
@property
def pid(self) -> int | None:
return self._proc.pid
@property
def exitcode(self) -> int | None:
return self._proc.poll()
def is_alive(self) -> bool:
return self._proc.poll() is None
def join(self, timeout: float | None = None) -> None:
try:
self._proc.wait(timeout=timeout)
except subprocess.TimeoutExpired:
pass
class ManagerProcess(ABC):
daemon = False
sigkill = False
should_run: Callable[[bool, Params, car.CarParams, SimpleNamespace], bool]
proc: Process | SubprocessProcess | None = None
proc: Process | None = None
enabled = True
name = ""
@@ -643,7 +615,7 @@ class NativeProcess(ManagerProcess):
class PythonProcess(ManagerProcess):
def __init__(self, name, module, should_run, enabled=True, sigkill=False, watchdog_max_dt=None, nice=None, start_method=None):
def __init__(self, name, module, should_run, enabled=True, sigkill=False, watchdog_max_dt=None, nice=None):
self.name = name
self.module = module
self.should_run = should_run
@@ -651,7 +623,6 @@ class PythonProcess(ManagerProcess):
self.sigkill = sigkill
self.watchdog_max_dt = watchdog_max_dt
self.nice = nice
self.start_method = start_method
self.launcher = launcher
def prepare(self) -> None:
@@ -682,20 +653,8 @@ class PythonProcess(ManagerProcess):
name = self.name if "modeld" not in self.name else "MainProcess"
cloudlog.info(f"starting python {self.module}")
start_method = self.start_method or os.getenv("PYTHON_PROCESS_START_METHOD", "fork")
if start_method == "subprocess":
launcher_code = (
"from openpilot.system.manager.process import launcher; "
f"launcher({self.module!r}, {self.name!r}, {self.nice!r})"
)
self.proc = SubprocessProcess(subprocess.Popen([sys.executable, "-c", launcher_code]))
else:
self.proc = multiprocessing.get_context(start_method).Process(
name=name,
target=self.launcher,
args=(self.module, self.name, self.nice),
)
self.proc.start()
self.proc = Process(name=name, target=self.launcher, args=(self.module, self.name, self.nice))
self.proc.start()
self.last_watchdog_time = 0
self.watchdog_seen = False
self.shutting_down = False
+8 -56
View File
@@ -7,42 +7,10 @@ from types import SimpleNamespace
from cereal import car
from openpilot.common.params import Params
from openpilot.system.hardware import HARDWARE, PC, TICI
from openpilot.system.manager.process import PythonProcess, NativeProcess, DaemonProcess
WEBCAM = os.getenv("USE_WEBCAM") is not None
UI_WATCHDOG_MAX_DT = int(os.getenv("UI_WATCHDOG_MAX_DT", "10"))
device_type = HARDWARE.get_device_type()
def _env_bool(name: str) -> bool | None:
value = os.getenv(name)
if value is None:
return None
return value.strip().lower() in ("1", "true", "yes", "on")
def python_ui_enabled(device_type: str) -> bool:
for env_name in ("USE_PYTHON_UI", "USE_RAYLIB_UI"):
enabled = _env_bool(env_name)
if enabled is not None:
return enabled
native_ui = _env_bool("USE_NATIVE_UI")
if native_ui is not None:
return not native_ui
return device_type not in ("tici", "tizi")
def python_ui_process_start_method(uses_python_ui: bool, is_pc: bool = PC) -> str:
return "fork" if is_pc or not uses_python_ui else "subprocess"
PYTHON_UI = python_ui_enabled(device_type)
PYTHON_UI_PROCESS_START_METHOD = python_ui_process_start_method(PYTHON_UI)
THE_GALAXY_PROCESS_START_METHOD = "fork" if PC else "subprocess"
UPDATED_PROCESS_START_METHOD = "fork" if PC else "subprocess"
from openpilot.system.manager.process import PythonProcess, NativeProcess, DaemonProcess
def driverview(started: bool, params: Params, CP: car.CarParams, starpilot_toggles: SimpleNamespace) -> bool:
return started or params.get_bool("IsDriverViewEnabled")
@@ -157,13 +125,7 @@ procs = [
PythonProcess("radard", "selfdrive.controls.radard", only_onroad),
PythonProcess("hardwared", "system.hardware.hardwared", always_run),
PythonProcess("tombstoned", "system.tombstoned", always_run, enabled=not PC),
PythonProcess(
"updated",
"system.updated.updated",
always_run,
enabled=not PC,
start_method=UPDATED_PROCESS_START_METHOD,
),
PythonProcess("updated", "system.updated.updated", always_run, enabled=not PC),
PythonProcess("uploader", "system.loggerd.uploader", allow_uploads),
PythonProcess("statsd", "system.statsd", always_run),
PythonProcess("feedbackd", "selfdrive.ui.feedback.feedbackd", only_onroad),
@@ -177,26 +139,16 @@ procs = [
# StarPilot variables
procs += [
PythonProcess(
"the_galaxy",
"starpilot.system.the_galaxy.the_galaxy",
always_run,
nice=19,
start_method=THE_GALAXY_PROCESS_START_METHOD,
),
PythonProcess("the_galaxy", "starpilot.system.the_galaxy.the_galaxy", always_run, nice=19),
PythonProcess("galaxy", "starpilot.system.galaxy.galaxy", always_run, nice=19),
]
if PYTHON_UI:
procs.append(PythonProcess(
"ui",
"selfdrive.ui.ui",
always_run,
watchdog_max_dt=UI_WATCHDOG_MAX_DT,
start_method=PYTHON_UI_PROCESS_START_METHOD,
))
else:
device_type = HARDWARE.get_device_type()
if device_type in ("tici", "tizi"):
procs.append(NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, watchdog_max_dt=UI_WATCHDOG_MAX_DT))
else:
# C4 (mici) runs the Python raylib UI path.
procs.append(PythonProcess("ui", "selfdrive.ui.ui", always_run, watchdog_max_dt=UI_WATCHDOG_MAX_DT))
procs += [
PythonProcess("device_syncd", "starpilot.system.device_syncd", always_run),
@@ -50,9 +50,7 @@ class FileBackedFakeParams:
def marker_path(tmp_path: Path, marker_name: str) -> Path:
path = tmp_path / MARKER_DIRNAME / "params" / marker_name
path.parent.mkdir(parents=True, exist_ok=True)
return path
return tmp_path / MARKER_DIRNAME / "params" / marker_name
def test_apply_launch_param_migrations_sets_branch_defaults_once(tmp_path):
+4 -102
View File
@@ -6,16 +6,10 @@ from pathlib import Path
import json
from cereal import car
from openpilot.common.params import Params, ParamKeyFlag
from openpilot.common.params import Params
import openpilot.system.manager.manager as manager
from openpilot.system.manager.process import ensure_running
from openpilot.system.manager.process import PythonProcess
from openpilot.system.manager.process_config import (
managed_processes,
procs,
python_ui_enabled,
python_ui_process_start_method,
)
from openpilot.system.manager.process_config import managed_processes, procs
from openpilot.system.hardware import HARDWARE
os.environ['FAKEUPLOAD'] = "1"
@@ -25,29 +19,15 @@ BLACKLIST_PROCS = ['manage_athenad', 'pandad', 'pigeond']
class FileBackedFakeParams:
def __init__(
self,
root: Path,
values: dict[str, object] | None = None,
keys: set[str] | None = None,
flags: dict[str, ParamKeyFlag] | None = None,
):
def __init__(self, root: Path, values: dict[str, object] | None = None):
self.root = root
self.keys = set(keys or [])
self.flags = dict(flags or {})
self.root.mkdir(parents=True, exist_ok=True)
for key, value in (values or {}).items():
self.put(key, value)
def get_param_path(self, key=""):
def get_param_path(self, key):
return str(self.root / (key.decode() if isinstance(key, bytes) else str(key)))
def all_keys(self):
return sorted(self.keys)
def get_key_flag(self, key):
return self.flags.get(key.decode() if isinstance(key, bytes) else str(key), ParamKeyFlag.PERSISTENT)
def get(self, key):
path = Path(self.get_param_path(key))
if not path.is_file():
@@ -68,7 +48,6 @@ class FileBackedFakeParams:
return str(value).strip().lower() in ("1", "true", "yes", "on")
def put(self, key, value):
self.keys.add(key.decode() if isinstance(key, bytes) else str(key))
path = Path(self.get_param_path(key))
path.parent.mkdir(parents=True, exist_ok=True)
@@ -117,83 +96,6 @@ class TestManager:
assert names.index("the_galaxy") < ui_idx
assert names.index("galaxy") < ui_idx
def test_python_ui_process_start_method_follows_ui_implementation(self):
assert python_ui_process_start_method(False, False) == "fork"
assert python_ui_process_start_method(True, False) == "subprocess"
assert python_ui_process_start_method(True, True) == "fork"
def test_python_ui_subprocess_is_scoped_to_ui(self):
ui_proc = managed_processes["ui"]
uses_python_ui = python_ui_enabled(HARDWARE.get_device_type())
subprocess_scoped_procs = {"the_galaxy", "updated"}
assert isinstance(ui_proc, PythonProcess) == uses_python_ui
if uses_python_ui:
assert ui_proc.start_method == python_ui_process_start_method(uses_python_ui)
for proc in procs:
if isinstance(proc, PythonProcess) and proc.name not in subprocess_scoped_procs | {"ui"}:
assert proc.start_method is None
def test_python_ui_env_override(self, monkeypatch):
monkeypatch.setenv("USE_RAYLIB_UI", "1")
assert python_ui_enabled("tici") is True
monkeypatch.setenv("USE_RAYLIB_UI", "0")
assert python_ui_enabled("mici") is False
def test_manager_startup_toggles_use_params_only(self, tmp_path, monkeypatch):
monkeypatch.setenv("GIT_BRANCH", "StarPilot")
params = FileBackedFakeParams(tmp_path / "params", {
"DeviceManagement": True,
"NoLogging": True,
"NoUploads": True,
"DisableOnroadUploads": True,
"SpeedLimitFiller": True,
"VisionSpeedLimitDetection": True,
"ForceOffroad": True,
"ForceOnroad": False,
})
toggles = manager._get_manager_startup_toggles(params)
assert toggles.no_logging is True
assert toggles.no_uploads is True
assert toggles.no_onroad_uploads is True
assert toggles.speed_limit_filler is True
assert toggles.vision_speed_limit_detection is True
assert toggles.force_offroad is True
assert toggles.force_onroad is False
def test_restore_missing_params_from_cache_preserves_live_values(self, tmp_path):
params = FileBackedFakeParams(
tmp_path / "params",
{"ExistingParam": "live"},
keys={"ExistingParam", "RestoredParam", "MissingParam", "TransientParam"},
flags={"TransientParam": ParamKeyFlag.CLEAR_ON_MANAGER_START},
)
params_cache = FileBackedFakeParams(
tmp_path / "cache",
{"ExistingParam": "cached", "RestoredParam": "restored", "TransientParam": "stale"},
)
restored_keys = manager._restore_missing_params_from_cache(params, params_cache)
assert restored_keys == ["RestoredParam"]
assert params.get("ExistingParam") == "live"
assert params.get("RestoredParam") == "restored"
assert params.get("MissingParam") is None
assert params.get("TransientParam") is None
def test_iter_param_store_keys_skips_lock_and_temp_files(self, tmp_path):
store_path = tmp_path / "params"
store_path.mkdir()
(store_path / "GoodParam").write_text("1")
(store_path / ".lock").write_text("")
(store_path / ".tmp_value_abc").write_text("stale")
(store_path / "nested").mkdir()
assert manager._iter_param_store_keys(store_path) == {"GoodParam"}
def test_blacklisted_procs(self):
# TODO: ensure there are blacklisted procs until we have a dedicated test
assert len(BLACKLIST_PROCS), "No blacklisted procs to test not_run"