UI overhaul, VPN management, controlsd fix, screen recorder removal

- Fix controlsd crash: self.state.name AttributeError when state is int
- Move telemetry tlog import to module level in carstate.py (was per-frame)
- Remove FrogPilot screen recorder from UI (was crashing OMX on init)
- Ready screen: boot logo background, 8-bit READY! sprite, error states
  (panda not connected, car not recognized) with 10s startup grace period
- ClearPilot menu: always opens to General, QButtonGroup for sidebar,
  System Status uses ButtonControl, VPN toggle with process control
- Sidebar hidden on construction (no flash before splash)
- Status window: threaded data collection (QtConcurrent), panda detection
  via scene.pandaType (SPI, not USB), only refreshes when visible
- VPN: moved to system/clearpilot/, SIGTERM graceful shutdown, keepalive
  ping through tunnel, killall openvpn on disable, launched from
  launch_openpilot.sh instead of continue.sh
- Disable gpsd and dashcamd temporarily for perf testing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-13 22:43:54 -05:00
parent bb561ded75
commit c33e155c56
20 changed files with 462 additions and 111 deletions

View File

@@ -1,5 +1,6 @@
#include "selfdrive/ui/qt/home.h"
#include <cstdlib>
#include <QHBoxLayout>
#include <QMouseEvent>
#include <QStackedWidget>
@@ -26,6 +27,7 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
main_layout->setSpacing(0);
sidebar = new Sidebar(this);
sidebar->setVisible(false);
main_layout->addWidget(sidebar);
QObject::connect(sidebar, &Sidebar::openSettings, this, &HomeWindow::openSettings);
QObject::connect(sidebar, &Sidebar::openOnroad, this, &HomeWindow::showOnroad);
@@ -52,6 +54,7 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
// CLEARPILOT
ready = new ReadyWindow(this);
slayout->addWidget(ready);
slayout->setCurrentWidget(ready); // show splash immediately, not ClearPilotPanel
driver_view = new DriverViewWindow(this);
connect(driver_view, &DriverViewWindow::done, [=] {
@@ -139,6 +142,7 @@ void HomeWindow::mousePressEvent(QMouseEvent* e) {
if (ready->isVisible() || onroad->isVisible()) {
LOGW("CLP UI: tap -> showing ClearPilotPanel");
sidebar->setVisible(false);
home->resetToGeneral();
slayout->setCurrentWidget(home);
}
}
@@ -166,18 +170,7 @@ static const char *clpSidebarBtnStyle = R"(
}
)";
static const char *clpActionBtnStyle = R"(
QPushButton {
background-color: #393939;
color: white;
border-radius: 15px;
font-size: 50px;
font-weight: 500;
text-align: left;
padding-left: 30px;
}
QPushButton:pressed { background-color: #4a4a4a; }
)";
// clpActionBtnStyle removed — no longer used
// Shutdown timer: param value -> display label
static QString shutdownLabel(int val) {
@@ -220,10 +213,8 @@ ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) {
general_panel->setContentsMargins(50, 25, 50, 25);
// Status button
QPushButton *status_btn = new QPushButton("Status");
status_btn->setFixedHeight(120);
status_btn->setStyleSheet(clpActionBtnStyle);
QObject::connect(status_btn, &QPushButton::clicked, [=]() { emit openStatus(); });
auto *status_btn = new ButtonControl("System Status", "VIEW", "");
connect(status_btn, &ButtonControl::clicked, [=]() { emit openStatus(); });
general_panel->addItem(status_btn);
// Reset Calibration
@@ -364,6 +355,18 @@ ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) {
"Captures only changed values for efficiency.", "", this);
debug_panel->addItem(telemetry_toggle);
auto *vpn_toggle = new ParamControl("VpnEnabled", "VPN",
"Connect to vpn.hanson.xyz for remote SSH access. "
"Disabling kills the active tunnel and stops reconnection attempts.", "", this);
QObject::connect(vpn_toggle, &ToggleControl::toggleFlipped, [](bool on) {
if (on) {
std::system("sudo bash -c 'nohup /data/openpilot/system/clearpilot/vpn-monitor.sh >> /tmp/vpn-monitor.log 2>&1 &'");
} else {
std::system("sudo pkill -TERM -f vpn-monitor.sh; sudo killall openvpn");
}
});
debug_panel->addItem(vpn_toggle);
// ── Register panels with sidebar buttons ──
QList<QPair<QString, QWidget *>> panels = {
{"General", general_panel},
@@ -372,31 +375,34 @@ ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) {
{"Debug", debug_panel},
};
nav_group = new QButtonGroup(this);
nav_group->setExclusive(true);
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);
nav_group->addButton(btn);
// Network panel handles its own scrolling/margins
if (name == "Network") {
panel_widget->addWidget(panel);
QObject::connect(btn, &QPushButton::clicked, [=, w = panel]() {
btn->setChecked(true);
panel_widget->setCurrentWidget(w);
});
} else {
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 General by default
nav_group->buttons().first()->setChecked(true);
panel_widget->setCurrentIndex(0);
// Main layout: sidebar + panels
@@ -425,3 +431,8 @@ ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) {
#poweroff_btn:pressed { background-color: #FF2424; }
)");
}
void ClearPilotPanel::resetToGeneral() {
panel_widget->setCurrentIndex(0);
nav_group->buttons().first()->setChecked(true);
}