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:
@@ -5,7 +5,9 @@
|
||||
#include <QStackedWidget>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "common/swaglog.h"
|
||||
#include "selfdrive/ui/qt/util.h"
|
||||
#include "selfdrive/ui/qt/widgets/scrollview.h"
|
||||
|
||||
// HomeWindow: the container for the offroad and onroad UIs
|
||||
|
||||
@@ -25,8 +27,17 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
|
||||
slayout = new QStackedLayout();
|
||||
main_layout->addLayout(slayout);
|
||||
|
||||
home = new OffroadHome(this);
|
||||
QObject::connect(home, &OffroadHome::openSettings, this, &HomeWindow::openSettings);
|
||||
home = new ClearPilotPanel(this);
|
||||
QObject::connect(home, &ClearPilotPanel::openSettings, this, &HomeWindow::openSettings);
|
||||
QObject::connect(home, &ClearPilotPanel::openStatus, this, &HomeWindow::openStatus);
|
||||
QObject::connect(home, &ClearPilotPanel::closePanel, [=]() {
|
||||
// Return to splash or onroad depending on state
|
||||
if (uiState()->scene.started) {
|
||||
slayout->setCurrentWidget(onroad);
|
||||
} else {
|
||||
slayout->setCurrentWidget(ready);
|
||||
}
|
||||
});
|
||||
slayout->addWidget(home);
|
||||
|
||||
onroad = new OnroadWindow(this);
|
||||
@@ -67,10 +78,10 @@ void HomeWindow::updateState(const UIState &s) {
|
||||
// CLEARPILOT: show splash screen when onroad but in park
|
||||
bool parked = s.scene.parked;
|
||||
if (parked && !was_parked_onroad) {
|
||||
// just shifted into park — show splash
|
||||
LOGW("CLP UI: park transition -> showing splash");
|
||||
slayout->setCurrentWidget(ready);
|
||||
} else if (!parked && was_parked_onroad) {
|
||||
// just shifted out of park — show onroad camera
|
||||
LOGW("CLP UI: drive transition -> showing onroad");
|
||||
slayout->setCurrentWidget(onroad);
|
||||
}
|
||||
was_parked_onroad = parked;
|
||||
@@ -90,11 +101,13 @@ void HomeWindow::updateState(const UIState &s) {
|
||||
void HomeWindow::offroadTransition(bool offroad) {
|
||||
sidebar->setVisible(false);
|
||||
if (offroad) {
|
||||
LOGW("CLP UI: offroad transition -> showing splash");
|
||||
was_parked_onroad = false;
|
||||
slayout->setCurrentWidget(ready);
|
||||
} else {
|
||||
// CLEARPILOT: start onroad in splash — updateState will switch to
|
||||
// camera view once the car shifts out of park
|
||||
LOGW("CLP UI: onroad transition -> showing splash (parked)");
|
||||
was_parked_onroad = true;
|
||||
slayout->setCurrentWidget(ready);
|
||||
}
|
||||
@@ -102,34 +115,26 @@ void HomeWindow::offroadTransition(bool offroad) {
|
||||
|
||||
void HomeWindow::showDriverView(bool show, bool started) {
|
||||
if (show) {
|
||||
LOGW("CLP UI: showDriverView(true) -> driver_view");
|
||||
emit closeSettings();
|
||||
slayout->setCurrentWidget(driver_view);
|
||||
sidebar->setVisible(show == false);
|
||||
} else {
|
||||
if (started) {
|
||||
slayout->setCurrentWidget(onroad);
|
||||
sidebar->setVisible(params.getBool("Sidebar"));
|
||||
} else {
|
||||
slayout->setCurrentWidget(home);
|
||||
sidebar->setVisible(show == false);
|
||||
}
|
||||
sidebar->setVisible(false);
|
||||
} else if (!started) {
|
||||
// Offroad, not started — show home menu
|
||||
slayout->setCurrentWidget(home);
|
||||
sidebar->setVisible(false);
|
||||
}
|
||||
// CLEARPILOT: when started, don't touch slayout here —
|
||||
// updateState handles park->splash and drive->onroad transitions
|
||||
}
|
||||
|
||||
void HomeWindow::mousePressEvent(QMouseEvent* e) {
|
||||
// CLEARPILOT todo - tap on main goes straight to settings
|
||||
// Unless we click a debug widget.
|
||||
|
||||
// CLEARPILOT - click ready shows home (no sidebar)
|
||||
if (!onroad->isVisible() && ready->isVisible()) {
|
||||
// CLEARPILOT: tap from any view goes to ClearPilotPanel
|
||||
if (ready->isVisible() || onroad->isVisible()) {
|
||||
LOGW("CLP UI: tap -> showing ClearPilotPanel");
|
||||
sidebar->setVisible(false);
|
||||
slayout->setCurrentWidget(home);
|
||||
}
|
||||
|
||||
// Todo: widgets
|
||||
if (onroad->isVisible()) {
|
||||
emit openSettings();
|
||||
}
|
||||
}
|
||||
|
||||
void HomeWindow::mouseDoubleClickEvent(QMouseEvent* e) {
|
||||
@@ -137,59 +142,149 @@ void HomeWindow::mouseDoubleClickEvent(QMouseEvent* e) {
|
||||
// const SubMaster &sm = *(uiState()->sm);
|
||||
}
|
||||
|
||||
// CLEARPILOT: OffroadHome — clean grid launcher
|
||||
// CLEARPILOT: ClearPilotPanel — settings-style sidebar menu
|
||||
|
||||
OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) {
|
||||
QVBoxLayout* main_layout = new QVBoxLayout(this);
|
||||
main_layout->setContentsMargins(80, 80, 80, 80);
|
||||
main_layout->setSpacing(0);
|
||||
static const char *clpSidebarBtnStyle = R"(
|
||||
QPushButton {
|
||||
color: grey;
|
||||
border: none;
|
||||
background: none;
|
||||
font-size: 65px;
|
||||
font-weight: 500;
|
||||
}
|
||||
QPushButton:checked {
|
||||
color: white;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
color: #ADADAD;
|
||||
}
|
||||
)";
|
||||
|
||||
// grid of launcher buttons
|
||||
QGridLayout *grid = new QGridLayout();
|
||||
grid->setSpacing(40);
|
||||
ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) {
|
||||
// Sidebar
|
||||
QWidget *sidebar_widget = new QWidget;
|
||||
QVBoxLayout *sidebar_layout = new QVBoxLayout(sidebar_widget);
|
||||
sidebar_layout->setContentsMargins(50, 50, 100, 50);
|
||||
|
||||
// Dashcam viewer button
|
||||
QPushButton *dashcam_btn = new QPushButton("Dashcam");
|
||||
dashcam_btn->setFixedSize(400, 300);
|
||||
dashcam_btn->setStyleSheet(R"(
|
||||
// Close button
|
||||
QPushButton *close_btn = new QPushButton("← Back");
|
||||
close_btn->setStyleSheet(R"(
|
||||
QPushButton {
|
||||
background-color: #333333;
|
||||
color: white;
|
||||
border-radius: 20px;
|
||||
font-size: 48px;
|
||||
font-weight: 600;
|
||||
border-radius: 25px;
|
||||
background: #292929;
|
||||
font-size: 50px;
|
||||
font-weight: 500;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #555555;
|
||||
color: #ADADAD;
|
||||
}
|
||||
)");
|
||||
grid->addWidget(dashcam_btn, 0, 0);
|
||||
close_btn->setFixedSize(300, 125);
|
||||
sidebar_layout->addSpacing(10);
|
||||
sidebar_layout->addWidget(close_btn, 0, Qt::AlignRight);
|
||||
QObject::connect(close_btn, &QPushButton::clicked, [=]() { emit closePanel(); });
|
||||
|
||||
// Settings button
|
||||
QPushButton *settings_btn = new QPushButton("Settings");
|
||||
settings_btn->setFixedSize(400, 300);
|
||||
settings_btn->setStyleSheet(R"(
|
||||
// Panel content area
|
||||
panel_widget = new QStackedWidget();
|
||||
|
||||
// Define panels: sidebar label -> content widget
|
||||
// Home panel: buttons for Status and System Settings
|
||||
QWidget *home_panel = new QWidget(this);
|
||||
QVBoxLayout *home_layout = new QVBoxLayout(home_panel);
|
||||
home_layout->setContentsMargins(50, 25, 50, 25);
|
||||
home_layout->setSpacing(20);
|
||||
|
||||
QPushButton *status_btn = new QPushButton("Status");
|
||||
status_btn->setFixedHeight(120);
|
||||
status_btn->setStyleSheet(R"(
|
||||
QPushButton {
|
||||
background-color: #333333;
|
||||
background-color: #393939;
|
||||
color: white;
|
||||
border-radius: 20px;
|
||||
font-size: 48px;
|
||||
font-weight: 600;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #555555;
|
||||
border-radius: 15px;
|
||||
font-size: 50px;
|
||||
font-weight: 500;
|
||||
text-align: left;
|
||||
padding-left: 30px;
|
||||
}
|
||||
QPushButton:pressed { background-color: #4a4a4a; }
|
||||
)");
|
||||
QObject::connect(settings_btn, &QPushButton::clicked, [=]() { emit openSettings(); });
|
||||
grid->addWidget(settings_btn, 0, 1);
|
||||
QObject::connect(status_btn, &QPushButton::clicked, [=]() { emit openStatus(); });
|
||||
home_layout->addWidget(status_btn);
|
||||
|
||||
main_layout->addStretch();
|
||||
main_layout->addLayout(grid);
|
||||
main_layout->addStretch();
|
||||
QPushButton *sysset_btn = new QPushButton("System Settings");
|
||||
sysset_btn->setFixedHeight(120);
|
||||
sysset_btn->setStyleSheet(status_btn->styleSheet());
|
||||
QObject::connect(sysset_btn, &QPushButton::clicked, [=]() { emit openSettings(); });
|
||||
home_layout->addWidget(sysset_btn);
|
||||
|
||||
home_layout->addStretch();
|
||||
|
||||
// Dashcam panel: placeholder
|
||||
QWidget *dashcam_panel = new QWidget(this);
|
||||
QVBoxLayout *dash_layout = new QVBoxLayout(dashcam_panel);
|
||||
dash_layout->setContentsMargins(50, 25, 50, 25);
|
||||
QLabel *dash_label = new QLabel("Dashcam viewer coming soon");
|
||||
dash_label->setStyleSheet("color: grey; font-size: 40px;");
|
||||
dash_label->setAlignment(Qt::AlignCenter);
|
||||
dash_layout->addWidget(dash_label);
|
||||
dash_layout->addStretch();
|
||||
|
||||
// Debug panel
|
||||
ListWidget *debug_panel = new ListWidget(this);
|
||||
debug_panel->setContentsMargins(50, 25, 50, 25);
|
||||
|
||||
auto *telemetry_toggle = new ParamControl("TelemetryEnabled", "Telemetry Logging",
|
||||
"Record telemetry data to CSV in the session log directory. "
|
||||
"Captures only changed values for efficiency.", "", this);
|
||||
debug_panel->addItem(telemetry_toggle);
|
||||
|
||||
// Register panels with sidebar buttons
|
||||
QList<QPair<QString, QWidget *>> panels = {
|
||||
{"Home", home_panel},
|
||||
{"Dashcam", dashcam_panel},
|
||||
{"Debug", debug_panel},
|
||||
};
|
||||
|
||||
for (auto &[name, panel] : panels) {
|
||||
QPushButton *btn = new QPushButton(name);
|
||||
btn->setCheckable(true);
|
||||
btn->setStyleSheet(clpSidebarBtnStyle);
|
||||
btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
|
||||
sidebar_layout->addWidget(btn, 0, Qt::AlignRight);
|
||||
|
||||
ScrollView *panel_frame = new ScrollView(panel, this);
|
||||
panel_widget->addWidget(panel_frame);
|
||||
|
||||
QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() {
|
||||
btn->setChecked(true);
|
||||
panel_widget->setCurrentWidget(w);
|
||||
});
|
||||
}
|
||||
|
||||
// Select Home by default
|
||||
if (auto *first_btn = sidebar_widget->findChild<QPushButton *>(QString(), Qt::FindDirectChildrenOnly)) {
|
||||
// Skip close_btn, find first sidebar btn
|
||||
}
|
||||
panel_widget->setCurrentIndex(0);
|
||||
|
||||
// Main layout: sidebar + panels
|
||||
QHBoxLayout *main_layout = new QHBoxLayout(this);
|
||||
sidebar_widget->setFixedWidth(500);
|
||||
main_layout->addWidget(sidebar_widget);
|
||||
main_layout->addWidget(panel_widget);
|
||||
|
||||
setStyleSheet(R"(
|
||||
OffroadHome {
|
||||
* {
|
||||
color: white;
|
||||
font-size: 50px;
|
||||
}
|
||||
ClearPilotPanel {
|
||||
background-color: black;
|
||||
}
|
||||
QStackedWidget, ScrollView {
|
||||
background-color: #292929;
|
||||
border-radius: 30px;
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user