#include "frogpilot/ui/qt/offroad/utilities.h" FrogPilotUtilitiesPanel::FrogPilotUtilitiesPanel(FrogPilotSettingsWindow *parent, bool forceOpen) : FrogPilotListWidget(parent), parent(parent) { forceOpenDescriptions = forceOpen; ParamControl *debugModeToggle = new ParamControl("DebugMode", tr("Debug Mode"), tr("Use all of FrogPilot's developer metrics on your next drive to diagnose issues and improve bug reports."), ""); if (forceOpenDescriptions) { debugModeToggle->showDescription(); } addItem(debugModeToggle); ButtonControl *flashPandaButton = new ButtonControl(tr("Flash Panda"), tr("FLASH"), tr("Flash the latest, official firmware onto your Panda device to restore core functionality, fix bugs, or ensure you have the most up-to-date software.")); QObject::connect(flashPandaButton, &ButtonControl::clicked, [parent, flashPandaButton, this]() { if (ConfirmationDialog::confirm(tr("Are you sure you want to flash the Panda firmware?"), tr("Flash"), this)) { std::thread([parent, flashPandaButton, this]() { parent->keepScreenOn = true; flashPandaButton->setEnabled(false); flashPandaButton->setValue(tr("Flashing...")); params_memory.putBool("FlashPanda", true); while (params_memory.getBool("FlashPanda")) { util::sleep_for(UI_FREQ); } flashPandaButton->setValue(tr("Flashed!")); util::sleep_for(2500); flashPandaButton->setValue(tr("Rebooting...")); util::sleep_for(2500); Hardware::reboot(); }).detach(); } }); if (forceOpenDescriptions) { flashPandaButton->showDescription(); } addItem(flashPandaButton); FrogPilotButtonsControl *forceStartedButton = new FrogPilotButtonsControl(tr("Force Drive State"), tr("Force openpilot to be offroad or onroad."), "", {tr("OFFROAD"), tr("ONROAD"), tr("OFF")}, true); QObject::connect(forceStartedButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) { if (id == 0) { params.putBool("ForceOffroad", true); params.putBool("ForceOnroad", false); updateFrogPilotToggles(); } else if (id == 1) { params.put("CarParams", params.get("CarParamsPersistent")); params.put("FrogPilotCarParams", params.get("FrogPilotCarParamsPersistent")); params.putBool("ForceOffroad", false); params.putBool("ForceOnroad", true); updateFrogPilotToggles(); } else if (id == 2) { params.putBool("ForceOffroad", false); params.putBool("ForceOnroad", false); updateFrogPilotToggles(); } }); forceStartedButton->setCheckedButton(2); if (forceOpenDescriptions) { forceStartedButton->showDescription(); } addItem(forceStartedButton); ButtonControl *reportIssueButton = new ButtonControl(tr("Report a Bug or an Issue"), tr("REPORT"), tr("Send a bug report so we can help fix the problem!")); QObject::connect(reportIssueButton, &ButtonControl::clicked, [this]() { if (!frogpilotUIState()->frogpilot_scene.online) { ConfirmationDialog::alert(tr("Please connect to the internet before sending a report!"), this); return; } QStringList report_messages = { tr("Acceleration feels harsh or jerky"), tr("An alert was unclear and I'm not sure what it meant"), tr("Braking is too sudden or uncomfortable"), tr("I'm not sure if this is normal or a bug:"), tr("My steering wheel buttons aren't working"), tr("openpilot disengages when I don't expect it"), tr("openpilot feels sluggish or slow to respond"), tr("Something else (please describe)") }; if (QFile::exists("/data/error_logs/error.txt")) { report_messages.prepend(tr("I saw an alert that said \"openpilot crashed\"")); } QString selected_issue = MultiOptionDialog::getSelection(tr("What's going on?"), report_messages, "", this); if (selected_issue.isEmpty()) { return; } if (selected_issue.contains("crashed") || selected_issue.contains("not sure") || selected_issue.contains("Something else")) { QString extra_input = InputDialog::getText(tr("Please describe what's happening"), this, tr("Send Report"), false, 10, "", 300).trimmed(); if (extra_input.isEmpty()) { return; } selected_issue += " — " + extra_input; } QString discord_user = InputDialog::getText(tr("What's your Discord username?"), this, tr("Send Report"), false, -1, QString::fromStdString(params.get("DiscordUsername"))).trimmed(); QJsonObject reportData; reportData["DiscordUser"] = discord_user; reportData["Issue"] = selected_issue; params.putNonBlocking("DiscordUsername", discord_user.toStdString()); params_memory.put("IssueReported", QJsonDocument(reportData).toJson(QJsonDocument::Compact).toStdString()); ConfirmationDialog::alert(tr("Report Sent! Thanks for letting us know!"), this); }); if (forceOpenDescriptions) { reportIssueButton->showDescription(); } addItem(reportIssueButton); reportIssueButton->setVisible(QString::fromStdString(params.get("GitRemote")).toLower() == "https://github.com/frogai/openpilot.git"); ButtonControl *resetTogglesButton = new ButtonControl(tr("Reset Toggles to Default"), tr("RESET"), tr("Reset all toggles to their default values.")); QObject::connect(resetTogglesButton, &ButtonControl::clicked, [parent, resetTogglesButton, this]() { if (ConfirmationDialog::confirm(tr("Are you sure you want to reset all toggles to their default values?"), tr("Reset"), this)) { std::thread([parent, resetTogglesButton, this]() { parent->keepScreenOn = true; resetTogglesButton->setEnabled(false); resetTogglesButton->setValue(tr("Resetting...")); std::vector all_keys = params.allKeys(); for (const std::string &key : all_keys) { if (excluded_keys.count(key)) { continue; } std::optional default_value = params.getKeyDefaultValue(key); if (default_value.has_value()) { params.put(key, default_value.value()); } } updateFrogPilotToggles(); resetTogglesButton->setValue(tr("Reset!")); util::sleep_for(2500); resetTogglesButton->setValue(""); }).detach(); } }); if (forceOpenDescriptions) { resetTogglesButton->showDescription(); } addItem(resetTogglesButton); ButtonControl *resetTogglesButtonStock = new ButtonControl(tr("Reset Toggles to Stock openpilot"), tr("RESET"), tr("Reset all toggles to match stock openpilot.")); QObject::connect(resetTogglesButtonStock, &ButtonControl::clicked, [parent, resetTogglesButtonStock, this]() { if (ConfirmationDialog::confirm(tr("Are you sure you want to reset all toggles to match stock openpilot?"), tr("Reset"), this)) { std::thread([parent, resetTogglesButtonStock, this]() { parent->keepScreenOn = true; resetTogglesButtonStock->setEnabled(false); resetTogglesButtonStock->setValue(tr("Resetting...")); std::vector all_keys = params.allKeys(); for (const std::string &key : all_keys) { if (excluded_keys.count(key)) { continue; } std::optional stock_value = params.getStockValue(key); if (stock_value.has_value()) { params.put(key, stock_value.value()); } } updateFrogPilotToggles(); resetTogglesButtonStock->setValue(tr("Reset!")); util::sleep_for(2500); resetTogglesButtonStock->setValue(""); }).detach(); } }); if (forceOpenDescriptions) { resetTogglesButtonStock->showDescription(); } addItem(resetTogglesButtonStock); }