bench mode, ClearPilot menu, status window, UI introspection RPC
Bench mode (--bench flag): - bench_onroad.py publishes fake vehicle state as managed process - manager blocks real car processes (pandad, thermald, controlsd, etc.) - bench_cmd.py for setting params and querying UI state - BLOCKER: UI segfaults (SIGSEGV) when OnroadWindow becomes visible without camera frames — need to make CameraWidget handle missing VisionIPC gracefully before bench drive mode works ClearPilot menu: - Replaced grid launcher with sidebar settings panel (ClearPilotPanel) - Sidebar: Home, Dashcam, Debug - Home panel: Status and System Settings buttons - Debug panel: telemetry logging toggle (ParamControl) - Tap from any view (splash, onroad) opens ClearPilotPanel - Back button returns to splash/onroad Status window: - Live system stats refreshing every 1 second - Storage, RAM, load, IP, WiFi, VPN, GPS, time, telemetry status - Tap anywhere to close, returns to previous view - Honors interactive timeout UI introspection RPC: - ZMQ REP server at ipc:///tmp/clearpilot_ui_rpc - Dumps full widget tree with visibility, geometry, stacked indices - bench_cmd dump queries it, detects crash loops via process uptime - ui_dump.py standalone tool Other: - Telemetry toggle wired to TelemetryEnabled param with disk space guard - Telemetry disabled on every manager start - Blinking red circle on onroad UI when telemetry recording - Fixed showDriverView overriding park/drive transitions every frame - Fixed offroadTransition sidebar visibility race in MainWindow - launch_openpilot.sh: cd to /data/openpilot, --bench flag support Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
122
selfdrive/clearpilot/bench_cmd.py
Normal file
122
selfdrive/clearpilot/bench_cmd.py
Normal file
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
ClearPilot bench command tool. Sets bench params and queries UI state.
|
||||
|
||||
Usage:
|
||||
python3 -m selfdrive.clearpilot.bench_cmd gear D
|
||||
python3 -m selfdrive.clearpilot.bench_cmd speed 20
|
||||
python3 -m selfdrive.clearpilot.bench_cmd speedlimit 45
|
||||
python3 -m selfdrive.clearpilot.bench_cmd cruise 55
|
||||
python3 -m selfdrive.clearpilot.bench_cmd engaged 1
|
||||
python3 -m selfdrive.clearpilot.bench_cmd dump
|
||||
python3 -m selfdrive.clearpilot.bench_cmd wait_ready
|
||||
"""
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import zmq
|
||||
from openpilot.common.params import Params
|
||||
|
||||
|
||||
def check_ui_process():
|
||||
"""Check if UI process is running and healthy. Returns error string or None if OK."""
|
||||
try:
|
||||
result = subprocess.run(["pgrep", "-a", "-f", "./ui"], capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
return "ERROR: UI process not running"
|
||||
# Get the PID and check its uptime
|
||||
for line in result.stdout.strip().split("\n"):
|
||||
parts = line.split(None, 1)
|
||||
if len(parts) >= 2 and "./ui" in parts[1]:
|
||||
pid = parts[0]
|
||||
try:
|
||||
stat = os.stat(f"/proc/{pid}")
|
||||
uptime = time.time() - stat.st_mtime
|
||||
if uptime < 5:
|
||||
return f"ERROR: UI process (pid {pid}) uptime {uptime:.1f}s — likely crash looping. Check: tail /data/log2/$(ls -t /data/log2/ | head -1)/session.log"
|
||||
except FileNotFoundError:
|
||||
return "ERROR: UI process disappeared"
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def ui_dump():
|
||||
ctx = zmq.Context()
|
||||
sock = ctx.socket(zmq.REQ)
|
||||
sock.setsockopt(zmq.RCVTIMEO, 2000)
|
||||
sock.connect("ipc:///tmp/clearpilot_ui_rpc")
|
||||
sock.send_string("dump")
|
||||
try:
|
||||
return sock.recv_string()
|
||||
except zmq.Again:
|
||||
return None
|
||||
finally:
|
||||
sock.close()
|
||||
ctx.term()
|
||||
|
||||
|
||||
def wait_ready(timeout=10):
|
||||
"""Wait until the UI shows ReadyWindow, up to timeout seconds."""
|
||||
start = time.time()
|
||||
while time.time() - start < timeout:
|
||||
dump = ui_dump()
|
||||
if dump and "ReadyWindow" in dump:
|
||||
# Check it's actually visible
|
||||
for line in dump.split("\n"):
|
||||
if "ReadyWindow" in line and "vis=Y" in line:
|
||||
print("UI ready (ReadyWindow visible)")
|
||||
return True
|
||||
time.sleep(1)
|
||||
print(f"ERROR: UI not ready after {timeout}s")
|
||||
if dump:
|
||||
print(dump)
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print(__doc__)
|
||||
return
|
||||
|
||||
cmd = sys.argv[1].lower()
|
||||
params = Params("/dev/shm/params")
|
||||
|
||||
param_map = {
|
||||
"gear": "BenchGear",
|
||||
"speed": "BenchSpeed",
|
||||
"speedlimit": "BenchSpeedLimit",
|
||||
"cruise": "BenchCruiseSpeed",
|
||||
"engaged": "BenchEngaged",
|
||||
}
|
||||
|
||||
if cmd == "dump":
|
||||
ui_status = check_ui_process()
|
||||
if ui_status:
|
||||
print(ui_status)
|
||||
else:
|
||||
result = ui_dump()
|
||||
if result:
|
||||
print(result)
|
||||
else:
|
||||
print("ERROR: UI not responding")
|
||||
|
||||
elif cmd == "wait_ready":
|
||||
wait_ready()
|
||||
|
||||
elif cmd in param_map:
|
||||
if len(sys.argv) < 3:
|
||||
print(f"Usage: bench_cmd {cmd} <value>")
|
||||
return
|
||||
value = sys.argv[2]
|
||||
params.put(param_map[cmd], value)
|
||||
print(f"{param_map[cmd]} = {value}")
|
||||
|
||||
else:
|
||||
print(f"Unknown command: {cmd}")
|
||||
print(__doc__)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user