#include "selfdrive/ui/qt/offroad/developer_panel.h" #include "selfdrive/ui/qt/widgets/ssh_keys.h" #include "selfdrive/ui/qt/widgets/controls.h" DeveloperPanel::DeveloperPanel(SettingsWindow *parent) : QFrame(parent) { mainLayout = new QStackedLayout(this); mainWidget = new QWidget(this); QVBoxLayout *mainListLayout = new QVBoxLayout(mainWidget); mainListLayout->setContentsMargins(50, 25, 50, 25); mainListLayout->setSpacing(20); StarPilotListWidget *mainList = new StarPilotListWidget(mainWidget); mainListLayout->addWidget(mainList); adbToggle = new ParamControl("AdbEnabled", tr("Enable ADB"), tr("ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info."), ""); mainList->addItem(adbToggle); auto *usePrebuiltToggle = new ParamControl("UsePrebuilt", tr("Use Prebuilt Binaries"), tr("When enabled (default), the device skips source compilation on boot if a prebuilt artifact exists. Disable this if you plan to edit code and rebuild on-device."), ""); mainList->addItem(usePrebuiltToggle); auto *showAllToggles = new ParamControl("ShowAllToggles", tr("Show All Toggles"), tr("Show every toggle in Settings and StarPilot, even when it would normally be hidden by tuning level, car support, or related feature gating."), ""); QObject::connect(showAllToggles, &ParamControl::toggleFlipped, [this](bool) { updateToggles(offroad); emit showAllTogglesChanged(); }); mainList->addItem(showAllToggles); // SSH keys mainList->addItem(new SshToggle()); mainList->addItem(new SshControl()); joystickToggle = new ParamControl("JoystickDebugMode", tr("Joystick Debug Mode"), "", ""); QObject::connect(joystickToggle, &ParamControl::toggleFlipped, [=](bool state) { params.putBool("LongitudinalManeuverMode", false); longManeuverToggle->refresh(); }); mainList->addItem(joystickToggle); longManeuverToggle = new ParamControl("LongitudinalManeuverMode", tr("Longitudinal Maneuver Mode"), "", ""); QObject::connect(longManeuverToggle, &ParamControl::toggleFlipped, [=](bool state) { params.putBool("JoystickDebugMode", false); joystickToggle->refresh(); }); mainList->addItem(longManeuverToggle); experimentalLongitudinalToggle = new ParamControl( "AlphaLongitudinalEnabled", tr("openpilot Longitudinal Control (Alpha)"), QString("%1

%2") .arg(tr("WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).")) .arg(tr("On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. " "Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.")), "" ); experimentalLongitudinalToggle->setConfirmation(true, false); QObject::connect(experimentalLongitudinalToggle, &ParamControl::toggleFlipped, [=]() { updateToggles(offroad); }); mainList->addItem(experimentalLongitudinalToggle); // Joystick and longitudinal maneuvers should be hidden on release branches is_release = false; // Toggles should be not available to change in onroad state QObject::connect(uiState(), &UIState::offroadTransition, this, &DeveloperPanel::updateToggles); QJsonObject shownDescriptions = QJsonDocument::fromJson(QString::fromStdString(params.get("ShownToggleDescriptions")).toUtf8()).object(); QString className = this->metaObject()->className(); if (!shownDescriptions.value(className).toBool(false)) { forceOpenDescriptions = true; } std::vector keys = params.allKeys(); for (const std::string &key : keys) { starpilotToggleLevels[QString::fromStdString(key)] = params.getTuningLevel(key); } developerUIToggle = new StarPilotManageControl("DeveloperUI", tr("Developer UI"), tr("Detailed information about openpilot's internal operations."), ""); QObject::connect(developerUIToggle, &StarPilotManageControl::manageButtonClicked, [this]() { mainLayout->setCurrentWidget(developerUIPanel); emit openSubPanel(); }); mainList->insertItem(0, developerUIToggle); mainLayout->addWidget(mainWidget); StarPilotListWidget *developerUIList = new StarPilotListWidget(this); developerUIList->setContentsMargins(50, 25, 50, 25); developerUIPanel = new ScrollView(developerUIList, this); mainLayout->addWidget(developerUIPanel); StarPilotListWidget *developerMetricList = new StarPilotListWidget(this); developerMetricList->setContentsMargins(50, 25, 50, 25); developerMetricPanel = new ScrollView(developerMetricList, this); mainLayout->addWidget(developerMetricPanel); StarPilotListWidget *developerSidebarList = new StarPilotListWidget(this); developerSidebarList->setContentsMargins(50, 25, 50, 25); developerSidebarPanel = new ScrollView(developerSidebarList, this); mainLayout->addWidget(developerSidebarPanel); StarPilotListWidget *developerWidgetList = new StarPilotListWidget(this); developerWidgetList->setContentsMargins(50, 25, 50, 25); developerWidgetPanel = new ScrollView(developerWidgetList, this); mainLayout->addWidget(developerWidgetPanel); std::vector> developerToggles { {"DeveloperMetrics", tr("Developer Metrics"), tr("Performance data, sensor readings, and system metrics for debugging and optimizing openpilot."), ""}, {"AdjacentPathMetrics", tr("Adjacent Lane Metrics"), tr("Show the width of the adjacent lanes."), ""}, {"BorderMetrics", tr("Border Metrics"), tr("Show statuses along the border of the driving screen.

Blind Spot: The border turns red when a vehicle is in a blind spot
Steering Torque: The border goes from green to red according to how much steering torque is being used
Turn Signal: The border flashes yellow when a turn signal is on"), ""}, {"FPSCounter", tr("FPS Display"), tr("Show the frames per second (FPS) at the bottom of the driving screen."), ""}, {"LeadInfo", tr("Lead Info"), tr("Show each tracked vehicle's distance and speed below its marker."), ""}, {"NumericalTemp", tr("Numerical Temperature Gauge"), tr("Show a numerical temperature in the sidebar instead of the status labels."), ""}, {"SidebarMetrics", tr("Sidebar Metrics"), tr("Display system information (CPU, GPU, RAM usage, IP address, device storage) in the sidebar."), ""}, {"UseSI", tr("Use International System of Units"), tr("Display measurements using the \"International System of Units\" (SI) standard."), ""}, {"DeveloperSidebar", tr("Developer Sidebar"), tr("Display debugging info and metrics in a dedicated sidebar on the right side of the screen."), ""}, {"DeveloperSidebarMetric1", tr("Metric #1"), tr("Select the metric shown in the first \"Developer Sidebar\" widget."), ""}, {"DeveloperSidebarMetric2", tr("Metric #2"), tr("Select the metric shown in the second \"Developer Sidebar\" widget."), ""}, {"DeveloperSidebarMetric3", tr("Metric #3"), tr("Select the metric shown in the third \"Developer Sidebar\" widget."), ""}, {"DeveloperSidebarMetric4", tr("Metric #4"), tr("Select the metric shown in the fourth \"Developer Sidebar\" widget."), ""}, {"DeveloperSidebarMetric5", tr("Metric #5"), tr("Select the metric shown in the fifth \"Developer Sidebar\" widget."), ""}, {"DeveloperSidebarMetric6", tr("Metric #6"), tr("Select the metric shown in the sixth \"Developer Sidebar\" widget."), ""}, {"DeveloperSidebarMetric7", tr("Metric #7"), tr("Select the metric shown in the seventh \"Developer Sidebar\" widget."), ""}, {"DeveloperWidgets", tr("Developer Widgets"), tr("Overlays for debugging visuals, internal states, and model predictions on the driving screen."), ""}, {"AdjacentLeadsUI", tr("Adjacent Leads Tracking"), tr("Display adjacent leads detected by the car's radar to the left and right of the current driving path."), ""}, {"ShowStoppingPoint", tr("Model Stopping Point"), tr("Show a stop-sign marker where the model intends to stop."), ""}, {"RadarTracksUI", tr("Radar Tracks"), tr("Display all radar points produced by the car's radar."), ""}, }; for (const auto &[param, title, desc, icon] : developerToggles) { AbstractControl *developerToggle; if (param == "DeveloperMetrics") { StarPilotManageControl *developerMetricsToggle = new StarPilotManageControl(param, title, desc, icon); QObject::connect(developerMetricsToggle, &StarPilotManageControl::manageButtonClicked, [this]() { mainLayout->setCurrentWidget(developerMetricPanel); emit openSubSubPanel(); }); developerToggle = developerMetricsToggle; } else if (param == "BorderMetrics") { std::vector borderToggles{"BlindSpotMetrics", "ShowSteering", "SignalMetrics"}; std::vector borderToggleNames{tr("Blind Spot"), tr("Steering Torque"), tr("Turn Signal")}; borderMetricsButton = new StarPilotButtonToggleControl(param, title, desc, icon, borderToggles, borderToggleNames); developerToggle = borderMetricsButton; } else if (param == "NumericalTemp") { std::vector temperatureToggles{"Fahrenheit"}; std::vector temperatureToggleNames{tr("Fahrenheit")}; developerToggle = new StarPilotButtonToggleControl(param, title, desc, icon, temperatureToggles, temperatureToggleNames); } else if (param == "SidebarMetrics") { sidebarMetricsToggles = {"ShowCPU", "ShowGPU", "ShowIP", "ShowMemoryUsage", "ShowStorageLeft", "ShowStorageUsed"}; std::vector sidebarMetricsToggleNames{tr("CPU"), tr("GPU"), tr("IP"), tr("RAM"), tr("SSD Left"), tr("SSD Used")}; sidebarMetricsToggle = new StarPilotButtonsControl(title, desc, icon, sidebarMetricsToggleNames, true, false, 150); for (int i = 0; i < sidebarMetricsToggles.size(); ++i) { if (params.getBool(sidebarMetricsToggles[i].toStdString())) { sidebarMetricsToggle->setCheckedButton(i); } } QObject::connect(sidebarMetricsToggle, &StarPilotButtonsControl::buttonClicked, [this](int id) { params.putBool(sidebarMetricsToggles[id].toStdString(), !params.getBool(sidebarMetricsToggles[id].toStdString())); if (id == 0) { params.putBool("ShowGPU", false); } else if (id == 1) { params.putBool("ShowCPU", false); } else if (id == 3) { params.putBool("ShowStorageLeft", false); params.putBool("ShowStorageUsed", false); } else if (id == 4) { params.putBool("ShowMemoryUsage", false); params.putBool("ShowStorageUsed", false); } else if (id == 5) { params.putBool("ShowMemoryUsage", false); params.putBool("ShowStorageLeft", false); } sidebarMetricsToggle->clearCheckedButtons(); for (int i = 0; i < sidebarMetricsToggles.size(); ++i) { if (params.getBool(sidebarMetricsToggles[i].toStdString())) { sidebarMetricsToggle->setCheckedButton(i); } } }); developerToggle = sidebarMetricsToggle; } else if (param == "DeveloperSidebar") { StarPilotManageControl *developerSidebarToggle = new StarPilotManageControl(param, title, desc, icon); QObject::connect(developerSidebarToggle, &StarPilotManageControl::manageButtonClicked, [this]() { mainLayout->setCurrentWidget(developerSidebarPanel); emit openSubSubPanel(); }); developerToggle = developerSidebarToggle; } else if (developerSidebarKeys.contains(param)) { QMap developerSidebarMetricOptions { {0, tr("None")}, {1, tr("Acceleration: Current")}, {2, tr("Acceleration: Max")}, {3, tr("Auto Tune: Actuator Delay")}, {4, tr("Auto Tune: Friction")}, {5, tr("Auto Tune: Lateral Acceleration")}, {6, tr("Auto Tune: Steer Ratio")}, {7, tr("Auto Tune: Stiffness Factor")}, {8, tr("Engagement %: Lateral")}, {9, tr("Engagement %: Longitudinal")}, {10, tr("Lateral Control: Steering Angle")}, {11, tr("Lateral Control: Torque % Used")}, {12, tr("Longitudinal Control: Actuator Acceleration Output")}, {13, tr("Longitudinal MPC: Danger Factor")}, {14, tr("Longitudinal MPC Jerk: Acceleration")}, {15, tr("Longitudinal MPC Jerk: Danger Zone")}, {16, tr("Longitudinal MPC Jerk: Speed Control")}, {17, tr("Model Name")}, }; ButtonControl *metricToggle = new ButtonControl(title, tr("SELECT"), desc); QObject::connect(metricToggle, &ButtonControl::clicked, [metricToggle, key = param, developerSidebarMetricOptions, this]() mutable { QString current = developerSidebarMetricOptions.value(params.getInt(key.toStdString()), tr("None")); QString selection = MultiOptionDialog::getSelection(tr("Select a metric to display"), developerSidebarMetricOptions.values(), current, this); if (!selection.isEmpty()) { int selectedMetric = developerSidebarMetricOptions.key(selection); params.putInt(key.toStdString(), selectedMetric); metricToggle->setValue(selection); } }); metricToggle->setValue(developerSidebarMetricOptions.value(params.getInt(param.toStdString()), tr("None"))); developerToggle = metricToggle; } else if (param == "DeveloperWidgets") { StarPilotManageControl *developerWidgetsToggle = new StarPilotManageControl(param, title, desc, icon); QObject::connect(developerWidgetsToggle, &StarPilotManageControl::manageButtonClicked, [this]() { mainLayout->setCurrentWidget(developerWidgetPanel); emit openSubSubPanel(); }); developerToggle = developerWidgetsToggle; } else if (param == "ShowStoppingPoint") { std::vector stoppingPointToggles{"ShowStoppingPointMetrics"}; std::vector stoppingPointToggleNames{tr("Show Distance")}; developerToggle = new StarPilotButtonToggleControl(param, title, desc, icon, stoppingPointToggles, stoppingPointToggleNames); } else { developerToggle = new ParamControl(param, title, desc, icon); } toggles[param] = developerToggle; if (developerMetricKeys.contains(param)) { developerMetricList->addItem(developerToggle); } else if (developerSidebarKeys.contains(param)) { developerSidebarList->addItem(developerToggle); } else if (developerWidgetKeys.contains(param)) { developerWidgetList->addItem(developerToggle); } else { developerUIList->addItem(developerToggle); parentKeys.insert(param); } } QObject::connect(parent, &SettingsWindow::closeSubPanel, [this]() {mainLayout->setCurrentWidget(mainWidget);}); QObject::connect(parent, &SettingsWindow::closeSubSubPanel, [this]() {mainLayout->setCurrentWidget(developerUIPanel);}); } void DeveloperPanel::updateToggles(bool _offroad) { const bool showAllToggles = params.getBool("ShowAllToggles"); for (auto btn : findChildren()) { btn->setVisible(showAllToggles || !is_release); /* * experimentalLongitudinalToggle should be toggelable when: * - visible, and * - during onroad & offroad states */ if (btn != experimentalLongitudinalToggle) { btn->setEnabled(_offroad); } for (auto &[key, toggle] : toggles) { if (toggle == btn) { btn->setEnabled(true); break; } } if (developerUIToggle == btn) { btn->setEnabled(true); } } // longManeuverToggle and experimentalLongitudinalToggle should not be toggleable if the car does not have longitudinal control auto cp_bytes = params.get("CarParamsPersistent"); if (!cp_bytes.empty()) { AlignedBuffer aligned_buf; capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); cereal::CarParams::Reader CP = cmsg.getRoot(); if ((!CP.getAlphaLongitudinalAvailable() || is_release) && !showAllToggles) { params.remove("AlphaLongitudinalEnabled"); experimentalLongitudinalToggle->setEnabled(false); } else { experimentalLongitudinalToggle->setEnabled(true); } /* * experimentalLongitudinalToggle should be visible when: * - is not a release branch, and * - the car supports experimental longitudinal control (alpha) */ experimentalLongitudinalToggle->setVisible(showAllToggles || (CP.getAlphaLongitudinalAvailable() && !is_release)); longManeuverToggle->setEnabled((showAllToggles || hasLongitudinalControl(CP)) && _offroad); hasOpenpilotLongitudinal = hasLongitudinalControl(CP); hasRadar = !CP.getRadarUnavailable(); borderMetricsButton->setVisibleButton(0, showAllToggles || CP.getEnableBsm()); } else { longManeuverToggle->setEnabled(showAllToggles && _offroad); experimentalLongitudinalToggle->setEnabled(showAllToggles); experimentalLongitudinalToggle->setVisible(showAllToggles); } experimentalLongitudinalToggle->refresh(); offroad = _offroad; tuningLevel = params.getInt("TuningLevel"); for (auto &[key, toggle] : toggles) { if (parentKeys.contains(key)) { toggle->setVisible(showAllToggles); } } for (auto &[key, toggle] : toggles) { if (parentKeys.contains(key)) { continue; } bool setVisible = showAllToggles || tuningLevel >= starpilotToggleLevels[key].toDouble(); if (!showAllToggles) { if (key == "AdjacentLeadsUI") { setVisible &= hasRadar && !(params.getBool("AdvancedCustomUI") && params.getBool("HideLeadMarker")); } else if (key == "RadarTracksUI") { setVisible &= hasRadar; } else if (key == "ShowStoppingPoint") { setVisible &= hasOpenpilotLongitudinal; } } toggle->setVisible(setVisible); if (setVisible) { if (developerMetricKeys.contains(key)) { toggles["DeveloperMetrics"]->setVisible(true); } else if (developerSidebarKeys.contains(key)) { toggles["DeveloperSidebar"]->setVisible(true); } else if (developerUIKeys.contains(key)) { toggles["DeveloperUI"]->setVisible(true); } else if (developerWidgetKeys.contains(key)) { toggles["DeveloperWidgets"]->setVisible(true); } } } borderMetricsButton->setVisibleButton(0, showAllToggles || hasBSM); developerUIToggle->setVisible(showAllToggles || tuningLevel >= starpilotToggleLevels["DeveloperUI"].toDouble()); openDescriptions(forceOpenDescriptions, toggles); update(); } void DeveloperPanel::showEvent(QShowEvent *event) { updateToggles(offroad); for (int i = 0; i < sidebarMetricsToggles.size(); ++i) { if (params.getBool(sidebarMetricsToggles[i].toStdString())) { sidebarMetricsToggle->setCheckedButton(i); } } QJsonObject shownDescriptions = QJsonDocument::fromJson(QString::fromStdString(params.get("ShownToggleDescriptions")).toUtf8()).object(); QString className = this->metaObject()->className(); if (!shownDescriptions.value(className).toBool(false)) { shownDescriptions.insert(className, true); params.put("ShownToggleDescriptions", QJsonDocument(shownDescriptions).toJson(QJsonDocument::Compact).toStdString()); } }