Compare commits
10 Commits
c33e155c56
...
55f73fd421
| Author | SHA1 | Date | |
|---|---|---|---|
| 55f73fd421 | |||
| 2b522021d5 | |||
| 4756d8e32c | |||
| 73472e5fab | |||
| b85ce8176d | |||
| 24f29dcfb7 | |||
| 6de3a8c68f | |||
| bf030db9fe | |||
| 3d9143c41b | |||
| 698a1647a0 |
29
CANBUS_ISSUE_INVESTIGATE.md
Normal file
29
CANBUS_ISSUE_INVESTIGATE.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# CAN-FD Issues to Investigate
|
||||||
|
|
||||||
|
## Cruise Control Desync on Hot Start
|
||||||
|
|
||||||
|
### Problem
|
||||||
|
When the car is already in cruise control mode before openpilot starts (or before controlsd initializes), pressing the cruise control button causes a desync:
|
||||||
|
|
||||||
|
1. Car has active cruise control when openpilot comes online
|
||||||
|
2. Driver presses cruise button — car interprets as "toggle off" (cruise was already on)
|
||||||
|
3. openpilot interprets it as "engage" (from its perspective, it hasn't seen a cruise enable transition)
|
||||||
|
4. Result: car disengages cruise, but openpilot thinks it just engaged lateral mode
|
||||||
|
|
||||||
|
### Proposed Fix
|
||||||
|
When controlsd first reads carState and discovers `cruiseState.enabled` is already true:
|
||||||
|
|
||||||
|
1. Set a flag `cruise_was_active_at_start = True`
|
||||||
|
2. While this flag is set, do NOT allow openpilot to transition to engaged state on button press
|
||||||
|
3. Only clear the flag after seeing `cruiseState.enabled` go to `False` at least once (a full stop of cruise, not just standstill/pause)
|
||||||
|
4. After the first full off cycle, normal engagement logic resumes
|
||||||
|
|
||||||
|
### Status
|
||||||
|
- Could not reproduce on 2026-04-14 — may require specific timing (openpilot restart while driving with cruise active)
|
||||||
|
- Need to capture telemetry data during reproduction to see exact signal sequence
|
||||||
|
- Key telemetry groups to watch: `cruise` (enabled, available, ACCMode, VSetDis), `buttons` (cruise_button)
|
||||||
|
|
||||||
|
### Relevant Code
|
||||||
|
- `selfdrive/controls/controlsd.py` — engagement state machine
|
||||||
|
- `selfdrive/car/hyundai/carstate.py` — CAN-FD cruise state parsing
|
||||||
|
- `selfdrive/car/interfaces.py` — pcm_enable / cruise state transitions
|
||||||
17
CLAUDE.md
17
CLAUDE.md
@@ -92,6 +92,18 @@ The params system uses a C++ whitelist. Adding a new param name in `manager.py`
|
|||||||
2. Add the default value in `selfdrive/manager/manager.py` in `manager_init()`
|
2. Add the default value in `selfdrive/manager/manager.py` in `manager_init()`
|
||||||
3. Remove `prebuilt`, `common/params.o`, and `common/libcommon.a` to force rebuild
|
3. Remove `prebuilt`, `common/params.o`, and `common/libcommon.a` to force rebuild
|
||||||
|
|
||||||
|
### Memory Params (paramsMemory)
|
||||||
|
|
||||||
|
ClearPilot uses memory params (`/dev/shm/params/d/`) for UI toggles that should reset on boot. Key rules:
|
||||||
|
|
||||||
|
- **Registration**: Register the key in `common/params.cc` as `PERSISTENT` (same as FrogPilot pattern — the registration flag does NOT control which path the param lives at)
|
||||||
|
- **C++ access**: Use `Params{"/dev/shm/params"}` — stored as a class member `paramsMemory` in onroad.h. Do NOT use `Params("/dev/shm/params/d")` — the Params class appends `/d/` internally, so that would resolve to `/dev/shm/params/d/d/`
|
||||||
|
- **Python access**: Use `Params("/dev/shm/params")`
|
||||||
|
- **Defaults**: Set in `manager_init()` via `Params("/dev/shm/params").put(key, value)`
|
||||||
|
- **UI toggles**: Use `ToggleControl` with manual `toggleFlipped` lambda that writes via `Params("/dev/shm/params")`. Do NOT use `ParamControl` for memory params — it reads/writes persistent params only
|
||||||
|
- **Current memory params**: `TelemetryEnabled` (default "0"), `VpnEnabled` (default "1"), `ModelStandby` (default "0"), `ScreenDisplayMode`
|
||||||
|
- **IMPORTANT — method names differ between C++ and Python**: C++ uses camelCase (`putBool`, `getBool`, `getInt`), Python uses snake_case (`put_bool`, `get_bool`, `get_int`). This is a common source of silent failures — the wrong casing compiles/runs but doesn't work.
|
||||||
|
|
||||||
### Building Native (C++) Processes
|
### Building Native (C++) Processes
|
||||||
|
|
||||||
- SCons is the build system. Static libraries (`common`, `messaging`, `cereal`, `visionipc`) must be imported as SCons objects, not `-l` flags
|
- SCons is the build system. Static libraries (`common`, `messaging`, `cereal`, `visionipc`) must be imported as SCons objects, not `-l` flags
|
||||||
@@ -482,10 +494,13 @@ Power On
|
|||||||
|
|
||||||
- **Client**: `selfdrive/clearpilot/telemetry.py` — `tlog(group, data)` sends JSON over ZMQ PUSH
|
- **Client**: `selfdrive/clearpilot/telemetry.py` — `tlog(group, data)` sends JSON over ZMQ PUSH
|
||||||
- **Collector**: `selfdrive/clearpilot/telemetryd.py` — diffs against previous state, writes changed values to CSV
|
- **Collector**: `selfdrive/clearpilot/telemetryd.py` — diffs against previous state, writes changed values to CSV
|
||||||
- **Toggle**: `TelemetryEnabled` param, controlled from Debug panel in ClearPilot menu
|
- **Toggle**: `TelemetryEnabled` memory param, controlled from Debug panel in ClearPilot menu
|
||||||
- **Auto-disable**: disabled on every manager start; disabled if `/data` free < 5GB
|
- **Auto-disable**: disabled on every manager start; disabled if `/data` free < 5GB
|
||||||
- **Hyundai CAN-FD data**: logged from `selfdrive/car/hyundai/carstate.py` `update_canfd()` — groups: `car`, `cruise`, `speed_limit`, `buttons`
|
- **Hyundai CAN-FD data**: logged from `selfdrive/car/hyundai/carstate.py` `update_canfd()` — groups: `car`, `cruise`, `speed_limit`, `buttons`
|
||||||
|
- **GPS data**: logged directly by telemetryd via cereal `gpsLocation` subscription at 1Hz — group: `gps` (latitude, longitude, speed, altitude, bearing, accuracy)
|
||||||
- **CSV location**: `/data/log2/current/telemetry.csv` (or session directory)
|
- **CSV location**: `/data/log2/current/telemetry.csv` (or session directory)
|
||||||
|
- **Onroad overlay**: when telemetry enabled, status bar shows temp, fan %, model FPS, and STANDSTILL indicator
|
||||||
|
- **Speed limit**: `speed_limit.calculated` is the final computed speed limit value (in vehicle units, MPH when `is_metric=False`). This is the value that will be used for the future speed limit warning chime feature
|
||||||
|
|
||||||
### Key Dependencies
|
### Key Dependencies
|
||||||
|
|
||||||
|
|||||||
@@ -198,8 +198,10 @@ std::unordered_map<std::string, uint32_t> keys = {
|
|||||||
{"TelemetryEnabled", PERSISTENT},
|
{"TelemetryEnabled", PERSISTENT},
|
||||||
{"Timezone", PERSISTENT},
|
{"Timezone", PERSISTENT},
|
||||||
{"TrainingVersion", PERSISTENT},
|
{"TrainingVersion", PERSISTENT},
|
||||||
|
{"ModelStandby", PERSISTENT},
|
||||||
|
{"ModelStandbyTs", PERSISTENT},
|
||||||
{"UbloxAvailable", PERSISTENT},
|
{"UbloxAvailable", PERSISTENT},
|
||||||
{"VpnEnabled", CLEAR_ON_MANAGER_START},
|
{"VpnEnabled", PERSISTENT},
|
||||||
{"UpdateAvailable", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
{"UpdateAvailable", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||||
{"UpdateFailedCount", CLEAR_ON_MANAGER_START},
|
{"UpdateFailedCount", CLEAR_ON_MANAGER_START},
|
||||||
{"UpdaterAvailableBranches", PERSISTENT},
|
{"UpdaterAvailableBranches", PERSISTENT},
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ bash /data/openpilot/system/clearpilot/on_start.sh
|
|||||||
# CLEARPILOT: start VPN monitor (kills previous instances, runs as root)
|
# CLEARPILOT: start VPN monitor (kills previous instances, runs as root)
|
||||||
sudo bash -c 'nohup /data/openpilot/system/clearpilot/vpn-monitor.sh >> /tmp/vpn-monitor.log 2>&1 &'
|
sudo bash -c 'nohup /data/openpilot/system/clearpilot/vpn-monitor.sh >> /tmp/vpn-monitor.log 2>&1 &'
|
||||||
|
|
||||||
|
# CLEARPILOT: start nice monitor (keeps claude at nice 19)
|
||||||
|
sudo bash -c 'nohup /data/openpilot/system/clearpilot/nice-monitor.sh > /dev/null 2>&1 &'
|
||||||
|
|
||||||
# CLEARPILOT: pass --bench flag through to manager via env var
|
# CLEARPILOT: pass --bench flag through to manager via env var
|
||||||
if [ "$1" = "--bench" ]; then
|
if [ "$1" = "--bench" ]; then
|
||||||
export BENCH_MODE=1
|
export BENCH_MODE=1
|
||||||
|
|||||||
@@ -421,59 +421,14 @@ class CarState(CarStateBase):
|
|||||||
# self.params_memory.put_float("CarSpeedLimitLiteral", self.calculate_speed_limit(cp, cp_cam))
|
# self.params_memory.put_float("CarSpeedLimitLiteral", self.calculate_speed_limit(cp, cp_cam))
|
||||||
self.params_memory.put_float("CarSpeedLimit", self.calculate_speed_limit(cp, cp_cam) * speed_factor)
|
self.params_memory.put_float("CarSpeedLimit", self.calculate_speed_limit(cp, cp_cam) * speed_factor)
|
||||||
|
|
||||||
# CLEARPILOT: telemetry logging
|
# CLEARPILOT: telemetry logging — disabled, re-enable when needed
|
||||||
speed_limit_bus = cp if self.CP.flags & HyundaiFlags.CANFD_HDA2 else cp_cam
|
# speed_limit_bus = cp if self.CP.flags & HyundaiFlags.CANFD_HDA2 else cp_cam
|
||||||
scc = cp_cam.vl["SCC_CONTROL"] if self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC else cp.vl["SCC_CONTROL"]
|
# scc = cp_cam.vl["SCC_CONTROL"] if self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC else cp.vl["SCC_CONTROL"]
|
||||||
cluster = speed_limit_bus.vl["CLUSTER_SPEED_LIMIT"]
|
# cluster = speed_limit_bus.vl["CLUSTER_SPEED_LIMIT"]
|
||||||
|
# tlog("car", { ... })
|
||||||
tlog("car", {
|
# tlog("cruise", { ... })
|
||||||
"vEgo": round(ret.vEgo, 3),
|
# tlog("speed_limit", { ... })
|
||||||
"vEgoRaw": round(ret.vEgoRaw, 3),
|
# tlog("buttons", { ... })
|
||||||
"aEgo": round(ret.aEgo, 3),
|
|
||||||
"steeringAngleDeg": round(ret.steeringAngleDeg, 1),
|
|
||||||
"gear": str(ret.gearShifter),
|
|
||||||
"brakePressed": ret.brakePressed,
|
|
||||||
"gasPressed": ret.gasPressed,
|
|
||||||
"standstill": ret.standstill,
|
|
||||||
"leftBlinker": ret.leftBlinker,
|
|
||||||
"rightBlinker": ret.rightBlinker,
|
|
||||||
})
|
|
||||||
|
|
||||||
tlog("cruise", {
|
|
||||||
"enabled": ret.cruiseState.enabled,
|
|
||||||
"available": ret.cruiseState.available,
|
|
||||||
"speed": round(ret.cruiseState.speed, 3),
|
|
||||||
"standstill": ret.cruiseState.standstill,
|
|
||||||
"accFaulted": ret.accFaulted,
|
|
||||||
"brakePressed": ret.brakePressed,
|
|
||||||
"ACCMode": scc.get("ACCMode", 0),
|
|
||||||
"VSetDis": scc.get("VSetDis", 0),
|
|
||||||
"aReqRaw": round(scc.get("aReqRaw", 0), 3),
|
|
||||||
"aReqValue": round(scc.get("aReqValue", 0), 3),
|
|
||||||
"DISTANCE_SETTING": scc.get("DISTANCE_SETTING", 0),
|
|
||||||
"ACC_ObjDist": round(scc.get("ACC_ObjDist", 0), 1),
|
|
||||||
})
|
|
||||||
|
|
||||||
tlog("speed_limit", {
|
|
||||||
"SPEED_LIMIT_1": cluster.get("SPEED_LIMIT_1", 0),
|
|
||||||
"SPEED_LIMIT_2": cluster.get("SPEED_LIMIT_2", 0),
|
|
||||||
"SPEED_LIMIT_3": cluster.get("SPEED_LIMIT_3", 0),
|
|
||||||
"SCHOOL_ZONE": cluster.get("SCHOOL_ZONE", 0),
|
|
||||||
"CHIME_1": cluster.get("CHIME_1", 0),
|
|
||||||
"CHIME_2": cluster.get("CHIME_2", 0),
|
|
||||||
"SPEED_CHANGE_BLINKING": cluster.get("SPEED_CHANGE_BLINKING", 0),
|
|
||||||
"calculated": self.calculate_speed_limit(cp, cp_cam),
|
|
||||||
"is_metric": self.is_metric,
|
|
||||||
})
|
|
||||||
|
|
||||||
tlog("buttons", {
|
|
||||||
"cruise_button": self.cruise_buttons[-1],
|
|
||||||
"prev_cruise_button": self.prev_cruise_buttons,
|
|
||||||
"main_button": self.main_buttons[-1],
|
|
||||||
"prev_main_button": self.prev_main_buttons,
|
|
||||||
"lkas_enabled": self.lkas_enabled,
|
|
||||||
"main_enabled": self.main_enabled,
|
|
||||||
})
|
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|||||||
@@ -484,10 +484,19 @@ class CarInterfaceBase(ABC):
|
|||||||
self.silent_steer_warning = True
|
self.silent_steer_warning = True
|
||||||
events.add(EventName.steerTempUnavailableSilent)
|
events.add(EventName.steerTempUnavailableSilent)
|
||||||
else:
|
else:
|
||||||
|
# CLEARPILOT: log once per instance of this warning
|
||||||
|
if not getattr(self, '_steer_fault_logged', False):
|
||||||
|
import sys
|
||||||
|
print(f"CLP steerTempUnavailable: steerFaultTemporary={cs_out.steerFaultTemporary} "
|
||||||
|
f"steeringPressed={cs_out.steeringPressed} standstill={cs_out.standstill} "
|
||||||
|
f"steering_unpressed={self.steering_unpressed} steeringAngleDeg={cs_out.steeringAngleDeg:.1f} "
|
||||||
|
f"steeringTorque={cs_out.steeringTorque:.1f} vEgo={cs_out.vEgo:.2f}", file=sys.stderr)
|
||||||
|
self._steer_fault_logged = True
|
||||||
events.add(EventName.steerTempUnavailable)
|
events.add(EventName.steerTempUnavailable)
|
||||||
else:
|
else:
|
||||||
self.no_steer_warning = False
|
self.no_steer_warning = False
|
||||||
self.silent_steer_warning = False
|
self.silent_steer_warning = False
|
||||||
|
self._steer_fault_logged = False
|
||||||
if cs_out.steerFaultPermanent:
|
if cs_out.steerFaultPermanent:
|
||||||
events.add(EventName.steerUnavailable)
|
events.add(EventName.steerUnavailable)
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -64,7 +64,8 @@
|
|||||||
|
|
||||||
const std::string VIDEOS_BASE = "/data/media/0/videos";
|
const std::string VIDEOS_BASE = "/data/media/0/videos";
|
||||||
const int SEGMENT_SECONDS = 180; // 3 minutes
|
const int SEGMENT_SECONDS = 180; // 3 minutes
|
||||||
const int CAMERA_FPS = 20;
|
const int SOURCE_FPS = 20;
|
||||||
|
const int CAMERA_FPS = 10;
|
||||||
const int FRAMES_PER_SEGMENT = SEGMENT_SECONDS * CAMERA_FPS;
|
const int FRAMES_PER_SEGMENT = SEGMENT_SECONDS * CAMERA_FPS;
|
||||||
const int BITRATE = 2500 * 1024; // 2500 kbps
|
const int BITRATE = 2500 * 1024; // 2500 kbps
|
||||||
const double IDLE_TIMEOUT_SECONDS = 600.0; // 10 minutes
|
const double IDLE_TIMEOUT_SECONDS = 600.0; // 10 minutes
|
||||||
@@ -129,18 +130,28 @@ int main(int argc, char *argv[]) {
|
|||||||
if (do_exit) return 0;
|
if (do_exit) return 0;
|
||||||
LOGW("dashcamd: system time valid");
|
LOGW("dashcamd: system time valid");
|
||||||
|
|
||||||
LOGW("dashcamd: connecting to camerad road stream");
|
LOGW("dashcamd: started, connecting to camerad road stream");
|
||||||
VisionIpcClient vipc("camerad", VISION_STREAM_ROAD, false);
|
VisionIpcClient vipc("camerad", VISION_STREAM_ROAD, false);
|
||||||
while (!do_exit && !vipc.connect(false)) {
|
while (!do_exit && !vipc.connect(false)) {
|
||||||
usleep(100000);
|
usleep(100000);
|
||||||
}
|
}
|
||||||
if (do_exit) return 0;
|
if (do_exit) return 0;
|
||||||
|
LOGW("dashcamd: vipc connected, waiting for valid frame");
|
||||||
|
|
||||||
int width = vipc.buffers[0].width;
|
// Wait for a frame with valid dimensions (camerad may still be initializing)
|
||||||
int height = vipc.buffers[0].height;
|
VisionBuf *init_buf = nullptr;
|
||||||
int y_stride = vipc.buffers[0].stride;
|
while (!do_exit) {
|
||||||
|
init_buf = vipc.recv();
|
||||||
|
if (init_buf != nullptr && init_buf->width > 0 && init_buf->height > 0) break;
|
||||||
|
usleep(100000);
|
||||||
|
}
|
||||||
|
if (do_exit) return 0;
|
||||||
|
|
||||||
|
int width = init_buf->width;
|
||||||
|
int height = init_buf->height;
|
||||||
|
int y_stride = init_buf->stride;
|
||||||
int uv_stride = y_stride;
|
int uv_stride = y_stride;
|
||||||
LOGW("dashcamd: connected %dx%d, stride=%d", width, height, y_stride);
|
LOGW("dashcamd: first valid frame %dx%d stride=%d", width, height, y_stride);
|
||||||
|
|
||||||
// Subscribe to carState (gear), deviceState (ignition), gpsLocation (subtitles)
|
// Subscribe to carState (gear), deviceState (ignition), gpsLocation (subtitles)
|
||||||
SubMaster sm({"carState", "deviceState", "gpsLocation"});
|
SubMaster sm({"carState", "deviceState", "gpsLocation"});
|
||||||
@@ -151,6 +162,7 @@ int main(int argc, char *argv[]) {
|
|||||||
OmxEncoder *encoder = nullptr;
|
OmxEncoder *encoder = nullptr;
|
||||||
std::string trip_dir;
|
std::string trip_dir;
|
||||||
int frame_count = 0;
|
int frame_count = 0;
|
||||||
|
int recv_count = 0;
|
||||||
uint64_t segment_start_ts = 0;
|
uint64_t segment_start_ts = 0;
|
||||||
double idle_timer_start = 0.0;
|
double idle_timer_start = 0.0;
|
||||||
|
|
||||||
@@ -227,6 +239,10 @@ int main(int argc, char *argv[]) {
|
|||||||
VisionBuf *buf = vipc.recv();
|
VisionBuf *buf = vipc.recv();
|
||||||
if (buf == nullptr) continue;
|
if (buf == nullptr) continue;
|
||||||
|
|
||||||
|
// CLEARPILOT: skip frames to match target FPS (SOURCE_FPS -> CAMERA_FPS)
|
||||||
|
recv_count++;
|
||||||
|
if (SOURCE_FPS > CAMERA_FPS && (recv_count % (SOURCE_FPS / CAMERA_FPS)) != 0) continue;
|
||||||
|
|
||||||
sm.update(0);
|
sm.update(0);
|
||||||
double now = nanos_since_boot() / 1e9;
|
double now = nanos_since_boot() / 1e9;
|
||||||
|
|
||||||
|
|||||||
@@ -6,20 +6,39 @@ Usage from any process:
|
|||||||
tlog("canbus", {"speed": 45.2, "speed_limit": 60})
|
tlog("canbus", {"speed": 45.2, "speed_limit": 60})
|
||||||
|
|
||||||
Sends JSON packets over ZMQ PUSH to telemetryd, which diffs and writes CSV.
|
Sends JSON packets over ZMQ PUSH to telemetryd, which diffs and writes CSV.
|
||||||
|
Only sends when TelemetryEnabled memory param is set — zero cost when disabled.
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
import zmq
|
import zmq
|
||||||
|
|
||||||
TELEMETRY_SOCK = "ipc:///tmp/clearpilot_telemetry"
|
TELEMETRY_SOCK = "ipc:///tmp/clearpilot_telemetry"
|
||||||
|
_PARAM_PATH = "/dev/shm/params/d/TelemetryEnabled"
|
||||||
|
|
||||||
_ctx = None
|
_ctx = None
|
||||||
_sock = None
|
_sock = None
|
||||||
|
_last_check = 0
|
||||||
|
_enabled = False
|
||||||
|
|
||||||
|
|
||||||
def tlog(group: str, data: dict):
|
def tlog(group: str, data: dict):
|
||||||
"""Log a telemetry packet. Only changed values will be written to CSV by telemetryd."""
|
"""Log a telemetry packet. Only changed values will be written to CSV by telemetryd."""
|
||||||
global _ctx, _sock
|
global _ctx, _sock, _last_check, _enabled
|
||||||
|
|
||||||
|
# Check param at most once per second to avoid filesystem overhead
|
||||||
|
now = time.monotonic()
|
||||||
|
if now - _last_check > 1.0:
|
||||||
|
_last_check = now
|
||||||
|
try:
|
||||||
|
with open(_PARAM_PATH, 'r') as f:
|
||||||
|
_enabled = f.read().strip() == "1"
|
||||||
|
except (FileNotFoundError, IOError):
|
||||||
|
_enabled = False
|
||||||
|
|
||||||
|
if not _enabled:
|
||||||
|
return
|
||||||
|
|
||||||
if _sock is None:
|
if _sock is None:
|
||||||
_ctx = zmq.Context.instance()
|
_ctx = zmq.Context.instance()
|
||||||
_sock = _ctx.socket(zmq.PUSH)
|
_sock = _ctx.socket(zmq.PUSH)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import shutil
|
|||||||
import time
|
import time
|
||||||
import zmq
|
import zmq
|
||||||
|
|
||||||
|
import cereal.messaging as messaging
|
||||||
from openpilot.common.params import Params
|
from openpilot.common.params import Params
|
||||||
from openpilot.selfdrive.clearpilot.telemetry import TELEMETRY_SOCK
|
from openpilot.selfdrive.clearpilot.telemetry import TELEMETRY_SOCK
|
||||||
from openpilot.selfdrive.manager.process import _log_dir, session_log
|
from openpilot.selfdrive.manager.process import _log_dir, session_log
|
||||||
@@ -26,12 +27,16 @@ DISK_CHECK_INTERVAL = 10 # seconds
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
params = Params()
|
params = Params("/dev/shm/params")
|
||||||
ctx = zmq.Context.instance()
|
ctx = zmq.Context.instance()
|
||||||
sock = ctx.socket(zmq.PULL)
|
sock = ctx.socket(zmq.PULL)
|
||||||
sock.setsockopt(zmq.RCVHWM, 1000)
|
sock.setsockopt(zmq.RCVHWM, 1000)
|
||||||
sock.bind(TELEMETRY_SOCK)
|
sock.bind(TELEMETRY_SOCK)
|
||||||
|
|
||||||
|
# GPS subscriber for location telemetry
|
||||||
|
sm = messaging.SubMaster(['gpsLocation'])
|
||||||
|
last_gps_log = 0
|
||||||
|
|
||||||
csv_path = os.path.join(_log_dir, "telemetry.csv")
|
csv_path = os.path.join(_log_dir, "telemetry.csv")
|
||||||
state: dict[str, dict] = {} # group -> {key: last_value}
|
state: dict[str, dict] = {} # group -> {key: last_value}
|
||||||
was_enabled = False
|
was_enabled = False
|
||||||
@@ -71,6 +76,34 @@ def main():
|
|||||||
|
|
||||||
was_enabled = enabled
|
was_enabled = enabled
|
||||||
|
|
||||||
|
# GPS telemetry at most 1Hz — fed through the same diff logic
|
||||||
|
if enabled and writer is not None:
|
||||||
|
sm.update(0)
|
||||||
|
now = time.monotonic()
|
||||||
|
if sm.updated['gpsLocation'] and (now - last_gps_log) >= 1.0:
|
||||||
|
gps = sm['gpsLocation']
|
||||||
|
gps_data = {
|
||||||
|
"latitude": f"{gps.latitude:.7f}",
|
||||||
|
"longitude": f"{gps.longitude:.7f}",
|
||||||
|
"speed": f"{gps.speed:.2f}",
|
||||||
|
"altitude": f"{gps.altitude:.1f}",
|
||||||
|
"bearing": f"{gps.bearingDeg:.1f}",
|
||||||
|
"accuracy": f"{gps.accuracy:.1f}",
|
||||||
|
}
|
||||||
|
ts = time.time()
|
||||||
|
if "gps" not in state:
|
||||||
|
state["gps"] = {}
|
||||||
|
prev_gps = state["gps"]
|
||||||
|
gps_changed = False
|
||||||
|
for key, value in gps_data.items():
|
||||||
|
if key not in prev_gps or prev_gps[key] != value:
|
||||||
|
writer.writerow([f"{ts:.6f}", "gps", key, value])
|
||||||
|
prev_gps[key] = value
|
||||||
|
gps_changed = True
|
||||||
|
if gps_changed:
|
||||||
|
f.flush()
|
||||||
|
last_gps_log = now
|
||||||
|
|
||||||
# Always drain the socket (even when disabled) to avoid backpressure
|
# Always drain the socket (even when disabled) to avoid backpressure
|
||||||
try:
|
try:
|
||||||
raw = sock.recv_string(zmq.NOBLOCK)
|
raw = sock.recv_string(zmq.NOBLOCK)
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ CAMERA_OFFSET = 0.04
|
|||||||
REPLAY = "REPLAY" in os.environ
|
REPLAY = "REPLAY" in os.environ
|
||||||
SIMULATION = "SIMULATION" in os.environ
|
SIMULATION = "SIMULATION" in os.environ
|
||||||
TESTING_CLOSET = "TESTING_CLOSET" in os.environ
|
TESTING_CLOSET = "TESTING_CLOSET" in os.environ
|
||||||
IGNORE_PROCESSES = {"loggerd", "encoderd", "statsd"}
|
IGNORE_PROCESSES = {"loggerd", "encoderd", "statsd", "telemetryd", "dashcamd"}
|
||||||
|
|
||||||
ThermalStatus = log.DeviceState.ThermalStatus
|
ThermalStatus = log.DeviceState.ThermalStatus
|
||||||
State = log.ControlsState.OpenpilotState
|
State = log.ControlsState.OpenpilotState
|
||||||
@@ -442,6 +442,13 @@ class Controls:
|
|||||||
# All events here should at least have NO_ENTRY and SOFT_DISABLE.
|
# All events here should at least have NO_ENTRY and SOFT_DISABLE.
|
||||||
num_events = len(self.events)
|
num_events = len(self.events)
|
||||||
|
|
||||||
|
# CLEARPILOT: compute model standby suppression early — used by multiple checks below
|
||||||
|
try:
|
||||||
|
standby_ts = float(self.params_memory.get("ModelStandbyTs") or "0")
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
standby_ts = 0
|
||||||
|
model_suppress = (time.monotonic() - standby_ts) < 2.0
|
||||||
|
|
||||||
not_running = {p.name for p in self.sm['managerState'].processes if not p.running and p.shouldBeRunning}
|
not_running = {p.name for p in self.sm['managerState'].processes if not p.running and p.shouldBeRunning}
|
||||||
if self.sm.recv_frame['managerState'] and (not_running - IGNORE_PROCESSES):
|
if self.sm.recv_frame['managerState'] and (not_running - IGNORE_PROCESSES):
|
||||||
self.events.add(EventName.processNotRunning)
|
self.events.add(EventName.processNotRunning)
|
||||||
@@ -455,9 +462,11 @@ class Controls:
|
|||||||
elif not self.sm.all_freq_ok(self.camera_packets):
|
elif not self.sm.all_freq_ok(self.camera_packets):
|
||||||
self.events.add(EventName.cameraFrameRate)
|
self.events.add(EventName.cameraFrameRate)
|
||||||
if not REPLAY and self.rk.lagging:
|
if not REPLAY and self.rk.lagging:
|
||||||
|
import sys
|
||||||
|
print(f"CLP controlsdLagging: remaining={self.rk.remaining:.4f} standstill={CS.standstill} vEgo={CS.vEgo:.2f}", file=sys.stderr)
|
||||||
self.events.add(EventName.controlsdLagging)
|
self.events.add(EventName.controlsdLagging)
|
||||||
if not self.radarless_model:
|
if not self.radarless_model:
|
||||||
if len(self.sm['radarState'].radarErrors) or (not self.rk.lagging and not self.sm.all_checks(['radarState'])):
|
if not model_suppress and (len(self.sm['radarState'].radarErrors) or (not self.rk.lagging and not self.sm.all_checks(['radarState']))):
|
||||||
self.events.add(EventName.radarFault)
|
self.events.add(EventName.radarFault)
|
||||||
if not self.sm.valid['pandaStates']:
|
if not self.sm.valid['pandaStates']:
|
||||||
self.events.add(EventName.usbError)
|
self.events.add(EventName.usbError)
|
||||||
@@ -469,7 +478,7 @@ class Controls:
|
|||||||
# generic catch-all. ideally, a more specific event should be added above instead
|
# generic catch-all. ideally, a more specific event should be added above instead
|
||||||
has_disable_events = self.events.contains(ET.NO_ENTRY) and (self.events.contains(ET.SOFT_DISABLE) or self.events.contains(ET.IMMEDIATE_DISABLE))
|
has_disable_events = self.events.contains(ET.NO_ENTRY) and (self.events.contains(ET.SOFT_DISABLE) or self.events.contains(ET.IMMEDIATE_DISABLE))
|
||||||
no_system_errors = (not has_disable_events) or (len(self.events) == num_events)
|
no_system_errors = (not has_disable_events) or (len(self.events) == num_events)
|
||||||
if (not self.sm.all_checks() or self.card.can_rcv_timeout) and no_system_errors:
|
if (not self.sm.all_checks() or self.card.can_rcv_timeout) and no_system_errors and not model_suppress:
|
||||||
if not self.sm.all_alive():
|
if not self.sm.all_alive():
|
||||||
self.events.add(EventName.commIssue)
|
self.events.add(EventName.commIssue)
|
||||||
elif not self.sm.all_freq_ok():
|
elif not self.sm.all_freq_ok():
|
||||||
@@ -490,13 +499,13 @@ class Controls:
|
|||||||
self.logged_comm_issue = None
|
self.logged_comm_issue = None
|
||||||
|
|
||||||
if not (self.CP.notCar and self.joystick_mode):
|
if not (self.CP.notCar and self.joystick_mode):
|
||||||
if not self.sm['liveLocationKalman'].posenetOK:
|
if not self.sm['liveLocationKalman'].posenetOK and not model_suppress:
|
||||||
self.events.add(EventName.posenetInvalid)
|
self.events.add(EventName.posenetInvalid)
|
||||||
if not self.sm['liveLocationKalman'].deviceStable:
|
if not self.sm['liveLocationKalman'].deviceStable:
|
||||||
self.events.add(EventName.deviceFalling)
|
self.events.add(EventName.deviceFalling)
|
||||||
if not self.sm['liveLocationKalman'].inputsOK:
|
if not self.sm['liveLocationKalman'].inputsOK and not model_suppress:
|
||||||
self.events.add(EventName.locationdTemporaryError)
|
self.events.add(EventName.locationdTemporaryError)
|
||||||
if not self.sm['liveParameters'].valid and not TESTING_CLOSET and (not SIMULATION or REPLAY):
|
if not self.sm['liveParameters'].valid and not TESTING_CLOSET and (not SIMULATION or REPLAY) and not model_suppress:
|
||||||
self.events.add(EventName.paramsdTemporaryError)
|
self.events.add(EventName.paramsdTemporaryError)
|
||||||
|
|
||||||
# conservative HW alert. if the data or frequency are off, locationd will throw an error
|
# conservative HW alert. if the data or frequency are off, locationd will throw an error
|
||||||
@@ -542,7 +551,7 @@ class Controls:
|
|||||||
self.distance_traveled = 0
|
self.distance_traveled = 0
|
||||||
self.distance_traveled += CS.vEgo * DT_CTRL
|
self.distance_traveled += CS.vEgo * DT_CTRL
|
||||||
|
|
||||||
if self.sm['modelV2'].frameDropPerc > 20:
|
if self.sm['modelV2'].frameDropPerc > 20 and not model_suppress:
|
||||||
self.events.add(EventName.modeldLagging)
|
self.events.add(EventName.modeldLagging)
|
||||||
|
|
||||||
|
|
||||||
@@ -630,15 +639,13 @@ class Controls:
|
|||||||
self.enabled = self.state in ENABLED_STATES
|
self.enabled = self.state in ENABLED_STATES
|
||||||
self.active = self.state in ACTIVE_STATES
|
self.active = self.state in ACTIVE_STATES
|
||||||
|
|
||||||
# CLEARPILOT: log engagement state for debugging cruise desync issues
|
# CLEARPILOT: engagement telemetry disabled — was running at 100Hz, causing CPU load
|
||||||
tlog("engage", {
|
# tlog("engage", {
|
||||||
"state": self.state.name if hasattr(self.state, 'name') else str(self.state),
|
# "state": self.state.name if hasattr(self.state, 'name') else str(self.state),
|
||||||
"enabled": self.enabled,
|
# "enabled": self.enabled, "active": self.active,
|
||||||
"active": self.active,
|
# "cruise_enabled": CS.cruiseState.enabled, "cruise_available": CS.cruiseState.available,
|
||||||
"cruise_enabled": CS.cruiseState.enabled,
|
# "brakePressed": CS.brakePressed,
|
||||||
"cruise_available": CS.cruiseState.available,
|
# })
|
||||||
"brakePressed": CS.brakePressed,
|
|
||||||
})
|
|
||||||
if self.active:
|
if self.active:
|
||||||
self.current_alert_types.append(ET.WARNING)
|
self.current_alert_types.append(ET.WARNING)
|
||||||
|
|
||||||
|
|||||||
@@ -83,9 +83,12 @@ def manager_init(frogpilot_functions) -> None:
|
|||||||
params_storage = Params("/persist/params")
|
params_storage = Params("/persist/params")
|
||||||
params.clear_all(ParamKeyType.CLEAR_ON_MANAGER_START)
|
params.clear_all(ParamKeyType.CLEAR_ON_MANAGER_START)
|
||||||
|
|
||||||
# CLEARPILOT: always start with telemetry disabled, VPN enabled
|
# CLEARPILOT: always start with telemetry disabled, VPN enabled (memory params)
|
||||||
params.put("TelemetryEnabled", "0")
|
params_memory = Params("/dev/shm/params")
|
||||||
params.put("VpnEnabled", "1")
|
params_memory.put("TelemetryEnabled", "0")
|
||||||
|
params_memory.put("VpnEnabled", "1")
|
||||||
|
params_memory.put("ModelStandby", "0")
|
||||||
|
params_memory.put("ModelStandbyTs", "0")
|
||||||
params.clear_all(ParamKeyType.CLEAR_ON_ONROAD_TRANSITION)
|
params.clear_all(ParamKeyType.CLEAR_ON_ONROAD_TRANSITION)
|
||||||
params.clear_all(ParamKeyType.CLEAR_ON_OFFROAD_TRANSITION)
|
params.clear_all(ParamKeyType.CLEAR_ON_OFFROAD_TRANSITION)
|
||||||
if is_release_branch():
|
if is_release_branch():
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ def allow_uploads(started, params, CP: car.CarParams) -> bool:
|
|||||||
|
|
||||||
# ClearPilot functions
|
# ClearPilot functions
|
||||||
def dashcam_should_run(started, params, CP: car.CarParams) -> bool:
|
def dashcam_should_run(started, params, CP: car.CarParams) -> bool:
|
||||||
return started or params.get_bool("DashcamDebug")
|
return True # CLEARPILOT: dashcamd manages its own trip lifecycle
|
||||||
|
|
||||||
procs = [
|
procs = [
|
||||||
DaemonProcess("manage_athenad", "selfdrive.athena.manage_athenad", "AthenadPid"),
|
DaemonProcess("manage_athenad", "selfdrive.athena.manage_athenad", "AthenadPid"),
|
||||||
@@ -85,7 +85,7 @@ procs = [
|
|||||||
PythonProcess("deleter", "system.loggerd.deleter", always_run),
|
PythonProcess("deleter", "system.loggerd.deleter", always_run),
|
||||||
PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", driverview, enabled=(not PC or WEBCAM)),
|
PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", driverview, enabled=(not PC or WEBCAM)),
|
||||||
# PythonProcess("qcomgpsd", "system.qcomgpsd.qcomgpsd", qcomgps, enabled=TICI),
|
# PythonProcess("qcomgpsd", "system.qcomgpsd.qcomgpsd", qcomgps, enabled=TICI),
|
||||||
# PythonProcess("gpsd", "system.clearpilot.gpsd", qcomgps, enabled=TICI), # DISABLED: testing perf
|
PythonProcess("gpsd", "system.clearpilot.gpsd", qcomgps, enabled=TICI),
|
||||||
# PythonProcess("ugpsd", "system.ugpsd", only_onroad, enabled=TICI),
|
# PythonProcess("ugpsd", "system.ugpsd", only_onroad, enabled=TICI),
|
||||||
#PythonProcess("navd", "selfdrive.navd.navd", only_onroad),
|
#PythonProcess("navd", "selfdrive.navd.navd", only_onroad),
|
||||||
PythonProcess("pandad", "selfdrive.boardd.pandad", always_run),
|
PythonProcess("pandad", "selfdrive.boardd.pandad", always_run),
|
||||||
@@ -110,7 +110,7 @@ procs = [
|
|||||||
PythonProcess("frogpilot_process", "selfdrive.frogpilot.frogpilot_process", always_run),
|
PythonProcess("frogpilot_process", "selfdrive.frogpilot.frogpilot_process", always_run),
|
||||||
|
|
||||||
# ClearPilot processes
|
# ClearPilot processes
|
||||||
# NativeProcess("dashcamd", "selfdrive/clearpilot", ["./dashcamd"], dashcam_should_run), # DISABLED: testing perf
|
NativeProcess("dashcamd", "selfdrive/clearpilot", ["./dashcamd"], dashcam_should_run),
|
||||||
PythonProcess("telemetryd", "selfdrive.clearpilot.telemetryd", always_run),
|
PythonProcess("telemetryd", "selfdrive.clearpilot.telemetryd", always_run),
|
||||||
PythonProcess("bench_onroad", "selfdrive.clearpilot.bench_onroad", always_run, enabled=BENCH_MODE),
|
PythonProcess("bench_onroad", "selfdrive.clearpilot.bench_onroad", always_run, enabled=BENCH_MODE),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -134,11 +134,15 @@ def main(demo=False):
|
|||||||
setproctitle(PROCESS_NAME)
|
setproctitle(PROCESS_NAME)
|
||||||
config_realtime_process(7, 54)
|
config_realtime_process(7, 54)
|
||||||
|
|
||||||
|
import time as _time
|
||||||
cloudlog.warning("setting up CL context")
|
cloudlog.warning("setting up CL context")
|
||||||
|
_t0 = _time.monotonic()
|
||||||
cl_context = CLContext()
|
cl_context = CLContext()
|
||||||
cloudlog.warning("CL context ready; loading model")
|
_t1 = _time.monotonic()
|
||||||
|
cloudlog.warning("CL context ready in %.3fs; loading model", _t1 - _t0)
|
||||||
model = ModelState(cl_context)
|
model = ModelState(cl_context)
|
||||||
cloudlog.warning("models loaded, modeld starting")
|
_t2 = _time.monotonic()
|
||||||
|
cloudlog.warning("model loaded in %.3fs (total init %.3fs), modeld starting", _t2 - _t1, _t2 - _t0)
|
||||||
|
|
||||||
# visionipc clients
|
# visionipc clients
|
||||||
while True:
|
while True:
|
||||||
@@ -179,6 +183,9 @@ def main(demo=False):
|
|||||||
model_transform_main = np.zeros((3, 3), dtype=np.float32)
|
model_transform_main = np.zeros((3, 3), dtype=np.float32)
|
||||||
model_transform_extra = np.zeros((3, 3), dtype=np.float32)
|
model_transform_extra = np.zeros((3, 3), dtype=np.float32)
|
||||||
live_calib_seen = False
|
live_calib_seen = False
|
||||||
|
model_standby = False
|
||||||
|
last_standby_ts_write = 0
|
||||||
|
params_memory = Params("/dev/shm/params")
|
||||||
nav_features = np.zeros(ModelConstants.NAV_FEATURE_LEN, dtype=np.float32)
|
nav_features = np.zeros(ModelConstants.NAV_FEATURE_LEN, dtype=np.float32)
|
||||||
nav_instructions = np.zeros(ModelConstants.NAV_INSTRUCTION_LEN, dtype=np.float32)
|
nav_instructions = np.zeros(ModelConstants.NAV_INSTRUCTION_LEN, dtype=np.float32)
|
||||||
buf_main, buf_extra = None, None
|
buf_main, buf_extra = None, None
|
||||||
@@ -233,6 +240,49 @@ def main(demo=False):
|
|||||||
meta_extra = meta_main
|
meta_extra = meta_main
|
||||||
|
|
||||||
sm.update(0)
|
sm.update(0)
|
||||||
|
|
||||||
|
# CLEARPILOT: power saving — three modes based on driving state
|
||||||
|
# Full 20fps: lat active or lane changing
|
||||||
|
# Reduced 4fps: not lat active, not standstill (driving without cruise)
|
||||||
|
# Standby 0fps: standstill
|
||||||
|
lat_active = sm['carControl'].latActive
|
||||||
|
lane_changing = params_memory.get_bool("no_lat_lane_change")
|
||||||
|
standstill = sm['carState'].standstill
|
||||||
|
full_rate = lat_active or lane_changing
|
||||||
|
|
||||||
|
# Standby transitions (standstill only)
|
||||||
|
should_standby = standstill and not full_rate
|
||||||
|
if should_standby and not model_standby:
|
||||||
|
params_memory.put_bool("ModelStandby", True)
|
||||||
|
model_standby = True
|
||||||
|
cloudlog.warning("modeld: standby ON (standstill)")
|
||||||
|
elif not should_standby and model_standby:
|
||||||
|
params_memory.put_bool("ModelStandby", False)
|
||||||
|
model_standby = False
|
||||||
|
run_count = 0
|
||||||
|
frame_dropped_filter.x = 0.
|
||||||
|
cloudlog.warning("modeld: standby OFF")
|
||||||
|
if model_standby:
|
||||||
|
now = _time.monotonic()
|
||||||
|
if now - last_standby_ts_write > 1.0:
|
||||||
|
params_memory.put("ModelStandbyTs", str(now))
|
||||||
|
last_standby_ts_write = now
|
||||||
|
last_vipc_frame_id = meta_main.frame_id
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Reduced framerate: 4fps when not lat active and not standstill
|
||||||
|
# Skip 4 out of every 5 frames (20fps -> 4fps)
|
||||||
|
# Write standby timestamp so controlsd suppresses transient errors
|
||||||
|
if not full_rate:
|
||||||
|
now = _time.monotonic()
|
||||||
|
if now - last_standby_ts_write > 1.0:
|
||||||
|
params_memory.put("ModelStandbyTs", str(now))
|
||||||
|
last_standby_ts_write = now
|
||||||
|
if run_count % 5 != 0:
|
||||||
|
last_vipc_frame_id = meta_main.frame_id
|
||||||
|
run_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
desire = DH.desire
|
desire = DH.desire
|
||||||
is_rhd = sm["driverMonitoringState"].isRHD
|
is_rhd = sm["driverMonitoringState"].isRHD
|
||||||
frame_id = sm["roadCameraState"].frameId
|
frame_id = sm["roadCameraState"].frameId
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from openpilot.selfdrive.controls.lib.pid import PIDController
|
|||||||
|
|
||||||
class BaseFanController(ABC):
|
class BaseFanController(ABC):
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def update(self, cur_temp: float, ignition: bool) -> int:
|
def update(self, cur_temp: float, ignition: bool, standstill: bool = False) -> int:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -20,9 +20,21 @@ class TiciFanController(BaseFanController):
|
|||||||
self.last_ignition = False
|
self.last_ignition = False
|
||||||
self.controller = PIDController(k_p=0, k_i=4e-3, k_f=1, rate=(1 / DT_TRML))
|
self.controller = PIDController(k_p=0, k_i=4e-3, k_f=1, rate=(1 / DT_TRML))
|
||||||
|
|
||||||
def update(self, cur_temp: float, ignition: bool) -> int:
|
def update(self, cur_temp: float, ignition: bool, standstill: bool = False) -> int:
|
||||||
self.controller.neg_limit = -(100 if ignition else 30)
|
# CLEARPILOT: at standstill below 74°C, clamp to 0-10% (near silent)
|
||||||
self.controller.pos_limit = -(30 if ignition else 0)
|
# at standstill above 74°C, allow full 0-100% range
|
||||||
|
if ignition and standstill and cur_temp < 74:
|
||||||
|
self.controller.neg_limit = -10
|
||||||
|
self.controller.pos_limit = 0
|
||||||
|
elif ignition and standstill:
|
||||||
|
self.controller.neg_limit = -100
|
||||||
|
self.controller.pos_limit = 0
|
||||||
|
elif ignition:
|
||||||
|
self.controller.neg_limit = -100
|
||||||
|
self.controller.pos_limit = -15
|
||||||
|
else:
|
||||||
|
self.controller.neg_limit = -30
|
||||||
|
self.controller.pos_limit = 0
|
||||||
|
|
||||||
if ignition != self.last_ignition:
|
if ignition != self.last_ignition:
|
||||||
self.controller.reset()
|
self.controller.reset()
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ def hw_state_thread(end_event, hw_queue):
|
|||||||
|
|
||||||
def thermald_thread(end_event, hw_queue) -> None:
|
def thermald_thread(end_event, hw_queue) -> None:
|
||||||
pm = messaging.PubMaster(['deviceState', 'frogpilotDeviceState'])
|
pm = messaging.PubMaster(['deviceState', 'frogpilotDeviceState'])
|
||||||
sm = messaging.SubMaster(["peripheralState", "gpsLocationExternal", "controlsState", "pandaStates"], poll="pandaStates")
|
sm = messaging.SubMaster(["peripheralState", "gpsLocationExternal", "controlsState", "pandaStates", "carState"], poll="pandaStates")
|
||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
|
|
||||||
@@ -290,7 +290,8 @@ def thermald_thread(end_event, hw_queue) -> None:
|
|||||||
msg.deviceState.maxTempC = all_comp_temp
|
msg.deviceState.maxTempC = all_comp_temp
|
||||||
|
|
||||||
if fan_controller is not None:
|
if fan_controller is not None:
|
||||||
msg.deviceState.fanSpeedPercentDesired = fan_controller.update(all_comp_temp, onroad_conditions["ignition"])
|
standstill = sm['carState'].standstill if sm.seen['carState'] else False
|
||||||
|
msg.deviceState.fanSpeedPercentDesired = fan_controller.update(all_comp_temp, onroad_conditions["ignition"], standstill)
|
||||||
|
|
||||||
is_offroad_for_5_min = (started_ts is None) and ((not started_seen) or (off_ts is None) or (time.monotonic() - off_ts > 60 * 5))
|
is_offroad_for_5_min = (started_ts is None) and ((not started_seen) or (off_ts is None) or (time.monotonic() - off_ts > 60 * 5))
|
||||||
if is_offroad_for_5_min and offroad_comp_temp > OFFROAD_DANGER_TEMP:
|
if is_offroad_for_5_min and offroad_comp_temp > OFFROAD_DANGER_TEMP:
|
||||||
|
|||||||
@@ -85,19 +85,25 @@ void HomeWindow::updateState(const UIState &s) {
|
|||||||
showDriverView(s.scene.driver_camera_timer >= 10, true);
|
showDriverView(s.scene.driver_camera_timer >= 10, true);
|
||||||
|
|
||||||
// CLEARPILOT: show splash screen when onroad but in park
|
// CLEARPILOT: show splash screen when onroad but in park
|
||||||
|
// In nightrider mode (states 1,4), stay on onroad view in park — only offroad transition shows splash
|
||||||
bool parked = s.scene.parked;
|
bool parked = s.scene.parked;
|
||||||
|
int screenMode = paramsMemory.getInt("ScreenDisplayMode");
|
||||||
|
bool nightrider = (screenMode == 1 || screenMode == 4);
|
||||||
|
|
||||||
if (parked && !was_parked_onroad) {
|
if (parked && !was_parked_onroad) {
|
||||||
|
if (!nightrider) {
|
||||||
LOGW("CLP UI: park transition -> showing splash");
|
LOGW("CLP UI: park transition -> showing splash");
|
||||||
slayout->setCurrentWidget(ready);
|
slayout->setCurrentWidget(ready);
|
||||||
|
}
|
||||||
} else if (!parked && was_parked_onroad) {
|
} else if (!parked && was_parked_onroad) {
|
||||||
LOGW("CLP UI: drive transition -> showing onroad");
|
LOGW("CLP UI: drive transition -> showing onroad");
|
||||||
slayout->setCurrentWidget(onroad);
|
slayout->setCurrentWidget(onroad);
|
||||||
|
ready->has_driven = true;
|
||||||
}
|
}
|
||||||
was_parked_onroad = parked;
|
was_parked_onroad = parked;
|
||||||
|
|
||||||
// CLEARPILOT: honor display on/off while showing splash in park
|
// CLEARPILOT: honor display on/off while showing splash in park (normal mode only)
|
||||||
if (parked && ready->isVisible()) {
|
if (parked && ready->isVisible()) {
|
||||||
int screenMode = paramsMemory.getInt("ScreenDisplayMode");
|
|
||||||
if (screenMode == 3) {
|
if (screenMode == 3) {
|
||||||
Hardware::set_display_power(false);
|
Hardware::set_display_power(false);
|
||||||
} else {
|
} else {
|
||||||
@@ -350,15 +356,21 @@ ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) {
|
|||||||
ListWidget *debug_panel = new ListWidget(this);
|
ListWidget *debug_panel = new ListWidget(this);
|
||||||
debug_panel->setContentsMargins(50, 25, 50, 25);
|
debug_panel->setContentsMargins(50, 25, 50, 25);
|
||||||
|
|
||||||
auto *telemetry_toggle = new ParamControl("TelemetryEnabled", "Telemetry Logging",
|
auto *telemetry_toggle = new ToggleControl("Telemetry Logging",
|
||||||
"Record telemetry data to CSV in the session log directory. "
|
"Record telemetry data to CSV in the session log directory. "
|
||||||
"Captures only changed values for efficiency.", "", this);
|
"Captures only changed values for efficiency.", "",
|
||||||
|
Params("/dev/shm/params").getBool("TelemetryEnabled"), this);
|
||||||
|
QObject::connect(telemetry_toggle, &ToggleControl::toggleFlipped, [](bool on) {
|
||||||
|
Params("/dev/shm/params").putBool("TelemetryEnabled", on);
|
||||||
|
});
|
||||||
debug_panel->addItem(telemetry_toggle);
|
debug_panel->addItem(telemetry_toggle);
|
||||||
|
|
||||||
auto *vpn_toggle = new ParamControl("VpnEnabled", "VPN",
|
auto *vpn_toggle = new ToggleControl("VPN",
|
||||||
"Connect to vpn.hanson.xyz for remote SSH access. "
|
"Connect to vpn.hanson.xyz for remote SSH access. "
|
||||||
"Disabling kills the active tunnel and stops reconnection attempts.", "", this);
|
"Disabling kills the active tunnel and stops reconnection attempts.", "",
|
||||||
|
Params("/dev/shm/params").getBool("VpnEnabled"), this);
|
||||||
QObject::connect(vpn_toggle, &ToggleControl::toggleFlipped, [](bool on) {
|
QObject::connect(vpn_toggle, &ToggleControl::toggleFlipped, [](bool on) {
|
||||||
|
Params("/dev/shm/params").putBool("VpnEnabled", on);
|
||||||
if (on) {
|
if (on) {
|
||||||
std::system("sudo bash -c 'nohup /data/openpilot/system/clearpilot/vpn-monitor.sh >> /tmp/vpn-monitor.log 2>&1 &'");
|
std::system("sudo bash -c 'nohup /data/openpilot/system/clearpilot/vpn-monitor.sh >> /tmp/vpn-monitor.log 2>&1 &'");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -177,7 +177,14 @@ void OnroadWindow::offroadTransition(bool offroad) {
|
|||||||
|
|
||||||
void OnroadWindow::paintEvent(QPaintEvent *event) {
|
void OnroadWindow::paintEvent(QPaintEvent *event) {
|
||||||
QPainter p(this);
|
QPainter p(this);
|
||||||
|
// CLEARPILOT: hide engagement border in nightrider mode
|
||||||
|
int dm = paramsMemory.getInt("ScreenDisplayMode");
|
||||||
|
bool nightrider = (dm == 1 || dm == 4);
|
||||||
|
if (nightrider) {
|
||||||
|
p.fillRect(rect(), Qt::black);
|
||||||
|
} else {
|
||||||
p.fillRect(rect(), QColor(bg.red(), bg.green(), bg.blue(), 255));
|
p.fillRect(rect(), QColor(bg.red(), bg.green(), bg.blue(), 255));
|
||||||
|
}
|
||||||
|
|
||||||
QString logicsDisplayString = QString();
|
QString logicsDisplayString = QString();
|
||||||
if (scene.show_jerk) {
|
if (scene.show_jerk) {
|
||||||
@@ -404,7 +411,7 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CLEARPILOT: blinking blue circle when telemetry is recording
|
// CLEARPILOT: blinking blue circle when telemetry is recording
|
||||||
if (Params().getBool("TelemetryEnabled")) {
|
if (paramsMemory.getBool("TelemetryEnabled")) {
|
||||||
// Blink: visible for 500ms, hidden for 500ms
|
// Blink: visible for 500ms, hidden for 500ms
|
||||||
int phase = (QDateTime::currentMSecsSinceEpoch() / 500) % 2;
|
int phase = (QDateTime::currentMSecsSinceEpoch() / 500) % 2;
|
||||||
if (phase == 0) {
|
if (phase == 0) {
|
||||||
@@ -596,11 +603,20 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
|
|||||||
// CLEARPILOT: nightrider mode — outline only, no fill
|
// CLEARPILOT: nightrider mode — outline only, no fill
|
||||||
bool outlineOnly = nightriderMode;
|
bool outlineOnly = nightriderMode;
|
||||||
|
|
||||||
|
// CLEARPILOT: in nightrider mode, hide all lines when not engaged
|
||||||
|
if (outlineOnly && edgeColor == bg_colors[STATUS_DISENGAGED]) {
|
||||||
|
painter.restore();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLEARPILOT: nightrider lines are 1px wider (3 instead of 2)
|
||||||
|
int outlineWidth = outlineOnly ? 3 : 2;
|
||||||
|
|
||||||
// lanelines
|
// lanelines
|
||||||
for (int i = 0; i < std::size(scene.lane_line_vertices); ++i) {
|
for (int i = 0; i < std::size(scene.lane_line_vertices); ++i) {
|
||||||
QColor lineColor = QColor::fromRgbF(1.0, 1.0, 1.0, std::clamp<float>(scene.lane_line_probs[i], 0.0, 0.7));
|
QColor lineColor = QColor::fromRgbF(1.0, 1.0, 1.0, std::clamp<float>(scene.lane_line_probs[i], 0.0, 0.7));
|
||||||
if (outlineOnly) {
|
if (outlineOnly) {
|
||||||
painter.setPen(QPen(lineColor, 2));
|
painter.setPen(QPen(lineColor, outlineWidth));
|
||||||
painter.setBrush(Qt::NoBrush);
|
painter.setBrush(Qt::NoBrush);
|
||||||
} else {
|
} else {
|
||||||
painter.setPen(Qt::NoPen);
|
painter.setPen(Qt::NoPen);
|
||||||
@@ -614,7 +630,7 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
|
|||||||
for (int i = 0; i < std::size(scene.road_edge_vertices); ++i) {
|
for (int i = 0; i < std::size(scene.road_edge_vertices); ++i) {
|
||||||
QColor edgeCol = QColor::fromRgbF(1.0, 0, 0, std::clamp<float>(1.0 - scene.road_edge_stds[i], 0.0, 1.0));
|
QColor edgeCol = QColor::fromRgbF(1.0, 0, 0, std::clamp<float>(1.0 - scene.road_edge_stds[i], 0.0, 1.0));
|
||||||
if (outlineOnly) {
|
if (outlineOnly) {
|
||||||
painter.setPen(QPen(edgeCol, 2));
|
painter.setPen(QPen(edgeCol, outlineWidth));
|
||||||
painter.setBrush(Qt::NoBrush);
|
painter.setBrush(Qt::NoBrush);
|
||||||
} else {
|
} else {
|
||||||
painter.setPen(Qt::NoPen);
|
painter.setPen(Qt::NoPen);
|
||||||
@@ -689,7 +705,7 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
|
|||||||
|
|
||||||
if (outlineOnly) {
|
if (outlineOnly) {
|
||||||
painter.setPen(QPen(QColor(center_lane_color.red(), center_lane_color.green(),
|
painter.setPen(QPen(QColor(center_lane_color.red(), center_lane_color.green(),
|
||||||
center_lane_color.blue(), 180), 2));
|
center_lane_color.blue(), 180), outlineWidth));
|
||||||
painter.setBrush(Qt::NoBrush);
|
painter.setBrush(Qt::NoBrush);
|
||||||
} else {
|
} else {
|
||||||
painter.setPen(Qt::NoPen);
|
painter.setPen(Qt::NoPen);
|
||||||
@@ -718,7 +734,7 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
|
|||||||
if (scene.blind_spot_path) {
|
if (scene.blind_spot_path) {
|
||||||
QColor bsColor = QColor::fromHslF(0 / 360., 0.75, 0.50, 0.6);
|
QColor bsColor = QColor::fromHslF(0 / 360., 0.75, 0.50, 0.6);
|
||||||
if (outlineOnly) {
|
if (outlineOnly) {
|
||||||
painter.setPen(QPen(bsColor, 2));
|
painter.setPen(QPen(bsColor, outlineWidth));
|
||||||
painter.setBrush(Qt::NoBrush);
|
painter.setBrush(Qt::NoBrush);
|
||||||
} else {
|
} else {
|
||||||
QLinearGradient bs(0, height(), 0, 0);
|
QLinearGradient bs(0, height(), 0, 0);
|
||||||
@@ -888,6 +904,10 @@ void AnnotatedCameraWidget::paintEvent(QPaintEvent *event) {
|
|||||||
} else {
|
} else {
|
||||||
CameraWidget::updateCalibration(DEFAULT_CALIBRATION);
|
CameraWidget::updateCalibration(DEFAULT_CALIBRATION);
|
||||||
}
|
}
|
||||||
|
// CLEARPILOT: force CameraWidget bg to black in nightrider to prevent color bleed
|
||||||
|
if (nightriderMode) {
|
||||||
|
CameraWidget::setBackgroundColor(Qt::black);
|
||||||
|
}
|
||||||
painter.beginNativePainting();
|
painter.beginNativePainting();
|
||||||
if (nightriderMode) {
|
if (nightriderMode) {
|
||||||
// CLEARPILOT: black background, no camera feed
|
// CLEARPILOT: black background, no camera feed
|
||||||
@@ -1059,7 +1079,8 @@ void AnnotatedCameraWidget::updateFrogPilotWidgets() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) {
|
void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) {
|
||||||
if ((showAlwaysOnLateralStatusBar || showConditionalExperimentalStatusBar || roadNameUI)) {
|
// CLEARPILOT: only show status bar when telemetry is enabled
|
||||||
|
if (paramsMemory.getBool("TelemetryEnabled")) {
|
||||||
drawStatusBar(p);
|
drawStatusBar(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1260,7 +1281,30 @@ void AnnotatedCameraWidget::drawStatusBar(QPainter &p) {
|
|||||||
|
|
||||||
QString roadName = roadNameUI ? QString::fromStdString(paramsMemory.get("RoadName")) : QString();
|
QString roadName = roadNameUI ? QString::fromStdString(paramsMemory.get("RoadName")) : QString();
|
||||||
|
|
||||||
if (alwaysOnLateralActive && showAlwaysOnLateralStatusBar) {
|
// CLEARPILOT: telemetry status bar — show live stats when telemetry enabled
|
||||||
|
if (paramsMemory.getBool("TelemetryEnabled")) {
|
||||||
|
SubMaster &sm = *(uiState()->sm);
|
||||||
|
auto deviceState = sm["deviceState"].getDeviceState();
|
||||||
|
int maxTempC = deviceState.getMaxTempC();
|
||||||
|
int fanPct = deviceState.getFanSpeedPercentDesired();
|
||||||
|
bool standstill = sm["carState"].getCarState().getStandstill();
|
||||||
|
|
||||||
|
static double last_model_status_t = 0;
|
||||||
|
static float model_status_fps = 0;
|
||||||
|
if (sm.updated("modelV2")) {
|
||||||
|
double now = millis_since_boot();
|
||||||
|
if (last_model_status_t > 0) {
|
||||||
|
double dt = now - last_model_status_t;
|
||||||
|
if (dt > 0) model_status_fps = model_status_fps * 0.8 + (1000.0 / dt) * 0.2;
|
||||||
|
}
|
||||||
|
last_model_status_t = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
newStatus = QString("%1\u00B0C FAN %2% MDL %3")
|
||||||
|
.arg(maxTempC).arg(fanPct).arg(model_status_fps, 0, 'f', 0);
|
||||||
|
if (standstill) newStatus += " STANDSTILL";
|
||||||
|
// CLEARPILOT: suppress "Always On Lateral active" status bar message
|
||||||
|
} else if (false && alwaysOnLateralActive && showAlwaysOnLateralStatusBar) {
|
||||||
newStatus = tr("Always On Lateral active") + (tr(". Press the \"Cruise Control\" button to disable"));
|
newStatus = tr("Always On Lateral active") + (tr(". Press the \"Cruise Control\" button to disable"));
|
||||||
} else if (showConditionalExperimentalStatusBar) {
|
} else if (showConditionalExperimentalStatusBar) {
|
||||||
newStatus = conditionalStatusMap[status != STATUS_DISENGAGED ? conditionalStatus : 0];
|
newStatus = conditionalStatusMap[status != STATUS_DISENGAGED ? conditionalStatus : 0];
|
||||||
|
|||||||
@@ -70,14 +70,13 @@ void ReadyWindow::paintEvent(QPaintEvent *event) {
|
|||||||
painter.drawPixmap(x, y, scaled);
|
painter.drawPixmap(x, y, scaled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error_msg.isEmpty()) {
|
if (error_msg.isEmpty() && !has_driven) {
|
||||||
// "READY!" 8-bit text sprite, 2x size, 15% below center
|
// "READY!" 8-bit text sprite, 15% below center — only before first drive
|
||||||
static QPixmap ready_text("/data/openpilot/selfdrive/clearpilot/theme/clearpilot/images/ready_text.png");
|
static QPixmap ready_text("/data/openpilot/selfdrive/clearpilot/theme/clearpilot/images/ready_text.png");
|
||||||
if (!ready_text.isNull()) {
|
if (!ready_text.isNull()) {
|
||||||
QPixmap scaled = ready_text.scaled(ready_text.width() * 3 / 2, ready_text.height() * 3 / 2, Qt::KeepAspectRatio, Qt::FastTransformation);
|
int tx = (width() - ready_text.width()) / 2;
|
||||||
int tx = (width() - scaled.width()) / 2;
|
|
||||||
int ty = height() / 2 + height() * 15 / 100;
|
int ty = height() / 2 + height() * 15 / 100;
|
||||||
painter.drawPixmap(tx, ty, scaled);
|
painter.drawPixmap(tx, ty, ready_text);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Error state: red text at 25% below center
|
// Error state: red text at 25% below center
|
||||||
@@ -126,5 +125,13 @@ void ReadyWindow::refresh() {
|
|||||||
if (error_msg != prev_error) changed = true;
|
if (error_msg != prev_error) changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset has_driven on ignition off→on (power cycle)
|
||||||
|
bool started = uiState()->scene.started;
|
||||||
|
if (!last_started && started) {
|
||||||
|
has_driven = false;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
last_started = started;
|
||||||
|
|
||||||
if (changed) update();
|
if (changed) update();
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,7 @@ class ReadyWindow : public QWidget {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ReadyWindow(QWidget* parent = nullptr);
|
ReadyWindow(QWidget* parent = nullptr);
|
||||||
|
bool has_driven = false;
|
||||||
private:
|
private:
|
||||||
void showEvent(QShowEvent *event) override;
|
void showEvent(QShowEvent *event) override;
|
||||||
void hideEvent(QHideEvent *event) override;
|
void hideEvent(QHideEvent *event) override;
|
||||||
@@ -29,6 +30,7 @@ private:
|
|||||||
QString cur_temp;
|
QString cur_temp;
|
||||||
QString error_msg; // non-empty = show red error instead of READY!
|
QString error_msg; // non-empty = show red error instead of READY!
|
||||||
QElapsedTimer uptime;
|
QElapsedTimer uptime;
|
||||||
|
bool last_started = false;
|
||||||
QPixmap img_bg;
|
QPixmap img_bg;
|
||||||
QPixmap img_hot;
|
QPixmap img_hot;
|
||||||
};
|
};
|
||||||
Binary file not shown.
19
system/clearpilot/nice-monitor.sh
Executable file
19
system/clearpilot/nice-monitor.sh
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
# Nice monitor — ensures claude processes run at lowest CPU priority.
|
||||||
|
# Checks every 30 seconds and renices any claude process not already at nice 19.
|
||||||
|
|
||||||
|
# Kill other instances of this script
|
||||||
|
for pid in $(pgrep -f 'nice-monitor.sh' | grep -v $$); do
|
||||||
|
kill "$pid" 2>/dev/null
|
||||||
|
done
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
for pid in $(pgrep -f 'claude' 2>/dev/null); do
|
||||||
|
cur=$(awk '{print $19}' /proc/$pid/stat 2>/dev/null)
|
||||||
|
if [ -n "$cur" ] && [ "$cur" != "19" ]; then
|
||||||
|
renice 19 -p "$pid" > /dev/null 2>&1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
sleep 30
|
||||||
|
done
|
||||||
@@ -3,5 +3,5 @@
|
|||||||
# Install logo
|
# Install logo
|
||||||
bash /data/openpilot/system/clearpilot/startup_logo/set_logo.sh
|
bash /data/openpilot/system/clearpilot/startup_logo/set_logo.sh
|
||||||
|
|
||||||
# Reverse ssh if brianbot dongle id
|
# Reverse ssh disabled — using VPN for remote access instead
|
||||||
bash /data/openpilot/system/clearpilot/dev/on_start.sh
|
# bash /data/openpilot/system/clearpilot/dev/on_start.sh
|
||||||
|
|||||||
@@ -101,6 +101,39 @@ def delete_oldest_video():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# CLEARPILOT: max total size for /data/log2 session logs
|
||||||
|
LOG2_MAX_BYTES = 4 * 1024 * 1024 * 1024
|
||||||
|
|
||||||
|
def cleanup_log2():
|
||||||
|
"""Delete oldest session log directories until /data/log2 is under LOG2_MAX_BYTES."""
|
||||||
|
log_base = "/data/log2"
|
||||||
|
if not os.path.isdir(log_base):
|
||||||
|
return
|
||||||
|
# Get all session dirs sorted oldest first (by name = timestamp)
|
||||||
|
dirs = []
|
||||||
|
for entry in sorted(os.listdir(log_base)):
|
||||||
|
if entry == "current":
|
||||||
|
continue
|
||||||
|
path = os.path.join(log_base, entry)
|
||||||
|
if os.path.isdir(path) and not os.path.islink(path):
|
||||||
|
size = sum(f.stat().st_size for f in os.scandir(path) if f.is_file())
|
||||||
|
dirs.append((entry, path, size))
|
||||||
|
total = sum(s for _, _, s in dirs)
|
||||||
|
# Also count current session
|
||||||
|
current = os.path.join(log_base, "current")
|
||||||
|
if os.path.isdir(current):
|
||||||
|
total += sum(f.stat().st_size for f in os.scandir(current) if f.is_file())
|
||||||
|
# Delete oldest until under quota
|
||||||
|
while total > LOG2_MAX_BYTES and dirs:
|
||||||
|
entry, path, size = dirs.pop(0)
|
||||||
|
try:
|
||||||
|
cloudlog.info(f"deleting log session {path} ({size // 1024 // 1024} MB)")
|
||||||
|
shutil.rmtree(path)
|
||||||
|
total -= size
|
||||||
|
except OSError:
|
||||||
|
cloudlog.exception(f"issue deleting log {path}")
|
||||||
|
|
||||||
|
|
||||||
def deleter_thread(exit_event):
|
def deleter_thread(exit_event):
|
||||||
while not exit_event.is_set():
|
while not exit_event.is_set():
|
||||||
out_of_bytes = get_available_bytes(default=MIN_BYTES + 1) < MIN_BYTES
|
out_of_bytes = get_available_bytes(default=MIN_BYTES + 1) < MIN_BYTES
|
||||||
@@ -135,6 +168,8 @@ def deleter_thread(exit_event):
|
|||||||
cloudlog.exception(f"issue deleting {delete_path}")
|
cloudlog.exception(f"issue deleting {delete_path}")
|
||||||
exit_event.wait(.1)
|
exit_event.wait(.1)
|
||||||
else:
|
else:
|
||||||
|
# CLEARPILOT: enforce log2 size quota even when disk space is fine
|
||||||
|
cleanup_log2()
|
||||||
exit_event.wait(30)
|
exit_event.wait(30)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user