wip
This commit is contained in:
0
selfdrive/ui/qt/offroad/driverview.cc
Executable file → Normal file
0
selfdrive/ui/qt/offroad/driverview.cc
Executable file → Normal file
0
selfdrive/ui/qt/offroad/driverview.h
Executable file → Normal file
0
selfdrive/ui/qt/offroad/driverview.h
Executable file → Normal file
0
selfdrive/ui/qt/offroad/experimental_mode.cc
Executable file → Normal file
0
selfdrive/ui/qt/offroad/experimental_mode.cc
Executable file → Normal file
0
selfdrive/ui/qt/offroad/experimental_mode.h
Executable file → Normal file
0
selfdrive/ui/qt/offroad/experimental_mode.h
Executable file → Normal file
14
selfdrive/ui/qt/offroad/onboarding.cc
Executable file → Normal file
14
selfdrive/ui/qt/offroad/onboarding.cc
Executable file → Normal 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
0
selfdrive/ui/qt/offroad/onboarding.h
Executable file → Normal file
385
selfdrive/ui/qt/offroad/settings.cc
Executable file → Normal file
385
selfdrive/ui/qt/offroad/settings.cc
Executable file → Normal 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 ¶m) {
|
||||
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
27
selfdrive/ui/qt/offroad/settings.h
Executable file → Normal 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 ¶m);
|
||||
|
||||
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
152
selfdrive/ui/qt/offroad/software_settings.cc
Executable file → Normal 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 ¶m_name, const QString ¶m_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(¤tTimeT);
|
||||
|
||||
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
0
selfdrive/ui/qt/offroad/text_view.qml
Executable file → Normal file
Reference in New Issue
Block a user