Files
onepilot/selfdrive/ui/qt/offroad/developer_panel.cc
T
firestar5683 d0e1db6766 StarPilot
2026-03-22 03:15:05 -05:00

391 lines
18 KiB
C++

#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);
FrogPilotListWidget *mainList = new FrogPilotListWidget(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);
// 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("<b>%1</b><br><br>%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);
// FrogPilot variables
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<std::string> keys = params.allKeys();
for (const std::string &key : keys) {
frogpilotToggleLevels[QString::fromStdString(key)] = params.getTuningLevel(key);
}
developerUIToggle = new FrogPilotManageControl("DeveloperUI", tr("Developer UI"), tr("<b>Detailed information about openpilot's internal operations.</b>"), "");
QObject::connect(developerUIToggle, &FrogPilotManageControl::manageButtonClicked, [this]() {
mainLayout->setCurrentWidget(developerUIPanel);
emit openSubPanel();
});
mainList->insertItem(0, developerUIToggle);
mainLayout->addWidget(mainWidget);
FrogPilotListWidget *developerUIList = new FrogPilotListWidget(this);
developerUIList->setContentsMargins(50, 25, 50, 25);
developerUIPanel = new ScrollView(developerUIList, this);
mainLayout->addWidget(developerUIPanel);
FrogPilotListWidget *developerMetricList = new FrogPilotListWidget(this);
developerMetricList->setContentsMargins(50, 25, 50, 25);
developerMetricPanel = new ScrollView(developerMetricList, this);
mainLayout->addWidget(developerMetricPanel);
FrogPilotListWidget *developerSidebarList = new FrogPilotListWidget(this);
developerSidebarList->setContentsMargins(50, 25, 50, 25);
developerSidebarPanel = new ScrollView(developerSidebarList, this);
mainLayout->addWidget(developerSidebarPanel);
FrogPilotListWidget *developerWidgetList = new FrogPilotListWidget(this);
developerWidgetList->setContentsMargins(50, 25, 50, 25);
developerWidgetPanel = new ScrollView(developerWidgetList, this);
mainLayout->addWidget(developerWidgetPanel);
std::vector<std::tuple<QString, QString, QString, QString>> developerToggles {
{"DeveloperMetrics", tr("Developer Metrics"), tr("<b>Performance data, sensor readings, and system metrics</b> for debugging and optimizing openpilot."), ""},
{"AdjacentPathMetrics", tr("Adjacent Lane Metrics"), tr("<b>Show the width of the adjacent lanes.</b>"), ""},
{"BorderMetrics", tr("Border Metrics"), tr("<b>Show statuses along the border of the driving screen.</b><br><br><b>Blind Spot</b>: The border turns red when a vehicle is in a blind spot<br><b>Steering Torque</b>: The border goes from green to red according to how much steering torque is being used<br><b>Turn Signal</b>: The border flashes yellow when a turn signal is on"), ""},
{"FPSCounter", tr("FPS Display"), tr("<b>Show the frames per second (FPS)</b> at the bottom of the driving screen."), ""},
{"LeadInfo", tr("Lead Info"), tr("<b>Show each tracked vehicle's distance and speed</b> below its marker."), ""},
{"NumericalTemp", tr("Numerical Temperature Gauge"), tr("<b>Show a numerical temperature in the sidebar</b> instead of the status labels."), ""},
{"SidebarMetrics", tr("Sidebar Metrics"), tr("<b>Display system information</b> (CPU, GPU, RAM usage, IP address, device storage) in the sidebar."), ""},
{"UseSI", tr("Use International System of Units"), tr("<b>Display measurements using the \"International System of Units\" (SI)</b> standard."), ""},
{"DeveloperSidebar", tr("Developer Sidebar"), tr("<b>Display debugging info and metrics</b> in a dedicated sidebar on the right side of the screen."), ""},
{"DeveloperSidebarMetric1", tr("Metric #1"), tr("<b>Select the metric shown in the first \"Developer Sidebar\" widget.</b>"), ""},
{"DeveloperSidebarMetric2", tr("Metric #2"), tr("<b>Select the metric shown in the second \"Developer Sidebar\" widget.</b>"), ""},
{"DeveloperSidebarMetric3", tr("Metric #3"), tr("<b>Select the metric shown in the third \"Developer Sidebar\" widget.</b>"), ""},
{"DeveloperSidebarMetric4", tr("Metric #4"), tr("<b>Select the metric shown in the fourth \"Developer Sidebar\" widget.</b>"), ""},
{"DeveloperSidebarMetric5", tr("Metric #5"), tr("<b>Select the metric shown in the fifth \"Developer Sidebar\" widget.</b>"), ""},
{"DeveloperSidebarMetric6", tr("Metric #6"), tr("<b>Select the metric shown in the sixth \"Developer Sidebar\" widget.</b>"), ""},
{"DeveloperSidebarMetric7", tr("Metric #7"), tr("<b>Select the metric shown in the seventh \"Developer Sidebar\" widget.</b>"), ""},
{"DeveloperWidgets", tr("Developer Widgets"), tr("<b>Overlays for debugging visuals, internal states, and model predictions</b> on the driving screen."), ""},
{"AdjacentLeadsUI", tr("Adjacent Leads Tracking"), tr("<b>Display adjacent leads detected by the car's radar</b> to the left and right of the current driving path."), ""},
{"ShowStoppingPoint", tr("Model Stopping Point"), tr("<b>Show a stop-sign marker where the model intends to stop.</b>"), ""},
{"RadarTracksUI", tr("Radar Tracks"), tr("<b>Display all radar points</b> produced by the car's radar."), ""},
};
for (const auto &[param, title, desc, icon] : developerToggles) {
AbstractControl *developerToggle;
if (param == "DeveloperMetrics") {
FrogPilotManageControl *developerMetricsToggle = new FrogPilotManageControl(param, title, desc, icon);
QObject::connect(developerMetricsToggle, &FrogPilotManageControl::manageButtonClicked, [this]() {
mainLayout->setCurrentWidget(developerMetricPanel);
emit openSubSubPanel();
});
developerToggle = developerMetricsToggle;
} else if (param == "BorderMetrics") {
std::vector<QString> borderToggles{"BlindSpotMetrics", "ShowSteering", "SignalMetrics"};
std::vector<QString> borderToggleNames{tr("Blind Spot"), tr("Steering Torque"), tr("Turn Signal")};
borderMetricsButton = new FrogPilotButtonToggleControl(param, title, desc, icon, borderToggles, borderToggleNames);
developerToggle = borderMetricsButton;
} else if (param == "NumericalTemp") {
std::vector<QString> temperatureToggles{"Fahrenheit"};
std::vector<QString> temperatureToggleNames{tr("Fahrenheit")};
developerToggle = new FrogPilotButtonToggleControl(param, title, desc, icon, temperatureToggles, temperatureToggleNames);
} else if (param == "SidebarMetrics") {
sidebarMetricsToggles = {"ShowCPU", "ShowGPU", "ShowIP", "ShowMemoryUsage", "ShowStorageLeft", "ShowStorageUsed"};
std::vector<QString> sidebarMetricsToggleNames{tr("CPU"), tr("GPU"), tr("IP"), tr("RAM"), tr("SSD Left"), tr("SSD Used")};
sidebarMetricsToggle = new FrogPilotButtonsControl(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, &FrogPilotButtonsControl::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") {
FrogPilotManageControl *developerSidebarToggle = new FrogPilotManageControl(param, title, desc, icon);
QObject::connect(developerSidebarToggle, &FrogPilotManageControl::manageButtonClicked, [this]() {
mainLayout->setCurrentWidget(developerSidebarPanel);
emit openSubSubPanel();
});
developerToggle = developerSidebarToggle;
} else if (developerSidebarKeys.contains(param)) {
QMap<int, QString> 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")},
};
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") {
FrogPilotManageControl *developerWidgetsToggle = new FrogPilotManageControl(param, title, desc, icon);
QObject::connect(developerWidgetsToggle, &FrogPilotManageControl::manageButtonClicked, [this]() {
mainLayout->setCurrentWidget(developerWidgetPanel);
emit openSubSubPanel();
});
developerToggle = developerWidgetsToggle;
} else if (param == "ShowStoppingPoint") {
std::vector<QString> stoppingPointToggles{"ShowStoppingPointMetrics"};
std::vector<QString> stoppingPointToggleNames{tr("Show Distance")};
developerToggle = new FrogPilotButtonToggleControl(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) {
for (auto btn : findChildren<ParamControl *>()) {
btn->setVisible(!is_release);
/*
* experimentalLongitudinalToggle should be toggelable when:
* - visible, and
* - during onroad & offroad states
*/
if (btn != experimentalLongitudinalToggle) {
btn->setEnabled(_offroad);
}
// FrogPilot variables
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<cereal::CarParams>();
if (!CP.getAlphaLongitudinalAvailable() || is_release) {
params.remove("AlphaLongitudinalEnabled");
experimentalLongitudinalToggle->setEnabled(false);
}
/*
* experimentalLongitudinalToggle should be visible when:
* - is not a release branch, and
* - the car supports experimental longitudinal control (alpha)
*/
experimentalLongitudinalToggle->setVisible(CP.getAlphaLongitudinalAvailable() && !is_release);
longManeuverToggle->setEnabled(hasLongitudinalControl(CP) && _offroad);
// FrogPilot variables
hasOpenpilotLongitudinal = hasLongitudinalControl(CP);
hasRadar = !CP.getRadarUnavailable();
borderMetricsButton->setVisibleButton(0, CP.getEnableBsm());
} else {
longManeuverToggle->setEnabled(false);
experimentalLongitudinalToggle->setVisible(false);
}
experimentalLongitudinalToggle->refresh();
offroad = _offroad;
// FrogPilot variables
tuningLevel = params.getInt("TuningLevel");
for (auto &[key, toggle] : toggles) {
if (parentKeys.contains(key)) {
toggle->setVisible(false);
}
}
for (auto &[key, toggle] : toggles) {
if (parentKeys.contains(key)) {
continue;
}
bool setVisible = tuningLevel >= frogpilotToggleLevels[key].toDouble();
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, hasBSM);
developerUIToggle->setVisible(tuningLevel >= frogpilotToggleLevels["DeveloperUI"].toDouble());
openDescriptions(forceOpenDescriptions, toggles);
update();
}
void DeveloperPanel::showEvent(QShowEvent *event) {
updateToggles(offroad);
// FrogPilot variables
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());
}
}