reset to pre-modification baseline; restart feature work from clean state
Restoring the working tree to the pristine pre-Claude baseline previously preserved at /data/clearpilot (now /data/clearpilot-baseline). The prior modified-but-broken tree is snapshotted at /data/openpilot-broken-2026-05-03 and tagged here as pre-reset-2026-05-03 for reference. From here, features (UI changes, dashcam, telemetry, GPS, display modes, speed logic, standstill power saving, etc.) will be re-introduced one at a time with proper testing.
This commit is contained in:
@@ -2,7 +2,6 @@ import importlib
|
||||
import os
|
||||
import signal
|
||||
import struct
|
||||
import sys
|
||||
import datetime
|
||||
import time
|
||||
import subprocess
|
||||
@@ -18,117 +17,17 @@ import openpilot.selfdrive.sentry as sentry
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.common.time import system_time_valid
|
||||
|
||||
WATCHDOG_FN = "/dev/shm/wd_"
|
||||
ENABLE_WATCHDOG = os.getenv("NO_WATCHDOG") is None
|
||||
|
||||
# CLEARPILOT: logging directory and session log
|
||||
# init_log_dir() must be called once from manager_init() before any process starts.
|
||||
# Until then, _log_dir and session_log are usable but write to a NullHandler.
|
||||
import logging
|
||||
|
||||
_log_dir = "/data/log2/current"
|
||||
_time_resolved = False
|
||||
_session_handler = None
|
||||
|
||||
session_log = logging.getLogger("clearpilot.session")
|
||||
session_log.setLevel(logging.DEBUG)
|
||||
session_log.addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
def init_log_dir():
|
||||
"""Create /data/log2/current as a real directory for this session.
|
||||
Called once from manager_init(). Previous current (if a real dir) is
|
||||
renamed to a timestamp or boot-monotonic name before we create a fresh one."""
|
||||
global _log_dir, _time_resolved, _session_handler
|
||||
|
||||
log_base = "/data/log2"
|
||||
current = os.path.join(log_base, "current")
|
||||
os.makedirs(log_base, exist_ok=True)
|
||||
|
||||
# If 'current' is a symlink, just remove the symlink
|
||||
if os.path.islink(current):
|
||||
os.unlink(current)
|
||||
# If 'current' is a real directory (leftover from previous session that
|
||||
# never got time-resolved), rename it out of the way
|
||||
elif os.path.isdir(current):
|
||||
# Use mtime of session.log (or the dir itself) for the rename
|
||||
session_file = os.path.join(current, "session.log")
|
||||
mtime = os.path.getmtime(session_file) if os.path.exists(session_file) else os.path.getmtime(current)
|
||||
ts = datetime.datetime.fromtimestamp(mtime).strftime('%Y-%m-%d-%H-%M-%S')
|
||||
dest = os.path.join(log_base, ts)
|
||||
# Avoid collision
|
||||
if os.path.exists(dest):
|
||||
dest = dest + f"-{int(time.monotonic())}"
|
||||
try:
|
||||
os.rename(current, dest)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
os.makedirs(current, exist_ok=True)
|
||||
_log_dir = current
|
||||
_time_resolved = False
|
||||
|
||||
# Set up session log file handler
|
||||
_session_handler = logging.FileHandler(os.path.join(_log_dir, "session.log"))
|
||||
_session_handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
|
||||
# Remove NullHandler and add file handler
|
||||
session_log.handlers.clear()
|
||||
session_log.addHandler(_session_handler)
|
||||
|
||||
session_log.info("session started, log dir: %s", _log_dir)
|
||||
|
||||
|
||||
def update_log_dir_timestamp():
|
||||
"""Rename /data/log2/current to a real timestamp and replace with a symlink
|
||||
once system time is valid."""
|
||||
global _log_dir, _time_resolved, _session_handler
|
||||
if _time_resolved:
|
||||
return
|
||||
if not system_time_valid():
|
||||
return
|
||||
|
||||
log_base = "/data/log2"
|
||||
current = os.path.join(log_base, "current")
|
||||
ts_name = datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S')
|
||||
new_dir = os.path.join(log_base, ts_name)
|
||||
|
||||
try:
|
||||
os.rename(current, new_dir)
|
||||
# Create symlink: current -> YYYY-MM-DD-HH-MM-SS
|
||||
os.symlink(ts_name, current)
|
||||
_log_dir = new_dir
|
||||
_time_resolved = True
|
||||
# Re-point session log handler (open files follow the inode, but
|
||||
# new opens should go through the symlink — update handler for clarity)
|
||||
session_log.removeHandler(_session_handler)
|
||||
_session_handler.close()
|
||||
_session_handler = logging.FileHandler(os.path.join(_log_dir, "session.log"))
|
||||
_session_handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
|
||||
session_log.addHandler(_session_handler)
|
||||
session_log.info("log directory renamed to %s", _log_dir)
|
||||
|
||||
# Signal via param that the directory has been time-resolved
|
||||
try:
|
||||
from openpilot.common.params import Params
|
||||
Params().put("LogDirInitialized", "1")
|
||||
except Exception:
|
||||
pass
|
||||
except OSError:
|
||||
pass
|
||||
timestamp = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
|
||||
_log_dir = f"/data/log2/{timestamp}"
|
||||
os.makedirs(_log_dir, exist_ok=True)
|
||||
|
||||
|
||||
|
||||
def launcher(proc: str, name: str, log_path: str) -> None:
|
||||
# CLEARPILOT: redirect stderr to per-process log file
|
||||
try:
|
||||
log_file = open(log_path, 'a')
|
||||
os.dup2(log_file.fileno(), sys.stderr.fileno())
|
||||
os.dup2(log_file.fileno(), sys.stdout.fileno())
|
||||
except Exception as e:
|
||||
print(f"CLEARPILOT: stderr redirect failed for {name}: {e}", file=sys.stderr)
|
||||
|
||||
def launcher(proc: str, name: str) -> None:
|
||||
try:
|
||||
# import the process
|
||||
mod = importlib.import_module(proc)
|
||||
@@ -154,17 +53,9 @@ def launcher(proc: str, name: str, log_path: str) -> None:
|
||||
raise
|
||||
|
||||
|
||||
def nativelauncher(pargs: list[str], cwd: str, name: str, log_path: str) -> None:
|
||||
def nativelauncher(pargs: list[str], cwd: str, name: str) -> None:
|
||||
os.environ['MANAGER_DAEMON'] = name
|
||||
|
||||
# CLEARPILOT: redirect stderr and stdout to per-process log file
|
||||
try:
|
||||
log_file = open(log_path, 'a')
|
||||
os.dup2(log_file.fileno(), sys.stderr.fileno())
|
||||
os.dup2(log_file.fileno(), sys.stdout.fileno())
|
||||
except Exception as e:
|
||||
print(f"CLEARPILOT: stderr redirect failed for {name}: {e}", file=sys.stderr)
|
||||
|
||||
# exec the process
|
||||
os.chdir(cwd)
|
||||
os.execvp(pargs[0], pargs)
|
||||
@@ -219,7 +110,6 @@ class ManagerProcess(ABC):
|
||||
if dt > self.watchdog_max_dt:
|
||||
if (self.watchdog_seen or self.always_watchdog and self.proc.exitcode is not None) and ENABLE_WATCHDOG:
|
||||
cloudlog.error(f"Watchdog timeout for {self.name} (exitcode {self.proc.exitcode}) restarting ({started=})")
|
||||
session_log.warning("watchdog timeout for %s (exitcode %s), restarting", self.name, self.proc.exitcode)
|
||||
self.restart()
|
||||
else:
|
||||
self.watchdog_seen = True
|
||||
@@ -249,10 +139,6 @@ class ManagerProcess(ABC):
|
||||
|
||||
ret = self.proc.exitcode
|
||||
cloudlog.info(f"{self.name} is dead with {ret}")
|
||||
if ret is not None and ret != 0:
|
||||
session_log.error("process %s died with exit code %s", self.name, ret)
|
||||
elif ret == 0:
|
||||
session_log.info("process %s stopped (exit 0)", self.name)
|
||||
|
||||
if self.proc.exitcode is not None:
|
||||
self.shutting_down = False
|
||||
@@ -312,13 +198,9 @@ class NativeProcess(ManagerProcess):
|
||||
global _log_dir
|
||||
log_path = _log_dir+"/"+self.name+".log"
|
||||
|
||||
# CLEARPILOT: ensure log file exists even if child redirect fails
|
||||
open(log_path, 'a').close()
|
||||
|
||||
cwd = os.path.join(BASEDIR, self.cwd)
|
||||
cloudlog.info(f"starting process {self.name}")
|
||||
session_log.info("starting %s", self.name)
|
||||
self.proc = Process(name=self.name, target=self.launcher, args=(self.cmdline, cwd, self.name, log_path))
|
||||
self.proc = Process(name=self.name, target=self.launcher, args=(self.cmdline, cwd, self.name))
|
||||
self.proc.start()
|
||||
self.watchdog_seen = False
|
||||
self.shutting_down = False
|
||||
@@ -349,12 +231,8 @@ class PythonProcess(ManagerProcess):
|
||||
|
||||
global _log_dir
|
||||
log_path = _log_dir+"/"+self.name+".log"
|
||||
# CLEARPILOT: ensure log file exists even if child redirect fails
|
||||
open(log_path, 'a').close()
|
||||
|
||||
cloudlog.info(f"starting python {self.module}")
|
||||
session_log.info("starting %s", self.name)
|
||||
self.proc = Process(name=self.name, target=self.launcher, args=(self.module, self.name, log_path))
|
||||
self.proc = Process(name=self.name, target=self.launcher, args=(self.module, self.name))
|
||||
self.proc.start()
|
||||
self.watchdog_seen = False
|
||||
self.shutting_down = False
|
||||
@@ -398,7 +276,6 @@ class DaemonProcess(ManagerProcess):
|
||||
pass
|
||||
|
||||
cloudlog.info(f"starting daemon {self.name}")
|
||||
session_log.info("starting daemon %s", self.name)
|
||||
proc = subprocess.Popen(['python', '-m', self.module],
|
||||
stdin=open('/dev/null'),
|
||||
stdout=open(log_path, 'a'),
|
||||
|
||||
Reference in New Issue
Block a user