This commit is contained in:
Your Name
2024-04-27 13:43:16 -05:00
parent 21363ce751
commit ea1aad5ed1
128 changed files with 3533 additions and 1918 deletions

0
selfdrive/ui/qt/offroad/driverview.cc Executable file → Normal file
View File

0
selfdrive/ui/qt/offroad/driverview.h Executable file → Normal file
View File

0
selfdrive/ui/qt/offroad/experimental_mode.cc Executable file → Normal file
View File

0
selfdrive/ui/qt/offroad/experimental_mode.h Executable file → Normal file
View File

14
selfdrive/ui/qt/offroad/onboarding.cc Executable file → Normal file
View File

@@ -183,8 +183,8 @@ void DeclinePage::showEvent(QShowEvent *event) {
void OnboardingWindow::updateActiveScreen() {
if (!accepted_terms) {
setCurrentIndex(0);
// } else if (!training_done && !params.getBool("Passive")) {
// setCurrentIndex(1);
} else if (!training_done) {
setCurrentIndex(1);
} else {
emit onboardingDone();
}
@@ -199,7 +199,7 @@ OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) {
TermsPage* terms = new TermsPage(this);
addWidget(terms);
connect(terms, &TermsPage::acceptedTerms, [=]() {
Params().put("HasAcceptedTerms", current_terms_version);
params.put("HasAcceptedTerms", current_terms_version);
accepted_terms = true;
updateActiveScreen();
});
@@ -209,7 +209,7 @@ OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) {
addWidget(tr);
connect(tr, &TrainingGuide::completedTraining, [=]() {
training_done = true;
Params().put("CompletedTrainingVersion", current_training_version);
params.put("CompletedTrainingVersion", current_training_version);
updateActiveScreen();
});
@@ -230,11 +230,5 @@ OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) {
background-color: #4F4F4F;
}
)");
// # Oscar sez
Params().put("HasAcceptedTerms", current_terms_version);
Params().put("CompletedTrainingVersion", current_training_version);
accepted_terms = true;
emit onboardingDone();
updateActiveScreen();
}

0
selfdrive/ui/qt/offroad/onboarding.h Executable file → Normal file
View File

385
selfdrive/ui/qt/offroad/settings.cc Executable file → Normal file
View File

@@ -2,6 +2,8 @@
#include <cassert>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
@@ -25,9 +27,9 @@
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/frogpilot/navigation/ui/navigation_settings.h"
#include "selfdrive/frogpilot/ui/control_settings.h"
#include "selfdrive/frogpilot/ui/vehicle_settings.h"
#include "selfdrive/frogpilot/ui/visual_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/control_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/visual_settings.h"
TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
// param, title, desc, icon
@@ -97,9 +99,14 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
std::vector<QString> longi_button_texts{tr("Aggressive"), tr("Standard"), tr("Relaxed")};
long_personality_setting = new ButtonParamControl("LongitudinalPersonality", tr("Driving Personality"),
tr("Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. "
"In relaxed mode openpilot will stay further away from lead cars."),
"In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with "
"your steering wheel distance button."),
"../assets/offroad/icon_speed_limit.png",
longi_button_texts);
// set up uiState update for personality setting
QObject::connect(uiState(), &UIState::uiUpdate, this, &TogglesPanel::updateState);
for (auto &[param, title, desc, icon] : toggle_defs) {
auto toggle = new ParamControl(param, title, desc, icon, this);
@@ -110,7 +117,7 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
toggles[param.toStdString()] = toggle;
// insert longitudinal personality after NDOG toggle
if (param == "DisengageOnAccelerator" && !params.getInt("AdjustablePersonalities")) {
if (param == "DisengageOnAccelerator") {
addItem(long_personality_setting);
}
}
@@ -129,6 +136,18 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
});
}
void TogglesPanel::updateState(const UIState &s) {
const SubMaster &sm = *(s.sm);
if (sm.updated("controlsState")) {
auto personality = sm["controlsState"].getControlsState().getPersonality();
if (personality != s.scene.personality && s.scene.started && isVisible()) {
long_personality_setting->setCheckedButton(static_cast<int>(personality));
}
uiState()->scene.personality = personality;
}
}
void TogglesPanel::expandToggleDescription(const QString &param) {
toggles[param.toStdString()]->showDescription();
}
@@ -138,6 +157,13 @@ void TogglesPanel::showEvent(QShowEvent *event) {
}
void TogglesPanel::updateToggles() {
auto disengage_on_accelerator_toggle = toggles["DisengageOnAccelerator"];
disengage_on_accelerator_toggle->setVisible(!params.getBool("AlwaysOnLateral"));
auto driver_camera_toggle = toggles["RecordFront"];
driver_camera_toggle->setVisible(!(params.getBool("DeviceManagement") && params.getBool("NoLogging") && params.getBool("NoUploads")));
auto nav_settings_left_toggle = toggles["NavSettingLeftSide"];
nav_settings_left_toggle->setVisible(!params.getBool("FullMap"));
auto experimental_mode_toggle = toggles["ExperimentalMode"];
auto op_long_toggle = toggles["ExperimentalLongitudinalEnabled"];
const QString e2e_description = QString("%1<br>"
@@ -173,7 +199,12 @@ void TogglesPanel::updateToggles() {
op_long_toggle->setVisible(CP.getExperimentalLongitudinalAvailable() && !is_release);
if (hasLongitudinalControl(CP)) {
// normal description and toggle
experimental_mode_toggle->setEnabled(!params.getBool("ConditionalExperimental"));
bool conditional_experimental = params.getBool("ConditionalExperimental");
if (conditional_experimental) {
params.putBool("ExperimentalMode", true);
experimental_mode_toggle->refresh();
}
experimental_mode_toggle->setEnabled(!conditional_experimental);
experimental_mode_toggle->setDescription(e2e_description);
long_personality_setting->setEnabled(true);
} else {
@@ -225,15 +256,13 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
});
addItem(resetCalibBtn);
if (!params.getBool("Passive")) {
auto retrainingBtn = new ButtonControl(tr("Review Training Guide"), tr("REVIEW"), tr("Review the rules, features, and limitations of openpilot"));
connect(retrainingBtn, &ButtonControl::clicked, [=]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to review the training guide?"), tr("Review"), this)) {
emit reviewTrainingGuide();
}
});
addItem(retrainingBtn);
}
auto retrainingBtn = new ButtonControl(tr("Review Training Guide"), tr("REVIEW"), tr("Review the rules, features, and limitations of openpilot"));
connect(retrainingBtn, &ButtonControl::clicked, [=]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to review the training guide?"), tr("Review"), this)) {
emit reviewTrainingGuide();
}
});
addItem(retrainingBtn);
if (Hardware::TICI()) {
auto regulatoryBtn = new ButtonControl(tr("Regulatory"), tr("VIEW"), "");
@@ -258,44 +287,251 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
addItem(translateBtn);
// Delete driving footage button
auto deleteFootageBtn = new ButtonControl(tr("Delete Driving Data"), tr("DELETE"), tr("This button provides a swift and secure way to permanently delete all "
auto deleteDrivingDataBtn = new ButtonControl(tr("Delete Driving Data"), tr("DELETE"), tr("This button provides a swift and secure way to permanently delete all "
"stored driving footage and data from your device. Ideal for maintaining privacy or freeing up space.")
);
connect(deleteFootageBtn, &ButtonControl::clicked, [this]() {
connect(deleteDrivingDataBtn, &ButtonControl::clicked, [=]() {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to permanently delete all of your driving footage and data?"), tr("Delete"), this)) return;
std::thread([&] {
deleteDrivingDataBtn->setValue(tr("Deleting footage..."));
std::system("rm -rf /data/media/0/realdata");
deleteDrivingDataBtn->setValue(tr("Deleted!"));
std::this_thread::sleep_for(std::chrono::seconds(3));
deleteDrivingDataBtn->setValue("");
}).detach();
});
addItem(deleteFootageBtn);
addItem(deleteDrivingDataBtn);
// Panda flashing button
auto flashPandaBtn = new ButtonControl(tr("Flash Panda"), tr("FLASH"), "Use this button to troubleshoot and update the Panda device's firmware.");
connect(flashPandaBtn, &ButtonControl::clicked, [this]() {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to flash the Panda?"), tr("Flash"), this)) return;
QProcess process;
// Get Panda type
SubMaster &sm = *(uiState()->sm);
auto pandaStates = sm["pandaStates"].getPandaStates();
// Choose recovery script based on Panda type
if (pandaStates.size() != 0) {
auto pandaType = pandaStates[0].getPandaType();
bool isRedPanda = (pandaType == cereal::PandaState::PandaType::RED_PANDA ||
pandaType == cereal::PandaState::PandaType::RED_PANDA_V2);
QString recoveryScript = isRedPanda ? "./recover.sh" : "./recover.py";
// Run recovery script and flash Panda
process.setWorkingDirectory("/data/openpilot/panda/board");
process.start("/bin/sh", QStringList{"-c", recoveryScript});
process.waitForFinished();
auto flashPandaBtn = new ButtonControl(tr("Flash Panda"), tr("FLASH"), tr("Use this button to troubleshoot and update the Panda device's firmware."));
connect(flashPandaBtn, &ButtonControl::clicked, [=]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to flash the Panda?"), tr("Flash"), this)) {
std::thread([=]() {
flashPandaBtn->setValue(tr("Flashing..."));
QProcess process;
process.setWorkingDirectory("/data/openpilot/panda/board");
process.start("/bin/sh", QStringList{"-c", "./recover.py"});
process.waitForFinished();
process.start("/bin/sh", QStringList{"-c", "./flash.py"});
process.waitForFinished();
process.setWorkingDirectory("/data/openpilot/panda/tests");
process.start("/bin/sh", QStringList{"-c", "python reflash_internal_panda.py"});
process.waitForFinished();
Hardware::soft_reboot();
}).detach();
}
// Run the killall script as a redundancy
process.setWorkingDirectory("/data/openpilot/panda");
process.start("/bin/sh", QStringList{"-c", "pkill -f boardd; PYTHONPATH=.. python -c \"from panda import Panda; Panda().flash()\""});
process.waitForFinished();
Hardware::reboot();
});
addItem(flashPandaBtn);
// Reset toggle button
auto resetTogglesBtn = new ButtonControl(tr("Reset Toggle Settings"), tr("RESET"), tr("Reset your toggle settings back to default."));
connect(resetTogglesBtn, &ButtonControl::clicked, [=]() {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to completely reset your toggle settings? This is irreversible!"), tr("Reset"), this)) return;
std::thread([&] {
resetTogglesBtn->setValue(tr("Resetting toggles..."));
std::system("find /data/params -type f ! -name 'FrogPilotDrives' ! -name 'FrogPilotMinutes' ! -name 'FrogPilotKilometers' -exec rm {} +");
std::system("find /persist/params -type f ! -name 'FrogPilotDrives' ! -name 'FrogPilotMinutes' ! -name 'FrogPilotKilometers' -exec rm {} +");
Hardware::soft_reboot();
}).detach();
});
addItem(resetTogglesBtn);
// Backup FrogPilot
std::vector<QString> frogpilotBackupOptions{tr("Backup"), tr("Delete"), tr("Restore")};
FrogPilotButtonsControl *frogpilotBackup = new FrogPilotButtonsControl(tr("FrogPilot Backups"), tr("Backup, delete, or restore your FrogPilot backups."), "", frogpilotBackupOptions);
connect(frogpilotBackup, &FrogPilotButtonsControl::buttonClicked, [=](int id) {
QDir backupDir("/data/backups");
if (id == 0) {
QString nameSelection = InputDialog::getText(tr("Name your backup"), this, "", false, 1);
if (!nameSelection.isEmpty()) {
std::thread([=]() {
frogpilotBackup->setValue(tr("Backing up..."));
std::string fullBackupPath = backupDir.absolutePath().toStdString() + "/" + nameSelection.toStdString();
std::ostringstream commandStream;
commandStream << "mkdir -p " << std::quoted(fullBackupPath)
<< " && rsync -av /data/openpilot/ " << std::quoted(fullBackupPath + "/");
std::string command = commandStream.str();
int result = std::system(command.c_str());
if (result == 0) {
std::cout << "Backup successful to " << fullBackupPath << std::endl;
frogpilotBackup->setValue(tr("Success!"));
std::this_thread::sleep_for(std::chrono::seconds(3));
frogpilotBackup->setValue("");
} else {
std::cerr << "Backup failed with error code: " << result << std::endl;
frogpilotBackup->setValue(tr("Failed..."));
std::this_thread::sleep_for(std::chrono::seconds(3));
frogpilotBackup->setValue("");
}
}).detach();
}
} else if (id == 1) {
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QString selection = MultiOptionDialog::getSelection(tr("Select a backup to delete"), backupNames, "", this);
if (!selection.isEmpty()) {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to delete this backup?"), tr("Delete"), this)) return;
std::thread([=]() {
frogpilotBackup->setValue(tr("Deleting..."));
QDir dirToDelete(backupDir.absoluteFilePath(selection));
if (dirToDelete.removeRecursively()) {
frogpilotBackup->setValue(tr("Deleted!"));
} else {
frogpilotBackup->setValue(tr("Failed..."));
}
std::this_thread::sleep_for(std::chrono::seconds(3));
frogpilotBackup->setValue("");
}).detach();
}
} else {
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QString selection = MultiOptionDialog::getSelection(tr("Select a restore point"), backupNames, "", this);
if (!selection.isEmpty()) {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to restore this version of FrogPilot?"), tr("Restore"), this)) return;
std::thread([=]() {
frogpilotBackup->setValue(tr("Restoring..."));
std::string sourcePath = backupDir.absolutePath().toStdString() + "/" + selection.toStdString();
std::string targetPath = "/data/safe_staging/finalized";
std::string consistentFilePath = targetPath + "/.overlay_consistent";
std::ostringstream commandStream;
commandStream << "rsync -av --delete --exclude='.overlay_consistent' " << std::quoted(sourcePath + "/") << " " << std::quoted(targetPath + "/");
std::string command = commandStream.str();
int result = std::system(command.c_str());
if (result == 0) {
std::cout << "Restore successful from " << sourcePath << " to " << targetPath << std::endl;
std::ofstream consistentFile(consistentFilePath);
if (consistentFile) {
consistentFile.close();
std::cout << ".overlay_consistent file created successfully." << std::endl;
} else {
std::cerr << "Failed to create .overlay_consistent file." << std::endl;
frogpilotBackup->setValue(tr("Failed..."));
std::this_thread::sleep_for(std::chrono::seconds(3));
frogpilotBackup->setValue("");
}
Hardware::soft_reboot();
} else {
std::cerr << "Restore failed with error code: " << result << std::endl;
}
}).detach();
}
}
});
addItem(frogpilotBackup);
// Backup toggles
std::vector<QString> toggleBackupOptions{tr("Backup"), tr("Delete"), tr("Restore")};
FrogPilotButtonsControl *toggleBackup = new FrogPilotButtonsControl(tr("Toggle Backups"), tr("Backup, delete, or restore your toggle backups."), "", toggleBackupOptions);
connect(toggleBackup, &FrogPilotButtonsControl::buttonClicked, [=](int id) {
QDir backupDir("/data/toggle_backups");
if (id == 0) {
QString nameSelection = InputDialog::getText(tr("Name your backup"), this, "", false, 1);
if (!nameSelection.isEmpty()) {
std::thread([=]() {
toggleBackup->setValue(tr("Backing up..."));
std::string fullBackupPath = backupDir.absolutePath().toStdString() + "/" + nameSelection.toStdString() + "/";
std::ostringstream commandStream;
commandStream << "mkdir -p " << std::quoted(fullBackupPath)
<< " && rsync -av /data/params/d/ " << std::quoted(fullBackupPath);
std::string command = commandStream.str();
int result = std::system(command.c_str());
if (result == 0) {
std::cout << "Backup successful to " << fullBackupPath << std::endl;
toggleBackup->setValue(tr("Success!"));
std::this_thread::sleep_for(std::chrono::seconds(3));
toggleBackup->setValue("");
} else {
std::cerr << "Backup failed with error code: " << result << std::endl;
toggleBackup->setValue(tr("Failed..."));
std::this_thread::sleep_for(std::chrono::seconds(3));
toggleBackup->setValue("");
}
}).detach();
}
} else if (id == 1) {
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QString selection = MultiOptionDialog::getSelection(tr("Select a backup to delete"), backupNames, "", this);
if (!selection.isEmpty()) {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to delete this backup?"), tr("Delete"), this)) return;
std::thread([=]() {
toggleBackup->setValue(tr("Deleting..."));
QDir dirToDelete(backupDir.absoluteFilePath(selection));
if (dirToDelete.removeRecursively()) {
toggleBackup->setValue(tr("Deleted!"));
} else {
toggleBackup->setValue(tr("Failed..."));
}
std::this_thread::sleep_for(std::chrono::seconds(3));
toggleBackup->setValue("");
}).detach();
}
} else {
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QString selection = MultiOptionDialog::getSelection(tr("Select a restore point"), backupNames, "", this);
if (!selection.isEmpty()) {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to restore this toggle backup?"), tr("Restore"), this)) return;
std::thread([=]() {
toggleBackup->setValue(tr("Restoring..."));
std::string sourcePath = backupDir.absolutePath().toStdString() + "/" + selection.toStdString() + "/";
std::string targetPath = "/data/params/d/";
std::ostringstream commandStream;
commandStream << "rsync -av --delete " << std::quoted(sourcePath) << " " << std::quoted(targetPath);
std::string command = commandStream.str();
int result = std::system(command.c_str());
if (result == 0) {
std::cout << "Restore successful from " << sourcePath << " to " << targetPath << std::endl;
toggleBackup->setValue(tr("Success!"));
paramsMemory.putBool("FrogPilotTogglesUpdated", true);
std::this_thread::sleep_for(std::chrono::seconds(3));
toggleBackup->setValue("");
paramsMemory.putBool("FrogPilotTogglesUpdated", false);
} else {
std::cerr << "Restore failed with error code: " << result << std::endl;
toggleBackup->setValue(tr("Failed..."));
std::this_thread::sleep_for(std::chrono::seconds(3));
toggleBackup->setValue("");
}
}).detach();
}
}
});
addItem(toggleBackup);
auto lockDoorsButton = new ButtonControl(tr("Lock Doors"), tr("LOCK"), tr("Use this button to lock the doors on Toyota/Lexus vehicles."));
connect(lockDoorsButton, &ButtonControl::clicked, [this]() {
QProcess *process = new QProcess(this);
QString scriptPath = "/data/openpilot/frogpilot/controls/lib/lock_doors.py";
QStringList arguments{"--lock"};
process->start("python3", QStringList() << scriptPath << arguments);
});
addItem(lockDoorsButton);
QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
for (auto btn : findChildren<ButtonControl *>()) {
btn->setEnabled(offroad);
@@ -311,6 +547,11 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
power_layout->addWidget(reboot_btn);
QObject::connect(reboot_btn, &QPushButton::clicked, this, &DevicePanel::reboot);
QPushButton *softreboot_btn = new QPushButton(tr("Soft Reboot"));
softreboot_btn->setObjectName("softreboot_btn");
power_layout->addWidget(softreboot_btn);
QObject::connect(softreboot_btn, &QPushButton::clicked, this, &DevicePanel::softreboot);
QPushButton *poweroff_btn = new QPushButton(tr("Power Off"));
poweroff_btn->setObjectName("poweroff_btn");
power_layout->addWidget(poweroff_btn);
@@ -321,6 +562,8 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
}
setStyleSheet(R"(
#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; }
@@ -366,6 +609,18 @@ void DevicePanel::reboot() {
}
}
void DevicePanel::softreboot() {
if (!uiState()->engaged()) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to soft reboot?"), tr("Soft Reboot"), this)) {
if (!uiState()->engaged()) {
params.putBool("DoSoftReboot", true);
}
}
} else {
ConfirmationDialog::alert(tr("Disengage to Soft Reboot"), this);
}
}
void DevicePanel::poweroff() {
if (!uiState()->engaged()) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to power off?"), tr("Power Off"), this)) {
@@ -379,6 +634,15 @@ void DevicePanel::poweroff() {
}
}
void SettingsWindow::hideEvent(QHideEvent *event) {
closeParentToggle();
parentToggleOpen = false;
subParentToggleOpen = false;
previousScrollPosition = 0;
}
void SettingsWindow::showEvent(QShowEvent *event) {
setCurrentPanel(0);
}
@@ -396,33 +660,34 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
// setup two main layouts
sidebar_widget = new QWidget;
QVBoxLayout *sidebar_layout = new QVBoxLayout(sidebar_widget);
sidebar_layout->setMargin(0);
panel_widget = new QStackedWidget();
// close button
QPushButton *close_btn = new QPushButton(tr("← Back"));
close_btn->setStyleSheet(R"(
QPushButton {
font-size: 50px;
padding-bottom: 0px;
border 1px grey solid;
color: white;
border-radius: 25px;
background-color: #292929;
background: #292929;
font-size: 50px;
font-weight: 500;
}
QPushButton:pressed {
background-color: #3B3B3B;
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, [this]() {
if (frogPilotTogglesOpen) {
frogPilotTogglesOpen = false;
this->closeParentToggle();
if (subParentToggleOpen) {
closeSubParentToggle();
subParentToggleOpen = false;
} else if (parentToggleOpen) {
closeParentToggle();
parentToggleOpen = false;
} else {
this->closeSettings();
closeSettings();
}
});
@@ -435,20 +700,19 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription);
QObject::connect(toggles, &TogglesPanel::updateMetric, this, &SettingsWindow::updateMetric);
// FrogPilotControlsPanel *frogpilotControls = new FrogPilotControlsPanel(this);
// QObject::connect(frogpilotControls, &FrogPilotControlsPanel::closeParentToggle, this, [this]() {frogPilotTogglesOpen = false;});
// QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openParentToggle, this, [this]() {frogPilotTogglesOpen = true;});
FrogPilotControlsPanel *frogpilotControls = new FrogPilotControlsPanel(this);
QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openSubParentToggle, this, [this]() {subParentToggleOpen = true;});
QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openParentToggle, this, [this]() {parentToggleOpen = true;});
FrogPilotVisualsPanel *frogpilotVisuals = new FrogPilotVisualsPanel(this);
QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::closeParentToggle, this, [this]() {frogPilotTogglesOpen = false;});
QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::openParentToggle, this, [this]() {frogPilotTogglesOpen = true;});
QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::openParentToggle, this, [this]() {parentToggleOpen = true;});
QList<QPair<QString, QWidget *>> panels = {
{tr("Device"), device},
{tr("Network"), new Networking(this)},
{tr("Toggles"), toggles},
{tr("Software"), new SoftwarePanel(this)},
// {tr("Controls"), frogpilotControls},
{tr("Controls"), frogpilotControls},
{tr("Navigation"), new FrogPilotNavigationPanel(this)},
{tr("Vehicles"), new FrogPilotVehiclesPanel(this)},
{tr("Visuals"), frogpilotVisuals},
@@ -484,21 +748,24 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
ScrollView *panel_frame = new ScrollView(panel, this);
panel_widget->addWidget(panel_frame);
if (name == tr("Controls") || name == tr("Visuals")) {
if (name == tr("Controls")) {
QScrollBar *scrollbar = panel_frame->verticalScrollBar();
QObject::connect(scrollbar, &QScrollBar::valueChanged, this, [this](int value) {
if (!frogPilotTogglesOpen) {
if (!parentToggleOpen) {
previousScrollPosition = value;
}
});
QObject::connect(scrollbar, &QScrollBar::rangeChanged, this, [this, panel_frame]() {
panel_frame->restorePosition(previousScrollPosition);
if (!parentToggleOpen) {
panel_frame->restorePosition(previousScrollPosition);
}
});
}
QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() {
closeParentToggle();
previousScrollPosition = 0;
btn->setChecked(true);
panel_widget->setCurrentWidget(w);

27
selfdrive/ui/qt/offroad/settings.h Executable file → Normal file
View File

@@ -11,6 +11,7 @@
#include <QWidget>
#include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/widgets/controls.h"
@@ -25,6 +26,9 @@ public:
protected:
void showEvent(QShowEvent *event) override;
// FrogPilot widgets
void hideEvent(QHideEvent *event) override;
signals:
void closeSettings();
void reviewTrainingGuide();
@@ -33,7 +37,9 @@ signals:
// FrogPilot signals
void closeParentToggle();
void closeSubParentToggle();
void updateMetric();
private:
QPushButton *sidebar_alert_widget;
QWidget *sidebar_widget;
@@ -41,7 +47,9 @@ private:
QStackedWidget *panel_widget;
// FrogPilot variables
bool frogPilotTogglesOpen;
bool parentToggleOpen;
bool subParentToggleOpen;
int previousScrollPosition;
};
@@ -56,10 +64,14 @@ signals:
private slots:
void poweroff();
void reboot();
void softreboot();
void updateCalibDescription();
private:
Params params;
// FrogPilot variables
Params paramsMemory{"/dev/shm/params"};
};
class TogglesPanel : public ListWidget {
@@ -75,6 +87,9 @@ signals:
public slots:
void expandToggleDescription(const QString &param);
private slots:
void updateState(const UIState &s);
private:
Params params;
std::map<std::string, ParamControl*> toggles;
@@ -97,7 +112,6 @@ private:
QLabel *onroadLbl;
LabelControl *versionLbl;
ButtonControl *errorLogBtn;
ButtonControl *installBtn;
ButtonControl *downloadBtn;
ButtonControl *targetBranchBtn;
@@ -106,11 +120,6 @@ private:
ParamWatcher *fs_watch;
// FrogPilot variables
void automaticUpdate();
ButtonControl *updateTime;
int deviceShutdown;
int schedule;
int time;
Params paramsMemory{"/dev/shm/params"};
UIScene &scene;
};

152
selfdrive/ui/qt/offroad/software_settings.cc Executable file → Normal file
View File

@@ -2,8 +2,6 @@
#include <cassert>
#include <cmath>
#include <iomanip>
#include <sstream>
#include <string>
#include <QDebug>
@@ -18,13 +16,14 @@
#include "selfdrive/ui/qt/widgets/input.h"
#include "system/hardware/hw.h"
#include "selfdrive/frogpilot/ui/qt/widgets/frogpilot_controls.h"
void SoftwarePanel::checkForUpdates() {
std::system("pkill -SIGUSR1 -f selfdrive.updated");
std::system("pkill -SIGUSR1 -f selfdrive.updated.updated");
}
SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
onroadLbl = new QLabel(tr("Updates are only downloaded while the car is off."));
SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent), scene(uiState()->scene) {
onroadLbl = new QLabel(tr("Updates are only downloaded while the car is off or in park."));
onroadLbl->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left; padding-top: 30px; padding-bottom: 30px;");
addItem(onroadLbl);
@@ -32,40 +31,17 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
versionLbl = new LabelControl(tr("Current Version"), "");
addItem(versionLbl);
// update scheduler
std::vector<QString> scheduleOptions{tr("Manually"), tr("Daily"), tr("Weekly")};
ButtonParamControl *preferredSchedule = new ButtonParamControl("UpdateSchedule", tr("Update Scheduler"),
tr("Choose the update frequency for FrogPilot's automatic updates.\n\n"
"This feature will handle the download, installation, and device reboot for a seamless 'Set and Forget' experience.\n\n"
"Weekly updates start at midnight every Sunday."),
"",
scheduleOptions);
schedule = params.getInt("UpdateSchedule");
addItem(preferredSchedule);
updateTime = new ButtonControl(tr("Update Time"), tr("SELECT"));
QStringList hours;
for (int h = 0; h < 24; h++) {
int displayHour = (h % 12 == 0) ? 12 : h % 12;
QString meridiem = (h < 12) ? "AM" : "PM";
hours << QString("%1:00 %2").arg(displayHour).arg(meridiem)
<< QString("%1:30 %2").arg(displayHour).arg(meridiem);
}
QObject::connect(updateTime, &ButtonControl::clicked, [=]() {
int currentHourIndex = params.getInt("UpdateTime");
QString currentHourLabel = hours[currentHourIndex];
QString selection = MultiOptionDialog::getSelection(tr("Select a time to automatically update"), hours, currentHourLabel, this);
if (!selection.isEmpty()) {
int selectedHourIndex = hours.indexOf(selection);
params.putInt("UpdateTime", selectedHourIndex);
updateTime->setValue(selection);
}
// automatic updates toggle
ParamControl *automaticUpdatesToggle = new ParamControl("AutomaticUpdates", tr("Automatically Update FrogPilot"),
tr("FrogPilot will automatically update itself and it's assets when you're offroad and connected to Wi-Fi."), "");
connect(automaticUpdatesToggle, &ToggleControl::toggleFlipped, [this]() {
std::thread([this]() {
paramsMemory.putBool("FrogPilotTogglesUpdated", true);
std::this_thread::sleep_for(std::chrono::seconds(1));
paramsMemory.putBool("FrogPilotTogglesUpdated", false);
}).detach();
});
time = params.getInt("UpdateTime");
deviceShutdown = params.getInt("DeviceShutdown") * 3600;
updateTime->setValue(hours[time]);
addItem(updateTime);
addItem(automaticUpdatesToggle);
// download update btn
downloadBtn = new ButtonControl(tr("Download"), tr("CHECK"));
@@ -74,8 +50,9 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
if (downloadBtn->text() == tr("CHECK")) {
checkForUpdates();
} else {
std::system("pkill -SIGHUP -f selfdrive.updated");
std::system("pkill -SIGHUP -f selfdrive.updated.updated");
}
paramsMemory.putBool("ManualUpdateInitiated", true);
});
addItem(downloadBtn);
@@ -92,6 +69,11 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
connect(targetBranchBtn, &ButtonControl::clicked, [=]() {
auto current = params.get("GitBranch");
QStringList branches = QString::fromStdString(params.get("UpdaterAvailableBranches")).split(",");
if (!Params("/persist/params").getBool("FrogsGoMoo")) {
branches.removeAll("FrogPilot-Development");
branches.removeAll("FrogPilot-New");
branches.removeAll("MAKE-PRS-HERE");
}
for (QString b : {current.c_str(), "devel-staging", "devel", "nightly", "master-ci", "master"}) {
auto i = branches.indexOf(b);
if (i >= 0) {
@@ -122,7 +104,7 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
addItem(uninstallBtn);
// error log button
errorLogBtn = new ButtonControl(tr("Error Log"), tr("VIEW"), "View the error log for debugging purposes when openpilot crashes.");
auto errorLogBtn = new ButtonControl(tr("Error Log"), tr("VIEW"), tr("View the error log for openpilot crashes."));
connect(errorLogBtn, &ButtonControl::clicked, [=]() {
std::string txt = util::read_file("/data/community/crashes/error.txt");
ConfirmationDialog::rich(QString::fromStdString(txt), this);
@@ -131,8 +113,6 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
fs_watch = new ParamWatcher(this);
QObject::connect(fs_watch, &ParamWatcher::paramChanged, [=](const QString &param_name, const QString &param_value) {
schedule = params.getInt("UpdateSchedule");
time = params.getInt("UpdateTime");
updateLabels();
});
@@ -141,8 +121,6 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
updateLabels();
});
QObject::connect(uiState(), &UIState::uiUpdate, this, &SoftwarePanel::automaticUpdate);
updateLabels();
}
@@ -160,16 +138,15 @@ void SoftwarePanel::updateLabels() {
fs_watch->addParam("UpdaterState");
fs_watch->addParam("UpdateAvailable");
fs_watch->addParam("UpdateSchedule");
fs_watch->addParam("UpdateTime");
if (!isVisible()) {
return;
}
// updater only runs offroad
onroadLbl->setVisible(is_onroad);
downloadBtn->setVisible(!is_onroad);
// updater only runs offroad or when parked
bool parked = scene.parked;
onroadLbl->setVisible(is_onroad && !parked);
downloadBtn->setVisible(!is_onroad || parked);
// download update
QString updater_state = QString::fromStdString(params.get("UpdaterState"));
@@ -201,82 +178,9 @@ void SoftwarePanel::updateLabels() {
versionLbl->setText(QString::fromStdString(params.get("UpdaterCurrentDescription")));
versionLbl->setDescription(QString::fromStdString(params.get("UpdaterCurrentReleaseNotes")));
installBtn->setVisible(!is_onroad && params.getBool("UpdateAvailable"));
installBtn->setVisible((!is_onroad || parked) && params.getBool("UpdateAvailable"));
installBtn->setValue(QString::fromStdString(params.get("UpdaterNewDescription")));
installBtn->setDescription(QString::fromStdString(params.get("UpdaterNewReleaseNotes")));
updateTime->setVisible(params.getInt("UpdateSchedule"));
update();
}
void SoftwarePanel::automaticUpdate() {
static int timer = 0;
static std::chrono::system_clock::time_point lastOffroadTime;
if (!is_onroad) {
timer = (timer == 0) ? 0 : std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - lastOffroadTime).count();
lastOffroadTime = std::chrono::system_clock::now();
} else {
timer = 0;
}
bool isWifiConnected = (*uiState()->sm)["deviceState"].getDeviceState().getNetworkType() == cereal::DeviceState::NetworkType::WIFI;
if (schedule == 0 || is_onroad || !isWifiConnected || isVisible()) return;
static bool isDownloadCompleted = false;
if (isDownloadCompleted && params.getBool("UpdateAvailable")) {
params.putBool(timer > deviceShutdown ? "DoShutdown" : "DoReboot", true);
return;
}
int updateHour = time / 2;
int updateMinute = (time % 2) * 30;
if (updateHour >= 1 && updateHour <= 11 && time >= 24) {
updateHour += 12;
} else if (updateHour == 12 && time < 24) {
updateHour = 0;
}
std::time_t currentTimeT = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::tm now = *std::localtime(&currentTimeT);
static bool updateCheckedToday = false;
static int lastCheckedDay = now.tm_yday;
if (lastCheckedDay != now.tm_yday) {
updateCheckedToday = false;
lastCheckedDay = now.tm_yday;
}
if (now.tm_hour != updateHour || now.tm_min < updateMinute) return;
std::string lastUpdateStr = params.get("Updated");
std::tm lastUpdate = {};
std::istringstream iss(lastUpdateStr);
if (iss >> std::get_time(&lastUpdate, "%Y-%m-%d %H:%M:%S")) {
lastUpdate.tm_year -= 1900;
lastUpdate.tm_mon -= 1;
}
std::time_t lastUpdateTimeT = std::mktime(&lastUpdate);
if (lastUpdate.tm_yday == now.tm_yday) {
return;
}
if (!isDownloadCompleted) {
std::chrono::hours durationSinceLastUpdate = std::chrono::duration_cast<std::chrono::hours>(std::chrono::system_clock::now() - std::chrono::system_clock::from_time_t(lastUpdateTimeT));
int daysSinceLastUpdate = durationSinceLastUpdate.count() / 24;
if ((schedule == 1 && daysSinceLastUpdate >= 1) || (schedule == 2 && (now.tm_yday / 7) != (std::localtime(&lastUpdateTimeT)->tm_yday / 7))) {
if (downloadBtn->text() == tr("CHECK") && !updateCheckedToday) {
checkForUpdates();
updateCheckedToday = true;
} else {
std::system("pkill -SIGHUP -f selfdrive.updated");
isDownloadCompleted = true;
}
}
}
}

0
selfdrive/ui/qt/offroad/text_view.qml Executable file → Normal file
View File