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:
+12
-3
@@ -39,7 +39,7 @@ qt_src = ["main.cc", "qt/sidebar.cc", "qt/onroad.cc", "qt/body.cc",
|
||||
"qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc",
|
||||
"qt/offroad/software_settings.cc", "qt/offroad/onboarding.cc",
|
||||
"qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc",
|
||||
"../frogpilot/screenrecorder/omx_encoder.cc", # kept for dashcamd .o dependency
|
||||
"../frogpilot/screenrecorder/omx_encoder.cc", "../frogpilot/screenrecorder/screenrecorder.cc",
|
||||
"qt/ready.cc"]
|
||||
|
||||
# build translation files
|
||||
@@ -77,11 +77,20 @@ qt_env.Program("_text", ["qt/text.cc"], LIBS=qt_libs)
|
||||
qt_env.Program("_spinner", ["qt/spinner.cc"], LIBS=qt_libs)
|
||||
|
||||
|
||||
# Clearpilot tools
|
||||
# Clearpilot
|
||||
# Add qtwebengine to build paths
|
||||
qt_env['CXXFLAGS'] += ["-I/usr/include/aarch64-linux-gnu/qt5/QtWebEngine"]
|
||||
qt_env['CXXFLAGS'] += ["-I/usr/include/aarch64-linux-gnu/qt5/QtWebEngineCore"]
|
||||
qt_env['CXXFLAGS'] += ["-I/usr/include/aarch64-linux-gnu/qt5/QtWebEngineWidgets"]
|
||||
qt_env['CXXFLAGS'] += ["-I/usr/include/aarch64-linux-gnu/qt5/QtWebChannel"]
|
||||
qt_webengine_libs = qt_libs + ['Qt5WebEngineWidgets']
|
||||
|
||||
# Create clearpilot tools
|
||||
qt_env.Program("/data/openpilot/system/clearpilot/tools/qt_shell", ["/data/openpilot/system/clearpilot/tools/qt_shell.cc"], LIBS=qt_libs)
|
||||
# qt_env.Program("/data/openpilot/system/clearpilot/tools/qt_webview", ["/data/openpilot/system/clearpilot/tools/qt_webview.cc"], LIBS=qt_webengine_libs)
|
||||
|
||||
# build main UI
|
||||
qt_env.Program("ui", qt_src + [asset_obj], LIBS=qt_libs)
|
||||
qt_env.Program("ui", qt_src + [asset_obj], LIBS=qt_webengine_libs)
|
||||
if GetOption('extras'):
|
||||
qt_src.remove("main.cc") # replaced by test_runner
|
||||
qt_env.Program('tests/test_translations', [asset_obj, 'tests/test_runner.cc', 'tests/test_translations.cc'] + qt_src, LIBS=qt_libs)
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
#include <sys/resource.h>
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <execinfo.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QTranslator>
|
||||
@@ -13,27 +8,7 @@
|
||||
#include "selfdrive/ui/qt/util.h"
|
||||
#include "selfdrive/ui/qt/window.h"
|
||||
|
||||
// CLEARPILOT: crash handler — prints stack trace to stderr on SIGSEGV/SIGABRT
|
||||
static void crash_handler(int sig) {
|
||||
const char *sig_name = (sig == SIGSEGV) ? "SIGSEGV" : (sig == SIGABRT) ? "SIGABRT" : "SIGNAL";
|
||||
fprintf(stderr, "\n=== CRASH: %s (signal %d) ===\n", sig_name, sig);
|
||||
|
||||
void *frames[64];
|
||||
int count = backtrace(frames, 64);
|
||||
fprintf(stderr, "Backtrace (%d frames):\n", count);
|
||||
backtrace_symbols_fd(frames, count, STDERR_FILENO);
|
||||
fprintf(stderr, "=== END CRASH ===\n");
|
||||
fflush(stderr);
|
||||
|
||||
// Re-raise to get default behavior (core dump / exit)
|
||||
signal(sig, SIG_DFL);
|
||||
raise(sig);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
signal(SIGSEGV, crash_handler);
|
||||
signal(SIGABRT, crash_handler);
|
||||
|
||||
setpriority(PRIO_PROCESS, 0, -20);
|
||||
|
||||
qInstallMessageHandler(swagLogMessageHandler);
|
||||
|
||||
+144
-328
@@ -1,20 +1,16 @@
|
||||
#include "selfdrive/ui/qt/home.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <QHBoxLayout>
|
||||
#include <QMouseEvent>
|
||||
#include <QStackedWidget>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "common/swaglog.h"
|
||||
#include "common/params.h"
|
||||
#include "system/hardware/hw.h"
|
||||
#include "selfdrive/ui/qt/offroad/experimental_mode.h"
|
||||
#include "selfdrive/ui/qt/util.h"
|
||||
#include "selfdrive/ui/qt/widgets/controls.h"
|
||||
#include "selfdrive/ui/qt/widgets/input.h"
|
||||
#include "selfdrive/ui/qt/widgets/scrollview.h"
|
||||
#include "selfdrive/ui/qt/network/networking.h"
|
||||
#include "cereal/messaging/messaging.h"
|
||||
#include "selfdrive/ui/qt/widgets/drive_stats.h"
|
||||
#include "system/hardware/hw.h"
|
||||
|
||||
#include <QWebEngineView>
|
||||
|
||||
// HomeWindow: the container for the offroad and onroad UIs
|
||||
|
||||
@@ -27,7 +23,6 @@ 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);
|
||||
@@ -35,17 +30,8 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
|
||||
slayout = new QStackedLayout();
|
||||
main_layout->addLayout(slayout);
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
home = new OffroadHome(this);
|
||||
QObject::connect(home, &OffroadHome::openSettings, this, &HomeWindow::openSettings);
|
||||
slayout->addWidget(home);
|
||||
|
||||
onroad = new OnroadWindow(this);
|
||||
@@ -54,8 +40,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, [=] {
|
||||
showDriverView(false);
|
||||
@@ -81,79 +66,52 @@ void HomeWindow::showSidebar(bool show) {
|
||||
}
|
||||
|
||||
void HomeWindow::updateState(const UIState &s) {
|
||||
// const SubMaster &sm = *(s.sm);
|
||||
if (s.scene.started) {
|
||||
showDriverView(s.scene.driver_camera_timer >= 10, true);
|
||||
|
||||
// CLEARPILOT: show splash screen when onroad but in park
|
||||
bool parked = s.scene.parked;
|
||||
int screenMode = paramsMemory.getInt("ScreenDisplayMode");
|
||||
bool nightrider = (screenMode == 1 || screenMode == 4);
|
||||
|
||||
if (parked && !was_parked_onroad) {
|
||||
LOGW("CLP UI: park transition -> showing splash");
|
||||
slayout->setCurrentWidget(ready);
|
||||
// If we were in nightrider mode, switch to screen off
|
||||
if (nightrider) {
|
||||
paramsMemory.putInt("ScreenDisplayMode", 3);
|
||||
}
|
||||
} else if (!parked && was_parked_onroad) {
|
||||
LOGW("CLP UI: drive transition -> showing onroad");
|
||||
slayout->setCurrentWidget(onroad);
|
||||
ready->has_driven = true;
|
||||
}
|
||||
was_parked_onroad = parked;
|
||||
|
||||
// CLEARPILOT: honor display on/off while showing splash in park (normal mode only)
|
||||
if (parked && ready->isVisible()) {
|
||||
if (screenMode == 3) {
|
||||
Hardware::set_display_power(false);
|
||||
} else {
|
||||
Hardware::set_display_power(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HomeWindow::offroadTransition(bool offroad) {
|
||||
sidebar->setVisible(false);
|
||||
if (offroad) {
|
||||
LOGW("CLP UI: offroad transition -> showing splash");
|
||||
was_parked_onroad = false;
|
||||
sidebar->setVisible(false);
|
||||
slayout->setCurrentWidget(ready);
|
||||
} else {
|
||||
// CLEARPILOT: start onroad in splash — updateState will switch to
|
||||
// camera view once the car shifts out of park. Reset has_driven so
|
||||
// fresh ignition shows the READY text (not the post-drive textless splash).
|
||||
LOGW("CLP UI: onroad transition -> showing splash (parked)");
|
||||
was_parked_onroad = true;
|
||||
ready->has_driven = false;
|
||||
slayout->setCurrentWidget(ready);
|
||||
sidebar->setVisible(false);
|
||||
slayout->setCurrentWidget(onroad);
|
||||
}
|
||||
}
|
||||
|
||||
void HomeWindow::showDriverView(bool show, bool started) {
|
||||
if (show) {
|
||||
LOGW("CLP UI: showDriverView(true) -> driver_view");
|
||||
emit closeSettings();
|
||||
slayout->setCurrentWidget(driver_view);
|
||||
sidebar->setVisible(false);
|
||||
} else if (!started) {
|
||||
// Offroad, not started — show home menu
|
||||
slayout->setCurrentWidget(home);
|
||||
sidebar->setVisible(false);
|
||||
sidebar->setVisible(show == false);
|
||||
} else {
|
||||
if (started) {
|
||||
slayout->setCurrentWidget(onroad);
|
||||
sidebar->setVisible(params.getBool("Sidebar"));
|
||||
} else {
|
||||
slayout->setCurrentWidget(home);
|
||||
sidebar->setVisible(show == false);
|
||||
}
|
||||
}
|
||||
// CLEARPILOT: when started, don't touch slayout here —
|
||||
// updateState handles park->splash and drive->onroad transitions
|
||||
}
|
||||
|
||||
void HomeWindow::mousePressEvent(QMouseEvent* e) {
|
||||
// CLEARPILOT: tap from any view goes to ClearPilotPanel
|
||||
if (ready->isVisible() || onroad->isVisible()) {
|
||||
LOGW("CLP UI: tap -> showing ClearPilotPanel");
|
||||
sidebar->setVisible(false);
|
||||
home->resetToGeneral();
|
||||
// CLEARPILOT todo - tap on main goes straight to settings
|
||||
// Unless we click a debug widget.
|
||||
|
||||
// CLEARPILOT - click ready shows home
|
||||
if (!onroad->isVisible() && ready->isVisible()) {
|
||||
sidebar->setVisible(true);
|
||||
slayout->setCurrentWidget(home);
|
||||
}
|
||||
|
||||
// Todo: widgets
|
||||
if (onroad->isVisible()) {
|
||||
emit openSettings();
|
||||
}
|
||||
}
|
||||
|
||||
void HomeWindow::mouseDoubleClickEvent(QMouseEvent* e) {
|
||||
@@ -161,274 +119,132 @@ void HomeWindow::mouseDoubleClickEvent(QMouseEvent* e) {
|
||||
// const SubMaster &sm = *(uiState()->sm);
|
||||
}
|
||||
|
||||
// CLEARPILOT: ClearPilotPanel — settings-style sidebar menu
|
||||
// OffroadHome: the offroad home page
|
||||
|
||||
static const char *clpSidebarBtnStyle = R"(
|
||||
QPushButton {
|
||||
color: grey;
|
||||
border: none;
|
||||
background: none;
|
||||
font-size: 65px;
|
||||
font-weight: 500;
|
||||
OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) {
|
||||
QVBoxLayout* main_layout = new QVBoxLayout(this);
|
||||
main_layout->setContentsMargins(40, 40, 40, 40);
|
||||
|
||||
// top header
|
||||
QHBoxLayout* header_layout = new QHBoxLayout();
|
||||
header_layout->setContentsMargins(0, 0, 0, 0);
|
||||
header_layout->setSpacing(16);
|
||||
|
||||
update_notif = new QPushButton(tr("UPDATE"));
|
||||
update_notif->setVisible(false);
|
||||
update_notif->setStyleSheet("background-color: #364DEF;");
|
||||
QObject::connect(update_notif, &QPushButton::clicked, [=]() { center_layout->setCurrentIndex(1); });
|
||||
header_layout->addWidget(update_notif, 0, Qt::AlignHCenter | Qt::AlignLeft);
|
||||
|
||||
alert_notif = new QPushButton();
|
||||
alert_notif->setVisible(false);
|
||||
alert_notif->setStyleSheet("background-color: #E22C2C;");
|
||||
QObject::connect(alert_notif, &QPushButton::clicked, [=] { center_layout->setCurrentIndex(2); });
|
||||
header_layout->addWidget(alert_notif, 0, Qt::AlignHCenter | Qt::AlignLeft);
|
||||
|
||||
date = new ElidedLabel();
|
||||
header_layout->addWidget(date, 0, Qt::AlignHCenter | Qt::AlignLeft);
|
||||
|
||||
version = new ElidedLabel();
|
||||
header_layout->addWidget(version, 0, Qt::AlignHCenter | Qt::AlignRight);
|
||||
|
||||
main_layout->addLayout(header_layout);
|
||||
|
||||
// main content
|
||||
main_layout->addSpacing(25);
|
||||
center_layout = new QStackedLayout();
|
||||
|
||||
QWidget *home_widget = new QWidget(this);
|
||||
{
|
||||
QHBoxLayout *home_layout = new QHBoxLayout(home_widget);
|
||||
home_layout->setContentsMargins(0, 0, 0, 0);
|
||||
home_layout->setSpacing(30);
|
||||
|
||||
// // // Create a QWebEngineView
|
||||
// QWebEngineView *web_view = new QWebEngineView();
|
||||
// web_view->load(QUrl("http://fark.com"));
|
||||
|
||||
// // Add the QWebEngineView to the layout
|
||||
// home_layout->addWidget(web_view);
|
||||
}
|
||||
QPushButton:checked {
|
||||
color: white;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
color: #ADADAD;
|
||||
}
|
||||
)";
|
||||
center_layout->addWidget(home_widget);
|
||||
|
||||
// clpActionBtnStyle removed — no longer used
|
||||
// add update & alerts widgets
|
||||
update_widget = new UpdateAlert();
|
||||
QObject::connect(update_widget, &UpdateAlert::dismiss, [=]() { center_layout->setCurrentIndex(0); });
|
||||
center_layout->addWidget(update_widget);
|
||||
alerts_widget = new OffroadAlert();
|
||||
QObject::connect(alerts_widget, &OffroadAlert::dismiss, [=]() { center_layout->setCurrentIndex(0); });
|
||||
center_layout->addWidget(alerts_widget);
|
||||
|
||||
main_layout->addLayout(center_layout, 1);
|
||||
|
||||
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);
|
||||
|
||||
// Close button
|
||||
QPushButton *close_btn = new QPushButton("← Back");
|
||||
close_btn->setStyleSheet(R"(
|
||||
QPushButton {
|
||||
color: white;
|
||||
border-radius: 25px;
|
||||
background: #292929;
|
||||
font-size: 50px;
|
||||
font-weight: 500;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
color: #ADADAD;
|
||||
}
|
||||
)");
|
||||
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(); });
|
||||
|
||||
// Panel content area
|
||||
panel_widget = new QStackedWidget();
|
||||
|
||||
// ── General panel ──
|
||||
ListWidget *general_panel = new ListWidget(this);
|
||||
general_panel->setContentsMargins(50, 25, 50, 25);
|
||||
|
||||
// Status button
|
||||
auto *status_btn = new ButtonControl("System Status", "VIEW", "");
|
||||
connect(status_btn, &ButtonControl::clicked, [=]() { emit openStatus(); });
|
||||
general_panel->addItem(status_btn);
|
||||
|
||||
// Reset Calibration
|
||||
auto resetCalibBtn = new ButtonControl("Reset Calibration", "RESET", "");
|
||||
connect(resetCalibBtn, &ButtonControl::showDescriptionEvent, [=]() {
|
||||
QString desc = "openpilot requires the device to be mounted within 4° left or right and "
|
||||
"within 5° up or 9° down. openpilot is continuously calibrating, resetting is rarely required.";
|
||||
std::string calib_bytes = Params().get("CalibrationParams");
|
||||
if (!calib_bytes.empty()) {
|
||||
try {
|
||||
AlignedBuffer aligned_buf;
|
||||
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(calib_bytes.data(), calib_bytes.size()));
|
||||
auto calib = cmsg.getRoot<cereal::Event>().getLiveCalibration();
|
||||
if (calib.getCalStatus() != cereal::LiveCalibrationData::Status::UNCALIBRATED) {
|
||||
double pitch = calib.getRpyCalib()[1] * (180 / M_PI);
|
||||
double yaw = calib.getRpyCalib()[2] * (180 / M_PI);
|
||||
desc += QString(" Your device is pointed %1° %2 and %3° %4.")
|
||||
.arg(QString::number(std::abs(pitch), 'g', 1), pitch > 0 ? "down" : "up",
|
||||
QString::number(std::abs(yaw), 'g', 1), yaw > 0 ? "left" : "right");
|
||||
}
|
||||
} catch (...) {
|
||||
qInfo() << "invalid CalibrationParams";
|
||||
}
|
||||
}
|
||||
qobject_cast<ButtonControl *>(sender())->setDescription(desc);
|
||||
});
|
||||
connect(resetCalibBtn, &ButtonControl::clicked, [=]() {
|
||||
if (ConfirmationDialog::confirm("Are you sure you want to reset calibration?", "Reset", this)) {
|
||||
Params().remove("CalibrationParams");
|
||||
Params().remove("LiveTorqueParameters");
|
||||
}
|
||||
});
|
||||
general_panel->addItem(resetCalibBtn);
|
||||
|
||||
// Power buttons
|
||||
QHBoxLayout *power_layout = new QHBoxLayout();
|
||||
power_layout->setSpacing(30);
|
||||
|
||||
QPushButton *reboot_btn = new QPushButton("Reboot");
|
||||
reboot_btn->setObjectName("reboot_btn");
|
||||
power_layout->addWidget(reboot_btn);
|
||||
|
||||
QPushButton *softreboot_btn = new QPushButton("Soft Reboot");
|
||||
softreboot_btn->setObjectName("softreboot_btn");
|
||||
power_layout->addWidget(softreboot_btn);
|
||||
|
||||
QPushButton *poweroff_btn = new QPushButton("Power Off");
|
||||
poweroff_btn->setObjectName("poweroff_btn");
|
||||
power_layout->addWidget(poweroff_btn);
|
||||
|
||||
QObject::connect(reboot_btn, &QPushButton::clicked, [=]() {
|
||||
if (!uiState()->engaged()) {
|
||||
if (ConfirmationDialog::confirm("Are you sure you want to reboot?", "Reboot", this)) {
|
||||
if (!uiState()->engaged()) {
|
||||
Params().putBool("DoReboot", true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ConfirmationDialog::alert("Disengage to Reboot", this);
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(softreboot_btn, &QPushButton::clicked, [=]() {
|
||||
if (!uiState()->engaged()) {
|
||||
if (ConfirmationDialog::confirm("Are you sure you want to soft reboot?", "Soft Reboot", this)) {
|
||||
if (!uiState()->engaged()) {
|
||||
Params().putBool("DoSoftReboot", true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ConfirmationDialog::alert("Disengage to Soft Reboot", this);
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(poweroff_btn, &QPushButton::clicked, [=]() {
|
||||
if (!uiState()->engaged()) {
|
||||
if (ConfirmationDialog::confirm("Are you sure you want to power off?", "Power Off", this)) {
|
||||
if (!uiState()->engaged()) {
|
||||
Params().putBool("DoShutdown", true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ConfirmationDialog::alert("Disengage to Power Off", this);
|
||||
}
|
||||
});
|
||||
|
||||
general_panel->addItem(power_layout);
|
||||
|
||||
// ── Network panel ──
|
||||
Networking *network_panel = new Networking(this);
|
||||
// Hide APN button — find by searching QPushButton labels inside AdvancedNetworking
|
||||
for (auto *btn : network_panel->findChildren<QPushButton *>()) {
|
||||
if (btn->text().contains("APN", Qt::CaseInsensitive)) {
|
||||
// Hide the parent AbstractControl frame, not just the button
|
||||
if (auto *frame = qobject_cast<QFrame *>(btn->parentWidget())) {
|
||||
frame->setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Dashcam panel ──
|
||||
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 ToggleControl("Telemetry Logging",
|
||||
"Record telemetry data to CSV in the session log directory. "
|
||||
"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);
|
||||
|
||||
auto *health_metrics_toggle = new ToggleControl("System Health Overlay",
|
||||
"Show controls lag, model frame drops, temperature, CPU, and memory usage "
|
||||
"in the lower-right of the onroad UI. For diagnosing slowdown issues.", "",
|
||||
Params().getBool("ClearpilotShowHealthMetrics"), this);
|
||||
QObject::connect(health_metrics_toggle, &ToggleControl::toggleFlipped, [](bool on) {
|
||||
Params().putBool("ClearpilotShowHealthMetrics", on);
|
||||
});
|
||||
debug_panel->addItem(health_metrics_toggle);
|
||||
|
||||
auto *vpn_toggle = new ToggleControl("VPN",
|
||||
"Connect to vpn.hanson.xyz for remote SSH access. "
|
||||
"Disabling kills the active tunnel and stops reconnection attempts.", "",
|
||||
Params("/dev/shm/params").getBool("VpnEnabled"), this);
|
||||
QObject::connect(vpn_toggle, &ToggleControl::toggleFlipped, [](bool on) {
|
||||
Params("/dev/shm/params").putBool("VpnEnabled", 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},
|
||||
{"Network", network_panel},
|
||||
{"Dashcam", dashcam_panel},
|
||||
{"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]() {
|
||||
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]() {
|
||||
panel_widget->setCurrentWidget(w);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Select General by default
|
||||
nav_group->buttons().first()->setChecked(true);
|
||||
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);
|
||||
// set up refresh timer
|
||||
timer = new QTimer(this);
|
||||
timer->callOnTimeout(this, &OffroadHome::refresh);
|
||||
|
||||
setStyleSheet(R"(
|
||||
* {
|
||||
color: white;
|
||||
font-size: 50px;
|
||||
}
|
||||
ClearPilotPanel {
|
||||
OffroadHome {
|
||||
background-color: black;
|
||||
}
|
||||
QStackedWidget, ScrollView, Networking {
|
||||
background-color: #292929;
|
||||
border-radius: 30px;
|
||||
OffroadHome > QPushButton {
|
||||
padding: 15px 30px;
|
||||
border-radius: 5px;
|
||||
font-size: 40px;
|
||||
font-weight: 500;
|
||||
}
|
||||
OffroadHome > QLabel {
|
||||
font-size: 55px;
|
||||
}
|
||||
#softreboot_btn { height: 120px; border-radius: 15px; background-color: #e2e22c; }
|
||||
#softreboot_btn:pressed { background-color: #ffe224; }
|
||||
#reboot_btn { height: 120px; border-radius: 15px; background-color: #393939; }
|
||||
#reboot_btn:pressed { background-color: #4a4a4a; }
|
||||
#poweroff_btn { height: 120px; border-radius: 15px; background-color: #E22C2C; }
|
||||
#poweroff_btn:pressed { background-color: #FF2424; }
|
||||
)");
|
||||
}
|
||||
|
||||
void ClearPilotPanel::resetToGeneral() {
|
||||
panel_widget->setCurrentIndex(0);
|
||||
nav_group->buttons().first()->setChecked(true);
|
||||
/* Refresh data on screen every 5 seconds. */
|
||||
void OffroadHome::showEvent(QShowEvent *event) {
|
||||
refresh();
|
||||
timer->start(5 * 1000);
|
||||
}
|
||||
|
||||
void OffroadHome::hideEvent(QHideEvent *event) {
|
||||
timer->stop();
|
||||
}
|
||||
|
||||
void OffroadHome::refresh() {
|
||||
QString model = QString::fromStdString(params.get("ModelName"));
|
||||
|
||||
date->setText(QLocale(uiState()->language.mid(5)).toString(QDateTime::currentDateTime(), "dddd, MMMM d"));
|
||||
version->setText(getBrand() + " v" + getVersion().left(14).trimmed() + " - " + model);
|
||||
|
||||
// bool updateAvailable = update_widget->refresh();
|
||||
|
||||
int alerts = alerts_widget->refresh();
|
||||
|
||||
if (alerts > 0 && !alerts_widget->isVisible()) {
|
||||
alerts_widget->setVisible(true);
|
||||
} else if (alerts == 0 && alerts_widget->isVisible()) {
|
||||
alerts_widget->setVisible(false);
|
||||
}
|
||||
|
||||
// pop-up new notification
|
||||
// CLEARPILOT temp disabled update notifications
|
||||
// int idx = center_layout->currentIndex();
|
||||
// if (!updateAvailable && !alerts && false) {
|
||||
// idx = 0;
|
||||
// } else if (updateAvailable && (!update_notif->isVisible() || (!alerts && idx == 2))) {
|
||||
// idx = 1;
|
||||
// } else if (alerts && (!alert_notif->isVisible() || (!updateAvailable && idx == 1))) {
|
||||
// idx = 2;
|
||||
// }
|
||||
// center_layout->setCurrentIndex(idx);
|
||||
|
||||
// CLEARPILOT temp disabled update notifications
|
||||
// update_notif->setVisible(updateAvailable);
|
||||
// alert_notif->setVisible(alerts);
|
||||
alert_notif->setVisible(false);
|
||||
if (alerts) {
|
||||
alert_notif->setText(QString::number(alerts) + (alerts > 1 ? tr(" ALERTS") : tr(" ALERT")));
|
||||
}
|
||||
}
|
||||
|
||||
+20
-15
@@ -1,11 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <QButtonGroup>
|
||||
#include <QFrame>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QStackedLayout>
|
||||
#include <QStackedWidget>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
@@ -18,23 +16,32 @@
|
||||
#include "selfdrive/ui/qt/widgets/offroad_alerts.h"
|
||||
#include "selfdrive/ui/ui.h"
|
||||
|
||||
class ClearPilotPanel : public QFrame {
|
||||
class OffroadHome : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ClearPilotPanel(QWidget* parent = 0);
|
||||
explicit OffroadHome(QWidget* parent = 0);
|
||||
|
||||
signals:
|
||||
void openSettings(int index = 0, const QString ¶m = "");
|
||||
void openStatus();
|
||||
void closePanel();
|
||||
|
||||
public:
|
||||
void resetToGeneral();
|
||||
|
||||
private:
|
||||
QStackedWidget *panel_widget;
|
||||
QButtonGroup *nav_group;
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void hideEvent(QHideEvent *event) override;
|
||||
void refresh();
|
||||
|
||||
Params params;
|
||||
|
||||
QTimer* timer;
|
||||
ElidedLabel* version;
|
||||
QStackedLayout* center_layout;
|
||||
UpdateAlert *update_widget;
|
||||
OffroadAlert* alerts_widget;
|
||||
QPushButton* alert_notif;
|
||||
QPushButton* update_notif;
|
||||
|
||||
// FrogPilot variables
|
||||
ElidedLabel* date;
|
||||
};
|
||||
|
||||
class HomeWindow : public QWidget {
|
||||
@@ -46,7 +53,6 @@ public:
|
||||
|
||||
signals:
|
||||
void openSettings(int index = 0, const QString ¶m = "");
|
||||
void openStatus();
|
||||
void closeSettings();
|
||||
|
||||
public slots:
|
||||
@@ -61,18 +67,17 @@ protected:
|
||||
|
||||
private:
|
||||
Sidebar *sidebar;
|
||||
ClearPilotPanel *home;
|
||||
OffroadHome *home;
|
||||
OnroadWindow *onroad;
|
||||
DriverViewWindow *driver_view;
|
||||
QStackedLayout *slayout;
|
||||
|
||||
// FrogPilot variables
|
||||
Params params;
|
||||
Params paramsMemory{"/dev/shm/params"};
|
||||
|
||||
// CLEARPILOT
|
||||
// bool show_ready;
|
||||
ReadyWindow *ready;
|
||||
bool was_parked_onroad = false;
|
||||
|
||||
private slots:
|
||||
void updateState(const UIState &s);
|
||||
|
||||
+72
-418
@@ -7,7 +7,6 @@
|
||||
#include <sstream>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QMouseEvent>
|
||||
|
||||
@@ -76,13 +75,12 @@ void OnroadWindow::updateState(const UIState &s) {
|
||||
alerts->updateAlert(alert);
|
||||
|
||||
nvg->updateState(s);
|
||||
nvg->update(); // CLEARPILOT: force repaint every frame for HUD elements
|
||||
|
||||
QColor bgColor = bg_colors[s.status];
|
||||
// CLEARPILOT: read from frogpilotCarControl cereal message (was paramsMemory)
|
||||
if ((*s.sm)["frogpilotCarControl"].getFrogpilotCarControl().getNoLatLaneChange()) {
|
||||
|
||||
if (paramsMemory.getBool("no_lat_lane_change") == 1 || screenDisplayMode == 2) {
|
||||
bgColor = bg_colors[STATUS_DISENGAGED];
|
||||
}
|
||||
}
|
||||
|
||||
if (bg != bgColor) {
|
||||
bg = bgColor;
|
||||
@@ -178,14 +176,7 @@ void OnroadWindow::offroadTransition(bool offroad) {
|
||||
|
||||
void OnroadWindow::paintEvent(QPaintEvent *event) {
|
||||
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();
|
||||
if (scene.show_jerk) {
|
||||
@@ -225,13 +216,6 @@ void OnroadWindow::paintEvent(QPaintEvent *event) {
|
||||
}
|
||||
}
|
||||
|
||||
// void OnroadWindow::update_screen_on_off() {
|
||||
// int screenDisaplayMode = paramsMemory.getInt("ScreenDisaplayMode");
|
||||
// if (screenDisaplayMode == 1) {
|
||||
// // Conditionally off
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// ***** onroad widgets *****
|
||||
|
||||
@@ -324,7 +308,10 @@ AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget* par
|
||||
QHBoxLayout *buttons_layout = new QHBoxLayout();
|
||||
buttons_layout->setSpacing(0);
|
||||
|
||||
// Neokii screen recorder — DISABLED: using dashcamd instead
|
||||
// Neokii screen recorder
|
||||
recorder_btn = new ScreenRecorder(this);
|
||||
recorder_btn->setVisible(false);
|
||||
// buttons_layout->addWidget(recorder_btn);
|
||||
|
||||
QVBoxLayout *top_right_layout = new QVBoxLayout();
|
||||
top_right_layout->setSpacing(0);
|
||||
@@ -340,13 +327,24 @@ AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget* par
|
||||
}
|
||||
|
||||
void AnnotatedCameraWidget::updateState(const UIState &s) {
|
||||
|
||||
screenDisplayMode = paramsMemory.getInt("ScreenDisplayMode");
|
||||
if (screenDisplayMode == 2 && !alert_is_visible) {
|
||||
// Draw black, filled, full-size rectangle to blank the screen
|
||||
// p.fillRect(0, 0, width(), height(), Qt::black);
|
||||
// p.restore();
|
||||
Hardware::set_display_power(false);
|
||||
return;
|
||||
} else {
|
||||
Hardware::set_display_power(true);
|
||||
}
|
||||
|
||||
const int SET_SPEED_NA = 255;
|
||||
const SubMaster &sm = *(s.sm);
|
||||
|
||||
const bool cs_alive = sm.alive("controlsState");
|
||||
const auto cs = sm["controlsState"].getControlsState();
|
||||
const auto car_state = sm["carState"].getCarState();
|
||||
(void)car_state; // CLEARPILOT: suppress unused warning, will use later
|
||||
|
||||
// Handle older routes where vCruiseCluster is not set
|
||||
float v_cruise = cs.getVCruiseCluster() == 0.0 ? cs.getVCruise() : cs.getVCruiseCluster();
|
||||
@@ -356,16 +354,11 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
|
||||
setSpeed *= KM_TO_MILE;
|
||||
}
|
||||
|
||||
// CLEARPILOT: read speed and speed limit from params (written by speed_logic.py ~2Hz)
|
||||
clpParamFrame++;
|
||||
if (clpParamFrame % 10 == 0) { // ~2Hz at 20Hz UI update rate
|
||||
clpHasSpeed = paramsMemory.get("ClearpilotHasSpeed") == "1";
|
||||
clpSpeedDisplay = QString::fromStdString(paramsMemory.get("ClearpilotSpeedDisplay"));
|
||||
clpSpeedLimitDisplay = QString::fromStdString(paramsMemory.get("ClearpilotSpeedLimitDisplay"));
|
||||
clpSpeedUnit = QString::fromStdString(paramsMemory.get("ClearpilotSpeedUnit"));
|
||||
clpCruiseWarning = QString::fromStdString(paramsMemory.get("ClearpilotCruiseWarning"));
|
||||
clpCruiseWarningSpeed = QString::fromStdString(paramsMemory.get("ClearpilotCruiseWarningSpeed"));
|
||||
}
|
||||
// Handle older routes where vEgoCluster is not set
|
||||
v_ego_cluster_seen = v_ego_cluster_seen || car_state.getVEgoCluster() != 0.0;
|
||||
float v_ego = v_ego_cluster_seen && !scene.wheel_speed ? car_state.getVEgoCluster() : car_state.getVEgo();
|
||||
speed = cs_alive ? std::max<float>(0.0, v_ego) : 0.0;
|
||||
speed *= s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH;
|
||||
|
||||
// auto speed_limit_sign = nav_instruction.getSpeedLimitSign();
|
||||
// speedLimit = slcOverridden ? scene.speed_limit_overridden_speed : speedLimitController ? scene.speed_limit : nav_alive ? nav_instruction.getSpeedLimit() : 0.0;
|
||||
@@ -401,28 +394,9 @@ if (edgeColor != bgColor) {
|
||||
}
|
||||
|
||||
void AnnotatedCameraWidget::drawHud(QPainter &p) {
|
||||
// CLEARPILOT: display power control based on ScreenDisplayMode
|
||||
// Blank when screenDisplayMode=1
|
||||
p.save();
|
||||
|
||||
if (displayMode == 3 && !alert_is_visible) {
|
||||
Hardware::set_display_power(false);
|
||||
p.restore();
|
||||
return;
|
||||
} else {
|
||||
Hardware::set_display_power(true);
|
||||
}
|
||||
|
||||
// CLEARPILOT: blinking blue circle when telemetry is recording
|
||||
if (paramsMemory.getBool("TelemetryEnabled")) {
|
||||
// Blink: visible for 500ms, hidden for 500ms
|
||||
int phase = (QDateTime::currentMSecsSinceEpoch() / 500) % 2;
|
||||
if (phase == 0) {
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(QColor(30, 100, 220));
|
||||
p.drawEllipse(width() - 150, 50, 100, 100);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Header gradient
|
||||
QLinearGradient bg(0, UI_HEADER_HEIGHT - (UI_HEADER_HEIGHT / 2.5), 0, UI_HEADER_HEIGHT);
|
||||
bg.setColorAt(0, QColor::fromRgbF(0, 0, 0, 0.45));
|
||||
@@ -433,7 +407,7 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) {
|
||||
|
||||
// QString speedLimitStr = (speedLimit > 1) ? QString::number(std::nearbyint(speedLimit)) : "–";
|
||||
// QString speedLimitOffsetStr = slcSpeedLimitOffset == 0 ? "–" : QString::number(slcSpeedLimitOffset, 'f', 0).prepend(slcSpeedLimitOffset > 0 ? "+" : "");
|
||||
// QString speedStr = QString::number(std::nearbyint(speed));
|
||||
QString speedStr = QString::number(std::nearbyint(speed));
|
||||
QString setSpeedStr = is_cruise_set ? QString::number(std::nearbyint(setSpeed - cruiseAdjustment)) : "–";
|
||||
|
||||
p.restore();
|
||||
@@ -457,21 +431,17 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) {
|
||||
// Todo: lead speed
|
||||
// Todo: Experimental speed
|
||||
|
||||
// CLEARPILOT: show speed from speed_logic params, hide when no speed or speed=0
|
||||
if (clpHasSpeed && !clpSpeedDisplay.isEmpty() && !scene.hide_speed) {
|
||||
// // current speed
|
||||
if (!(scene.hide_speed)) {
|
||||
// CLEARPILOT changes to 120 from ~176
|
||||
// Maybe we want to hide this?
|
||||
p.setFont(InterFont(140, QFont::Bold));
|
||||
drawText(p, rect().center().x(), 210, clpSpeedDisplay);
|
||||
drawText(p, rect().center().x(), 210, speedStr);
|
||||
// CLEARPILOT changes to 40 from 66
|
||||
p.setFont(InterFont(50));
|
||||
drawText(p, rect().center().x(), 290, clpSpeedUnit, 200);
|
||||
drawText(p, rect().center().x(), 290, speedUnit, 200);
|
||||
}
|
||||
|
||||
// CLEARPILOT: speed limit sign in lower-left, cruise warning above it
|
||||
drawSpeedLimitSign(p);
|
||||
drawCruiseWarningSign(p);
|
||||
|
||||
// CLEARPILOT: system health metrics in lower-right (debug overlay)
|
||||
drawHealthMetrics(p);
|
||||
|
||||
|
||||
// Draw FrogPilot widgets
|
||||
paintFrogPilotWidgets(p);
|
||||
}
|
||||
@@ -565,164 +535,6 @@ void AnnotatedCameraWidget::drawSpeedWidget(QPainter &p, int x, int y, const QSt
|
||||
}
|
||||
|
||||
|
||||
void AnnotatedCameraWidget::drawSpeedLimitSign(QPainter &p) {
|
||||
// Hide when no speed limit or speed limit is 0
|
||||
if (clpSpeedLimitDisplay.isEmpty() || clpSpeedLimitDisplay == "0") return;
|
||||
|
||||
p.save();
|
||||
|
||||
const int signW = 189;
|
||||
const int signH = 239;
|
||||
const int margin = 20;
|
||||
const int borderW = 6;
|
||||
const int innerBorderW = 4;
|
||||
const int innerMargin = 10;
|
||||
const int cornerR = 15;
|
||||
|
||||
// Position: 20px from lower-left corner
|
||||
QRect signRect(margin, height() - signH - margin, signW, signH);
|
||||
|
||||
if (nightriderMode) {
|
||||
// Nightrider: black background, light gray-blue border and text
|
||||
QColor borderColor(160, 180, 210);
|
||||
QColor textColor(160, 180, 210);
|
||||
|
||||
// Outer border
|
||||
p.setPen(QPen(borderColor, borderW));
|
||||
p.setBrush(QColor(0, 0, 0, 220));
|
||||
p.drawRoundedRect(signRect, cornerR, cornerR);
|
||||
|
||||
// Inner border
|
||||
QRect innerRect = signRect.adjusted(innerMargin, innerMargin, -innerMargin, -innerMargin);
|
||||
p.setPen(QPen(borderColor, innerBorderW));
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.drawRoundedRect(innerRect, cornerR - 4, cornerR - 4);
|
||||
|
||||
// "SPEED" text
|
||||
p.setPen(textColor);
|
||||
p.setFont(InterFont(30, QFont::Bold));
|
||||
p.drawText(innerRect.adjusted(0, 15, 0, 0), Qt::AlignTop | Qt::AlignHCenter, "SPEED");
|
||||
|
||||
// "LIMIT" text
|
||||
p.setFont(InterFont(30, QFont::Bold));
|
||||
p.drawText(innerRect.adjusted(0, 48, 0, 0), Qt::AlignTop | Qt::AlignHCenter, "LIMIT");
|
||||
|
||||
// Speed limit number — shifted down ~10% of innerRect height via extra top inset
|
||||
p.setFont(InterFont(90, QFont::Bold));
|
||||
p.drawText(innerRect.adjusted(0, 86, 0, 0), Qt::AlignCenter, clpSpeedLimitDisplay);
|
||||
} else {
|
||||
// Normal: white background, black border and text
|
||||
QColor borderColor(0, 0, 0);
|
||||
QColor textColor(0, 0, 0);
|
||||
|
||||
// Outer border
|
||||
p.setPen(QPen(borderColor, borderW));
|
||||
p.setBrush(QColor(255, 255, 255, 240));
|
||||
p.drawRoundedRect(signRect, cornerR, cornerR);
|
||||
|
||||
// Inner border
|
||||
QRect innerRect = signRect.adjusted(innerMargin, innerMargin, -innerMargin, -innerMargin);
|
||||
p.setPen(QPen(borderColor, innerBorderW));
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.drawRoundedRect(innerRect, cornerR - 4, cornerR - 4);
|
||||
|
||||
// "SPEED" text
|
||||
p.setPen(textColor);
|
||||
p.setFont(InterFont(30, QFont::Bold));
|
||||
p.drawText(innerRect.adjusted(0, 15, 0, 0), Qt::AlignTop | Qt::AlignHCenter, "SPEED");
|
||||
|
||||
// "LIMIT" text
|
||||
p.setFont(InterFont(30, QFont::Bold));
|
||||
p.drawText(innerRect.adjusted(0, 48, 0, 0), Qt::AlignTop | Qt::AlignHCenter, "LIMIT");
|
||||
|
||||
// Speed limit number — shifted down ~10% of innerRect height via extra top inset
|
||||
p.setFont(InterFont(90, QFont::Bold));
|
||||
p.drawText(innerRect.adjusted(0, 86, 0, 0), Qt::AlignCenter, clpSpeedLimitDisplay);
|
||||
}
|
||||
|
||||
p.restore();
|
||||
}
|
||||
|
||||
void AnnotatedCameraWidget::drawCruiseWarningSign(QPainter &p) {
|
||||
// Only show when there's an active warning and the speed limit sign is visible
|
||||
if (clpCruiseWarning.isEmpty() || clpCruiseWarningSpeed.isEmpty()) return;
|
||||
if (clpSpeedLimitDisplay.isEmpty() || clpSpeedLimitDisplay == "0") return;
|
||||
|
||||
bool isOver = (clpCruiseWarning == "over");
|
||||
if (!isOver && clpCruiseWarning != "under") return;
|
||||
|
||||
p.save();
|
||||
|
||||
// Same dimensions as speed limit sign
|
||||
const int signW = 189;
|
||||
const int signH = 239;
|
||||
const int margin = 20;
|
||||
const int borderW = 6;
|
||||
const int innerBorderW = 4;
|
||||
const int innerMargin = 10;
|
||||
const int cornerR = 15;
|
||||
const int gap = 20;
|
||||
|
||||
// Position: directly above the speed limit sign
|
||||
int speedLimitY = height() - signH - margin;
|
||||
QRect signRect(margin, speedLimitY - signH - gap, signW, signH);
|
||||
|
||||
if (nightriderMode) {
|
||||
// Nightrider: black background with colored border/text
|
||||
QColor accentColor = isOver ? QColor(220, 50, 50) : QColor(50, 180, 80);
|
||||
|
||||
p.setPen(QPen(accentColor, borderW));
|
||||
p.setBrush(QColor(0, 0, 0, 220));
|
||||
p.drawRoundedRect(signRect, cornerR, cornerR);
|
||||
|
||||
QRect innerRect = signRect.adjusted(innerMargin, innerMargin, -innerMargin, -innerMargin);
|
||||
p.setPen(QPen(accentColor, innerBorderW));
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.drawRoundedRect(innerRect, cornerR - 4, cornerR - 4);
|
||||
|
||||
// "CRUISE" text
|
||||
p.setPen(accentColor);
|
||||
p.setFont(InterFont(26, QFont::Bold));
|
||||
p.drawText(innerRect.adjusted(0, 15, 0, 0), Qt::AlignTop | Qt::AlignHCenter, "CRUISE");
|
||||
|
||||
// "SET" text
|
||||
p.setFont(InterFont(26, QFont::Bold));
|
||||
p.drawText(innerRect.adjusted(0, 45, 0, 0), Qt::AlignTop | Qt::AlignHCenter, "SET");
|
||||
|
||||
// Cruise speed number
|
||||
p.setFont(InterFont(90, QFont::Bold));
|
||||
p.drawText(innerRect.adjusted(0, 86, 0, 0), Qt::AlignCenter, clpCruiseWarningSpeed);
|
||||
} else {
|
||||
// Normal: colored background with white border/text
|
||||
QColor bgColor = isOver ? QColor(200, 30, 30, 240) : QColor(40, 160, 60, 240);
|
||||
QColor fgColor(255, 255, 255);
|
||||
|
||||
p.setPen(QPen(fgColor, borderW));
|
||||
p.setBrush(bgColor);
|
||||
p.drawRoundedRect(signRect, cornerR, cornerR);
|
||||
|
||||
QRect innerRect = signRect.adjusted(innerMargin, innerMargin, -innerMargin, -innerMargin);
|
||||
p.setPen(QPen(fgColor, innerBorderW));
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.drawRoundedRect(innerRect, cornerR - 4, cornerR - 4);
|
||||
|
||||
// "CRUISE" text
|
||||
p.setPen(fgColor);
|
||||
p.setFont(InterFont(26, QFont::Bold));
|
||||
p.drawText(innerRect.adjusted(0, 15, 0, 0), Qt::AlignTop | Qt::AlignHCenter, "CRUISE");
|
||||
|
||||
// "SET" text
|
||||
p.setFont(InterFont(26, QFont::Bold));
|
||||
p.drawText(innerRect.adjusted(0, 45, 0, 0), Qt::AlignTop | Qt::AlignHCenter, "SET");
|
||||
|
||||
// Cruise speed number
|
||||
p.setFont(InterFont(90, QFont::Bold));
|
||||
p.drawText(innerRect.adjusted(0, 86, 0, 0), Qt::AlignCenter, clpCruiseWarningSpeed);
|
||||
}
|
||||
|
||||
p.restore();
|
||||
}
|
||||
|
||||
void AnnotatedCameraWidget::drawText(QPainter &p, int x, int y, const QString &text, int alpha) {
|
||||
QRect real_rect = p.fontMetrics().boundingRect(text);
|
||||
real_rect.moveCenter({x, y - real_rect.height() / 2});
|
||||
@@ -731,87 +543,6 @@ void AnnotatedCameraWidget::drawText(QPainter &p, int x, int y, const QString &t
|
||||
p.drawText(real_rect.x(), real_rect.bottom(), text);
|
||||
}
|
||||
|
||||
// CLEARPILOT: System health overlay — shows metrics that indicate the system
|
||||
// is overburdened or behind. Toggled via ClearpilotShowHealthMetrics param.
|
||||
// Metrics (top→bottom): FPS, DROP, TEMP, CPU, MEM, FAN
|
||||
// FPS: modeld framerate — 20 normally, 0 in park. Read from ModelFps memory
|
||||
// param which modeld writes only on transition.
|
||||
// DROP (%): modelV2 frameDropPerc — modeld losing frames; controlsd errors >20%
|
||||
// TEMP (°C): deviceState.maxTempC — thermal throttling starts ~75, serious >88
|
||||
// CPU (%): max core from deviceState.cpuUsagePercent
|
||||
// MEM (%): deviceState.memoryUsagePercent
|
||||
// FAN (%): actual fan duty from peripheralState RPM (scaled to 6500 RPM = 100%)
|
||||
// Each value color-codes green/yellow/red by severity.
|
||||
void AnnotatedCameraWidget::drawHealthMetrics(QPainter &p) {
|
||||
static bool enabled = Params().getBool("ClearpilotShowHealthMetrics");
|
||||
static int check_counter = 0;
|
||||
// re-check the param every ~2s without a toggle signal path
|
||||
if (++check_counter >= 40) {
|
||||
check_counter = 0;
|
||||
enabled = Params().getBool("ClearpilotShowHealthMetrics");
|
||||
}
|
||||
if (!enabled) return;
|
||||
|
||||
SubMaster &sm = *(uiState()->sm);
|
||||
auto ds = sm["deviceState"].getDeviceState();
|
||||
auto mv = sm["modelV2"].getModelV2();
|
||||
auto ps = sm["peripheralState"].getPeripheralState();
|
||||
|
||||
int model_fps = paramsMemory.getInt("ModelFps");
|
||||
float drop_pct = mv.getFrameDropPerc();
|
||||
float temp_c = ds.getMaxTempC();
|
||||
int mem_pct = ds.getMemoryUsagePercent();
|
||||
int cpu_pct = 0;
|
||||
for (auto v : ds.getCpuUsagePercent()) cpu_pct = std::max(cpu_pct, (int)v);
|
||||
// Actual fan (not commanded): scale RPM to 0-100 using 6500 RPM as full scale
|
||||
int fan_pct = std::min(100, (int)(ps.getFanSpeedRpm() * 100 / 6500));
|
||||
|
||||
auto color_for = [](float v, float warn, float crit) {
|
||||
if (v >= crit) return QColor(0xff, 0x50, 0x50); // red
|
||||
if (v >= warn) return QColor(0xff, 0xd0, 0x40); // yellow
|
||||
return QColor(0xff, 0xff, 0xff); // white (ok)
|
||||
};
|
||||
|
||||
struct Row { QString label; QString value; QColor color; };
|
||||
Row rows[] = {
|
||||
{"FPS", QString::number(model_fps), QColor(0xff, 0xff, 0xff)},
|
||||
{"DROP", QString::number((int)drop_pct),color_for(drop_pct, 5.f, 15.f)},
|
||||
{"TEMP", QString::number((int)temp_c), color_for(temp_c, 75.f, 88.f)},
|
||||
{"CPU", QString::number(cpu_pct), color_for((float)cpu_pct, 75.f, 90.f)},
|
||||
{"MEM", QString::number(mem_pct), color_for((float)mem_pct, 70.f, 85.f)},
|
||||
{"FAN", QString::number(fan_pct), QColor(0xff, 0xff, 0xff)},
|
||||
};
|
||||
|
||||
p.save();
|
||||
p.setFont(InterFont(90, QFont::Bold));
|
||||
QFontMetrics fm = p.fontMetrics();
|
||||
int row_h = fm.height(); // natural line height at 90pt bold
|
||||
int gap = 40; // requested 40px between values
|
||||
int margin = 30; // requested 30px margin
|
||||
int panel_w = 360; // fixed width — fits "TEMP 99"
|
||||
int n = sizeof(rows) / sizeof(rows[0]);
|
||||
int panel_h = n * row_h + (n - 1) * gap + 2 * margin;
|
||||
int x = width() - panel_w - margin;
|
||||
int y = height() - panel_h - margin;
|
||||
|
||||
// black background
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(QColor(0, 0, 0, 200));
|
||||
p.drawRoundedRect(QRect(x, y, panel_w, panel_h), 20, 20);
|
||||
|
||||
// rows
|
||||
int text_y = y + margin + fm.ascent();
|
||||
for (int i = 0; i < n; i++) {
|
||||
p.setPen(rows[i].color);
|
||||
// label left (shifted -50px per user request), value right
|
||||
p.drawText(x + margin - 50, text_y, rows[i].label);
|
||||
QRect vrect = fm.boundingRect(rows[i].value);
|
||||
p.drawText(x + panel_w - margin - vrect.width(), text_y, rows[i].value);
|
||||
text_y += row_h + gap;
|
||||
}
|
||||
p.restore();
|
||||
}
|
||||
|
||||
void AnnotatedCameraWidget::initializeGL() {
|
||||
CameraWidget::initializeGL();
|
||||
qInfo() << "OpenGL version:" << QString((const char*)glGetString(GL_VERSION));
|
||||
@@ -831,6 +562,13 @@ void AnnotatedCameraWidget::updateFrameMat() {
|
||||
s->fb_w = w;
|
||||
s->fb_h = h;
|
||||
|
||||
if (screenDisplayMode == 1 || screenDisplayMode == 2) {
|
||||
// Render a black box instead of the video feed
|
||||
QPainter painter(this);
|
||||
painter.fillRect(0, 0, w, h, Qt::black);
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply transformation such that video pixel coordinates match video
|
||||
// 1) Put (0, 0) in the middle of the video
|
||||
// 2) Apply same scaling as video
|
||||
@@ -842,59 +580,32 @@ void AnnotatedCameraWidget::updateFrameMat() {
|
||||
}
|
||||
|
||||
void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
|
||||
if (screenDisplayMode == 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
painter.save();
|
||||
|
||||
// CLEARPILOT: color channel code rewriten to allow custom colors
|
||||
|
||||
SubMaster &sm = *(s->sm);
|
||||
|
||||
// CLEARPILOT: nightrider mode — outline only, no fill
|
||||
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;
|
||||
// CLEARPILOT: lane lines 5% thinner than the generic outline (QPen accepts float width)
|
||||
float laneLineWidth = outlineOnly ? (float)outlineWidth * 0.95f : (float)outlineWidth;
|
||||
|
||||
// lanelines
|
||||
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));
|
||||
if (outlineOnly) {
|
||||
painter.setPen(QPen(lineColor, laneLineWidth));
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
} else {
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(lineColor);
|
||||
}
|
||||
painter.setBrush(QColor::fromRgbF(1.0, 1.0, 1.0, std::clamp<float>(scene.lane_line_probs[i], 0.0, 0.7)));
|
||||
painter.drawPolygon(scene.lane_line_vertices[i]);
|
||||
}
|
||||
if (outlineOnly) painter.setPen(Qt::NoPen);
|
||||
|
||||
// road edges
|
||||
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));
|
||||
if (outlineOnly) {
|
||||
painter.setPen(QPen(edgeCol, outlineWidth));
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
} else {
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(edgeCol);
|
||||
}
|
||||
painter.setBrush(QColor::fromRgbF(1.0, 0, 0, std::clamp<float>(1.0 - scene.road_edge_stds[i], 0.0, 1.0)));
|
||||
painter.drawPolygon(scene.road_edge_vertices[i]);
|
||||
}
|
||||
if (outlineOnly) painter.setPen(Qt::NoPen);
|
||||
|
||||
// paint center lane path
|
||||
// QColor bg_colors[CHANGE_LANE_PATH_COLOR];
|
||||
|
||||
// CLEARPILOT: read from frogpilotCarControl cereal message (was paramsMemory)
|
||||
bool is_no_lat_lane_change = sm["frogpilotCarControl"].getFrogpilotCarControl().getNoLatLaneChange();
|
||||
bool is_no_lat_lane_change = paramsMemory.getBool("no_lat_lane_change");
|
||||
|
||||
QColor center_lane_color;
|
||||
|
||||
@@ -954,23 +665,12 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
|
||||
path_gradient.setColorAt(1.0, QColor(center_lane_color.red(), center_lane_color.green(), center_lane_color.blue(), static_cast<int>(CENTER_LANE_ALPHA * 255 * 0.0)));
|
||||
}
|
||||
|
||||
if (outlineOnly) {
|
||||
// CLEARPILOT: in nightrider, the tire path outline is light blue at 3px.
|
||||
// Uses a fixed light-blue instead of center_lane_color (which is status-tinted) so
|
||||
// the path reads as a neutral guide, not as engagement/status feedback.
|
||||
QColor lightBlue(153, 204, 255, 220); // #99CCFF light blue, mostly opaque
|
||||
painter.setPen(QPen(lightBlue, 3));
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
} else {
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(path_gradient);
|
||||
}
|
||||
painter.setBrush(path_gradient);
|
||||
painter.drawPolygon(scene.track_vertices);
|
||||
if (outlineOnly) painter.setPen(Qt::NoPen);
|
||||
}
|
||||
|
||||
// Paint path edges ,Use current background color
|
||||
if (edgeColor != bg_colors[STATUS_DISENGAGED] && !outlineOnly) {
|
||||
if (edgeColor != bg_colors[STATUS_DISENGAGED]) {
|
||||
QLinearGradient edge_gradient;
|
||||
edge_gradient.setColorAt(0.0, QColor(edgeColor.red(), edgeColor.green(), edgeColor.blue(), static_cast<int>(255)));
|
||||
edge_gradient.setColorAt(0.5, QColor(edgeColor.red(), edgeColor.green(), edgeColor.blue(), static_cast<int>(255 * 0.8) ));
|
||||
@@ -986,24 +686,18 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
|
||||
|
||||
// Paint blindspot path
|
||||
if (scene.blind_spot_path) {
|
||||
QColor bsColor = QColor::fromHslF(0 / 360., 0.75, 0.50, 0.6);
|
||||
if (outlineOnly) {
|
||||
painter.setPen(QPen(bsColor, outlineWidth));
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
} else {
|
||||
QLinearGradient bs(0, height(), 0, 0);
|
||||
bs.setColorAt(0.0, QColor::fromHslF(0 / 360., 0.75, 0.50, 0.6));
|
||||
bs.setColorAt(0.5, QColor::fromHslF(0 / 360., 0.75, 0.50, 0.4));
|
||||
bs.setColorAt(1.0, QColor::fromHslF(0 / 360., 0.75, 0.50, 0.2));
|
||||
painter.setBrush(bs);
|
||||
}
|
||||
QLinearGradient bs(0, height(), 0, 0);
|
||||
bs.setColorAt(0.0, QColor::fromHslF(0 / 360., 0.75, 0.50, 0.6));
|
||||
bs.setColorAt(0.5, QColor::fromHslF(0 / 360., 0.75, 0.50, 0.4));
|
||||
bs.setColorAt(1.0, QColor::fromHslF(0 / 360., 0.75, 0.50, 0.2));
|
||||
|
||||
painter.setBrush(bs);
|
||||
if (blindSpotLeft) {
|
||||
painter.drawPolygon(scene.track_adjacent_vertices[4]);
|
||||
}
|
||||
if (blindSpotRight) {
|
||||
painter.drawPolygon(scene.track_adjacent_vertices[5]);
|
||||
}
|
||||
if (outlineOnly) painter.setPen(Qt::NoPen);
|
||||
}
|
||||
|
||||
// Paint adjacent lane paths
|
||||
@@ -1014,19 +708,16 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
|
||||
float maxLaneWidth = laneDetectionWidth * 1.5;
|
||||
|
||||
auto paintLane = [=](QPainter &painter, const QPolygonF &lane, float laneWidth, bool blindspot) {
|
||||
QLinearGradient al(0, height(), 0, 0);
|
||||
|
||||
bool redPath = laneWidth < minLaneWidth || laneWidth > maxLaneWidth || blindspot;
|
||||
float hue = redPath ? 0.0 : 120.0 * (laneWidth - minLaneWidth) / (maxLaneWidth - minLaneWidth);
|
||||
|
||||
if (outlineOnly) {
|
||||
painter.setPen(QPen(QColor::fromHslF(hue / 360.0, 0.75, 0.50, 0.6), 2));
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
} else {
|
||||
QLinearGradient al(0, height(), 0, 0);
|
||||
al.setColorAt(0.0, QColor::fromHslF(hue / 360.0, 0.75, 0.50, 0.6));
|
||||
al.setColorAt(0.5, QColor::fromHslF(hue / 360.0, 0.75, 0.50, 0.4));
|
||||
al.setColorAt(1.0, QColor::fromHslF(hue / 360.0, 0.75, 0.50, 0.2));
|
||||
painter.setBrush(al);
|
||||
}
|
||||
al.setColorAt(0.0, QColor::fromHslF(hue / 360.0, 0.75, 0.50, 0.6));
|
||||
al.setColorAt(0.5, QColor::fromHslF(hue / 360.0, 0.75, 0.50, 0.4));
|
||||
al.setColorAt(1.0, QColor::fromHslF(hue / 360.0, 0.75, 0.50, 0.2));
|
||||
|
||||
painter.setBrush(al);
|
||||
painter.drawPolygon(lane);
|
||||
|
||||
painter.setFont(InterFont(30, QFont::DemiBold));
|
||||
@@ -1114,10 +805,6 @@ void AnnotatedCameraWidget::paintEvent(QPaintEvent *event) {
|
||||
const cereal::ModelDataV2::Reader &model = sm["modelV2"].getModelV2();
|
||||
const float v_ego = sm["carState"].getCarState().getVEgo();
|
||||
|
||||
// CLEARPILOT: read display mode early — needed for camera suppression
|
||||
displayMode = paramsMemory.getInt("ScreenDisplayMode");
|
||||
nightriderMode = (displayMode == 1 || displayMode == 4);
|
||||
|
||||
// draw camera frame
|
||||
{
|
||||
std::lock_guard lk(frame_lock);
|
||||
@@ -1158,19 +845,9 @@ void AnnotatedCameraWidget::paintEvent(QPaintEvent *event) {
|
||||
} else {
|
||||
CameraWidget::updateCalibration(DEFAULT_CALIBRATION);
|
||||
}
|
||||
// CLEARPILOT: force CameraWidget bg to black in nightrider to prevent color bleed
|
||||
if (nightriderMode) {
|
||||
CameraWidget::setBackgroundColor(Qt::black);
|
||||
}
|
||||
painter.beginNativePainting();
|
||||
if (nightriderMode) {
|
||||
// CLEARPILOT: black background, no camera feed
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
} else {
|
||||
CameraWidget::setFrameId(model.getFrameId());
|
||||
CameraWidget::paintGL();
|
||||
}
|
||||
CameraWidget::setFrameId(model.getFrameId());
|
||||
CameraWidget::paintGL();
|
||||
painter.endNativePainting();
|
||||
}
|
||||
|
||||
@@ -1248,7 +925,7 @@ void AnnotatedCameraWidget::initializeFrogPilotWidgets() {
|
||||
animationFrameIndex = (animationFrameIndex + 1) % totalFrames;
|
||||
});
|
||||
|
||||
// CLEARPILOT: screen recorder disabled — replaced by dedicated dashcamd process
|
||||
// Initialize the timer for the screen recorder
|
||||
// QTimer *record_timer = new QTimer(this);
|
||||
// connect(record_timer, &QTimer::timeout, this, [this]() {
|
||||
// if (recorder_btn) {
|
||||
@@ -1333,8 +1010,7 @@ void AnnotatedCameraWidget::updateFrogPilotWidgets() {
|
||||
}
|
||||
|
||||
void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) {
|
||||
// CLEARPILOT: only show status bar when telemetry is enabled
|
||||
if (paramsMemory.getBool("TelemetryEnabled")) {
|
||||
if ((showAlwaysOnLateralStatusBar || showConditionalExperimentalStatusBar || roadNameUI)) {
|
||||
drawStatusBar(p);
|
||||
}
|
||||
|
||||
@@ -1346,7 +1022,8 @@ void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) {
|
||||
drawSLCConfirmation(p);
|
||||
}
|
||||
|
||||
// CLEARPILOT: screen recorder disabled, using dashcamd instead
|
||||
// recorder_btn->setVisible(scene.screen_recorder && !mapOpen);
|
||||
recorder_btn->setVisible(false);
|
||||
}
|
||||
|
||||
void AnnotatedCameraWidget::drawLeadInfo(QPainter &p) {
|
||||
@@ -1535,30 +1212,7 @@ void AnnotatedCameraWidget::drawStatusBar(QPainter &p) {
|
||||
|
||||
QString roadName = roadNameUI ? QString::fromStdString(paramsMemory.get("RoadName")) : QString();
|
||||
|
||||
// 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) {
|
||||
if (alwaysOnLateralActive && showAlwaysOnLateralStatusBar) {
|
||||
newStatus = tr("Always On Lateral active") + (tr(". Press the \"Cruise Control\" button to disable"));
|
||||
} else if (showConditionalExperimentalStatusBar) {
|
||||
newStatus = conditionalStatusMap[status != STATUS_DISENGAGED ? conditionalStatus : 0];
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "selfdrive/ui/ui.h"
|
||||
#include "selfdrive/ui/qt/widgets/cameraview.h"
|
||||
|
||||
// #include "selfdrive/frogpilot/screenrecorder/screenrecorder.h" // DISABLED: using dashcamd instead
|
||||
#include "selfdrive/frogpilot/screenrecorder/screenrecorder.h"
|
||||
|
||||
const int btn_size = 192;
|
||||
const int img_size = (btn_size / 4) * 3;
|
||||
@@ -47,31 +47,16 @@ public:
|
||||
explicit AnnotatedCameraWidget(VisionStreamType type, QWidget* parent = 0);
|
||||
void updateState(const UIState &s);
|
||||
void updateLaneEdgeColor(QColor &bgColor);
|
||||
int screenDisplayMode = 0;
|
||||
|
||||
private:
|
||||
void drawText(QPainter &p, int x, int y, const QString &text, int alpha = 255);
|
||||
void drawSpeedWidget(QPainter &p, int x, int y, const QString &title, const QString &speedLimitStr, QColor colorSpeed, int width = 176);
|
||||
void drawSpeedLimitSign(QPainter &p);
|
||||
void drawCruiseWarningSign(QPainter &p);
|
||||
void drawHealthMetrics(QPainter &p);
|
||||
|
||||
QVBoxLayout *main_layout;
|
||||
QPixmap dm_img;
|
||||
float speed;
|
||||
bool has_gps_speed = false;
|
||||
bool nightriderMode = false;
|
||||
int displayMode = 0;
|
||||
QString speedUnit;
|
||||
|
||||
// ClearPilot speed state (from params_memory, updated ~2Hz)
|
||||
bool clpHasSpeed = false;
|
||||
QString clpSpeedDisplay;
|
||||
QString clpSpeedLimitDisplay;
|
||||
QString clpSpeedUnit;
|
||||
QString clpCruiseWarning;
|
||||
QString clpCruiseWarningSpeed;
|
||||
int clpParamFrame = 0;
|
||||
|
||||
float setSpeed;
|
||||
float speedLimit;
|
||||
bool is_cruise_set = false;
|
||||
@@ -104,6 +89,8 @@ private:
|
||||
Params paramsMemory{"/dev/shm/params"};
|
||||
UIScene &scene;
|
||||
|
||||
ScreenRecorder *recorder_btn;
|
||||
|
||||
QHBoxLayout *bottom_layout;
|
||||
|
||||
bool alwaysOnLateralActive;
|
||||
|
||||
+13
-74
@@ -30,12 +30,11 @@ ReadyWindow::ReadyWindow(QWidget *parent) : QWidget(parent) {
|
||||
|
||||
timer = new QTimer(this);
|
||||
timer->callOnTimeout(this, &ReadyWindow::refresh);
|
||||
uptime.start();
|
||||
}
|
||||
|
||||
void ReadyWindow::showEvent(QShowEvent *event) {
|
||||
refresh();
|
||||
timer->start(2 * 1000);
|
||||
timer->start(5 * 1000);
|
||||
}
|
||||
|
||||
void ReadyWindow::hideEvent(QHideEvent *event) {
|
||||
@@ -44,94 +43,34 @@ void ReadyWindow::hideEvent(QHideEvent *event) {
|
||||
|
||||
void ReadyWindow::paintEvent(QPaintEvent *event) {
|
||||
QPainter painter(this);
|
||||
painter.fillRect(rect(), Qt::black);
|
||||
QPixmap *img_shown = nullptr;
|
||||
|
||||
if (is_hot) {
|
||||
if (img_hot.isNull()) {
|
||||
img_hot.load("/data/openpilot/selfdrive/clearpilot/theme/clearpilot/images/hot.png");
|
||||
}
|
||||
int x = (width() - img_hot.width()) / 2;
|
||||
int y = (height() - img_hot.height()) / 2;
|
||||
painter.drawPixmap(x, y, img_hot);
|
||||
img_shown = &img_hot;
|
||||
} else {
|
||||
// Boot logo — same rotation as spinner (bg.jpg is pre-rotated 90° CW for framebuffer)
|
||||
if (img_bg.isNull()) {
|
||||
QPixmap raw("/usr/comma/bg.jpg");
|
||||
if (!raw.isNull()) {
|
||||
QTransform rot;
|
||||
rot.rotate(-90);
|
||||
img_bg = raw.transformed(rot);
|
||||
}
|
||||
}
|
||||
if (!img_bg.isNull()) {
|
||||
QPixmap scaled = img_bg.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
int x = (width() - scaled.width()) / 2;
|
||||
int y = (height() - scaled.height()) / 2;
|
||||
painter.drawPixmap(x, y, scaled);
|
||||
}
|
||||
|
||||
if (error_msg.isEmpty() && !has_driven) {
|
||||
// "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");
|
||||
if (!ready_text.isNull()) {
|
||||
int tx = (width() - ready_text.width()) / 2;
|
||||
int ty = height() / 2 + height() * 15 / 100;
|
||||
painter.drawPixmap(tx, ty, ready_text);
|
||||
}
|
||||
} else {
|
||||
// Error state: red text at 25% below center
|
||||
QFont font("Inter", 50, QFont::Bold);
|
||||
painter.setFont(font);
|
||||
painter.setPen(QColor(0xFF, 0x44, 0x44));
|
||||
int ty = height() / 2 + height() * 25 / 100;
|
||||
QRect text_rect(0, ty, width(), 100);
|
||||
painter.drawText(text_rect, Qt::AlignHCenter | Qt::AlignTop, error_msg);
|
||||
if (img_ready.isNull()) {
|
||||
img_ready.load("/data/openpilot/selfdrive/clearpilot/theme/clearpilot/images/ready.png");
|
||||
}
|
||||
img_shown = &img_ready;
|
||||
}
|
||||
|
||||
int x = (width() - img_shown->width()) / 2;
|
||||
int y = (height() - img_shown->height()) / 2;
|
||||
painter.drawPixmap(x, y, *img_shown);
|
||||
}
|
||||
|
||||
void ReadyWindow::refresh() {
|
||||
bool changed = false;
|
||||
|
||||
// Temperature check
|
||||
std::string bytes = params.get("Offroad_TemperatureTooHigh");
|
||||
bool was_hot = is_hot;
|
||||
if (!bytes.empty()) {
|
||||
auto doc = QJsonDocument::fromJson(bytes.data());
|
||||
is_hot = true;
|
||||
cur_temp = doc["extra"].toString();
|
||||
} else {
|
||||
update();
|
||||
} else if (is_hot) {
|
||||
is_hot = false;
|
||||
update();
|
||||
}
|
||||
if (is_hot != was_hot) changed = true;
|
||||
|
||||
// Error state checks (only when not hot — hot has its own display)
|
||||
if (!is_hot) {
|
||||
QString prev_error = error_msg;
|
||||
|
||||
// Panda check — same logic as sidebar, with 10s grace period on startup
|
||||
if (uptime.elapsed() > 10000 &&
|
||||
uiState()->scene.pandaType == cereal::PandaState::PandaType::UNKNOWN) {
|
||||
error_msg = "PANDA NOT CONNECTED";
|
||||
}
|
||||
// Car unrecognized check
|
||||
else if (!params.get("Offroad_CarUnrecognized").empty()) {
|
||||
error_msg = "CAR NOT RECOGNIZED";
|
||||
}
|
||||
else {
|
||||
error_msg = "";
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -8,9 +8,8 @@
|
||||
#include <QSocketNotifier>
|
||||
#include <QVariantAnimation>
|
||||
#include <QWidget>
|
||||
#include <QElapsedTimer>
|
||||
#include <QTimer>
|
||||
|
||||
|
||||
#include "common/util.h"
|
||||
#include "selfdrive/ui/ui.h"
|
||||
|
||||
@@ -18,7 +17,6 @@ class ReadyWindow : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ReadyWindow(QWidget* parent = nullptr);
|
||||
bool has_driven = false;
|
||||
private:
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void hideEvent(QHideEvent *event) override;
|
||||
@@ -28,9 +26,6 @@ private:
|
||||
QTimer* timer;
|
||||
bool is_hot = false;
|
||||
QString cur_temp;
|
||||
QString error_msg; // non-empty = show red error instead of READY!
|
||||
QElapsedTimer uptime;
|
||||
bool last_started = false;
|
||||
QPixmap img_bg;
|
||||
QPixmap img_ready;
|
||||
QPixmap img_hot;
|
||||
};
|
||||
Binary file not shown.
+38
-28
@@ -15,24 +15,48 @@
|
||||
#include "selfdrive/ui/qt/qt_window.h"
|
||||
#include "selfdrive/ui/qt/util.h"
|
||||
|
||||
// CLEARPILOT: full-screen boot logo background with progress bar overlay
|
||||
TrackWidget::TrackWidget(QWidget *parent) : QWidget(parent) {
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
setFixedSize(spinner_size);
|
||||
|
||||
Spinner::Spinner(QWidget *parent) : QWidget(parent) {
|
||||
// Load boot logo as full-screen background, rotated 90° CCW
|
||||
// (bg.jpg is pre-rotated 90° CW for the raw framebuffer)
|
||||
QPixmap boot_logo("/usr/comma/bg.jpg");
|
||||
if (!boot_logo.isNull()) {
|
||||
QTransform rot;
|
||||
rot.rotate(-90);
|
||||
bg_img = boot_logo.transformed(rot);
|
||||
// pre-compute all the track imgs. make this a gif instead?
|
||||
QPixmap comma_img = loadPixmap("../assets/img_spinner_comma.png", spinner_size);
|
||||
QPixmap track_img = loadPixmap("../assets/img_spinner_track.png", spinner_size);
|
||||
|
||||
QTransform transform(1, 0, 0, 1, width() / 2, height() / 2);
|
||||
QPixmap pm(spinner_size);
|
||||
QPainter p(&pm);
|
||||
p.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||
for (int i = 0; i < track_imgs.size(); ++i) {
|
||||
p.resetTransform();
|
||||
p.fillRect(0, 0, spinner_size.width(), spinner_size.height(), Qt::black);
|
||||
p.drawPixmap(0, 0, comma_img);
|
||||
p.setTransform(transform.rotate(360 / spinner_fps));
|
||||
p.drawPixmap(-width() / 2, -height() / 2, track_img);
|
||||
track_imgs[i] = pm.copy();
|
||||
}
|
||||
|
||||
m_anim.setDuration(1000);
|
||||
m_anim.setStartValue(0);
|
||||
m_anim.setEndValue(int(track_imgs.size() -1));
|
||||
m_anim.setLoopCount(-1);
|
||||
m_anim.start();
|
||||
connect(&m_anim, SIGNAL(valueChanged(QVariant)), SLOT(update()));
|
||||
}
|
||||
|
||||
void TrackWidget::paintEvent(QPaintEvent *event) {
|
||||
QPainter painter(this);
|
||||
painter.drawPixmap(0, 0, track_imgs[m_anim.currentValue().toInt()]);
|
||||
}
|
||||
|
||||
// Spinner
|
||||
|
||||
Spinner::Spinner(QWidget *parent) : QWidget(parent) {
|
||||
QGridLayout *main_layout = new QGridLayout(this);
|
||||
main_layout->setSpacing(0);
|
||||
main_layout->setMargin(0);
|
||||
main_layout->setMargin(200);
|
||||
|
||||
// Spacer to push progress bar toward bottom
|
||||
main_layout->setRowStretch(0, 1);
|
||||
main_layout->addWidget(new TrackWidget(this), 0, 0, Qt::AlignHCenter | Qt::AlignVCenter);
|
||||
|
||||
text = new QLabel();
|
||||
text->setWordWrap(true);
|
||||
@@ -45,10 +69,7 @@ Spinner::Spinner(QWidget *parent) : QWidget(parent) {
|
||||
progress_bar->setTextVisible(false);
|
||||
progress_bar->setVisible(false);
|
||||
progress_bar->setFixedHeight(20);
|
||||
main_layout->addWidget(progress_bar, 2, 0, Qt::AlignHCenter | Qt::AlignBottom);
|
||||
|
||||
// Bottom margin for progress bar
|
||||
main_layout->setContentsMargins(0, 0, 0, 80);
|
||||
main_layout->addWidget(progress_bar, 1, 0, Qt::AlignHCenter);
|
||||
|
||||
setStyleSheet(R"(
|
||||
Spinner {
|
||||
@@ -67,7 +88,7 @@ Spinner::Spinner(QWidget *parent) : QWidget(parent) {
|
||||
}
|
||||
QProgressBar::chunk {
|
||||
border-radius: 10px;
|
||||
background-color: white;
|
||||
background-color: rgba(23, 134, 68, 255);
|
||||
}
|
||||
)");
|
||||
|
||||
@@ -75,17 +96,6 @@ Spinner::Spinner(QWidget *parent) : QWidget(parent) {
|
||||
QObject::connect(notifier, &QSocketNotifier::activated, this, &Spinner::update);
|
||||
}
|
||||
|
||||
void Spinner::paintEvent(QPaintEvent *event) {
|
||||
QPainter p(this);
|
||||
p.fillRect(rect(), Qt::black);
|
||||
if (!bg_img.isNull()) {
|
||||
QPixmap scaled = bg_img.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
int x = (width() - scaled.width()) / 2;
|
||||
int y = (height() - scaled.height()) / 2;
|
||||
p.drawPixmap(x, y, scaled);
|
||||
}
|
||||
}
|
||||
|
||||
void Spinner::update(int n) {
|
||||
std::string line;
|
||||
std::getline(std::cin, line);
|
||||
|
||||
@@ -1,23 +1,36 @@
|
||||
#include <array>
|
||||
|
||||
#include <QLabel>
|
||||
#include <QPixmap>
|
||||
#include <QProgressBar>
|
||||
#include <QSocketNotifier>
|
||||
#include <QVariantAnimation>
|
||||
#include <QWidget>
|
||||
|
||||
constexpr int spinner_fps = 30;
|
||||
constexpr QSize spinner_size = QSize(360, 360);
|
||||
|
||||
class TrackWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TrackWidget(QWidget *parent = nullptr);
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
std::array<QPixmap, spinner_fps> track_imgs;
|
||||
QVariantAnimation m_anim;
|
||||
};
|
||||
|
||||
class Spinner : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Spinner(QWidget *parent = 0);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
QLabel *text;
|
||||
QProgressBar *progress_bar;
|
||||
QSocketNotifier *notifier;
|
||||
QPixmap bg_img;
|
||||
|
||||
public slots:
|
||||
void update(int n);
|
||||
|
||||
+3
-310
@@ -1,9 +1,6 @@
|
||||
#include "selfdrive/ui/qt/window.h"
|
||||
|
||||
#include <QFontDatabase>
|
||||
#include <QMouseEvent>
|
||||
|
||||
#include <zmq.h>
|
||||
|
||||
#include "system/hardware/hw.h"
|
||||
|
||||
@@ -14,7 +11,6 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
|
||||
homeWindow = new HomeWindow(this);
|
||||
main_layout->addWidget(homeWindow);
|
||||
QObject::connect(homeWindow, &HomeWindow::openSettings, this, &MainWindow::openSettings);
|
||||
QObject::connect(homeWindow, &HomeWindow::openStatus, this, &MainWindow::openStatus);
|
||||
QObject::connect(homeWindow, &HomeWindow::closeSettings, this, &MainWindow::closeSettings);
|
||||
|
||||
settingsWindow = new SettingsWindow(this);
|
||||
@@ -28,11 +24,6 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
|
||||
homeWindow->showDriverView(true);
|
||||
});
|
||||
|
||||
// CLEARPILOT: Status window
|
||||
statusWindow = new StatusWindow(this);
|
||||
main_layout->addWidget(statusWindow);
|
||||
QObject::connect(statusWindow, &StatusWindow::closeStatus, this, &MainWindow::closeSettings);
|
||||
|
||||
onboardingWindow = new OnboardingWindow(this);
|
||||
main_layout->addWidget(onboardingWindow);
|
||||
QObject::connect(onboardingWindow, &OnboardingWindow::onboardingDone, [=]() {
|
||||
@@ -44,17 +35,13 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
|
||||
|
||||
QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
|
||||
if (!offroad) {
|
||||
// CLEARPILOT: just switch to homeWindow, don't show sidebar
|
||||
// HomeWindow::offroadTransition handles the internal view
|
||||
main_layout->setCurrentWidget(homeWindow);
|
||||
closeSettings();
|
||||
}
|
||||
});
|
||||
QObject::connect(device(), &Device::interactiveTimeout, [=]() {
|
||||
// CLEARPILOT: on timeout, return to splash/onroad (not ClearPilotPanel)
|
||||
if (main_layout->currentWidget() != homeWindow) {
|
||||
main_layout->setCurrentWidget(homeWindow);
|
||||
if (main_layout->currentWidget() == settingsWindow) {
|
||||
closeSettings();
|
||||
}
|
||||
homeWindow->offroadTransition(!uiState()->scene.started);
|
||||
});
|
||||
|
||||
// load fonts
|
||||
@@ -76,74 +63,6 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
|
||||
}
|
||||
)");
|
||||
setAttribute(Qt::WA_NoSystemBackground);
|
||||
|
||||
// CLEARPILOT: UI introspection RPC server
|
||||
zmq_ctx = zmq_ctx_new();
|
||||
zmq_sock = zmq_socket(zmq_ctx, ZMQ_REP);
|
||||
zmq_bind(zmq_sock, "ipc:///tmp/clearpilot_ui_rpc");
|
||||
int fd;
|
||||
size_t fd_sz = sizeof(fd);
|
||||
zmq_getsockopt(zmq_sock, ZMQ_FD, &fd, &fd_sz);
|
||||
rpc_notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
|
||||
connect(rpc_notifier, &QSocketNotifier::activated, this, &MainWindow::handleRpcRequest);
|
||||
}
|
||||
|
||||
void MainWindow::handleRpcRequest() {
|
||||
int events = 0;
|
||||
size_t events_sz = sizeof(events);
|
||||
zmq_getsockopt(zmq_sock, ZMQ_EVENTS, &events, &events_sz);
|
||||
if (!(events & ZMQ_POLLIN)) return;
|
||||
|
||||
char buf[256];
|
||||
int rc = zmq_recv(zmq_sock, buf, sizeof(buf) - 1, ZMQ_DONTWAIT);
|
||||
if (rc < 0) return;
|
||||
buf[rc] = 0;
|
||||
|
||||
QString response;
|
||||
if (strcmp(buf, "dump") == 0) {
|
||||
response = dumpWidgetTree(this);
|
||||
} else {
|
||||
response = "unknown command";
|
||||
}
|
||||
|
||||
QByteArray resp = response.toUtf8();
|
||||
zmq_send(zmq_sock, resp.data(), resp.size(), 0);
|
||||
}
|
||||
|
||||
QString MainWindow::dumpWidgetTree(QWidget *w, int depth) {
|
||||
QString result;
|
||||
QString indent(depth * 2, ' ');
|
||||
QString className = w->metaObject()->className();
|
||||
QString name = w->objectName().isEmpty() ? "(no name)" : w->objectName();
|
||||
bool visible = w->isVisible();
|
||||
QRect geo = w->geometry();
|
||||
|
||||
result += QString("%1%2 [%3] vis=%4 geo=%5,%6 %7x%8")
|
||||
.arg(indent, className, name)
|
||||
.arg(visible ? "Y" : "N")
|
||||
.arg(geo.x()).arg(geo.y()).arg(geo.width()).arg(geo.height());
|
||||
|
||||
// Show stacked layout/widget current index
|
||||
if (auto *sl = w->findChild<QStackedLayout *>(QString(), Qt::FindDirectChildrenOnly)) {
|
||||
QWidget *cur = sl->currentWidget();
|
||||
QString curClass = cur ? cur->metaObject()->className() : "null";
|
||||
result += QString(" stack_cur=%1/%2(%3)").arg(sl->currentIndex()).arg(sl->count()).arg(curClass);
|
||||
}
|
||||
if (auto *sw = qobject_cast<QStackedWidget *>(w)) {
|
||||
QWidget *cur = sw->currentWidget();
|
||||
QString curClass = cur ? cur->metaObject()->className() : "null";
|
||||
result += QString(" stack_cur=%1/%2(%3)").arg(sw->currentIndex()).arg(sw->count()).arg(curClass);
|
||||
}
|
||||
|
||||
result += "\n";
|
||||
|
||||
for (QObject *child : w->children()) {
|
||||
QWidget *cw = qobject_cast<QWidget *>(child);
|
||||
if (cw && depth < 4) {
|
||||
result += dumpWidgetTree(cw, depth + 1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void MainWindow::openSettings(int index, const QString ¶m) {
|
||||
@@ -151,10 +70,6 @@ void MainWindow::openSettings(int index, const QString ¶m) {
|
||||
settingsWindow->setCurrentPanel(index, param);
|
||||
}
|
||||
|
||||
void MainWindow::openStatus() {
|
||||
main_layout->setCurrentWidget(statusWindow);
|
||||
}
|
||||
|
||||
void MainWindow::closeSettings() {
|
||||
main_layout->setCurrentWidget(homeWindow);
|
||||
|
||||
@@ -171,16 +86,6 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
|
||||
case QEvent::TouchEnd:
|
||||
case QEvent::MouseButtonPress:
|
||||
case QEvent::MouseMove: {
|
||||
// CLEARPILOT: tap while screen-off (mode 3) -> wake to auto-normal (mode 0)
|
||||
Params pmem{"/dev/shm/params"};
|
||||
if (!device()->isAwake()) {
|
||||
if (pmem.getInt("ScreenDisplayMode") == 3) {
|
||||
pmem.putInt("ScreenDisplayMode", 0);
|
||||
}
|
||||
}
|
||||
// CLEARPILOT: reset shutdown timer on any screen touch
|
||||
static int touch_counter = 0;
|
||||
pmem.put("ShutdownTouchReset", std::to_string(++touch_counter));
|
||||
// ignore events when device is awakened by resetInteractiveTimeout
|
||||
ignore = !device()->isAwake();
|
||||
device()->resetInteractiveTimeout(uiState()->scene.screen_timeout, uiState()->scene.screen_timeout_onroad);
|
||||
@@ -191,215 +96,3 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
|
||||
}
|
||||
return ignore;
|
||||
}
|
||||
|
||||
// CLEARPILOT: Status window — live system stats, collected on background thread
|
||||
|
||||
#include <QFile>
|
||||
#include <QProcess>
|
||||
#include <QDateTime>
|
||||
#include <QtConcurrent>
|
||||
|
||||
static QString readFile(const QString &path) {
|
||||
QFile f(path);
|
||||
if (f.open(QIODevice::ReadOnly)) return QString(f.readAll()).trimmed();
|
||||
return "";
|
||||
}
|
||||
|
||||
static QString shellCmd(const QString &cmd) {
|
||||
QProcess p;
|
||||
p.start("bash", QStringList() << "-c" << cmd);
|
||||
p.waitForFinished(1000);
|
||||
return QString(p.readAllStandardOutput()).trimmed();
|
||||
}
|
||||
|
||||
static StatusWindow::StatusData collectStatus() {
|
||||
StatusWindow::StatusData d;
|
||||
|
||||
d.time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
|
||||
d.storage = shellCmd("df -h /data | tail -1 | awk '{print $3 \" / \" $2 \" (\" $5 \" used)\"}'");
|
||||
|
||||
// RAM from /proc/meminfo (no subprocess needed)
|
||||
QString meminfo = readFile("/proc/meminfo");
|
||||
long total = 0, avail = 0;
|
||||
for (const QString &line : meminfo.split('\n')) {
|
||||
if (line.startsWith("MemTotal:")) total = line.split(QRegExp("\\s+"))[1].toLong();
|
||||
if (line.startsWith("MemAvailable:")) avail = line.split(QRegExp("\\s+"))[1].toLong();
|
||||
}
|
||||
if (total > 0) {
|
||||
long used = total - avail;
|
||||
d.ram = QString("%1 / %2 MB").arg(used / 1024).arg(total / 1024);
|
||||
}
|
||||
|
||||
// Load from /proc/loadavg
|
||||
QString loadavg = readFile("/proc/loadavg");
|
||||
QStringList parts = loadavg.split(' ');
|
||||
if (parts.size() >= 3) {
|
||||
d.load = QString("%1 %2 %3").arg(parts[0], parts[1], parts[2]);
|
||||
}
|
||||
|
||||
// Temperature
|
||||
QString temps = shellCmd("cat /sys/class/thermal/thermal_zone*/temp 2>/dev/null | sort -rn | head -1");
|
||||
if (!temps.isEmpty()) {
|
||||
d.temp_c = temps.toLong() / 1000.0f;
|
||||
d.temp = QString("%1\u00B0C").arg(d.temp_c, 0, 'f', 1);
|
||||
}
|
||||
|
||||
// Fan
|
||||
QString fan = shellCmd("cat /sys/class/hwmon/hwmon*/fan1_input 2>/dev/null | head -1");
|
||||
if (fan.isEmpty()) fan = readFile("/dev/shm/params/d/LastFanSpeed");
|
||||
d.fan = fan.isEmpty() ? QString::fromUtf8("\u2014") : fan + " RPM";
|
||||
|
||||
// Network
|
||||
d.ip = shellCmd("ip route get 1.1.1.1 2>/dev/null | head -1 | awk '{print $7}'");
|
||||
d.wifi = shellCmd("iwconfig wlan0 2>/dev/null | grep -oP 'ESSID:\"\\K[^\"]*'");
|
||||
|
||||
// VPN
|
||||
QString tun = shellCmd("ip link show tun0 2>/dev/null | head -1");
|
||||
if (tun.contains("UP")) {
|
||||
d.vpn_status = "up";
|
||||
d.vpn_ip = shellCmd("ip addr show tun0 2>/dev/null | grep 'inet ' | awk '{print $2}'");
|
||||
}
|
||||
|
||||
// GPS
|
||||
d.gps = readFile("/data/params/d/LastGPSPosition");
|
||||
|
||||
// Telemetry
|
||||
d.telemetry = readFile("/data/params/d/TelemetryEnabled");
|
||||
|
||||
// Dashcam
|
||||
d.dashcam_state = readFile("/dev/shm/params/d/DashcamState");
|
||||
if (d.dashcam_state.isEmpty()) d.dashcam_state = "stopped";
|
||||
d.dashcam_frames = readFile("/dev/shm/params/d/DashcamFrames");
|
||||
|
||||
// Panda: checked on UI thread in applyResults() via scene.pandaType
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
StatusWindow::StatusWindow(QWidget *parent) : QFrame(parent) {
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(50, 60, 50, 40);
|
||||
layout->setSpacing(0);
|
||||
|
||||
// Status rows
|
||||
auto makeRow = [&](const QString &label) -> QLabel* {
|
||||
QHBoxLayout *row = new QHBoxLayout();
|
||||
row->setContentsMargins(20, 0, 20, 0);
|
||||
|
||||
QLabel *name = new QLabel(label);
|
||||
name->setStyleSheet("color: grey; font-size: 38px;");
|
||||
name->setFixedWidth(350);
|
||||
row->addWidget(name);
|
||||
|
||||
QLabel *value = new QLabel("—");
|
||||
value->setStyleSheet("color: white; font-size: 38px;");
|
||||
row->addWidget(value);
|
||||
row->addStretch();
|
||||
|
||||
layout->addLayout(row);
|
||||
layout->addSpacing(12);
|
||||
return value;
|
||||
};
|
||||
|
||||
time_label = makeRow("Time");
|
||||
storage_label = makeRow("Storage");
|
||||
ram_label = makeRow("Memory");
|
||||
load_label = makeRow("Load");
|
||||
temp_label = makeRow("Temperature");
|
||||
fan_label = makeRow("Fan Speed");
|
||||
panda_label = makeRow("Panda");
|
||||
ip_label = makeRow("IP Address");
|
||||
wifi_label = makeRow("WiFi");
|
||||
vpn_label = makeRow("VPN");
|
||||
gps_label = makeRow("GPS");
|
||||
telemetry_label = makeRow("Telemetry");
|
||||
dashcam_label = makeRow("Dashcam");
|
||||
|
||||
layout->addStretch();
|
||||
|
||||
setStyleSheet("StatusWindow { background-color: black; }");
|
||||
|
||||
connect(&watcher, &QFutureWatcher<StatusData>::finished, this, &StatusWindow::applyResults);
|
||||
|
||||
QTimer *timer = new QTimer(this);
|
||||
connect(timer, &QTimer::timeout, this, &StatusWindow::kickRefresh);
|
||||
timer->start(1000);
|
||||
}
|
||||
|
||||
void StatusWindow::kickRefresh() {
|
||||
if (!isVisible() || collecting) return;
|
||||
collecting = true;
|
||||
watcher.setFuture(QtConcurrent::run(collectStatus));
|
||||
}
|
||||
|
||||
void StatusWindow::applyResults() {
|
||||
collecting = false;
|
||||
StatusData d = watcher.result();
|
||||
|
||||
time_label->setText(d.time);
|
||||
storage_label->setText(d.storage);
|
||||
if (!d.ram.isEmpty()) ram_label->setText(d.ram);
|
||||
if (!d.load.isEmpty()) load_label->setText(d.load);
|
||||
|
||||
if (!d.temp.isEmpty()) {
|
||||
temp_label->setText(d.temp);
|
||||
temp_label->setStyleSheet(d.temp_c > 70 ? "color: #ff4444; font-size: 38px;" :
|
||||
d.temp_c > 55 ? "color: #ffaa00; font-size: 38px;" :
|
||||
"color: white; font-size: 38px;");
|
||||
}
|
||||
|
||||
fan_label->setText(d.fan);
|
||||
|
||||
// Panda: same check as sidebar — read scene.pandaType on UI thread
|
||||
if (uiState()->scene.pandaType != cereal::PandaState::PandaType::UNKNOWN) {
|
||||
panda_label->setText("Connected");
|
||||
panda_label->setStyleSheet("color: #17c44d; font-size: 38px;");
|
||||
} else {
|
||||
panda_label->setText("Not connected");
|
||||
panda_label->setStyleSheet("color: #ff4444; font-size: 38px;");
|
||||
}
|
||||
|
||||
ip_label->setText(d.ip.isEmpty() ? "No connection" : d.ip);
|
||||
wifi_label->setText(d.wifi.isEmpty() ? "Not connected" : d.wifi);
|
||||
|
||||
if (d.vpn_status == "up") {
|
||||
vpn_label->setText("Connected (" + d.vpn_ip + ")");
|
||||
vpn_label->setStyleSheet("color: #17c44d; font-size: 38px;");
|
||||
} else {
|
||||
vpn_label->setText("Not connected");
|
||||
vpn_label->setStyleSheet("color: #ff4444; font-size: 38px;");
|
||||
}
|
||||
|
||||
if (d.gps.isEmpty()) {
|
||||
gps_label->setText("No fix");
|
||||
gps_label->setStyleSheet("color: #ff4444; font-size: 38px;");
|
||||
} else {
|
||||
gps_label->setText(d.gps);
|
||||
gps_label->setStyleSheet("color: white; font-size: 38px;");
|
||||
}
|
||||
|
||||
if (d.telemetry == "1") {
|
||||
telemetry_label->setText("Enabled");
|
||||
telemetry_label->setStyleSheet("color: #17c44d; font-size: 38px;");
|
||||
} else {
|
||||
telemetry_label->setText("Disabled");
|
||||
telemetry_label->setStyleSheet("color: grey; font-size: 38px;");
|
||||
}
|
||||
|
||||
if (d.dashcam_state == "recording") {
|
||||
QString text = "Recording";
|
||||
if (!d.dashcam_frames.isEmpty() && d.dashcam_frames != "0") text += " (" + d.dashcam_frames + " frames)";
|
||||
dashcam_label->setText(text);
|
||||
dashcam_label->setStyleSheet("color: #17c44d; font-size: 38px;");
|
||||
} else if (d.dashcam_state == "waiting") {
|
||||
dashcam_label->setText("Waiting");
|
||||
dashcam_label->setStyleSheet("color: #ffaa00; font-size: 38px;");
|
||||
} else {
|
||||
dashcam_label->setText("Stopped");
|
||||
dashcam_label->setStyleSheet("color: #ff4444; font-size: 38px;");
|
||||
}
|
||||
}
|
||||
|
||||
void StatusWindow::mousePressEvent(QMouseEvent *e) {
|
||||
emit closeStatus();
|
||||
}
|
||||
|
||||
@@ -1,59 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <QFutureWatcher>
|
||||
#include <QStackedLayout>
|
||||
#include <QLabel>
|
||||
#include <QSocketNotifier>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#include "selfdrive/ui/qt/home.h"
|
||||
#include "selfdrive/ui/qt/offroad/onboarding.h"
|
||||
#include "selfdrive/ui/qt/offroad/settings.h"
|
||||
|
||||
class StatusWindow : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit StatusWindow(QWidget *parent = 0);
|
||||
|
||||
struct StatusData {
|
||||
QString time, storage, ram, load, temp, fan, ip, wifi;
|
||||
QString vpn_status, vpn_ip, gps, telemetry;
|
||||
QString dashcam_state, dashcam_frames;
|
||||
float temp_c = 0;
|
||||
};
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
|
||||
signals:
|
||||
void closeStatus();
|
||||
|
||||
private slots:
|
||||
void kickRefresh();
|
||||
void applyResults();
|
||||
|
||||
private:
|
||||
|
||||
QFutureWatcher<StatusData> watcher;
|
||||
bool collecting = false;
|
||||
|
||||
QLabel *storage_label;
|
||||
QLabel *ram_label;
|
||||
QLabel *load_label;
|
||||
QLabel *temp_label;
|
||||
QLabel *fan_label;
|
||||
QLabel *ip_label;
|
||||
QLabel *wifi_label;
|
||||
QLabel *vpn_label;
|
||||
QLabel *gps_label;
|
||||
QLabel *time_label;
|
||||
QLabel *telemetry_label;
|
||||
QLabel *dashcam_label;
|
||||
QLabel *panda_label;
|
||||
};
|
||||
|
||||
class MainWindow : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -63,24 +16,13 @@ public:
|
||||
private:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
void openSettings(int index = 0, const QString ¶m = "");
|
||||
void openStatus();
|
||||
void closeSettings();
|
||||
QString dumpWidgetTree(QWidget *w, int depth = 0);
|
||||
|
||||
QStackedLayout *main_layout;
|
||||
HomeWindow *homeWindow;
|
||||
SettingsWindow *settingsWindow;
|
||||
StatusWindow *statusWindow;
|
||||
OnboardingWindow *onboardingWindow;
|
||||
|
||||
// CLEARPILOT: UI introspection RPC
|
||||
void *zmq_ctx = nullptr;
|
||||
void *zmq_sock = nullptr;
|
||||
QSocketNotifier *rpc_notifier = nullptr;
|
||||
|
||||
// FrogPilot variables
|
||||
Params params;
|
||||
|
||||
private slots:
|
||||
void handleRpcRequest();
|
||||
};
|
||||
|
||||
+1
-43
@@ -93,27 +93,6 @@ class Soundd:
|
||||
|
||||
self.spl_filter_weighted = FirstOrderFilter(0, 2.5, FILTER_DT, initialized=False)
|
||||
|
||||
# ClearPilot ding (plays independently of alerts)
|
||||
self.ding_sound = None
|
||||
self.ding_frame = 0
|
||||
self.ding_playing = False
|
||||
self.ding_check_counter = 0
|
||||
self._load_ding()
|
||||
|
||||
def _load_ding(self):
|
||||
ding_path = BASEDIR + "/selfdrive/clearpilot/sounds/ding.wav"
|
||||
try:
|
||||
wavefile = wave.open(ding_path, 'r')
|
||||
assert wavefile.getnchannels() == 1
|
||||
assert wavefile.getsampwidth() == 2
|
||||
assert wavefile.getframerate() == SAMPLE_RATE
|
||||
length = wavefile.getnframes()
|
||||
self.ding_sound = np.frombuffer(wavefile.readframes(length), dtype=np.int16).astype(np.float32) / (2**16/2)
|
||||
cloudlog.info(f"ClearPilot ding loaded: {length} frames")
|
||||
except Exception as e:
|
||||
cloudlog.error(f"Failed to load ding sound: {e}")
|
||||
self.ding_sound = None
|
||||
|
||||
def load_sounds(self):
|
||||
self.loaded_sounds: dict[int, np.ndarray] = {}
|
||||
|
||||
@@ -158,20 +137,7 @@ class Soundd:
|
||||
written_frames += frames_to_write
|
||||
self.current_sound_frame += frames_to_write
|
||||
|
||||
ret = ret * self.current_volume
|
||||
|
||||
# Mix in ClearPilot ding (independent of alerts, always max volume)
|
||||
if self.ding_playing and self.ding_sound is not None:
|
||||
ding_remaining = len(self.ding_sound) - self.ding_frame
|
||||
if ding_remaining > 0:
|
||||
frames_to_write = min(ding_remaining, frames)
|
||||
ret[:frames_to_write] += self.ding_sound[self.ding_frame:self.ding_frame + frames_to_write] * MAX_VOLUME
|
||||
self.ding_frame += frames_to_write
|
||||
else:
|
||||
self.ding_playing = False
|
||||
self.ding_frame = 0
|
||||
|
||||
return ret
|
||||
return ret * self.current_volume
|
||||
|
||||
def callback(self, data_out: np.ndarray, frames: int, time, status) -> None:
|
||||
if status:
|
||||
@@ -231,14 +197,6 @@ class Soundd:
|
||||
|
||||
self.get_audible_alert(sm)
|
||||
|
||||
# ClearPilot: check for ding trigger at ~2Hz
|
||||
self.ding_check_counter += 1
|
||||
if self.ding_check_counter % 10 == 0 and self.ding_sound is not None:
|
||||
if self.params_memory.get("ClearpilotPlayDing") == b"1":
|
||||
self.params_memory.put("ClearpilotPlayDing", "0")
|
||||
self.ding_playing = True
|
||||
self.ding_frame = 0
|
||||
|
||||
rk.keep_time()
|
||||
|
||||
assert stream.active
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
# CLEARPILOT: prefer freshly built _spinner over prebuilt qt/spinner
|
||||
if [ -f ./_spinner ]; then
|
||||
exec ./_spinner "$1"
|
||||
fi
|
||||
|
||||
if [ -f /TICI ] && [ ! -f qt/spinner ]; then
|
||||
cp qt/spinner_larch64 qt/spinner
|
||||
fi
|
||||
|
||||
@@ -818,15 +818,15 @@
|
||||
<name>OffroadHome</name>
|
||||
<message>
|
||||
<source>UPDATE</source>
|
||||
<translation type="vanished">تحديث</translation>
|
||||
<translation>تحديث</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERTS</source>
|
||||
<translation type="vanished"> التنبهات</translation>
|
||||
<translation> التنبهات</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERT</source>
|
||||
<translation type="vanished"> تنبيه</translation>
|
||||
<translation> تنبيه</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -771,15 +771,15 @@
|
||||
<name>OffroadHome</name>
|
||||
<message>
|
||||
<source>UPDATE</source>
|
||||
<translation type="vanished">Aktualisieren</translation>
|
||||
<translation>Aktualisieren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERTS</source>
|
||||
<translation type="vanished"> HINWEISE</translation>
|
||||
<translation> HINWEISE</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERT</source>
|
||||
<translation type="vanished"> HINWEIS</translation>
|
||||
<translation> HINWEIS</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -814,15 +814,15 @@
|
||||
<name>OffroadHome</name>
|
||||
<message>
|
||||
<source>UPDATE</source>
|
||||
<translation type="vanished">MISE À JOUR</translation>
|
||||
<translation>MISE À JOUR</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERTS</source>
|
||||
<translation type="vanished"> ALERTES</translation>
|
||||
<translation> ALERTES</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERT</source>
|
||||
<translation type="vanished"> ALERTE</translation>
|
||||
<translation> ALERTE</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -770,15 +770,15 @@
|
||||
<name>OffroadHome</name>
|
||||
<message>
|
||||
<source>UPDATE</source>
|
||||
<translation type="vanished">更新</translation>
|
||||
<translation>更新</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERTS</source>
|
||||
<translation type="vanished"> 警告</translation>
|
||||
<translation> 警告</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERT</source>
|
||||
<translation type="vanished"> 警告</translation>
|
||||
<translation> 警告</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -813,15 +813,15 @@
|
||||
<name>OffroadHome</name>
|
||||
<message>
|
||||
<source>UPDATE</source>
|
||||
<translation type="vanished">업데이트</translation>
|
||||
<translation>업데이트</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERTS</source>
|
||||
<translation type="vanished"> 알림</translation>
|
||||
<translation> 알림</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERT</source>
|
||||
<translation type="vanished"> 알림</translation>
|
||||
<translation> 알림</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -814,15 +814,15 @@
|
||||
<name>OffroadHome</name>
|
||||
<message>
|
||||
<source>UPDATE</source>
|
||||
<translation type="vanished">ATUALIZAÇÃO</translation>
|
||||
<translation>ATUALIZAÇÃO</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERTS</source>
|
||||
<translation type="vanished"> ALERTAS</translation>
|
||||
<translation> ALERTAS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERT</source>
|
||||
<translation type="vanished"> ALERTA</translation>
|
||||
<translation> ALERTA</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -813,15 +813,15 @@
|
||||
<name>OffroadHome</name>
|
||||
<message>
|
||||
<source>UPDATE</source>
|
||||
<translation type="vanished">อัปเดต</translation>
|
||||
<translation>อัปเดต</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERTS</source>
|
||||
<translation type="vanished"> การแจ้งเตือน</translation>
|
||||
<translation> การแจ้งเตือน</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERT</source>
|
||||
<translation type="vanished"> การแจ้งเตือน</translation>
|
||||
<translation> การแจ้งเตือน</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -770,15 +770,15 @@
|
||||
<name>OffroadHome</name>
|
||||
<message>
|
||||
<source>UPDATE</source>
|
||||
<translation type="vanished">GÜNCELLE</translation>
|
||||
<translation>GÜNCELLE</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERTS</source>
|
||||
<translation type="vanished"> UYARILAR</translation>
|
||||
<translation> UYARILAR</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERT</source>
|
||||
<translation type="vanished"> UYARI</translation>
|
||||
<translation> UYARI</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -813,15 +813,15 @@
|
||||
<name>OffroadHome</name>
|
||||
<message>
|
||||
<source>UPDATE</source>
|
||||
<translation type="vanished">更新</translation>
|
||||
<translation>更新</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERTS</source>
|
||||
<translation type="vanished"> 警报</translation>
|
||||
<translation> 警报</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERT</source>
|
||||
<translation type="vanished"> 警报</translation>
|
||||
<translation> 警报</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -813,15 +813,15 @@
|
||||
<name>OffroadHome</name>
|
||||
<message>
|
||||
<source>UPDATE</source>
|
||||
<translation type="vanished">更新</translation>
|
||||
<translation>更新</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERTS</source>
|
||||
<translation type="vanished"> 提醒</translation>
|
||||
<translation> 提醒</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ALERT</source>
|
||||
<translation type="vanished"> 提醒</translation>
|
||||
<translation> 提醒</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
+4
-17
@@ -93,9 +93,6 @@ void update_model(UIState *s,
|
||||
if (plan_position.getX().size() < model.getPosition().getX().size()) {
|
||||
plan_position = model.getPosition();
|
||||
}
|
||||
// CLEARPILOT: guard against empty model data (bench mode, no modeld running)
|
||||
if (plan_position.getX().size() == 0) return;
|
||||
|
||||
float max_distance = scene.unlimited_road_ui_length ? *(plan_position.getX().end() - 1) :
|
||||
std::clamp(*(plan_position.getX().end() - 1),
|
||||
MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE);
|
||||
@@ -118,10 +115,8 @@ void update_model(UIState *s,
|
||||
}
|
||||
|
||||
// update path
|
||||
// CLEARPILOT: read from frogpilotCarControl cereal message (was paramsMemory)
|
||||
bool no_lat_lane_change = (*s->sm)["frogpilotCarControl"].getFrogpilotCarControl().getNoLatLaneChange();
|
||||
float path;
|
||||
if (no_lat_lane_change) {
|
||||
if (paramsMemory.getBool("no_lat_lane_change")) {
|
||||
path = (float)LANE_CHANGE_NO_LAT_PATH_WIDTH / 20; // Release: better calc for EU users
|
||||
} else {
|
||||
path = (float)CENTER_LANE_WIDTH / 20; // Release: better calc for EU users
|
||||
@@ -396,7 +391,6 @@ void ui_update_frogpilot_params(UIState *s) {
|
||||
scene.screen_brightness = screen_management ? params.getInt("ScreenBrightness") : 101;
|
||||
scene.screen_brightness_onroad = screen_management ? params.getInt("ScreenBrightnessOnroad") : 101;
|
||||
scene.screen_recorder = screen_management && params.getBool("ScreenRecorder");
|
||||
scene.screen_recorder_debug = params.getBool("ScreenRecorderDebug");
|
||||
scene.screen_timeout = screen_management ? params.getInt("ScreenTimeout") : 120;
|
||||
scene.screen_timeout_onroad = screen_management ? params.getInt("ScreenTimeoutOnroad") : 10;
|
||||
scene.standby_mode = screen_management && params.getBool("StandbyMode");
|
||||
@@ -441,10 +435,9 @@ void UIState::updateStatus() {
|
||||
UIState::UIState(QObject *parent) : QObject(parent) {
|
||||
sm = std::make_unique<SubMaster, const std::initializer_list<const char *>>({
|
||||
"modelV2", "controlsState", "liveCalibration", "radarState", "deviceState",
|
||||
"pandaStates", "peripheralState", "carParams", "driverMonitoringState", "carState", "liveLocationKalman", "driverStateV2",
|
||||
"pandaStates", "carParams", "driverMonitoringState", "carState", "liveLocationKalman", "driverStateV2",
|
||||
"wideRoadCameraState", "managerState", "uiPlan", "liveTorqueParameters",
|
||||
"frogpilotCarControl", "frogpilotDeviceState", "frogpilotPlan",
|
||||
"gpsLocation",
|
||||
});
|
||||
|
||||
Params params;
|
||||
@@ -558,10 +551,7 @@ void Device::updateWakefulness(const UIState &s) {
|
||||
}
|
||||
|
||||
if (ignition_state_changed) {
|
||||
if (!ignition_on) {
|
||||
// CLEARPILOT: ignition on→off blanks the screen immediately (tap still wakes).
|
||||
resetInteractiveTimeout(0, 0);
|
||||
} else if (s.scene.screen_brightness_onroad == 0 && !s.scene.standby_mode) {
|
||||
if (ignition_on && s.scene.screen_brightness_onroad == 0 && !s.scene.standby_mode) {
|
||||
resetInteractiveTimeout(0, 0);
|
||||
} else {
|
||||
resetInteractiveTimeout(s.scene.screen_timeout, s.scene.screen_timeout_onroad);
|
||||
@@ -570,10 +560,7 @@ void Device::updateWakefulness(const UIState &s) {
|
||||
emit interactiveTimeout();
|
||||
}
|
||||
|
||||
// CLEARPILOT: ScreenDisplayMode 3 = screen off — override awake state
|
||||
if (paramsMemory.getInt("ScreenDisplayMode") == 3) {
|
||||
setAwake(false);
|
||||
} else if (s.scene.screen_brightness_onroad != 0) {
|
||||
if (s.scene.screen_brightness_onroad != 0) {
|
||||
setAwake(s.scene.ignition || interactive_timeout > 0);
|
||||
} else {
|
||||
setAwake(interactive_timeout > 0);
|
||||
|
||||
@@ -231,7 +231,6 @@ typedef struct UIScene {
|
||||
bool road_name_ui;
|
||||
bool rotating_wheel;
|
||||
bool screen_recorder;
|
||||
bool screen_recorder_debug;
|
||||
bool show_aol_status_bar;
|
||||
bool show_cem_status_bar;
|
||||
bool show_jerk;
|
||||
|
||||
Reference in New Issue
Block a user