mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-03 14:43:48 +08:00
FrogPilot 0.9.7
This commit is contained in:
@@ -0,0 +1,437 @@
|
||||
#include <filesystem>
|
||||
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/data_settings.h"
|
||||
|
||||
FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
|
||||
ButtonControl *deleteDrivingDataBtn = new ButtonControl(tr("Delete Driving Footage and Data"), tr("DELETE"), tr("Permanently deletes all stored driving footage and data from your device. Ideal for maintaining privacy or freeing up space."));
|
||||
QObject::connect(deleteDrivingDataBtn, &ButtonControl::clicked, [=]() {
|
||||
QDir realdataDir("/data/media/0/realdata");
|
||||
|
||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to permanently delete all of your driving footage and data?"), tr("Delete"), this)) {
|
||||
std::thread([=]() mutable {
|
||||
parent->keepScreenOn = true;
|
||||
|
||||
deleteDrivingDataBtn->setEnabled(false);
|
||||
deleteDrivingDataBtn->setValue(tr("Deleting..."));
|
||||
|
||||
realdataDir.removeRecursively();
|
||||
realdataDir.mkpath(".");
|
||||
|
||||
deleteDrivingDataBtn->setValue(tr("Deleted!"));
|
||||
util::sleep_for(2500);
|
||||
deleteDrivingDataBtn->setEnabled(true);
|
||||
deleteDrivingDataBtn->setValue("");
|
||||
|
||||
parent->keepScreenOn = false;
|
||||
}).detach();
|
||||
}
|
||||
});
|
||||
addItem(deleteDrivingDataBtn);
|
||||
|
||||
FrogPilotButtonsControl *screenRecordingsBtn = new FrogPilotButtonsControl(tr("Screen Recordings"), tr("Manage your screen recordings."), {tr("DELETE"), tr("DELETE ALL"), tr("RENAME")});
|
||||
QObject::connect(screenRecordingsBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) {
|
||||
QDir recordingsDir("/data/media/screen_recordings");
|
||||
QStringList recordingsNames = recordingsDir.entryList(QDir::Files | QDir::NoDotAndDotDot);
|
||||
|
||||
if (id == 0) {
|
||||
QString selection = MultiOptionDialog::getSelection(tr("Select a recording to delete"), recordingsNames, "", this);
|
||||
if (!selection.isEmpty()) {
|
||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to delete this recording?"), tr("Delete"), this)) {
|
||||
std::thread([=]() {
|
||||
parent->keepScreenOn = true;
|
||||
|
||||
screenRecordingsBtn->setEnabled(false);
|
||||
screenRecordingsBtn->setValue(tr("Deleting..."));
|
||||
|
||||
screenRecordingsBtn->setVisibleButton(1, false);
|
||||
screenRecordingsBtn->setVisibleButton(2, false);
|
||||
|
||||
QFile::remove(recordingsDir.absoluteFilePath(selection));
|
||||
|
||||
screenRecordingsBtn->setValue(tr("Deleted!"));
|
||||
util::sleep_for(2500);
|
||||
screenRecordingsBtn->setEnabled(true);
|
||||
screenRecordingsBtn->setValue("");
|
||||
|
||||
screenRecordingsBtn->setVisibleButton(1, true);
|
||||
screenRecordingsBtn->setVisibleButton(2, true);
|
||||
|
||||
parent->keepScreenOn = false;
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (id == 1) {
|
||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to delete all screen recordings?"), tr("Delete All"), this)) {
|
||||
std::thread([=]() mutable {
|
||||
parent->keepScreenOn = true;
|
||||
|
||||
screenRecordingsBtn->setEnabled(false);
|
||||
screenRecordingsBtn->setValue(tr("Deleting..."));
|
||||
|
||||
screenRecordingsBtn->setVisibleButton(0, false);
|
||||
screenRecordingsBtn->setVisibleButton(2, false);
|
||||
|
||||
recordingsDir.removeRecursively();
|
||||
recordingsDir.mkpath(".");
|
||||
|
||||
screenRecordingsBtn->setValue(tr("Deleted!"));
|
||||
util::sleep_for(2500);
|
||||
screenRecordingsBtn->setEnabled(true);
|
||||
screenRecordingsBtn->setValue("");
|
||||
|
||||
screenRecordingsBtn->setVisibleButton(0, true);
|
||||
screenRecordingsBtn->setVisibleButton(2, true);
|
||||
|
||||
parent->keepScreenOn = false;
|
||||
}).detach();
|
||||
}
|
||||
|
||||
} else if (id == 2) {
|
||||
QString selection = MultiOptionDialog::getSelection(tr("Select a recording to rename"), recordingsNames, "", this);
|
||||
if (!selection.isEmpty()) {
|
||||
QString newName = InputDialog::getText(tr("Enter a new name"), this, tr("Rename Recording")).trimmed().replace(" ", "_");
|
||||
if (!newName.isEmpty()) {
|
||||
if (recordingsNames.contains(newName)) {
|
||||
ConfirmationDialog::alert(tr("A recording with this name already exists. Please choose a different name."), this);
|
||||
return;
|
||||
}
|
||||
std::thread([=]() {
|
||||
parent->keepScreenOn = true;
|
||||
|
||||
screenRecordingsBtn->setEnabled(false);
|
||||
screenRecordingsBtn->setValue(tr("Renaming..."));
|
||||
|
||||
screenRecordingsBtn->setVisibleButton(0, false);
|
||||
screenRecordingsBtn->setVisibleButton(1, false);
|
||||
|
||||
QString newPath = recordingsDir.absoluteFilePath(newName);
|
||||
QString oldPath = recordingsDir.absoluteFilePath(selection);
|
||||
QFile::rename(oldPath, newPath);
|
||||
|
||||
screenRecordingsBtn->setValue(tr("Renamed!"));
|
||||
util::sleep_for(2500);
|
||||
screenRecordingsBtn->setEnabled(true);
|
||||
screenRecordingsBtn->setValue("");
|
||||
|
||||
screenRecordingsBtn->setVisibleButton(0, true);
|
||||
screenRecordingsBtn->setVisibleButton(1, true);
|
||||
|
||||
parent->keepScreenOn = false;
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
addItem(screenRecordingsBtn);
|
||||
|
||||
FrogPilotButtonsControl *frogpilotBackupBtn = new FrogPilotButtonsControl(tr("FrogPilot Backups"), tr("Manage your FrogPilot backups."), {tr("BACKUP"), tr("DELETE"), tr("DELETE ALL"), tr("RESTORE")});
|
||||
QObject::connect(frogpilotBackupBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) {
|
||||
QDir backupDir("/data/backups");
|
||||
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, QDir::Name).filter(QRegularExpression("^(?!.*_in_progress$).*$"));
|
||||
|
||||
if (id == 0) {
|
||||
QString nameSelection = InputDialog::getText(tr("Name your backup"), this, "", false, 1).trimmed().replace(" ", "_");
|
||||
if (!nameSelection.isEmpty()) {
|
||||
if (backupNames.contains(nameSelection)) {
|
||||
ConfirmationDialog::alert(tr("A backup with this name already exists. Please choose a different name."), this);
|
||||
return;
|
||||
}
|
||||
bool compressed = FrogPilotConfirmationDialog::yesorno(tr("Do you want to compress this backup? The final result will be 2.25x smaller and will run in the background, but can take 10+ minutes."), this);
|
||||
std::thread([=]() {
|
||||
parent->keepScreenOn = true;
|
||||
|
||||
frogpilotBackupBtn->setEnabled(false);
|
||||
frogpilotBackupBtn->setValue(tr("Backing up..."));
|
||||
|
||||
frogpilotBackupBtn->setVisibleButton(1, false);
|
||||
frogpilotBackupBtn->setVisibleButton(2, false);
|
||||
frogpilotBackupBtn->setVisibleButton(3, false);
|
||||
|
||||
std::string fullBackupPath = backupDir.filePath(nameSelection).toStdString();
|
||||
std::string inProgressBackupPath = fullBackupPath + "_in_progress";
|
||||
|
||||
std::filesystem::create_directories(inProgressBackupPath);
|
||||
std::system(("rsync -av /data/openpilot/ " + inProgressBackupPath + "/").c_str());
|
||||
|
||||
if (compressed) {
|
||||
frogpilotBackupBtn->setValue(tr("Compressing..."));
|
||||
|
||||
std::system(("tar -czf " + fullBackupPath + "_in_progress.tar.gz -C " + inProgressBackupPath + " .").c_str());
|
||||
std::filesystem::remove_all(inProgressBackupPath);
|
||||
std::filesystem::rename(fullBackupPath + "_in_progress.tar.gz", fullBackupPath + ".tar.gz");
|
||||
} else {
|
||||
std::filesystem::rename(inProgressBackupPath, fullBackupPath);
|
||||
}
|
||||
|
||||
frogpilotBackupBtn->setValue(tr("Backup created!"));
|
||||
util::sleep_for(2500);
|
||||
frogpilotBackupBtn->setEnabled(true);
|
||||
frogpilotBackupBtn->setValue("");
|
||||
|
||||
frogpilotBackupBtn->setVisibleButton(1, true);
|
||||
frogpilotBackupBtn->setVisibleButton(2, true);
|
||||
frogpilotBackupBtn->setVisibleButton(3, true);
|
||||
|
||||
parent->keepScreenOn = false;
|
||||
}).detach();
|
||||
}
|
||||
|
||||
} else if (id == 1) {
|
||||
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)) {
|
||||
std::thread([=]() {
|
||||
parent->keepScreenOn = true;
|
||||
|
||||
frogpilotBackupBtn->setEnabled(false);
|
||||
frogpilotBackupBtn->setValue(tr("Deleting..."));
|
||||
|
||||
frogpilotBackupBtn->setVisibleButton(0, false);
|
||||
frogpilotBackupBtn->setVisibleButton(2, false);
|
||||
frogpilotBackupBtn->setVisibleButton(3, false);
|
||||
|
||||
QDir dirToDelete(backupDir.filePath(selection));
|
||||
if (selection.endsWith(".tar.gz")) {
|
||||
QFile::remove(dirToDelete.absolutePath());
|
||||
} else {
|
||||
dirToDelete.removeRecursively();
|
||||
}
|
||||
|
||||
frogpilotBackupBtn->setValue(tr("Deleted!"));
|
||||
util::sleep_for(2500);
|
||||
frogpilotBackupBtn->setEnabled(true);
|
||||
frogpilotBackupBtn->setValue("");
|
||||
|
||||
frogpilotBackupBtn->setVisibleButton(0, true);
|
||||
frogpilotBackupBtn->setVisibleButton(2, true);
|
||||
frogpilotBackupBtn->setVisibleButton(3, true);
|
||||
|
||||
parent->keepScreenOn = false;
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (id == 2) {
|
||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to delete all FrogPilot backups?"), tr("Delete All"), this)) {
|
||||
std::thread([=]() mutable {
|
||||
parent->keepScreenOn = true;
|
||||
|
||||
frogpilotBackupBtn->setEnabled(false);
|
||||
frogpilotBackupBtn->setValue(tr("Deleting..."));
|
||||
|
||||
frogpilotBackupBtn->setVisibleButton(0, false);
|
||||
frogpilotBackupBtn->setVisibleButton(1, false);
|
||||
frogpilotBackupBtn->setVisibleButton(3, false);
|
||||
|
||||
backupDir.removeRecursively();
|
||||
backupDir.mkpath(".");
|
||||
|
||||
frogpilotBackupBtn->setValue(tr("Deleted!"));
|
||||
util::sleep_for(2500);
|
||||
frogpilotBackupBtn->setEnabled(true);
|
||||
frogpilotBackupBtn->setValue("");
|
||||
|
||||
frogpilotBackupBtn->setVisibleButton(0, true);
|
||||
frogpilotBackupBtn->setVisibleButton(1, true);
|
||||
frogpilotBackupBtn->setVisibleButton(3, true);
|
||||
|
||||
parent->keepScreenOn = false;
|
||||
}).detach();
|
||||
}
|
||||
|
||||
} else if (id == 3) {
|
||||
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)) {
|
||||
std::thread([=]() {
|
||||
parent->keepScreenOn = true;
|
||||
|
||||
frogpilotBackupBtn->setEnabled(false);
|
||||
frogpilotBackupBtn->setValue(tr("Restoring..."));
|
||||
|
||||
frogpilotBackupBtn->setVisibleButton(0, false);
|
||||
frogpilotBackupBtn->setVisibleButton(1, false);
|
||||
frogpilotBackupBtn->setVisibleButton(2, false);
|
||||
|
||||
std::string extractDirectory = "/data/restore_temp";
|
||||
std::string sourcePath = backupDir.filePath(selection).toStdString();
|
||||
std::string targetPath = "/data/safe_staging/finalized";
|
||||
|
||||
if (selection.endsWith(".tar.gz")) {
|
||||
frogpilotBackupBtn->setValue(tr("Extracting..."));
|
||||
|
||||
std::filesystem::create_directories(extractDirectory);
|
||||
std::system(("tar --strip-components=1 -xzf " + sourcePath + " -C " + extractDirectory).c_str());
|
||||
|
||||
sourcePath = extractDirectory;
|
||||
}
|
||||
|
||||
std::filesystem::create_directories(targetPath);
|
||||
std::system(("rsync -av --delete -l " + sourcePath + "/ " + targetPath + "/").c_str());
|
||||
|
||||
std::filesystem::path overlayFile = targetPath + "/.overlay_consistent";
|
||||
std::ofstream(overlayFile).close();
|
||||
|
||||
if (std::filesystem::exists(extractDirectory)) {
|
||||
std::filesystem::remove_all(extractDirectory);
|
||||
}
|
||||
|
||||
params.putBool("AutomaticUpdates", false);
|
||||
|
||||
frogpilotBackupBtn->setValue(tr("Restored!"));
|
||||
util::sleep_for(2500);
|
||||
frogpilotBackupBtn->setValue(tr("Rebooting..."));
|
||||
util::sleep_for(2500);
|
||||
|
||||
Hardware::reboot();
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
addItem(frogpilotBackupBtn);
|
||||
|
||||
FrogPilotButtonsControl *toggleBackupBtn = new FrogPilotButtonsControl(tr("Toggle Backups"), tr("Manage your toggle backups."), {tr("BACKUP"), tr("DELETE"), tr("DELETE ALL"), tr("RESTORE")});
|
||||
QObject::connect(toggleBackupBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) {
|
||||
QDir backupDir("/data/toggle_backups");
|
||||
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, QDir::Name).filter(QRegularExpression("^(?!.*_in_progress$).*$"));
|
||||
|
||||
if (id == 0) {
|
||||
QString nameSelection = InputDialog::getText(tr("Name your backup"), this, "", false, 1).trimmed().replace(" ", "_");
|
||||
if (!nameSelection.isEmpty()) {
|
||||
if (backupNames.contains(nameSelection)) {
|
||||
ConfirmationDialog::alert(tr("A backup with this name already exists. Please choose a different name."), this);
|
||||
return;
|
||||
}
|
||||
std::thread([=]() {
|
||||
parent->keepScreenOn = true;
|
||||
|
||||
toggleBackupBtn->setEnabled(false);
|
||||
toggleBackupBtn->setValue(tr("Backing up..."));
|
||||
|
||||
toggleBackupBtn->setVisibleButton(1, false);
|
||||
toggleBackupBtn->setVisibleButton(2, false);
|
||||
toggleBackupBtn->setVisibleButton(3, false);
|
||||
|
||||
std::string fullBackupPath = backupDir.filePath(nameSelection).toStdString();
|
||||
std::string inProgressBackupPath = fullBackupPath + "_in_progress";
|
||||
|
||||
std::filesystem::create_directories(inProgressBackupPath);
|
||||
|
||||
std::system(("rsync -av /data/params/d/ " + inProgressBackupPath + "/").c_str());
|
||||
|
||||
std::filesystem::rename(inProgressBackupPath, fullBackupPath);
|
||||
|
||||
toggleBackupBtn->setValue(tr("Backup created!"));
|
||||
util::sleep_for(2500);
|
||||
toggleBackupBtn->setEnabled(true);
|
||||
toggleBackupBtn->setValue("");
|
||||
|
||||
toggleBackupBtn->setVisibleButton(1, true);
|
||||
toggleBackupBtn->setVisibleButton(2, true);
|
||||
toggleBackupBtn->setVisibleButton(3, true);
|
||||
|
||||
parent->keepScreenOn = false;
|
||||
}).detach();
|
||||
}
|
||||
|
||||
} else if (id == 1) {
|
||||
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)) {
|
||||
std::thread([=]() {
|
||||
parent->keepScreenOn = true;
|
||||
|
||||
toggleBackupBtn->setEnabled(false);
|
||||
toggleBackupBtn->setValue(tr("Deleting..."));
|
||||
|
||||
toggleBackupBtn->setVisibleButton(0, false);
|
||||
toggleBackupBtn->setVisibleButton(2, false);
|
||||
toggleBackupBtn->setVisibleButton(3, false);
|
||||
|
||||
QDir dirToDelete(backupDir.filePath(selection));
|
||||
dirToDelete.removeRecursively();
|
||||
|
||||
toggleBackupBtn->setValue(tr("Deleted!"));
|
||||
util::sleep_for(2500);
|
||||
toggleBackupBtn->setEnabled(true);
|
||||
toggleBackupBtn->setValue("");
|
||||
|
||||
toggleBackupBtn->setVisibleButton(0, true);
|
||||
toggleBackupBtn->setVisibleButton(2, true);
|
||||
toggleBackupBtn->setVisibleButton(3, true);
|
||||
|
||||
parent->keepScreenOn = false;
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (id == 2) {
|
||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to delete all toggle backups?"), tr("Delete All"), this)) {
|
||||
std::thread([=]() mutable {
|
||||
parent->keepScreenOn = true;
|
||||
|
||||
toggleBackupBtn->setEnabled(false);
|
||||
toggleBackupBtn->setValue(tr("Deleting..."));
|
||||
|
||||
toggleBackupBtn->setVisibleButton(0, false);
|
||||
toggleBackupBtn->setVisibleButton(1, false);
|
||||
toggleBackupBtn->setVisibleButton(3, false);
|
||||
|
||||
backupDir.removeRecursively();
|
||||
backupDir.mkpath(".");
|
||||
|
||||
toggleBackupBtn->setValue(tr("Deleted!"));
|
||||
util::sleep_for(2500);
|
||||
toggleBackupBtn->setEnabled(true);
|
||||
toggleBackupBtn->setValue("");
|
||||
|
||||
toggleBackupBtn->setVisibleButton(0, true);
|
||||
toggleBackupBtn->setVisibleButton(1, true);
|
||||
toggleBackupBtn->setVisibleButton(3, true);
|
||||
|
||||
parent->keepScreenOn = false;
|
||||
}).detach();
|
||||
}
|
||||
|
||||
} else if (id == 3) {
|
||||
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)) {
|
||||
std::thread([=]() {
|
||||
parent->keepScreenOn = true;
|
||||
|
||||
toggleBackupBtn->setEnabled(false);
|
||||
toggleBackupBtn->setValue(tr("Restoring..."));
|
||||
|
||||
toggleBackupBtn->setVisibleButton(0, false);
|
||||
toggleBackupBtn->setVisibleButton(1, false);
|
||||
toggleBackupBtn->setVisibleButton(2, false);
|
||||
|
||||
std::string sourcePath = backupDir.filePath(selection).toStdString();
|
||||
std::string targetPath = "/data/params/d";
|
||||
|
||||
std::filesystem::create_directories(targetPath);
|
||||
|
||||
std::system(("rsync -av -l " + sourcePath + "/ " + targetPath + "/").c_str());
|
||||
|
||||
updateFrogPilotToggles();
|
||||
|
||||
toggleBackupBtn->setValue(tr("Restored!"));
|
||||
util::sleep_for(2500);
|
||||
toggleBackupBtn->setEnabled(true);
|
||||
toggleBackupBtn->setValue("");
|
||||
|
||||
toggleBackupBtn->setVisibleButton(0, true);
|
||||
toggleBackupBtn->setVisibleButton(1, true);
|
||||
toggleBackupBtn->setVisibleButton(2, true);
|
||||
|
||||
parent->keepScreenOn = false;
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
addItem(toggleBackupBtn);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h"
|
||||
|
||||
class FrogPilotDataPanel : public FrogPilotListWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FrogPilotDataPanel(FrogPilotSettingsWindow *parent);
|
||||
|
||||
private:
|
||||
FrogPilotSettingsWindow *parent;
|
||||
|
||||
Params params;
|
||||
};
|
||||
@@ -0,0 +1,128 @@
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/device_settings.h"
|
||||
|
||||
FrogPilotDevicePanel::FrogPilotDevicePanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
|
||||
const std::vector<std::tuple<QString, QString, QString, QString>> deviceToggles {
|
||||
{"DeviceManagement", tr("Device Settings"), tr("Device behavior settings."), "../frogpilot/assets/toggle_icons/icon_device.png"},
|
||||
{"DeviceShutdown", tr("Device Shutdown Timer"), tr("Controls how long the device stays on after you stop driving."), ""},
|
||||
{"OfflineMode", tr("Disable Internet Requirement"), tr("Allows the device to work without an internet connection."), ""},
|
||||
{"IncreaseThermalLimits", tr("Increase Thermal Safety Limit"), QString("<b>%1</b><br><br>%2").arg(tr("WARNING: This can cause premature wear or damage by running the device over comma's recommended temperature limits!")).arg(tr("Allows the device to run at higher temperatures than recommended.")), ""},
|
||||
{"LowVoltageShutdown", tr("Low Battery Shutdown Threshold"), tr("Manages the threshold for shutting down the device to protect the car's battery from excessive drain and potential damage."), ""},
|
||||
{"NoLogging", tr("Turn Off Data Tracking"), QString("<b>%1</b><br><br>%2").arg(tr("WARNING: This will prevent your drives from being recorded and the data will be unobtainable!")).arg(tr("Disables all data tracking to improve privacy.")), ""},
|
||||
{"NoUploads", tr("Turn Off Data Uploads"), QString("<b>%1</b><br><br>%2").arg(tr("WARNING: This will prevent your drives from appearing on comma connect which may impact debugging and support!")).arg(tr("Stops the device from sending any data to the servers.")), ""},
|
||||
|
||||
{"ScreenManagement", tr("Screen Settings"), tr("Screen behavior settings."), "../frogpilot/assets/toggle_icons/icon_light.png"},
|
||||
{"ScreenBrightness", tr("Screen Brightness (Offroad)"), tr("Controls the screen brightness when you're not driving."), ""},
|
||||
{"ScreenBrightnessOnroad", tr("Screen Brightness (Onroad)"), tr("Controls the screen brightness while you're driving."), ""},
|
||||
{"ScreenRecorder", tr("Screen Recorder"), tr("Enables a button in the onroad UI to record the screen."), ""},
|
||||
{"ScreenTimeout", tr("Screen Timeout (Offroad)"), tr("Controls how long it takes for the screen to turn off when you're not driving."), ""},
|
||||
{"ScreenTimeoutOnroad", tr("Screen Timeout (Onroad)"), tr("Controls how long it takes for the screen to turn off while you're driving."), ""}
|
||||
};
|
||||
|
||||
for (const auto &[param, title, desc, icon] : deviceToggles) {
|
||||
AbstractControl *deviceToggle;
|
||||
|
||||
if (param == "DeviceManagement") {
|
||||
FrogPilotParamManageControl *deviceManagementToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(deviceManagementToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
showToggles(deviceManagementKeys);
|
||||
});
|
||||
deviceToggle = deviceManagementToggle;
|
||||
} else if (param == "DeviceShutdown") {
|
||||
std::map<int, QString> shutdownLabels;
|
||||
for (int i = 0; i <= 33; ++i) {
|
||||
shutdownLabels[i] = i == 0 ? tr("5 mins") : i <= 3 ? QString::number(i * 15) + tr(" mins") : QString::number(i - 3) + (i == 4 ? tr(" hour") : tr(" hours"));
|
||||
}
|
||||
deviceToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 33, QString(), shutdownLabels);
|
||||
} else if (param == "NoUploads") {
|
||||
std::vector<QString> uploadsToggles{"DisableOnroadUploads"};
|
||||
std::vector<QString> uploadsToggleNames{tr("Only Onroad")};
|
||||
deviceToggle = new FrogPilotButtonToggleControl(param, title, desc, uploadsToggles, uploadsToggleNames);
|
||||
} else if (param == "LowVoltageShutdown") {
|
||||
deviceToggle = new FrogPilotParamValueControl(param, title, desc, icon, 11.8, 12.5, tr(" volts"), std::map<int, QString>(), 0.01);
|
||||
|
||||
} else if (param == "ScreenManagement") {
|
||||
FrogPilotParamManageControl *screenToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(screenToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
std::set<QString> modifiedScreenKeys = screenKeys;
|
||||
|
||||
showToggles(modifiedScreenKeys);
|
||||
});
|
||||
deviceToggle = screenToggle;
|
||||
} else if (param == "ScreenBrightness" || param == "ScreenBrightnessOnroad") {
|
||||
std::map<int, QString> brightnessLabels;
|
||||
int minBrightness = (param == "ScreenBrightnessOnroad") ? 0 : 1;
|
||||
for (int i = 0; i <= 101; ++i) {
|
||||
brightnessLabels[i] = i == 101 ? tr("Auto") : i == 0 ? tr("Screen Off") : QString::number(i) + "%";
|
||||
}
|
||||
deviceToggle = new FrogPilotParamValueControl(param, title, desc, icon, minBrightness, 101, QString(), brightnessLabels);
|
||||
} else if (param == "ScreenTimeout" || param == "ScreenTimeoutOnroad") {
|
||||
deviceToggle = new FrogPilotParamValueControl(param, title, desc, icon, 5, 60, tr(" seconds"));
|
||||
|
||||
} else {
|
||||
deviceToggle = new ParamControl(param, title, desc, icon);
|
||||
}
|
||||
|
||||
addItem(deviceToggle);
|
||||
toggles[param] = deviceToggle;
|
||||
|
||||
if (FrogPilotParamManageControl *frogPilotManageToggle = qobject_cast<FrogPilotParamManageControl*>(deviceToggle)) {
|
||||
QObject::connect(frogPilotManageToggle, &FrogPilotParamManageControl::manageButtonClicked, this, &FrogPilotDevicePanel::openParentToggle);
|
||||
}
|
||||
}
|
||||
|
||||
static_cast<ParamControl*>(toggles["IncreaseThermalLimits"])->setConfirmation(true, false);
|
||||
static_cast<ParamControl*>(toggles["NoLogging"])->setConfirmation(true, false);
|
||||
static_cast<ParamControl*>(toggles["NoUploads"])->setConfirmation(true, false);
|
||||
|
||||
std::set<QString> brightnessKeys = {"ScreenBrightness", "ScreenBrightnessOnroad"};
|
||||
for (const QString &key : brightnessKeys) {
|
||||
FrogPilotParamValueControl *paramControl = static_cast<FrogPilotParamValueControl*>(toggles[key]);
|
||||
QObject::connect(paramControl, &FrogPilotParamValueControl::valueChanged, [](int value) {
|
||||
Hardware::set_brightness(value);
|
||||
});
|
||||
}
|
||||
|
||||
QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotDevicePanel::hideToggles);
|
||||
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotDevicePanel::updateState);
|
||||
}
|
||||
|
||||
void FrogPilotDevicePanel::showEvent(QShowEvent *event) {
|
||||
frogpilotToggleLevels = parent->frogpilotToggleLevels;
|
||||
tuningLevel = parent->tuningLevel;
|
||||
|
||||
hideToggles();
|
||||
}
|
||||
|
||||
void FrogPilotDevicePanel::updateState(const UIState &s) {
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
started = s.scene.started;
|
||||
}
|
||||
|
||||
void FrogPilotDevicePanel::showToggles(const std::set<QString> &keys) {
|
||||
setUpdatesEnabled(false);
|
||||
|
||||
for (auto &[key, toggle] : toggles) {
|
||||
toggle->setVisible(keys.find(key) != keys.end() && tuningLevel >= frogpilotToggleLevels[key].toDouble());
|
||||
}
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void FrogPilotDevicePanel::hideToggles() {
|
||||
setUpdatesEnabled(false);
|
||||
|
||||
for (auto &[key, toggle] : toggles) {
|
||||
bool subToggles = deviceManagementKeys.find(key) != deviceManagementKeys.end() ||
|
||||
screenKeys.find(key) != screenKeys.end();
|
||||
toggle->setVisible(!subToggles && tuningLevel >= frogpilotToggleLevels[key].toDouble());
|
||||
}
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
|
||||
update();
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h"
|
||||
|
||||
class FrogPilotDevicePanel : public FrogPilotListWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FrogPilotDevicePanel(FrogPilotSettingsWindow *parent);
|
||||
|
||||
signals:
|
||||
void openParentToggle();
|
||||
|
||||
private:
|
||||
void hideToggles();
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void showToggles(const std::set<QString> &keys);
|
||||
void updateState(const UIState &s);
|
||||
|
||||
bool started;
|
||||
|
||||
int tuningLevel;
|
||||
|
||||
std::map<QString, AbstractControl*> toggles;
|
||||
|
||||
std::set<QString> deviceManagementKeys = {"DeviceShutdown", "IncreaseThermalLimits", "LowVoltageShutdown", "NoLogging", "NoUploads", "OfflineMode"};
|
||||
std::set<QString> screenKeys = {"ScreenBrightness", "ScreenBrightnessOnroad", "ScreenRecorder", "ScreenTimeout", "ScreenTimeoutOnroad"};
|
||||
|
||||
FrogPilotSettingsWindow *parent;
|
||||
|
||||
QJsonObject frogpilotToggleLevels;
|
||||
};
|
||||
@@ -0,0 +1,291 @@
|
||||
#include "selfdrive/frogpilot/navigation/ui/maps_settings.h"
|
||||
#include "selfdrive/frogpilot/navigation/ui/primeless_settings.h"
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/data_settings.h"
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/device_settings.h"
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h"
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h"
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h"
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/model_settings.h"
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h"
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/theme_settings.h"
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/utilities.h"
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h"
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/visual_settings.h"
|
||||
|
||||
bool checkNNFFLogFileExists(const std::string &carFingerprint) {
|
||||
static std::vector<std::string> files;
|
||||
if (files.empty()) {
|
||||
for (std::filesystem::directory_entry entry : std::filesystem::directory_iterator("../car/torque_data/lat_models")) {
|
||||
files.emplace_back(entry.path().filename().stem().string());
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::string &file : files) {
|
||||
if (file.rfind(carFingerprint, 0) == 0) {
|
||||
std::cout << "NNFF supports fingerprint: " << file << std::endl;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FrogPilotSettingsWindow::createPanelButtons(FrogPilotListWidget *list) {
|
||||
FrogPilotDevicePanel *frogpilotDevicePanel = new FrogPilotDevicePanel(this);
|
||||
FrogPilotLateralPanel *frogpilotLateralPanel = new FrogPilotLateralPanel(this);
|
||||
FrogPilotLongitudinalPanel *frogpilotLongitudinalPanel = new FrogPilotLongitudinalPanel(this);
|
||||
FrogPilotMapsPanel *frogpilotMapsPanel = new FrogPilotMapsPanel(this);
|
||||
FrogPilotModelPanel *frogpilotModelPanel = new FrogPilotModelPanel(this);
|
||||
FrogPilotPrimelessPanel *frogpilotPrimelessPanel = new FrogPilotPrimelessPanel(this);
|
||||
FrogPilotSoundsPanel *frogpilotSoundsPanel = new FrogPilotSoundsPanel(this);
|
||||
FrogPilotThemesPanel *frogpilotThemesPanel = new FrogPilotThemesPanel(this);
|
||||
FrogPilotVehiclesPanel *frogpilotVehiclesPanel = new FrogPilotVehiclesPanel(this);
|
||||
FrogPilotVisualsPanel *frogpilotVisualsPanel = new FrogPilotVisualsPanel(this);
|
||||
|
||||
QObject::connect(frogpilotDevicePanel, &FrogPilotDevicePanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle);
|
||||
QObject::connect(frogpilotLateralPanel, &FrogPilotLateralPanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle);
|
||||
QObject::connect(frogpilotLongitudinalPanel, &FrogPilotLongitudinalPanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle);
|
||||
QObject::connect(frogpilotLongitudinalPanel, &FrogPilotLongitudinalPanel::openSubParentToggle, this, &FrogPilotSettingsWindow::openSubParentToggle);
|
||||
QObject::connect(frogpilotMapsPanel, &FrogPilotMapsPanel::openMapSelection, this, &FrogPilotSettingsWindow::openMapSelection);
|
||||
QObject::connect(frogpilotModelPanel, &FrogPilotModelPanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle);
|
||||
QObject::connect(frogpilotModelPanel, &FrogPilotModelPanel::openSubParentToggle, this, &FrogPilotSettingsWindow::openSubParentToggle);
|
||||
QObject::connect(frogpilotPrimelessPanel, &FrogPilotPrimelessPanel::closeMapBoxInstructions, this, &FrogPilotSettingsWindow::closeMapBoxInstructions);
|
||||
QObject::connect(frogpilotPrimelessPanel, &FrogPilotPrimelessPanel::openMapBoxInstructions, this, &FrogPilotSettingsWindow::openMapBoxInstructions);
|
||||
QObject::connect(frogpilotSoundsPanel, &FrogPilotSoundsPanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle);
|
||||
QObject::connect(frogpilotThemesPanel, &FrogPilotThemesPanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle);
|
||||
QObject::connect(frogpilotVehiclesPanel, &FrogPilotVehiclesPanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle);
|
||||
QObject::connect(frogpilotVisualsPanel, &FrogPilotVisualsPanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle);
|
||||
QObject::connect(frogpilotVisualsPanel, &FrogPilotVisualsPanel::openSubParentToggle, this, &FrogPilotSettingsWindow::openSubParentToggle);
|
||||
|
||||
std::vector<std::vector<std::tuple<QString, QWidget*>>> panelButtons = {
|
||||
{{tr("MANAGE"), frogpilotSoundsPanel}},
|
||||
{{tr("DRIVING MODEL"), frogpilotModelPanel}, {tr("GAS / BRAKE"), frogpilotLongitudinalPanel}, {tr("STEERING"), frogpilotLateralPanel}},
|
||||
{{tr("MAP DATA"), frogpilotMapsPanel}, {tr("PRIMELESS NAVIGATION"), frogpilotPrimelessPanel}},
|
||||
{{tr("DATA"), new FrogPilotDataPanel(this)}, {tr("DEVICE CONTROLS"), frogpilotDevicePanel}, {tr("UTILITIES"), new FrogPilotUtilitiesPanel(this)}},
|
||||
{{tr("APPEARANCE"), frogpilotVisualsPanel}, {tr("THEME"), frogpilotThemesPanel}},
|
||||
{{tr("MANAGE"), frogpilotVehiclesPanel}}
|
||||
};
|
||||
|
||||
std::vector<std::tuple<QString, QString, QString>> panelInfo = {
|
||||
{tr("Alerts and Sounds"), tr("Manage FrogPilot's alerts and sounds."), "../frogpilot/assets/toggle_icons/icon_sound.png"},
|
||||
{tr("Driving Controls"), tr("Manage FrogPilot's features that affect acceleration, braking, and steering."), "../frogpilot/assets/toggle_icons/icon_steering.png"},
|
||||
{tr("Navigation"), tr("Manage map data to be used with 'Curve Speed Control' and 'Speed Limit Controller' and setup 'Navigate On openpilot (NOO)' without a comma prime subscription."), "../frogpilot/assets/toggle_icons/icon_map.png"},
|
||||
{tr("System Management"), tr("Manage the device's internal settings along with other tools and utilities to maintain and troubleshoot FrogPilot."), "../frogpilot/assets/toggle_icons/icon_system.png"},
|
||||
{tr("Theme and Appearance"), tr("Manage openpilot's theme and onroad widgets."), "../frogpilot/assets/toggle_icons/icon_display.png"},
|
||||
{tr("Vehicle Controls"), tr("Manage vehicle-specific settings."), "../frogpilot/assets/toggle_icons/icon_vehicle.png"}
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < panelInfo.size(); ++i) {
|
||||
const QString &title = std::get<0>(panelInfo[i]);
|
||||
const QString &description = std::get<1>(panelInfo[i]);
|
||||
const QString &icon = std::get<2>(panelInfo[i]);
|
||||
|
||||
const std::vector<std::tuple<QString, QWidget*>> &widgetLabels = panelButtons[i];
|
||||
|
||||
std::vector<QString> labels;
|
||||
std::vector<QWidget*> widgets;
|
||||
|
||||
for (size_t j = 0; j < widgetLabels.size(); ++j) {
|
||||
labels.push_back(std::get<0>(widgetLabels[j]));
|
||||
|
||||
QWidget *panel = std::get<1>(widgetLabels[j]);
|
||||
panel->setContentsMargins(50, 25, 50, 25);
|
||||
|
||||
ScrollView *panelFrame = new ScrollView(panel, this);
|
||||
mainLayout->addWidget(panelFrame);
|
||||
widgets.push_back(panelFrame);
|
||||
}
|
||||
|
||||
FrogPilotButtonsControl *panelButton = new FrogPilotButtonsControl(title, description, labels, false, true, icon);
|
||||
if (title == tr("Driving Controls")) drivingPanelButtons = panelButton;
|
||||
if (title == tr("Navigation")) navigationPanelButtons = panelButton;
|
||||
if (title == tr("System Management")) systemPanelButtons = panelButton;
|
||||
|
||||
QObject::connect(panelButton, &FrogPilotButtonsControl::buttonClicked, [this, widgets](int id) {
|
||||
mainLayout->setCurrentWidget(widgets[id]);
|
||||
panelOpen = true;
|
||||
openPanel();
|
||||
});
|
||||
|
||||
list->addItem(panelButton);
|
||||
}
|
||||
}
|
||||
|
||||
FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFrame(parent) {
|
||||
mainLayout = new QStackedLayout(this);
|
||||
|
||||
QWidget *frogpilotWidget = new QWidget(this);
|
||||
QVBoxLayout *frogpilotLayout = new QVBoxLayout(frogpilotWidget);
|
||||
frogpilotLayout->setContentsMargins(50, 25, 50, 25);
|
||||
frogpilotWidget->setLayout(frogpilotLayout);
|
||||
|
||||
frogpilotPanel = new ScrollView(frogpilotWidget, this);
|
||||
mainLayout->addWidget(frogpilotPanel);
|
||||
frogpilotPanel->setWidget(frogpilotWidget);
|
||||
|
||||
FrogPilotListWidget *list = new FrogPilotListWidget(this);
|
||||
frogpilotLayout->addWidget(list);
|
||||
|
||||
std::vector<QString> togglePresets{tr("Minimal"), tr("Standard"), tr("Advanced"), tr("Developer")};
|
||||
ButtonParamControl *togglePreset = new ButtonParamControl("TuningLevel", tr("Tuning Level"),
|
||||
tr("Select a tuning level that suits your preferences:\n\n"
|
||||
"Minimal - Ideal for those who prefer simplicity or ease of use\n"
|
||||
"Standard - Recommended for most users for a balanced experience\n"
|
||||
"Advanced - Unlocks fine-tuning controls for more experienced users\n"
|
||||
"Developer - Unlocks highly customizable settings for seasoned enthusiasts"),
|
||||
"../frogpilot/assets/toggle_icons/icon_customization.png",
|
||||
togglePresets);
|
||||
|
||||
int timeTo100FPHours = 100 - (paramsTracking.getInt("FrogPilotMinutes") / 60);
|
||||
int timeTo250OPHours = 250 - (params.getInt("openpilotMinutes") / 60);
|
||||
togglePreset->setEnabledButtons(3, timeTo100FPHours <= 0 || timeTo250OPHours <= 0);
|
||||
|
||||
QObject::connect(togglePreset, &ButtonParamControl::buttonClicked, [this](int id) {
|
||||
tuningLevel = id;
|
||||
if (id == 3) {
|
||||
FrogPilotConfirmationDialog::toggleAlert(
|
||||
tr("WARNING: This unlocks some potentially dangerous settings that can DRASTICALLY alter your driving experience!"),
|
||||
tr("I understand the risks."), this
|
||||
);
|
||||
}
|
||||
updateVariables();
|
||||
});
|
||||
QObject::connect(togglePreset, &ButtonParamControl::disabledButtonClicked, [this](int id) {
|
||||
if (id == 3) {
|
||||
FrogPilotConfirmationDialog::toggleAlert(
|
||||
tr("The 'Developer' preset is only available for users with either over 100 hours on FrogPilot, or 250 hours with openpilot."),
|
||||
tr("Ok"), this
|
||||
);
|
||||
}
|
||||
});
|
||||
list->addItem(togglePreset);
|
||||
|
||||
createPanelButtons(list);
|
||||
|
||||
QObject::connect(parent, &SettingsWindow::closeMapBoxInstructions, this, &FrogPilotSettingsWindow::closeMapBoxInstructions);
|
||||
QObject::connect(parent, &SettingsWindow::closeMapSelection, this, &FrogPilotSettingsWindow::closeMapSelection);
|
||||
QObject::connect(parent, &SettingsWindow::closePanel, this, &FrogPilotSettingsWindow::closePanel);
|
||||
QObject::connect(parent, &SettingsWindow::closeParentToggle, this, &FrogPilotSettingsWindow::closeParentToggle);
|
||||
QObject::connect(parent, &SettingsWindow::closeSubParentToggle, this, &FrogPilotSettingsWindow::closeSubParentToggle);
|
||||
QObject::connect(parent, &SettingsWindow::updateMetric, this, &FrogPilotSettingsWindow::updateMetric);
|
||||
QObject::connect(uiState(), &UIState::offroadTransition, this, &FrogPilotSettingsWindow::updateVariables);
|
||||
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotSettingsWindow::updateState);
|
||||
|
||||
frogpilotToggleLevels = QJsonDocument::fromJson(params_memory.get("FrogPilotTuningLevels", true).c_str()).object();
|
||||
tuningLevel = params.getInt("TuningLevel");
|
||||
|
||||
closeParentToggle();
|
||||
updateMetric(params.getBool("IsMetric"), true);
|
||||
updateVariables();
|
||||
}
|
||||
|
||||
void FrogPilotSettingsWindow::hideEvent(QHideEvent *event) {
|
||||
closePanel();
|
||||
}
|
||||
|
||||
void FrogPilotSettingsWindow::closePanel() {
|
||||
mainLayout->setCurrentWidget(frogpilotPanel);
|
||||
panelOpen = false;
|
||||
updateFrogPilotToggles();
|
||||
}
|
||||
|
||||
void FrogPilotSettingsWindow::updateState() {
|
||||
UIState *s = uiState();
|
||||
UIScene &scene = s->scene;
|
||||
|
||||
scene.frogpilot_panel_active = panelOpen && keepScreenOn;
|
||||
}
|
||||
|
||||
void FrogPilotSettingsWindow::updateVariables() {
|
||||
std::string carParams = params.get("CarParamsPersistent");
|
||||
if (!carParams.empty()) {
|
||||
AlignedBuffer aligned_buf;
|
||||
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(carParams.data(), carParams.size()));
|
||||
cereal::CarParams::Reader CP = cmsg.getRoot<cereal::CarParams>();
|
||||
cereal::CarParams::SafetyModel safetyModel = CP.getSafetyConfigs()[0].getSafetyModel();
|
||||
|
||||
std::string carFingerprint = CP.getCarFingerprint();
|
||||
std::string carMake = CP.getCarName();
|
||||
|
||||
hasAutoTune = (carMake == "hyundai" || carMake == "toyota") && CP.getLateralTuning().which() == cereal::CarParams::LateralTuning::TORQUE;
|
||||
hasBSM = CP.getEnableBsm();
|
||||
hasDashSpeedLimits = carMake == "hyundai" || carMake == "toyota";
|
||||
hasExperimentalOpenpilotLongitudinal = CP.getExperimentalLongitudinalAvailable();
|
||||
hasNNFFLog = checkNNFFLogFileExists(carFingerprint);
|
||||
hasOpenpilotLongitudinal = hasLongitudinalControl(CP);
|
||||
hasPCMCruise = CP.getPcmCruise();
|
||||
hasRadar = !CP.getRadarUnavailable();
|
||||
hasSNG = CP.getMinEnableSpeed() <= 0;
|
||||
isBolt = carFingerprint == "CHEVROLET_BOLT_CC" || carFingerprint == "CHEVROLET_BOLT_EUV";
|
||||
isGM = carMake == "gm";
|
||||
isHKG = carMake == "hyundai";
|
||||
isHKGCanFd = isHKG && safetyModel == cereal::CarParams::SafetyModel::HYUNDAI_CANFD;
|
||||
isPIDCar = CP.getLateralTuning().which() == cereal::CarParams::LateralTuning::PID;
|
||||
isSubaru = carMake == "subaru";
|
||||
isToyota = carMake == "toyota";
|
||||
isVolt = carFingerprint == "CHEVROLET_VOLT";
|
||||
frictionStock = CP.getLateralTuning().getTorque().getFriction();
|
||||
kpStock = CP.getLateralTuning().getTorque().getKp();
|
||||
latAccelStock = CP.getLateralTuning().getTorque().getLatAccelFactor();
|
||||
steerRatioStock = CP.getSteerRatio();
|
||||
|
||||
float currentFrictionStock = params.getFloat("SteerFrictionStock");
|
||||
float currentKPStock = params.getFloat("SteerKPStock");
|
||||
float currentLatAccelStock = params.getFloat("SteerLatAccelStock");
|
||||
float currentSteerRatioStock = params.getFloat("SteerRatioStock");
|
||||
|
||||
if (currentFrictionStock != frictionStock && frictionStock != 0) {
|
||||
if (params.getFloat("SteerFriction") == currentFrictionStock || currentFrictionStock == 0) {
|
||||
params.putFloat("SteerFriction", frictionStock);
|
||||
}
|
||||
params.putFloat("SteerFrictionStock", frictionStock);
|
||||
}
|
||||
|
||||
if (currentKPStock != kpStock && kpStock != 0) {
|
||||
if (params.getFloat("SteerKP") == currentKPStock || currentKPStock == 0) {
|
||||
params.putFloat("SteerKP", kpStock);
|
||||
}
|
||||
params.putFloat("SteerKPStock", kpStock);
|
||||
}
|
||||
|
||||
if (currentLatAccelStock != latAccelStock && latAccelStock != 0) {
|
||||
if (params.getFloat("SteerLatAccel") == currentLatAccelStock || currentLatAccelStock == 0) {
|
||||
params.putFloat("SteerLatAccel", latAccelStock);
|
||||
}
|
||||
params.putFloat("SteerLatAccelStock", latAccelStock);
|
||||
}
|
||||
|
||||
if (currentSteerRatioStock != steerRatioStock && steerRatioStock != 0) {
|
||||
if (params.getFloat("SteerRatio") == currentSteerRatioStock || currentSteerRatioStock == 0) {
|
||||
params.putFloat("SteerRatio", steerRatioStock);
|
||||
}
|
||||
params.putFloat("SteerRatioStock", steerRatioStock);
|
||||
}
|
||||
|
||||
if (params.checkKey("LiveTorqueParameters")) {
|
||||
std::string torqueParams = params.get("LiveTorqueParameters");
|
||||
if (!torqueParams.empty()) {
|
||||
capnp::FlatArrayMessageReader cmsgtp(aligned_buf.align(torqueParams.data(), torqueParams.size()));
|
||||
cereal::Event::Reader LTP = cmsgtp.getRoot<cereal::Event>();
|
||||
|
||||
cereal::LiveTorqueParametersData::Reader liveTorqueParams = LTP.getLiveTorqueParameters();
|
||||
|
||||
liveValid = liveTorqueParams.getLiveValid();
|
||||
} else {
|
||||
liveValid = false;
|
||||
}
|
||||
} else {
|
||||
liveValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
drivingPanelButtons->setVisible(hasOpenpilotLongitudinal || tuningLevel >= frogpilotToggleLevels.value("Model").toDouble());
|
||||
drivingPanelButtons->setVisibleButton(0, tuningLevel >= frogpilotToggleLevels.value("Model").toDouble());
|
||||
drivingPanelButtons->setVisibleButton(1, hasOpenpilotLongitudinal);
|
||||
|
||||
navigationPanelButtons->setVisibleButton(1, !uiState()->hasPrime());
|
||||
|
||||
systemPanelButtons->setVisibleButton(1, tuningLevel >= frogpilotToggleLevels.value("DeviceManagement").toDouble() || tuningLevel >= frogpilotToggleLevels.value("ScreenManagement").toDouble());
|
||||
|
||||
update();
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include "selfdrive/ui/qt/offroad/settings.h"
|
||||
#include "selfdrive/ui/qt/widgets/scrollview.h"
|
||||
|
||||
class FrogPilotSettingsWindow : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FrogPilotSettingsWindow(SettingsWindow *parent);
|
||||
|
||||
void updateVariables();
|
||||
|
||||
bool hasAutoTune = true;
|
||||
bool hasBSM = true;
|
||||
bool hasDashSpeedLimits = true;
|
||||
bool hasExperimentalOpenpilotLongitudinal = false;
|
||||
bool hasNNFFLog = true;
|
||||
bool hasOpenpilotLongitudinal = true;
|
||||
bool hasPCMCruise = false;
|
||||
bool hasRadar = true;
|
||||
bool hasSNG = false;
|
||||
bool isBolt = false;
|
||||
bool isGM = true;
|
||||
bool isHKG = true;
|
||||
bool isHKGCanFd = true;
|
||||
bool isPIDCar = false;
|
||||
bool isSubaru = false;
|
||||
bool isToyota = true;
|
||||
bool isVolt = true;
|
||||
bool keepScreenOn = false;
|
||||
bool liveValid = false;
|
||||
|
||||
float frictionStock;
|
||||
float kpStock;
|
||||
float latAccelStock;
|
||||
float steerRatioStock;
|
||||
|
||||
int tuningLevel;
|
||||
|
||||
QJsonObject frogpilotToggleLevels;
|
||||
|
||||
signals:
|
||||
void closeMapBoxInstructions();
|
||||
void closeMapSelection();
|
||||
void closeParentToggle();
|
||||
void closeSubParentToggle();
|
||||
void openMapBoxInstructions();
|
||||
void openMapSelection();
|
||||
void openPanel();
|
||||
void openParentToggle();
|
||||
void openSubParentToggle();
|
||||
void updateMetric(bool metric, bool bootRun=false);
|
||||
|
||||
private:
|
||||
void closePanel();
|
||||
void createPanelButtons(FrogPilotListWidget *list);
|
||||
void hideEvent(QHideEvent *event) override;
|
||||
void updateState();
|
||||
|
||||
bool panelOpen;
|
||||
|
||||
FrogPilotButtonsControl *drivingPanelButtons;
|
||||
FrogPilotButtonsControl *navigationPanelButtons;
|
||||
FrogPilotButtonsControl *systemPanelButtons;
|
||||
|
||||
Params params;
|
||||
Params params_memory{"/dev/shm/params"};
|
||||
Params paramsTracking{"/persist/tracking"};
|
||||
|
||||
QStackedLayout *mainLayout;
|
||||
|
||||
ScrollView *frogpilotPanel;
|
||||
};
|
||||
@@ -0,0 +1,370 @@
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h"
|
||||
|
||||
FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
|
||||
const std::vector<std::tuple<QString, QString, QString, QString>> lateralToggles {
|
||||
{"AdvancedLateralTune", tr("Advanced Lateral Tuning"), tr("Advanced settings for fine tuning openpilot's lateral controls."), "../frogpilot/assets/toggle_icons/icon_advanced_lateral_tune.png"},
|
||||
{"SteerFriction", frictionStock != 0 ? QString(tr("Friction (Default: %1)")).arg(QString::number(frictionStock, 'f', 2)) : tr("Friction"), tr("Adjusts the resistance in steering. Higher values provide more stable steering but can make it feel heavy, while lower values allow lighter steering but may feel too sensitive."), ""},
|
||||
{"SteerKP", kpStock != 0 ? QString(tr("Kp Factor (Default: %1)")).arg(QString::number(kpStock, 'f', 2)) : tr("Kp Factor"), tr("Adjusts how aggressively the car corrects its steering. Higher values offer quicker corrections but may feel jerky, while lower values make steering smoother but slower to respond."), ""},
|
||||
{"SteerLatAccel", latAccelStock != 0 ? QString(tr("Lateral Accel (Default: %1)")).arg(QString::number(latAccelStock, 'f', 2)) : tr("Lateral Accel"), tr("Adjusts how fast the car can steer from side to side. Higher values allow quicker lane changes but can feel unstable, while lower values provide smoother steering but may feel sluggish."), ""},
|
||||
{"SteerRatio", steerRatioStock != 0 ? QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(steerRatioStock, 'f', 2)) : tr("Steer Ratio"), tr("Adjusts how much openpilot needs to turn the wheel to steer. Higher values feel like driving a truck, more stable at high speeds, but harder to steer quickly at low speeds, while lower values feel like a go-kart, easier to steer in tight spots but more sensitive and less stable at high speeds."), ""},
|
||||
{"ForceAutoTune", tr("Force Auto Tune On"), tr("Forces comma's auto lateral tuning for unsupported vehicles."), ""},
|
||||
{"ForceAutoTuneOff", tr("Force Auto Tune Off"), tr("Forces comma's auto lateral tuning off for supported vehicles."), ""},
|
||||
|
||||
{"AlwaysOnLateral", tr("Always on Lateral"), tr("openpilot's steering control stays active even when the brake or gas pedals are pressed.\n\nDeactivate only occurs with the 'Cruise Control' button."), "../frogpilot/assets/toggle_icons/icon_always_on_lateral.png"},
|
||||
{"AlwaysOnLateralLKAS", tr("Control with LKAS Button"), tr("Controls the current state of 'Always on Lateral' with the 'LKAS' button."), ""},
|
||||
{"AlwaysOnLateralMain", tr("Enable with Cruise Control"), tr("Activates 'Always on Lateral' whenever 'Cruise Control' is active bypassing the requirement to enable openpilot first."), ""},
|
||||
{"PauseAOLOnBrake", tr("Pause on Brake Below"), tr("Pauses 'Always on Lateral' when the brake pedal is pressed below the set speed."), ""},
|
||||
|
||||
{"LaneChangeCustomizations", tr("Lane Change Settings"), tr("How openpilot handles lane changes."), "../frogpilot/assets/toggle_icons/icon_lane.png"},
|
||||
{"NudgelessLaneChange", tr("Automatic Lane Changes"), tr("Conducts lane changes without needing to touch the steering wheel upon turn signal activation."), ""},
|
||||
{"LaneChangeTime", tr("Lane Change Delay"), tr("Delays lane changes by the set time to prevent sudden changes."), ""},
|
||||
{"LaneDetectionWidth", tr("Lane Width Requirement"), tr("Sets the minimum lane width for openpilot to detect a lane as a lane."), ""},
|
||||
{"MinimumLaneChangeSpeed", tr("Minimum Speed for Lane Change"), tr("Sets the minimum speed required for openpilot to perform a lane change."), ""},
|
||||
{"OneLaneChange", tr("Only One Lane Change Per Signal"), tr("Limits lane changes to one per turn signal activation."), ""},
|
||||
|
||||
{"LateralTune", tr("Lateral Tuning"), tr("Settings for fine tuning openpilot's lateral controls."), "../frogpilot/assets/toggle_icons/icon_lateral_tune.png"},
|
||||
{"TurnDesires", tr("Force Turn Desires Below Lane Change Speed"), tr("Forces the model to use turn desires when driving below the minimum lane change speed to help make left and right turns more precisely."), ""},
|
||||
{"NNFF", tr("Neural Network Feedforward (NNFF)"), tr("Uses Twilsonco's 'Neural Network FeedForward' for more precise steering control."), ""},
|
||||
{"NNFFLite", tr("Smooth Curve Handling"), tr("Smoothens the steering control when entering and exiting curves by using Twilsonco's torque adjustments."), ""},
|
||||
|
||||
{"QOLLateral", tr("Quality of Life Improvements"), tr("Miscellaneous lateral focused features to improve your overall openpilot experience."), "../frogpilot/assets/toggle_icons/quality_of_life.png"},
|
||||
{"PauseLateralSpeed", tr("Pause Steering Below"), tr("Pauses steering control when driving below the set speed."), ""}
|
||||
};
|
||||
|
||||
for (const auto &[param, title, desc, icon] : lateralToggles) {
|
||||
AbstractControl *lateralToggle;
|
||||
|
||||
if (param == "AdvancedLateralTune") {
|
||||
FrogPilotParamManageControl *advancedLateralTuneToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(advancedLateralTuneToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
std::set<QString> modifiedAdvancedLateralTuneKeys = advancedLateralTuneKeys;
|
||||
|
||||
bool forcingAutoTune = params.getBool("ForceAutoTune");
|
||||
bool forcingAutoTuneOff = params.getBool("ForceAutoTuneOff");
|
||||
if (!hasAutoTune && forcingAutoTune || hasAutoTune && !forcingAutoTuneOff) {
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerFriction");
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerKP");
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerLatAccel");
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerRatio");
|
||||
}
|
||||
|
||||
if (hasAutoTune) {
|
||||
modifiedAdvancedLateralTuneKeys.erase("ForceAutoTune");
|
||||
} else if (isPIDCar) {
|
||||
modifiedAdvancedLateralTuneKeys.erase("ForceAutoTuneOff");
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerFriction");
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerKP");
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerLatAccel");
|
||||
} else {
|
||||
modifiedAdvancedLateralTuneKeys.erase("ForceAutoTuneOff");
|
||||
}
|
||||
|
||||
bool usingNNFF = hasNNFFLog && params.getBool("LateralTune") && params.getBool("NNFF");
|
||||
if (!liveValid || usingNNFF) {
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerFriction");
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerLatAccel");
|
||||
}
|
||||
|
||||
if (params.getFloat("SteerFrictionStock") == 0) {
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerFriction");
|
||||
}
|
||||
if (params.getFloat("SteerKPStock") == 0) {
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerKP");
|
||||
}
|
||||
if (params.getFloat("SteerLatAccelStock") == 0) {
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerLatAccel");
|
||||
}
|
||||
if (params.getFloat("SteerRatioStock") == 0) {
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerRatio");
|
||||
}
|
||||
|
||||
showToggles(modifiedAdvancedLateralTuneKeys);
|
||||
});
|
||||
lateralToggle = advancedLateralTuneToggle;
|
||||
} else if (param == "SteerFriction") {
|
||||
std::vector<QString> steerFrictionButton{"Reset"};
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0.01, 0.25, QString(), std::map<int, QString>(), 0.01, {}, steerFrictionButton, false, false);
|
||||
} else if (param == "SteerKP") {
|
||||
std::vector<QString> steerKPButton{"Reset"};
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, kpStock * 0.50, kpStock * 1.50, QString(), std::map<int, QString>(), 0.01, {}, steerKPButton, false, false);
|
||||
} else if (param == "SteerLatAccel") {
|
||||
std::vector<QString> steerLatAccelButton{"Reset"};
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, latAccelStock * 0.25, latAccelStock * 1.25, QString(), std::map<int, QString>(), 0.01, {}, steerLatAccelButton, false, false);
|
||||
} else if (param == "SteerRatio") {
|
||||
std::vector<QString> steerRatioButton{"Reset"};
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, steerRatioStock * 0.75, steerRatioStock * 1.25, QString(), std::map<int, QString>(), 0.01, {}, steerRatioButton, false, false);
|
||||
|
||||
} else if (param == "AlwaysOnLateral") {
|
||||
FrogPilotParamManageControl *aolToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(aolToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
std::set<QString> modifiedAOLKeys = aolKeys;
|
||||
|
||||
if (isSubaru || (params.getBool("ExperimentalModeActivation") && params.getBool("ExperimentalModeViaLKAS"))) {
|
||||
modifiedAOLKeys.erase("AlwaysOnLateralLKAS");
|
||||
}
|
||||
|
||||
showToggles(modifiedAOLKeys);
|
||||
});
|
||||
lateralToggle = aolToggle;
|
||||
} else if (param == "PauseAOLOnBrake") {
|
||||
lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, tr("mph"));
|
||||
|
||||
} else if (param == "LaneChangeCustomizations") {
|
||||
FrogPilotParamManageControl *laneChangeToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(laneChangeToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
showToggles(laneChangeKeys);
|
||||
});
|
||||
lateralToggle = laneChangeToggle;
|
||||
} else if (param == "LaneChangeTime") {
|
||||
lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 5, tr(" seconds"), {{0, "Instant"}, {10, "1.0 second"}}, 0.1);
|
||||
} else if (param == "LaneDetectionWidth") {
|
||||
lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 15, tr(" feet"), std::map<int, QString>(), 0.1);
|
||||
} else if (param == "MinimumLaneChangeSpeed") {
|
||||
lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, tr("mph"));
|
||||
|
||||
} else if (param == "LateralTune") {
|
||||
FrogPilotParamManageControl *lateralTuneToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(lateralTuneToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
std::set<QString> modifiedLateralTuneKeys = lateralTuneKeys;
|
||||
|
||||
bool usingNNFF = hasNNFFLog && params.getBool("LateralTune") && params.getBool("NNFF");
|
||||
if (!hasNNFFLog) {
|
||||
modifiedLateralTuneKeys.erase("NNFF");
|
||||
} else if (usingNNFF) {
|
||||
modifiedLateralTuneKeys.erase("NNFFLite");
|
||||
}
|
||||
|
||||
showToggles(modifiedLateralTuneKeys);
|
||||
});
|
||||
lateralToggle = lateralTuneToggle;
|
||||
|
||||
} else if (param == "QOLLateral") {
|
||||
FrogPilotParamManageControl *qolLateralToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(qolLateralToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
showToggles(qolKeys);
|
||||
});
|
||||
lateralToggle = qolLateralToggle;
|
||||
} else if (param == "PauseLateralSpeed") {
|
||||
std::vector<QString> pauseLateralToggles{"PauseLateralOnSignal"};
|
||||
std::vector<QString> pauseLateralToggleNames{"Turn Signal Only"};
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0, 99, tr("mph"), std::map<int, QString>(), 1, pauseLateralToggles, pauseLateralToggleNames, true);
|
||||
} else if (param == "PauseLateralOnSignal") {
|
||||
lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, tr("mph"));
|
||||
|
||||
} else {
|
||||
lateralToggle = new ParamControl(param, title, desc, icon);
|
||||
}
|
||||
|
||||
addItem(lateralToggle);
|
||||
toggles[param] = lateralToggle;
|
||||
|
||||
if (FrogPilotParamManageControl *frogPilotManageToggle = qobject_cast<FrogPilotParamManageControl*>(lateralToggle)) {
|
||||
QObject::connect(frogPilotManageToggle, &FrogPilotParamManageControl::manageButtonClicked, this, &FrogPilotLateralPanel::openParentToggle);
|
||||
}
|
||||
}
|
||||
|
||||
QObject::connect(static_cast<ToggleControl*>(toggles["AlwaysOnLateralLKAS"]), &ToggleControl::toggleFlipped, [this](bool state) {
|
||||
if (state && params.getBool("ExperimentalModeViaLKAS")) {
|
||||
params.putBool("ExperimentalModeViaLKAS", false);
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(static_cast<ToggleControl*>(toggles["ForceAutoTune"]), &ToggleControl::toggleFlipped, [this](bool state) {
|
||||
std::set<QString> modifiedAdvancedLateralTuneKeys = advancedLateralTuneKeys;
|
||||
|
||||
modifiedAdvancedLateralTuneKeys.erase("ForceAutoTuneOff");
|
||||
|
||||
if (state) {
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerFriction");
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerKP");
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerLatAccel");
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerRatio");
|
||||
} else if (isPIDCar) {
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerFriction");
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerKP");
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerLatAccel");
|
||||
} else if (!liveValid || hasNNFFLog && params.getBool("LateralTune") && params.getBool("NNFF")) {
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerFriction");
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerLatAccel");
|
||||
}
|
||||
|
||||
showToggles(modifiedAdvancedLateralTuneKeys);
|
||||
});
|
||||
|
||||
QObject::connect(static_cast<ToggleControl*>(toggles["ForceAutoTuneOff"]), &ToggleControl::toggleFlipped, [this](bool state) {
|
||||
std::set<QString> modifiedAdvancedLateralTuneKeys = advancedLateralTuneKeys;
|
||||
|
||||
modifiedAdvancedLateralTuneKeys.erase("ForceAutoTune");
|
||||
|
||||
if (!state) {
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerFriction");
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerKP");
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerLatAccel");
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerRatio");
|
||||
} else if (!liveValid || hasNNFFLog && params.getBool("LateralTune") && params.getBool("NNFF")) {
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerFriction");
|
||||
modifiedAdvancedLateralTuneKeys.erase("SteerLatAccel");
|
||||
}
|
||||
|
||||
showToggles(modifiedAdvancedLateralTuneKeys);
|
||||
});
|
||||
|
||||
QObject::connect(static_cast<ToggleControl*>(toggles["NNFF"]), &ToggleControl::toggleFlipped, [this](bool state) {
|
||||
std::set<QString> modifiedLateralTuneKeys = lateralTuneKeys;
|
||||
|
||||
bool usingNNFF = hasNNFFLog && state;
|
||||
if (!hasNNFFLog) {
|
||||
modifiedLateralTuneKeys.erase("NNFF");
|
||||
} else if (usingNNFF) {
|
||||
modifiedLateralTuneKeys.erase("NNFFLite");
|
||||
}
|
||||
|
||||
showToggles(modifiedLateralTuneKeys);
|
||||
});
|
||||
|
||||
std::set<QString> rebootKeys = {"AlwaysOnLateral", "NNFF", "NNFFLite"};
|
||||
for (const QString &key : rebootKeys) {
|
||||
QObject::connect(static_cast<ToggleControl*>(toggles[key]), &ToggleControl::toggleFlipped, [this, key](bool state) {
|
||||
if (started) {
|
||||
if (key == "AlwaysOnLateral" && state) {
|
||||
if (FrogPilotConfirmationDialog::toggleReboot(this)) {
|
||||
Hardware::reboot();
|
||||
}
|
||||
} else if (key != "AlwaysOnLateral") {
|
||||
if (FrogPilotConfirmationDialog::toggleReboot(this)) {
|
||||
Hardware::reboot();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
steerFrictionToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerFriction"]);
|
||||
QObject::connect(steerFrictionToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
|
||||
params.putFloat("SteerFriction", frictionStock);
|
||||
steerFrictionToggle->refresh();
|
||||
});
|
||||
|
||||
steerKPToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerKP"]);
|
||||
QObject::connect(steerKPToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
|
||||
params.putFloat("SteerKP", kpStock);
|
||||
steerKPToggle->refresh();
|
||||
});
|
||||
|
||||
steerLatAccelToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerLatAccel"]);
|
||||
QObject::connect(steerLatAccelToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
|
||||
params.putFloat("SteerLatAccel", latAccelStock);
|
||||
steerLatAccelToggle->refresh();
|
||||
});
|
||||
|
||||
steerRatioToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerRatio"]);
|
||||
QObject::connect(steerRatioToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() {
|
||||
params.putFloat("SteerRatio", steerRatioStock);
|
||||
steerRatioToggle->refresh();
|
||||
});
|
||||
|
||||
QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotLateralPanel::hideToggles);
|
||||
QObject::connect(parent, &FrogPilotSettingsWindow::updateMetric, this, &FrogPilotLateralPanel::updateMetric);
|
||||
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotLateralPanel::updateState);
|
||||
}
|
||||
|
||||
void FrogPilotLateralPanel::showEvent(QShowEvent *event) {
|
||||
frogpilotToggleLevels = parent->frogpilotToggleLevels;
|
||||
hasAutoTune = parent->hasAutoTune;
|
||||
hasNNFFLog = parent->hasNNFFLog;
|
||||
isPIDCar = parent->isPIDCar;
|
||||
isSubaru = parent->isSubaru;
|
||||
liveValid = parent->liveValid;
|
||||
frictionStock = parent->frictionStock;
|
||||
kpStock = parent->kpStock;
|
||||
latAccelStock = parent->latAccelStock;
|
||||
steerRatioStock = parent->steerRatioStock;
|
||||
tuningLevel = parent->tuningLevel;
|
||||
|
||||
steerFrictionToggle->setTitle(QString(tr("Friction (Default: %1)")).arg(QString::number(frictionStock, 'f', 2)));
|
||||
steerKPToggle->setTitle(QString(tr("Kp Factor (Default: %1)")).arg(QString::number(kpStock, 'f', 2)));
|
||||
steerKPToggle->updateControl(kpStock * 0.50, kpStock * 1.50);
|
||||
steerLatAccelToggle->setTitle(QString(tr("Lateral Accel (Default: %1)")).arg(QString::number(latAccelStock, 'f', 2)));
|
||||
steerLatAccelToggle->updateControl(latAccelStock * 0.75, latAccelStock * 1.25);
|
||||
steerRatioToggle->setTitle(QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(steerRatioStock, 'f', 2)));
|
||||
steerRatioToggle->updateControl(steerRatioStock * 0.75, steerRatioStock * 1.25);
|
||||
|
||||
hideToggles();
|
||||
}
|
||||
|
||||
void FrogPilotLateralPanel::updateState(const UIState &s) {
|
||||
if (!isVisible()) return;
|
||||
|
||||
started = s.scene.started;
|
||||
}
|
||||
|
||||
void FrogPilotLateralPanel::updateMetric(bool metric, bool bootRun) {
|
||||
static bool previousMetric;
|
||||
if (metric != previousMetric && !bootRun) {
|
||||
double distanceConversion = metric ? FOOT_TO_METER : METER_TO_FOOT;
|
||||
double speedConversion = metric ? MILE_TO_KM : KM_TO_MILE;
|
||||
|
||||
params.putFloatNonBlocking("LaneDetectionWidth", params.getFloat("LaneDetectionWidth") * distanceConversion);
|
||||
|
||||
params.putFloatNonBlocking("MinimumLaneChangeSpeed", params.getFloat("MinimumLaneChangeSpeed") * speedConversion);
|
||||
params.putFloatNonBlocking("PauseAOLOnBrake", params.getFloat("PauseAOLOnBrake") * speedConversion);
|
||||
params.putFloatNonBlocking("PauseLateralOnSignal", params.getFloat("PauseLateralOnSignal") * speedConversion);
|
||||
params.putFloatNonBlocking("PauseLateralSpeed", params.getFloat("PauseLateralSpeed") * speedConversion);
|
||||
}
|
||||
previousMetric = metric;
|
||||
|
||||
FrogPilotParamValueControl *laneWidthToggle = static_cast<FrogPilotParamValueControl*>(toggles["LaneDetectionWidth"]);
|
||||
FrogPilotParamValueControl *minimumLaneChangeSpeedToggle = static_cast<FrogPilotParamValueControl*>(toggles["MinimumLaneChangeSpeed"]);
|
||||
FrogPilotParamValueControl *pauseAOLOnBrakeToggle = static_cast<FrogPilotParamValueControl*>(toggles["PauseAOLOnBrake"]);
|
||||
FrogPilotParamValueControl *pauseLateralToggle = static_cast<FrogPilotParamValueControl*>(toggles["PauseLateralSpeed"]);
|
||||
|
||||
if (metric) {
|
||||
minimumLaneChangeSpeedToggle->updateControl(0, 150, tr("kph"));
|
||||
pauseAOLOnBrakeToggle->updateControl(0, 99, tr("kph"));
|
||||
pauseLateralToggle->updateControl(0, 99, tr("kph"));
|
||||
|
||||
laneWidthToggle->updateControl(0, 5, tr(" meters"));
|
||||
} else {
|
||||
minimumLaneChangeSpeedToggle->updateControl(0, 99, tr("mph"));
|
||||
pauseAOLOnBrakeToggle->updateControl(0, 99, tr("mph"));
|
||||
pauseLateralToggle->updateControl(0, 99, tr("mph"));
|
||||
|
||||
laneWidthToggle->updateControl(0, 15, tr(" feet"));
|
||||
}
|
||||
}
|
||||
|
||||
void FrogPilotLateralPanel::showToggles(const std::set<QString> &keys) {
|
||||
setUpdatesEnabled(false);
|
||||
|
||||
for (auto &[key, toggle] : toggles) {
|
||||
toggle->setVisible(keys.find(key) != keys.end() && tuningLevel >= frogpilotToggleLevels[key].toDouble());
|
||||
}
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void FrogPilotLateralPanel::hideToggles() {
|
||||
setUpdatesEnabled(false);
|
||||
|
||||
for (auto &[key, toggle] : toggles) {
|
||||
bool subToggles = advancedLateralTuneKeys.find(key) != advancedLateralTuneKeys.end() ||
|
||||
aolKeys.find(key) != aolKeys.end() ||
|
||||
laneChangeKeys.find(key) != laneChangeKeys.end() ||
|
||||
lateralTuneKeys.find(key) != lateralTuneKeys.end() ||
|
||||
qolKeys.find(key) != qolKeys.end();
|
||||
|
||||
toggle->setVisible(!subToggles && tuningLevel >= frogpilotToggleLevels[key].toDouble());
|
||||
}
|
||||
|
||||
std::set<QString> toggleKeys = {"AlwaysOnLateral"};
|
||||
for (const QString &key : toggleKeys) {
|
||||
FrogPilotParamManageControl *control = static_cast<FrogPilotParamManageControl*>(toggles[key]);
|
||||
control->setVisibleButton(tuningLevel > frogpilotToggleLevels[key].toDouble());
|
||||
}
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
|
||||
update();
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h"
|
||||
|
||||
class FrogPilotLateralPanel : public FrogPilotListWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FrogPilotLateralPanel(FrogPilotSettingsWindow *parent);
|
||||
|
||||
signals:
|
||||
void openParentToggle();
|
||||
|
||||
private:
|
||||
void hideToggles();
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void showToggles(const std::set<QString> &keys);
|
||||
void updateMetric(bool metric, bool bootRun);
|
||||
void updateState(const UIState &s);
|
||||
|
||||
bool hasAutoTune;
|
||||
bool hasNNFFLog;
|
||||
bool isPIDCar;
|
||||
bool isSubaru;
|
||||
bool liveValid;
|
||||
bool started;
|
||||
|
||||
float frictionStock;
|
||||
float latAccelStock;
|
||||
float kpStock;
|
||||
float steerRatioStock;
|
||||
|
||||
int tuningLevel;
|
||||
|
||||
std::map<QString, AbstractControl*> toggles;
|
||||
|
||||
std::set<QString> advancedLateralTuneKeys = {"ForceAutoTune", "ForceAutoTuneOff", "SteerFriction", "SteerLatAccel", "SteerKP", "SteerRatio"};
|
||||
std::set<QString> aolKeys = {"AlwaysOnLateralLKAS", "AlwaysOnLateralMain", "PauseAOLOnBrake"};
|
||||
std::set<QString> laneChangeKeys = {"LaneChangeTime", "LaneDetectionWidth", "MinimumLaneChangeSpeed", "NudgelessLaneChange", "OneLaneChange"};
|
||||
std::set<QString> lateralTuneKeys = {"NNFF", "NNFFLite", "TurnDesires"};
|
||||
std::set<QString> qolKeys = {"PauseLateralSpeed"};
|
||||
|
||||
FrogPilotParamValueButtonControl *steerFrictionToggle;
|
||||
FrogPilotParamValueButtonControl *steerLatAccelToggle;
|
||||
FrogPilotParamValueButtonControl *steerKPToggle;
|
||||
FrogPilotParamValueButtonControl *steerRatioToggle;
|
||||
|
||||
FrogPilotSettingsWindow *parent;
|
||||
|
||||
QJsonObject frogpilotToggleLevels;
|
||||
|
||||
Params params;
|
||||
};
|
||||
@@ -0,0 +1,676 @@
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h"
|
||||
|
||||
FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
|
||||
const std::vector<std::tuple<QString, QString, QString, QString>> longitudinalToggles {
|
||||
{"ConditionalExperimental", tr("Conditional Experimental Mode"), tr("Automatically switch to 'Experimental Mode' when specific conditions are met."), "../frogpilot/assets/toggle_icons/icon_conditional.png"},
|
||||
{"CESpeed", tr("Below"), tr("Triggers 'Experimental Mode' when driving below the set speed without a lead vehicle."), ""},
|
||||
{"CECurves", tr("Curve Detected Ahead"), tr("Triggers 'Experimental Mode' when a curve is detected in the road ahead."), ""},
|
||||
{"CELead", tr("Lead Detected Ahead"), tr("Triggers 'Experimental Mode' when a slower or stopped vehicle is detected ahead."), ""},
|
||||
{"CENavigation", tr("Navigation Data"), tr("Triggers 'Experimental Mode' based on navigation data, such as upcoming intersections or turns."), ""},
|
||||
{"CEModelStopTime", tr("openpilot Wants to Stop In"), tr("Triggers 'Experimental Mode' when openpilot wants to stop such as for a stop sign or red light."), ""},
|
||||
{"CESignalSpeed", tr("Turn Signal Below"), tr("Triggers 'Experimental Mode' when using turn signals below the set speed."), ""},
|
||||
|
||||
{"CurveSpeedControl", tr("Curve Speed Control"), tr("Automatically slow down for curves detected ahead or through the downloaded maps."), "../frogpilot/assets/toggle_icons/icon_speed_map.png"},
|
||||
{"CurveDetectionMethod", tr("Curve Detection Method"), tr("Uses data from either the downloaded maps or the model to determine where curves are."), ""},
|
||||
{"MTSCCurvatureCheck", tr("Curve Detection Failsafe"), tr("Triggers 'Curve Speed Control' only when a curve is detected with the model as well when using the 'Map Based' method."), ""},
|
||||
{"CurveSensitivity", tr("Curve Detection Sensitivity"), tr("Controls how sensitive openpilot is to detecting curves. Higher values trigger earlier responses at the risk of triggering too often, while lower values increase confidence at the risk of triggering too infrequently."), ""},
|
||||
{"TurnAggressiveness", tr("Speed Aggressiveness"), tr("Controls how aggressive openpilot takes turns. Higher values result in faster turns, while lower values result in slower turns."), ""},
|
||||
{"HideCSCUI", tr("Hide Desired Speed Widget From UI"), tr("Hides the desired speed widget from the onroad UI."), ""},
|
||||
|
||||
{"CustomPersonalities", tr("Customize Driving Personalities"), tr("Customize the personality profiles to suit your driving style."), "../frogpilot/assets/toggle_icons/icon_personality.png"},
|
||||
{"TrafficPersonalityProfile", tr("Traffic Personality"), tr("Customizes the 'Traffic' personality profile, tailored for navigating through traffic."), "../frogpilot/assets/stock_theme/distance_icons/traffic.png"},
|
||||
{"TrafficFollow", tr("Following Distance"), tr("Controls the minimum following distance in 'Traffic' mode. openpilot will automatically dynamically between this value and the 'Aggressive' profile distance based on your current speed."), ""},
|
||||
{"TrafficJerkAcceleration", tr("Acceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in acceleration in 'Traffic' mode. Higher values result in smoother, more gradual acceleration, while lower values allow for quicker, more responsive changes that may feel abrupt."), ""},
|
||||
{"TrafficJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in deceleration in 'Traffic' mode. Higher values result in smoother, more gradual deceleration, while lower values allow for quicker, more responsive changes that may feel abrupt."), ""},
|
||||
{"TrafficJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious openpilot is around other vehicles or obstacles in 'Traffic' mode. Higher values increase following distances and prioritize safety, leading to more cautious driving, while lower values allow for closer following but may reduce reaction time."), ""},
|
||||
{"TrafficJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly openpilot increases speed in 'Traffic' mode. Higher values ensure smoother, more gradual speed changes when accelerating, while lower values allow for quicker, more responsive changes that may feel abrupt."), ""},
|
||||
{"TrafficJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Controls how quickly openpilot decreases speed in 'Traffic' mode. Higher values ensure smoother, more gradual speed changes when slowing down, while lower values allow for quicker, more responsive changes that may feel abrupt."), ""},
|
||||
{"ResetTrafficPersonality", tr("Reset Settings"), tr("Restores the 'Traffic Mode' settings to their default values."), ""},
|
||||
|
||||
{"AggressivePersonalityProfile", tr("Aggressive Personality"), tr("Customize the 'Aggressive' personality profile, designed for a more assertive driving style."), "../frogpilot/assets/stock_theme/distance_icons/aggressive.png"},
|
||||
{"AggressiveFollow", tr("Following Distance"), tr("Sets the following distance for 'Aggressive' mode. This determines roughly how many seconds you'll follow behind the car ahead.\n\nDefault: 1.25 seconds."), ""},
|
||||
{"AggressiveJerkAcceleration", tr("Acceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in acceleration in 'Aggressive' mode. Higher values result in smoother, more gradual acceleration, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 0.5."), ""},
|
||||
{"AggressiveJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in deceleration in 'Aggressive' mode. Higher values result in smoother, more gradual deceleration, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 0.5."), ""},
|
||||
{"AggressiveJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious openpilot is around other vehicles or obstacles in 'Aggressive' mode. Higher values increase following distances and prioritize safety, leading to more cautious driving, while lower values allow for closer following but may reduce reaction time.\n\nDefault: 1.0."), ""},
|
||||
{"AggressiveJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly openpilot increases speed in 'Aggressive' mode. Higher values ensure smoother, more gradual speed changes when accelerating, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 0.5."), ""},
|
||||
{"AggressiveJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Controls how quickly openpilot decreases speed in 'Aggressive' mode. Higher values ensure smoother, more gradual speed changes when slowing down, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 0.5."), ""},
|
||||
{"ResetAggressivePersonality", tr("Reset Settings"), tr("Restores the 'Aggressive' settings to their default values."), ""},
|
||||
|
||||
{"StandardPersonalityProfile", tr("Standard Personality"), tr("Customize the 'Standard' personality profile, optimized for balanced driving."), "../frogpilot/assets/stock_theme/distance_icons/standard.png"},
|
||||
{"StandardFollow", tr("Following Distance"), tr("Set the following distance for 'Standard' mode. This determines roughly how many seconds you'll follow behind the car ahead.\n\nDefault: 1.45 seconds."), ""},
|
||||
{"StandardJerkAcceleration", tr("Acceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in acceleration in 'Standard' mode. Higher values result in smoother, more gradual acceleration, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 1.0."), ""},
|
||||
{"StandardJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in deceleration in 'Standard' mode. Higher values result in smoother braking, while lower values allow for quicker, more immediate braking that may feel abrupt.\n\nDefault: 1.0."), ""},
|
||||
{"StandardJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious openpilot is around other vehicles or obstacles in 'Standard' mode. Higher values increase following distances and prioritize safety, leading to more cautious driving, while lower values allow for closer following but may reduce reaction time.\n\nDefault: 1.0."), ""},
|
||||
{"StandardJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly openpilot increases speed in 'Standard' mode. Higher values ensure smoother, more gradual speed changes when accelerating, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 1.0."), ""},
|
||||
{"StandardJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Controls how quickly openpilot decreases speed in 'Standard' mode. Higher values ensure smoother, more gradual speed changes when slowing down, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 1.0."), ""},
|
||||
{"ResetStandardPersonality", tr("Reset Settings"), tr("Restores the 'Standard' settings to their default values."), ""},
|
||||
|
||||
{"RelaxedPersonalityProfile", tr("Relaxed Personality"), tr("Customize the 'Relaxed' personality profile, ideal for a more laid-back driving style."), "../frogpilot/assets/stock_theme/distance_icons/relaxed.png"},
|
||||
{"RelaxedFollow", tr("Following Distance"), tr("Set the following distance for 'Relaxed' mode. This determines roughly how many seconds you'll follow behind the car ahead.\n\nDefault: 1.75 seconds."), ""},
|
||||
{"RelaxedJerkAcceleration", tr("Acceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in acceleration in 'Relaxed' mode. Higher values result in smoother, more gradual acceleration, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 1.0."), ""},
|
||||
{"RelaxedJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in deceleration in 'Relaxed' mode. Higher values result in smoother braking, while lower values allow for quicker, more immediate braking that may feel abrupt.\n\nDefault: 1.0."), ""},
|
||||
{"RelaxedJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious openpilot is around other vehicles or obstacles in 'Relaxed' mode. Higher values increase following distances and prioritize safety, leading to more cautious driving, while lower values allow for closer following but may reduce reaction time.\n\nDefault: 1.0."), ""},
|
||||
{"RelaxedJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly openpilot increases speed in 'Relaxed' mode. Higher values ensure smoother, more gradual speed changes when accelerating, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 1.0."), ""},
|
||||
{"RelaxedJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Controls how quickly openpilot decreases speed in 'Relaxed' mode. Higher values ensure smoother, more gradual speed changes when slowing down, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 1.0."), ""},
|
||||
{"ResetRelaxedPersonality", tr("Reset Settings"), tr("Restores the 'Relaxed' settings to their default values."), ""},
|
||||
|
||||
{"ExperimentalModeActivation", tr("Experimental Mode Activation"), tr("Toggle 'Experimental Mode' on/off using either the steering wheel buttons or screen.\n\nThis overrides 'Conditional Experimental Mode'."), "../assets/img_experimental_white.svg"},
|
||||
{"ExperimentalModeViaLKAS", tr("Click the LKAS Button"), tr("Toggles 'Experimental Mode' by pressing the 'LKAS' button on the steering wheel."), ""},
|
||||
{"ExperimentalModeViaTap", tr("Double-Tap the Screen"), tr("Toggles 'Experimental Mode' by double-tapping the onroad UI within a 0.5 second period."), ""},
|
||||
{"ExperimentalModeViaDistance", tr("Long Press the Distance Button"), tr("Toggles 'Experimental Mode' by holding down the 'distance' button on the steering wheel for 0.5 seconds."), ""},
|
||||
|
||||
{"LongitudinalTune", tr("Longitudinal Tuning"), tr("Settings that control how openpilot manages speed and acceleration."), "../frogpilot/assets/toggle_icons/icon_longitudinal_tune.png"},
|
||||
{"AccelerationProfile", tr("Acceleration Profile"), tr("Enables either a sporty or eco-friendly acceleration rate. 'Sport+' aims to make openpilot accelerate as fast as possible."), ""},
|
||||
{"DecelerationProfile", tr("Deceleration Profile"), tr("Enables either a sporty or eco-friendly deceleration rate."), ""},
|
||||
{"HumanAcceleration", tr("Human-Like Acceleration"), tr("Uses the lead's acceleration rate when at a takeoff and ramps off the acceleration rate when approaching the maximum set speed for a more 'human-like' driving experience."), ""},
|
||||
{"HumanFollowing", tr("Human-Like Approach Behind Leads"), tr("Dynamically adjusts the following distance when approaching slower or stopped vehicles for a more 'human-like' driving experience."), ""},
|
||||
{"LeadDetectionThreshold", tr("Lead Detection Confidence"), tr("Controls how sensitive openpilot is to detecting vehicles ahead. A lower value can help detect vehicles sooner and from farther away, but increases the chance openpilot mistakes other objects for vehicles."), ""},
|
||||
{"MaxDesiredAcceleration", tr("Maximum Acceleration Rate"), tr("Sets a cap on how fast openpilot can accelerate."), ""},
|
||||
{"TacoTune", tr("'Taco Bell Run' Turn Speed Hack"), tr("Uses comma's speed hack they used to help handle left and right turns more precisely during their 2022 'Taco Bell' drive by reducing the maximum allowed speed and acceleration while turning."), ""},
|
||||
|
||||
{"QOLLongitudinal", tr("Quality of Life Improvements"), tr("Miscellaneous longitudinal focused features to improve your overall openpilot experience."), "../frogpilot/assets/toggle_icons/quality_of_life.png"},
|
||||
{"CustomCruise", tr("Cruise Increase"), tr("Controls the interval used when increasing the cruise control speed."), ""},
|
||||
{"CustomCruiseLong", tr("Cruise Increase (Long Press)"), tr("Controls the interval used when increasing the cruise control speed while holding down the button for 0.5+ seconds."), ""},
|
||||
{"ForceStandstill", tr("Force Keep openpilot in the Standstill State"), tr("Keeps openpilot in the 'standstill' state until the gas pedal or 'resume' button is pressed."), ""},
|
||||
{"ForceStops", tr("Force Stop for 'Detected' Stop Lights/Signs"), tr("Forces a stop whenever openpilot 'detects' a potential red light/stop sign to prevent it from running the red light/stop sign."), ""},
|
||||
{"IncreasedStoppedDistance", tr("Increase Stopped Distance"), tr("Increases the distance to stop behind vehicles."), ""},
|
||||
{"SetSpeedOffset", tr("Set Speed Offset"), tr("Controls how much higher or lower the set speed should be compared to your current set speed. For example, if you prefer to drive 5 mph above the speed limit, this setting will automatically add that difference when you adjust your set speed."), ""},
|
||||
{"MapGears", tr("Map Accel/Decel to Gears"), tr("Maps the acceleration and deceleration profiles to your car's 'Eco' or 'Sport' gear modes."), ""},
|
||||
{"ReverseCruise", tr("Reverse Cruise Increase"), tr("Reverses the long press cruise increase feature to increase the max speed by 5 mph instead of 1 on short presses."), ""},
|
||||
|
||||
{"SpeedLimitController", tr("Speed Limit Controller"), tr("Automatically adjust your max speed to match the speed limit using downloaded 'Open Street Maps' data, 'Navigate on openpilot', or your car's dashboard (Toyota/Lexus/HKG only)."), "../assets/offroad/icon_speed_limit.png"},
|
||||
{"SLCFallback", tr("Fallback Method"), tr("Controls what happens when no speed limit data is available."), ""},
|
||||
{"SLCOverride", tr("Override Method"), tr("Controls how the current speed limit is overriden.\n\n"), ""},
|
||||
{"SLCQOL", tr("Quality of Life Improvements"), tr("Miscellaneous 'Speed Limit Controller' focused features to improve your overall openpilot experience."), ""},
|
||||
{"SLCConfirmation", tr("Confirm New Speed Limits"), tr("Enables manual confirmations before using a new speed limit."), ""},
|
||||
{"ForceMPHDashboard", tr("Force MPH Readings from Dashboard"), tr("Forces speed limit readings from the dashboard to MPH if it normally displays them in KPH."), ""},
|
||||
{"SLCLookaheadHigher", tr("Prepare for Higher Speed Limits"), tr("Sets a lookahead value to prepare for upcoming higher speed limits when using downloaded map data."), ""},
|
||||
{"SLCLookaheadLower", tr("Prepare for Lower Speed Limits"), tr("Sets a lookahead value to prepare for upcoming lower speed limits when using downloaded map data."), ""},
|
||||
{"SetSpeedLimit", tr("Set Speed to Current Limit"), tr("Sets your max speed to match the current speed limit when enabling openpilot."), ""},
|
||||
{"SLCPriority", tr("Speed Limit Source Priority Order"), tr("Sets the order of priority for speed limit data sources."), ""},
|
||||
{"SLCOffsets", tr("Speed Limit Offsets"), tr("Set speed limit offsets to drive over the posted speed limit."), ""},
|
||||
{"Offset1", tr("Speed Limit Offset (0-34 mph)"), tr("Sets the speed limit offset for speeds between 0 and 34 mph."), ""},
|
||||
{"Offset2", tr("Speed Limit Offset (35-54 mph)"), tr("Sets the speed limit offset for speeds between 35 and 54 mph."), ""},
|
||||
{"Offset3", tr("Speed Limit Offset (55-64 mph)"), tr("Sets the speed limit offset for speeds between 55 and 64 mph."), ""},
|
||||
{"Offset4", tr("Speed Limit Offset (65-99 mph)"), tr("Sets the speed limit offset for speeds between 65 and 99 mph."), ""},
|
||||
{"SLCVisuals", tr("Visuals"), tr("Visual 'Speed Limit Controller' features to improve your overall openpilot experience."), ""},
|
||||
{"ShowSLCOffset", tr("Show Speed Limit Offset"), tr("Displays the speed limit offset separately in the onroad UI when using 'Speed Limit Controller'."), ""},
|
||||
{"SpeedLimitSources", tr("Show Speed Limit Sources"), tr("Displays the speed limit sources in the onroad UI when using 'Speed Limit Controller'."), ""},
|
||||
};
|
||||
|
||||
for (const auto &[param, title, desc, icon] : longitudinalToggles) {
|
||||
AbstractControl *longitudinalToggle;
|
||||
|
||||
if (param == "CustomPersonalities") {
|
||||
FrogPilotParamManageControl *customPersonalitiesToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(customPersonalitiesToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
showToggles(customDrivingPersonalityKeys);
|
||||
});
|
||||
longitudinalToggle = customPersonalitiesToggle;
|
||||
} else if (param == "ResetTrafficPersonality" || param == "ResetAggressivePersonality" || param == "ResetStandardPersonality" || param == "ResetRelaxedPersonality") {
|
||||
FrogPilotButtonsControl *profileBtn = new FrogPilotButtonsControl(title, desc, {tr("Reset")});
|
||||
longitudinalToggle = profileBtn;
|
||||
} else if (param == "TrafficPersonalityProfile") {
|
||||
FrogPilotParamManageControl *trafficPersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(trafficPersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
customPersonalityOpen = true;
|
||||
openSubParentToggle();
|
||||
showToggles(trafficPersonalityKeys);
|
||||
});
|
||||
longitudinalToggle = trafficPersonalityToggle;
|
||||
} else if (param == "AggressivePersonalityProfile") {
|
||||
FrogPilotParamManageControl *aggressivePersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(aggressivePersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
customPersonalityOpen = true;
|
||||
openSubParentToggle();
|
||||
showToggles(aggressivePersonalityKeys);
|
||||
});
|
||||
longitudinalToggle = aggressivePersonalityToggle;
|
||||
} else if (param == "StandardPersonalityProfile") {
|
||||
FrogPilotParamManageControl *standardPersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(standardPersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
customPersonalityOpen = true;
|
||||
openSubParentToggle();
|
||||
showToggles(standardPersonalityKeys);
|
||||
});
|
||||
longitudinalToggle = standardPersonalityToggle;
|
||||
} else if (param == "RelaxedPersonalityProfile") {
|
||||
FrogPilotParamManageControl *relaxedPersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(relaxedPersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
customPersonalityOpen = true;
|
||||
openSubParentToggle();
|
||||
showToggles(relaxedPersonalityKeys);
|
||||
});
|
||||
longitudinalToggle = relaxedPersonalityToggle;
|
||||
} else if (trafficPersonalityKeys.find(param) != trafficPersonalityKeys.end() ||
|
||||
aggressivePersonalityKeys.find(param) != aggressivePersonalityKeys.end() ||
|
||||
standardPersonalityKeys.find(param) != standardPersonalityKeys.end() ||
|
||||
relaxedPersonalityKeys.find(param) != relaxedPersonalityKeys.end()) {
|
||||
if (param == "TrafficFollow" || param == "AggressiveFollow" || param == "StandardFollow" || param == "RelaxedFollow") {
|
||||
if (param == "TrafficFollow") {
|
||||
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0.5, 5, tr(" seconds"), std::map<int, QString>(), 0.01);
|
||||
} else {
|
||||
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 5, tr(" seconds"), std::map<int, QString>(), 0.01);
|
||||
}
|
||||
} else {
|
||||
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 500, "%");
|
||||
}
|
||||
|
||||
} else if (param == "ConditionalExperimental") {
|
||||
FrogPilotParamManageControl *conditionalExperimentalToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(conditionalExperimentalToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
showToggles(conditionalExperimentalKeys);
|
||||
});
|
||||
longitudinalToggle = conditionalExperimentalToggle;
|
||||
} else if (param == "CESpeed") {
|
||||
FrogPilotParamValueControl *CESpeed = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, tr("mph"), std::map<int, QString>(), 1.0, true);
|
||||
FrogPilotParamValueControl *CESpeedLead = new FrogPilotParamValueControl("CESpeedLead", tr(" With Lead"), tr("Switches to 'Experimental Mode' when driving below the set speed with a lead vehicle."), icon, 0, 99, tr("mph"), std::map<int, QString>(), 1.0, true);
|
||||
FrogPilotDualParamControl *conditionalSpeeds = new FrogPilotDualParamControl(CESpeed, CESpeedLead);
|
||||
longitudinalToggle = reinterpret_cast<AbstractControl*>(conditionalSpeeds);
|
||||
} else if (param == "CECurves") {
|
||||
std::vector<QString> curveToggles{"CECurvesLead"};
|
||||
std::vector<QString> curveToggleNames{tr("With Lead")};
|
||||
longitudinalToggle = new FrogPilotButtonToggleControl(param, title, desc, curveToggles, curveToggleNames);
|
||||
} else if (param == "CELead") {
|
||||
std::vector<QString> leadToggles{"CESlowerLead", "CEStoppedLead"};
|
||||
std::vector<QString> leadToggleNames{tr("Slower Lead"), tr("Stopped Lead")};
|
||||
longitudinalToggle = new FrogPilotButtonToggleControl(param, title, desc, leadToggles, leadToggleNames);
|
||||
} else if (param == "CENavigation") {
|
||||
std::vector<QString> navigationToggles{"CENavigationIntersections", "CENavigationTurns", "CENavigationLead"};
|
||||
std::vector<QString> navigationToggleNames{tr("Intersections"), tr("Turns"), tr("With Lead")};
|
||||
longitudinalToggle = new FrogPilotButtonToggleControl(param, title, desc, navigationToggles, navigationToggleNames);
|
||||
} else if (param == "CEModelStopTime") {
|
||||
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 10, tr(" seconds"), {{0, "Off"}});
|
||||
} else if (param == "CESignalSpeed") {
|
||||
std::vector<QString> ceSignalToggles{"CESignalLaneDetection"};
|
||||
std::vector<QString> ceSignalToggleNames{"Only For Detected Lanes"};
|
||||
longitudinalToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0, 99, tr("mph"), std::map<int, QString>(), 1.0, ceSignalToggles, ceSignalToggleNames, true);
|
||||
|
||||
} else if (param == "CurveSpeedControl") {
|
||||
FrogPilotParamManageControl *curveControlToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(curveControlToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
curveDetectionToggle->setEnabledButtons(0, QDir("/data/media/0/osm/offline").exists());
|
||||
|
||||
std::set<QString> modifiedCurveSpeedKeys = curveSpeedKeys;
|
||||
|
||||
if (!params.getBool("MapTurnControl")) {
|
||||
modifiedCurveSpeedKeys.erase("MTSCCurvatureCheck");
|
||||
}
|
||||
|
||||
showToggles(modifiedCurveSpeedKeys);
|
||||
});
|
||||
longitudinalToggle = curveControlToggle;
|
||||
} else if (param == "CurveDetectionMethod") {
|
||||
std::vector<QString> curveDetectionToggles{"MapTurnControl", "VisionTurnControl"};
|
||||
std::vector<QString> curveDetectionToggleNames{tr("Map Based"), tr("Vision")};
|
||||
curveDetectionToggle = new FrogPilotButtonToggleControl(param, title, desc, curveDetectionToggles, curveDetectionToggleNames, false, true);
|
||||
QObject::connect(curveDetectionToggle, &FrogPilotButtonToggleControl::buttonClicked, [this](int index) {
|
||||
std::set<QString> modifiedCurveSpeedKeys = curveSpeedKeys;
|
||||
|
||||
if (!params.getBool("MapTurnControl")) {
|
||||
modifiedCurveSpeedKeys.erase("MTSCCurvatureCheck");
|
||||
}
|
||||
|
||||
if (!(params.getBool("MapTurnControl") || params.getBool("VisionTurnControl"))) {
|
||||
modifiedCurveSpeedKeys.erase("CurveSensitivity");
|
||||
modifiedCurveSpeedKeys.erase("TurnAggressiveness");
|
||||
}
|
||||
|
||||
showToggles(modifiedCurveSpeedKeys);
|
||||
|
||||
curveDetectionToggle->refresh();
|
||||
});
|
||||
QObject::connect(curveDetectionToggle, &FrogPilotButtonToggleControl::disabledButtonClicked, [=](int id) {
|
||||
if (id == 0) {
|
||||
FrogPilotConfirmationDialog::toggleAlert(
|
||||
tr("The 'Map Based' option is only available when some 'Map Data' has been downloaded!"),
|
||||
tr("Ok"), this
|
||||
);
|
||||
}
|
||||
});
|
||||
longitudinalToggle = curveDetectionToggle;
|
||||
} else if (param == "CurveSensitivity" || param == "TurnAggressiveness") {
|
||||
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 200, "%");
|
||||
|
||||
} else if (param == "ExperimentalModeActivation") {
|
||||
FrogPilotParamManageControl *experimentalModeActivationToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(experimentalModeActivationToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
std::set<QString> modifiedExperimentalModeActivationKeys = experimentalModeActivationKeys;
|
||||
|
||||
if (isSubaru || (params.getBool("AlwaysOnLateral") && params.getBool("AlwaysOnLateralLKAS"))) {
|
||||
modifiedExperimentalModeActivationKeys.erase("ExperimentalModeViaLKAS");
|
||||
}
|
||||
|
||||
showToggles(modifiedExperimentalModeActivationKeys);
|
||||
});
|
||||
longitudinalToggle = experimentalModeActivationToggle;
|
||||
|
||||
} else if (param == "LongitudinalTune") {
|
||||
FrogPilotParamManageControl *longitudinalTuneToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(longitudinalTuneToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
showToggles(longitudinalTuneKeys);
|
||||
});
|
||||
longitudinalToggle = longitudinalTuneToggle;
|
||||
} else if (param == "AccelerationProfile") {
|
||||
std::vector<QString> accelerationProfiles{tr("Standard"), tr("Eco"), tr("Sport"), tr("Sport+")};
|
||||
ButtonParamControl *accelerationProfileToggle = new ButtonParamControl(param, title, desc, icon, accelerationProfiles);
|
||||
longitudinalToggle = accelerationProfileToggle;
|
||||
} else if (param == "DecelerationProfile") {
|
||||
std::vector<QString> decelerationProfiles{tr("Standard"), tr("Eco"), tr("Sport")};
|
||||
ButtonParamControl *decelerationProfileToggle = new ButtonParamControl(param, title, desc, icon, decelerationProfiles);
|
||||
longitudinalToggle = decelerationProfileToggle;
|
||||
} else if (param == "LeadDetectionThreshold") {
|
||||
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 99, "%");
|
||||
} else if (param == "MaxDesiredAcceleration") {
|
||||
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0.1, 4.0, "m/s", std::map<int, QString>(), 0.1);
|
||||
|
||||
} else if (param == "QOLLongitudinal") {
|
||||
FrogPilotParamManageControl *qolLongitudinalToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(qolLongitudinalToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
std::set<QString> modifiedQolKeys = qolKeys;
|
||||
|
||||
if (!hasPCMCruise) {
|
||||
modifiedQolKeys.erase("ReverseCruise");
|
||||
} else {
|
||||
modifiedQolKeys.erase("CustomCruise");
|
||||
modifiedQolKeys.erase("CustomCruiseLong");
|
||||
modifiedQolKeys.erase("SetSpeedOffset");
|
||||
}
|
||||
|
||||
if (!(isGM || isHKGCanFd || isToyota)) {
|
||||
modifiedQolKeys.erase("MapGears");
|
||||
}
|
||||
|
||||
showToggles(modifiedQolKeys);
|
||||
});
|
||||
longitudinalToggle = qolLongitudinalToggle;
|
||||
} else if (param == "CustomCruise") {
|
||||
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 99, tr("mph"));
|
||||
} else if (param == "CustomCruiseLong") {
|
||||
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 99, tr("mph"));
|
||||
} else if (param == "IncreasedStoppedDistance") {
|
||||
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 10, tr(" feet"));
|
||||
} else if (param == "MapGears") {
|
||||
std::vector<QString> mapGearsToggles{"MapAcceleration", "MapDeceleration"};
|
||||
std::vector<QString> mapGearsToggleNames{tr("Acceleration"), tr("Deceleration")};
|
||||
longitudinalToggle = new FrogPilotButtonToggleControl(param, title, desc, mapGearsToggles, mapGearsToggleNames);
|
||||
} else if (param == "SetSpeedOffset") {
|
||||
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, tr("mph"));
|
||||
|
||||
} else if (param == "SpeedLimitController") {
|
||||
FrogPilotParamManageControl *speedLimitControllerToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(speedLimitControllerToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
slcOpen = true;
|
||||
|
||||
showToggles(speedLimitControllerKeys);
|
||||
});
|
||||
longitudinalToggle = speedLimitControllerToggle;
|
||||
} else if (param == "SLCFallback") {
|
||||
std::vector<QString> fallbackOptions{tr("Set Speed"), tr("Experimental Mode"), tr("Previous Limit")};
|
||||
ButtonParamControl *fallbackSelection = new ButtonParamControl(param, title, desc, icon, fallbackOptions);
|
||||
longitudinalToggle = fallbackSelection;
|
||||
} else if (param == "SLCOverride") {
|
||||
std::vector<QString> overrideOptions{tr("None"), tr("Set With Gas Pedal"), tr("Max Set Speed")};
|
||||
ButtonParamControl *overrideSelection = new ButtonParamControl(param, title, desc, icon, overrideOptions);
|
||||
longitudinalToggle = overrideSelection;
|
||||
} else if (param == "SLCPriority") {
|
||||
ButtonControl *slcPriorityButton = new ButtonControl(title, tr("SELECT"), desc);
|
||||
QStringList primaryPriorities = {tr("Dashboard"), tr("Map Data"), tr("Navigation"), tr("Highest"), tr("Lowest")};
|
||||
QStringList otherPriorities = {tr("None"), tr("Dashboard"), tr("Map Data"), tr("Navigation")};
|
||||
QStringList priorityPrompts = {tr("Select your primary priority"), tr("Select your secondary priority"), tr("Select your tertiary priority")};
|
||||
|
||||
QObject::connect(slcPriorityButton, &ButtonControl::clicked, [=]() {
|
||||
QStringList selectedPriorities;
|
||||
|
||||
for (int i = 1; i <= 3; ++i) {
|
||||
QStringList availablePriorities = (i == 1) ? primaryPriorities : otherPriorities;
|
||||
availablePriorities = availablePriorities.toSet().subtract(selectedPriorities.toSet()).toList();
|
||||
|
||||
if (!hasDashSpeedLimits) {
|
||||
availablePriorities.removeAll(tr("Dashboard"));
|
||||
}
|
||||
if (availablePriorities.size() == 1 && availablePriorities.contains(tr("None"))) {
|
||||
break;
|
||||
}
|
||||
|
||||
QString selection = MultiOptionDialog::getSelection(priorityPrompts[i - 1], availablePriorities, "", this);
|
||||
if (selection.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
params.put(QString("SLCPriority%1").arg(i).toStdString(), selection.toStdString());
|
||||
selectedPriorities.append(selection);
|
||||
|
||||
if (selection == tr("None")) {
|
||||
for (int j = i + 1; j <= 3; ++j) {
|
||||
params.put(QString("SLCPriority%1").arg(j).toStdString(), tr("None").toStdString());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (selection == tr("Lowest") || selection == tr("Highest")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
selectedPriorities.removeAll(tr("None"));
|
||||
slcPriorityButton->setValue(selectedPriorities.join(", "));
|
||||
});
|
||||
|
||||
QStringList initialPriorities;
|
||||
for (int i = 1; i <= 3; ++i) {
|
||||
QString priority = QString::fromStdString(params.get(QString("SLCPriority%1").arg(i).toStdString()));
|
||||
if (!priority.isEmpty() && priority != tr("None") && primaryPriorities.contains(priority)) {
|
||||
initialPriorities.append(priority);
|
||||
}
|
||||
}
|
||||
slcPriorityButton->setValue(initialPriorities.join(", "));
|
||||
longitudinalToggle = slcPriorityButton;
|
||||
} else if (param == "SLCOffsets") {
|
||||
ButtonControl *manageSLCOffsetsBtn = new ButtonControl(title, tr("MANAGE"), desc);
|
||||
QObject::connect(manageSLCOffsetsBtn, &ButtonControl::clicked, [this]() {
|
||||
openSubParentToggle();
|
||||
showToggles(speedLimitControllerOffsetsKeys);
|
||||
});
|
||||
longitudinalToggle = manageSLCOffsetsBtn;
|
||||
} else if (param == "SLCQOL") {
|
||||
ButtonControl *manageSLCQOLBtn = new ButtonControl(title, tr("MANAGE"), desc);
|
||||
QObject::connect(manageSLCQOLBtn, &ButtonControl::clicked, [this]() {
|
||||
openSubParentToggle();
|
||||
|
||||
std::set<QString> modifiedSpeedLimitControllerQOLKeys = speedLimitControllerQOLKeys;
|
||||
|
||||
if (hasPCMCruise) {
|
||||
modifiedSpeedLimitControllerQOLKeys.erase("SetSpeedLimit");
|
||||
}
|
||||
|
||||
if (!isToyota) {
|
||||
modifiedSpeedLimitControllerQOLKeys.erase("ForceMPHDashboard");
|
||||
}
|
||||
|
||||
showToggles(modifiedSpeedLimitControllerQOLKeys);
|
||||
});
|
||||
longitudinalToggle = manageSLCQOLBtn;
|
||||
} else if (param == "SLCConfirmation") {
|
||||
std::vector<QString> confirmationToggles{"SLCConfirmationLower", "SLCConfirmationHigher"};
|
||||
std::vector<QString> confirmationToggleNames{tr("Lower Limits"), tr("Higher Limits")};
|
||||
longitudinalToggle = new FrogPilotButtonToggleControl(param, title, desc, confirmationToggles, confirmationToggleNames);
|
||||
} else if (param == "SLCLookaheadHigher" || param == "SLCLookaheadLower") {
|
||||
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 30, tr(" seconds"));
|
||||
} else if (param == "Offset1" || param == "Offset2" || param == "Offset3" || param == "Offset4") {
|
||||
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, -99, 99, tr("mph"));
|
||||
} else if (param == "SLCVisuals") {
|
||||
ButtonControl *manageSLCVisualsBtn = new ButtonControl(title, tr("MANAGE"), desc);
|
||||
QObject::connect(manageSLCVisualsBtn, &ButtonControl::clicked, [this]() {
|
||||
openSubParentToggle();
|
||||
showToggles(speedLimitControllerVisualKeys);
|
||||
});
|
||||
longitudinalToggle = manageSLCVisualsBtn;
|
||||
|
||||
} else {
|
||||
longitudinalToggle = new ParamControl(param, title, desc, icon);
|
||||
}
|
||||
|
||||
addItem(longitudinalToggle);
|
||||
toggles[param] = longitudinalToggle;
|
||||
|
||||
if (FrogPilotParamManageControl *frogPilotManageToggle = qobject_cast<FrogPilotParamManageControl*>(longitudinalToggle)) {
|
||||
QObject::connect(frogPilotManageToggle, &FrogPilotParamManageControl::manageButtonClicked, this, &FrogPilotLongitudinalPanel::openParentToggle);
|
||||
}
|
||||
}
|
||||
|
||||
QObject::connect(static_cast<ToggleControl*>(toggles["ExperimentalModeViaLKAS"]), &ToggleControl::toggleFlipped, [this](bool state) {
|
||||
if (state && params.getBool("AlwaysOnLateralLKAS")) {
|
||||
params.putBool("AlwaysOnLateralLKAS", false);
|
||||
}
|
||||
});
|
||||
|
||||
FrogPilotParamValueControl *trafficFollowToggle = static_cast<FrogPilotParamValueControl*>(toggles["TrafficFollow"]);
|
||||
FrogPilotParamValueControl *trafficAccelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["TrafficJerkAcceleration"]);
|
||||
FrogPilotParamValueControl *trafficDecelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["TrafficJerkDeceleration"]);
|
||||
FrogPilotParamValueControl *trafficDangerToggle = static_cast<FrogPilotParamValueControl*>(toggles["TrafficJerkDanger"]);
|
||||
FrogPilotParamValueControl *trafficSpeedToggle = static_cast<FrogPilotParamValueControl*>(toggles["TrafficJerkSpeed"]);
|
||||
FrogPilotParamValueControl *trafficSpeedDecreaseToggle = static_cast<FrogPilotParamValueControl*>(toggles["TrafficJerkSpeedDecrease"]);
|
||||
FrogPilotButtonsControl *trafficResetButton = static_cast<FrogPilotButtonsControl*>(toggles["ResetTrafficPersonality"]);
|
||||
QObject::connect(trafficResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for 'Traffic Mode'?"), this)) {
|
||||
params.putFloat("TrafficFollow", params_default.getFloat("TrafficFollow"));
|
||||
params.putFloat("TrafficJerkAcceleration", params_default.getFloat("TrafficJerkAcceleration"));
|
||||
params.putFloat("TrafficJerkDeceleration", params_default.getFloat("TrafficJerkDeceleration"));
|
||||
params.putFloat("TrafficJerkDanger", params_default.getFloat("TrafficJerkDanger"));
|
||||
params.putFloat("TrafficJerkSpeed", params_default.getFloat("TrafficJerkSpeed"));
|
||||
params.putFloat("TrafficJerkSpeedDecrease", params_default.getFloat("TrafficJerkSpeedDecrease"));
|
||||
trafficFollowToggle->refresh();
|
||||
trafficAccelerationToggle->refresh();
|
||||
trafficDecelerationToggle->refresh();
|
||||
trafficDangerToggle->refresh();
|
||||
trafficSpeedToggle->refresh();
|
||||
trafficSpeedDecreaseToggle->refresh();
|
||||
}
|
||||
});
|
||||
|
||||
FrogPilotParamValueControl *aggressiveFollowToggle = static_cast<FrogPilotParamValueControl*>(toggles["AggressiveFollow"]);
|
||||
FrogPilotParamValueControl *aggressiveAccelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["AggressiveJerkAcceleration"]);
|
||||
FrogPilotParamValueControl *aggressiveDecelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["AggressiveJerkDeceleration"]);
|
||||
FrogPilotParamValueControl *aggressiveDangerToggle = static_cast<FrogPilotParamValueControl*>(toggles["AggressiveJerkDanger"]);
|
||||
FrogPilotParamValueControl *aggressiveSpeedToggle = static_cast<FrogPilotParamValueControl*>(toggles["AggressiveJerkSpeed"]);
|
||||
FrogPilotParamValueControl *aggressiveSpeedDecreaseToggle = static_cast<FrogPilotParamValueControl*>(toggles["AggressiveJerkSpeedDecrease"]);
|
||||
FrogPilotButtonsControl *aggressiveResetButton = static_cast<FrogPilotButtonsControl*>(toggles["ResetAggressivePersonality"]);
|
||||
QObject::connect(aggressiveResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for the 'Aggressive' personality?"), this)) {
|
||||
params.putFloat("AggressiveFollow", params_default.getFloat("AggressiveFollow"));
|
||||
params.putFloat("AggressiveJerkAcceleration", params_default.getFloat("AggressiveJerkAcceleration"));
|
||||
params.putFloat("AggressiveJerkDeceleration", params_default.getFloat("AggressiveJerkDeceleration"));
|
||||
params.putFloat("AggressiveJerkDanger", params_default.getFloat("AggressiveJerkDanger"));
|
||||
params.putFloat("AggressiveJerkSpeed", params_default.getFloat("AggressiveJerkSpeed"));
|
||||
params.putFloat("AggressiveJerkSpeedDecrease", params_default.getFloat("AggressiveJerkSpeedDecrease"));
|
||||
aggressiveFollowToggle->refresh();
|
||||
aggressiveAccelerationToggle->refresh();
|
||||
aggressiveDecelerationToggle->refresh();
|
||||
aggressiveDangerToggle->refresh();
|
||||
aggressiveSpeedToggle->refresh();
|
||||
aggressiveSpeedDecreaseToggle->refresh();
|
||||
}
|
||||
});
|
||||
|
||||
FrogPilotParamValueControl *standardFollowToggle = static_cast<FrogPilotParamValueControl*>(toggles["StandardFollow"]);
|
||||
FrogPilotParamValueControl *standardAccelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["StandardJerkAcceleration"]);
|
||||
FrogPilotParamValueControl *standardDecelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["StandardJerkDeceleration"]);
|
||||
FrogPilotParamValueControl *standardDangerToggle = static_cast<FrogPilotParamValueControl*>(toggles["StandardJerkDanger"]);
|
||||
FrogPilotParamValueControl *standardSpeedToggle = static_cast<FrogPilotParamValueControl*>(toggles["StandardJerkSpeed"]);
|
||||
FrogPilotParamValueControl *standardSpeedDecreaseToggle = static_cast<FrogPilotParamValueControl*>(toggles["StandardJerkSpeedDecrease"]);
|
||||
FrogPilotButtonsControl *standardResetButton = static_cast<FrogPilotButtonsControl*>(toggles["ResetStandardPersonality"]);
|
||||
QObject::connect(standardResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for the 'Standard' personality?"), this)) {
|
||||
params.putFloat("StandardFollow", params_default.getFloat("StandardFollow"));
|
||||
params.putFloat("StandardJerkAcceleration", params_default.getFloat("StandardJerkAcceleration"));
|
||||
params.putFloat("StandardJerkDeceleration", params_default.getFloat("StandardJerkDeceleration"));
|
||||
params.putFloat("StandardJerkDanger", params_default.getFloat("StandardJerkDanger"));
|
||||
params.putFloat("StandardJerkSpeed", params_default.getFloat("StandardJerkSpeed"));
|
||||
params.putFloat("StandardJerkSpeedDecrease", params_default.getFloat("StandardJerkSpeedDecrease"));
|
||||
standardFollowToggle->refresh();
|
||||
standardAccelerationToggle->refresh();
|
||||
standardDecelerationToggle->refresh();
|
||||
standardDangerToggle->refresh();
|
||||
standardSpeedToggle->refresh();
|
||||
standardSpeedDecreaseToggle->refresh();
|
||||
}
|
||||
});
|
||||
|
||||
FrogPilotParamValueControl *relaxedFollowToggle = static_cast<FrogPilotParamValueControl*>(toggles["RelaxedFollow"]);
|
||||
FrogPilotParamValueControl *relaxedAccelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["RelaxedJerkAcceleration"]);
|
||||
FrogPilotParamValueControl *relaxedDecelerationToggle = static_cast<FrogPilotParamValueControl*>(toggles["RelaxedJerkDeceleration"]);
|
||||
FrogPilotParamValueControl *relaxedDangerToggle = static_cast<FrogPilotParamValueControl*>(toggles["RelaxedJerkDanger"]);
|
||||
FrogPilotParamValueControl *relaxedSpeedToggle = static_cast<FrogPilotParamValueControl*>(toggles["RelaxedJerkSpeed"]);
|
||||
FrogPilotParamValueControl *relaxedSpeedDecreaseToggle = static_cast<FrogPilotParamValueControl*>(toggles["RelaxedJerkSpeedDecrease"]);
|
||||
FrogPilotButtonsControl *relaxedResetButton = static_cast<FrogPilotButtonsControl*>(toggles["ResetRelaxedPersonality"]);
|
||||
QObject::connect(relaxedResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for the 'Relaxed' personality?"), this)) {
|
||||
params.putFloat("RelaxedFollow", params_default.getFloat("RelaxedFollow"));
|
||||
params.putFloat("RelaxedJerkAcceleration", params_default.getFloat("RelaxedJerkAcceleration"));
|
||||
params.putFloat("RelaxedJerkDeceleration", params_default.getFloat("RelaxedJerkDeceleration"));
|
||||
params.putFloat("RelaxedJerkDanger", params_default.getFloat("RelaxedJerkDanger"));
|
||||
params.putFloat("RelaxedJerkSpeed", params_default.getFloat("RelaxedJerkSpeed"));
|
||||
params.putFloat("RelaxedJerkSpeedDecrease", params_default.getFloat("RelaxedJerkSpeedDecrease"));
|
||||
relaxedFollowToggle->refresh();
|
||||
relaxedAccelerationToggle->refresh();
|
||||
relaxedDecelerationToggle->refresh();
|
||||
relaxedDangerToggle->refresh();
|
||||
relaxedSpeedToggle->refresh();
|
||||
relaxedSpeedDecreaseToggle->refresh();
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotLongitudinalPanel::hideToggles);
|
||||
QObject::connect(parent, &FrogPilotSettingsWindow::closeSubParentToggle, this, &FrogPilotLongitudinalPanel::hideSubToggles);
|
||||
QObject::connect(parent, &FrogPilotSettingsWindow::updateMetric, this, &FrogPilotLongitudinalPanel::updateMetric);
|
||||
}
|
||||
|
||||
void FrogPilotLongitudinalPanel::showEvent(QShowEvent *event) {
|
||||
frogpilotToggleLevels = parent->frogpilotToggleLevels;
|
||||
hasDashSpeedLimits = parent->hasDashSpeedLimits;
|
||||
hasPCMCruise = parent->hasPCMCruise;
|
||||
isGM = parent->isGM;
|
||||
isHKGCanFd = parent->isHKGCanFd;
|
||||
isSubaru = parent->isSubaru;
|
||||
isToyota = parent->isToyota;
|
||||
tuningLevel = parent->tuningLevel;
|
||||
|
||||
hideToggles();
|
||||
}
|
||||
|
||||
void FrogPilotLongitudinalPanel::updateMetric(bool metric, bool bootRun) {
|
||||
static bool previousMetric;
|
||||
if (metric != previousMetric && !bootRun) {
|
||||
double distanceConversion = metric ? FOOT_TO_METER : METER_TO_FOOT;
|
||||
double speedConversion = metric ? MILE_TO_KM : KM_TO_MILE;
|
||||
|
||||
params.putFloatNonBlocking("IncreasedStoppedDistance", params.getFloat("IncreasedStoppedDistance") * distanceConversion);
|
||||
|
||||
params.putFloatNonBlocking("CESignalSpeed", params.getFloat("CESignalSpeed") * speedConversion);
|
||||
params.putFloatNonBlocking("CESpeed", params.getFloat("CESpeed") * speedConversion);
|
||||
params.putFloatNonBlocking("CESpeedLead", params.getFloat("CESpeedLead") * speedConversion);
|
||||
params.putFloatNonBlocking("CustomCruise", params.getFloat("CustomCruise") * speedConversion);
|
||||
params.putFloatNonBlocking("CustomCruiseLong", params.getFloat("CustomCruiseLong") * speedConversion);
|
||||
params.putFloatNonBlocking("Offset1", params.getFloat("Offset1") * speedConversion);
|
||||
params.putFloatNonBlocking("Offset2", params.getFloat("Offset2") * speedConversion);
|
||||
params.putFloatNonBlocking("Offset3", params.getFloat("Offset3") * speedConversion);
|
||||
params.putFloatNonBlocking("Offset4", params.getFloat("Offset4") * speedConversion);
|
||||
params.putFloatNonBlocking("SetSpeedOffset", params.getFloat("SetSpeedOffset") * speedConversion);
|
||||
}
|
||||
previousMetric = metric;
|
||||
|
||||
FrogPilotDualParamControl *ceSpeedToggle = reinterpret_cast<FrogPilotDualParamControl*>(toggles["CESpeed"]);
|
||||
FrogPilotParamValueButtonControl *ceSignal = static_cast<FrogPilotParamValueButtonControl*>(toggles["CESignalSpeed"]);
|
||||
FrogPilotParamValueControl *customCruiseToggle = static_cast<FrogPilotParamValueControl*>(toggles["CustomCruise"]);
|
||||
FrogPilotParamValueControl *customCruiseLongToggle = static_cast<FrogPilotParamValueControl*>(toggles["CustomCruiseLong"]);
|
||||
FrogPilotParamValueControl *offset1Toggle = static_cast<FrogPilotParamValueControl*>(toggles["Offset1"]);
|
||||
FrogPilotParamValueControl *offset2Toggle = static_cast<FrogPilotParamValueControl*>(toggles["Offset2"]);
|
||||
FrogPilotParamValueControl *offset3Toggle = static_cast<FrogPilotParamValueControl*>(toggles["Offset3"]);
|
||||
FrogPilotParamValueControl *offset4Toggle = static_cast<FrogPilotParamValueControl*>(toggles["Offset4"]);
|
||||
FrogPilotParamValueControl *increasedStoppedDistanceToggle = static_cast<FrogPilotParamValueControl*>(toggles["IncreasedStoppedDistance"]);
|
||||
FrogPilotParamValueControl *setSpeedOffsetToggle = static_cast<FrogPilotParamValueControl*>(toggles["SetSpeedOffset"]);
|
||||
|
||||
if (metric) {
|
||||
offset1Toggle->setTitle(tr("Speed Limit Offset (0-34 kph)"));
|
||||
offset2Toggle->setTitle(tr("Speed Limit Offset (35-54 kph)"));
|
||||
offset3Toggle->setTitle(tr("Speed Limit Offset (55-64 kph)"));
|
||||
offset4Toggle->setTitle(tr("Speed Limit Offset (65-99 kph)"));
|
||||
|
||||
offset1Toggle->setDescription(tr("Sets the speed limit offset for speeds between 0-34 kph."));
|
||||
offset2Toggle->setDescription(tr("Sets the speed limit offset for speeds between 35-54 kph."));
|
||||
offset3Toggle->setDescription(tr("Sets the speed limit offset for speeds between 55-64 kph."));
|
||||
offset4Toggle->setDescription(tr("Sets the speed limit offset for speeds between 65-99 kph."));
|
||||
|
||||
ceSignal->updateControl(0, 150, tr("kph"));
|
||||
ceSpeedToggle->updateControl(0, 150, tr("kph"));
|
||||
customCruiseToggle->updateControl(1, 150, tr("kph"));
|
||||
customCruiseLongToggle->updateControl(1, 150, tr("kph"));
|
||||
offset1Toggle->updateControl(-99, 99, tr("kph"));
|
||||
offset2Toggle->updateControl(-99, 99, tr("kph"));
|
||||
offset3Toggle->updateControl(-99, 99, tr("kph"));
|
||||
offset4Toggle->updateControl(-99, 99, tr("kph"));
|
||||
setSpeedOffsetToggle->updateControl(0, 150, tr("kph"));
|
||||
|
||||
increasedStoppedDistanceToggle->updateControl(0, 3, tr(" meters"));
|
||||
} else {
|
||||
offset1Toggle->setTitle(tr("Speed Limit Offset (0-34 mph)"));
|
||||
offset2Toggle->setTitle(tr("Speed Limit Offset (35-54 mph)"));
|
||||
offset3Toggle->setTitle(tr("Speed Limit Offset (55-64 mph)"));
|
||||
offset4Toggle->setTitle(tr("Speed Limit Offset (65-99 mph)"));
|
||||
|
||||
offset1Toggle->setDescription(tr("Sets the speed limit offset for speeds between 0-34 mph."));
|
||||
offset2Toggle->setDescription(tr("Sets the speed limit offset for speeds between 35-54 mph."));
|
||||
offset3Toggle->setDescription(tr("Sets the speed limit offset for speeds between 55-64 mph."));
|
||||
offset4Toggle->setDescription(tr("Sets the speed limit offset for speeds between 65-99 mph."));
|
||||
|
||||
ceSignal->updateControl(0, 99, tr("mph"));
|
||||
ceSpeedToggle->updateControl(0, 99, tr("mph"));
|
||||
customCruiseToggle->updateControl(1, 99, tr("mph"));
|
||||
customCruiseLongToggle->updateControl(1, 99, tr("mph"));
|
||||
offset1Toggle->updateControl(-99, 99, tr("mph"));
|
||||
offset2Toggle->updateControl(-99, 99, tr("mph"));
|
||||
offset3Toggle->updateControl(-99, 99, tr("mph"));
|
||||
offset4Toggle->updateControl(-99, 99, tr("mph"));
|
||||
setSpeedOffsetToggle->updateControl(0, 99, tr("mph"));
|
||||
|
||||
increasedStoppedDistanceToggle->updateControl(0, 10, tr(" feet"));
|
||||
}
|
||||
}
|
||||
|
||||
void FrogPilotLongitudinalPanel::showToggles(const std::set<QString> &keys) {
|
||||
setUpdatesEnabled(false);
|
||||
|
||||
for (auto &[key, toggle] : toggles) {
|
||||
toggle->setVisible(keys.find(key) != keys.end() && tuningLevel >= frogpilotToggleLevels[key].toDouble());
|
||||
}
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void FrogPilotLongitudinalPanel::hideToggles() {
|
||||
setUpdatesEnabled(false);
|
||||
|
||||
customPersonalityOpen = false;
|
||||
slcOpen = false;
|
||||
|
||||
for (auto &[key, toggle] : toggles) {
|
||||
bool subToggles = aggressivePersonalityKeys.find(key) != aggressivePersonalityKeys.end() ||
|
||||
conditionalExperimentalKeys.find(key) != conditionalExperimentalKeys.end() ||
|
||||
curveSpeedKeys.find(key) != curveSpeedKeys.end() ||
|
||||
customDrivingPersonalityKeys.find(key) != customDrivingPersonalityKeys.end() ||
|
||||
experimentalModeActivationKeys.find(key) != experimentalModeActivationKeys.end() ||
|
||||
longitudinalTuneKeys.find(key) != longitudinalTuneKeys.end() ||
|
||||
qolKeys.find(key) != qolKeys.end() ||
|
||||
relaxedPersonalityKeys.find(key) != relaxedPersonalityKeys.end() ||
|
||||
speedLimitControllerKeys.find(key) != speedLimitControllerKeys.end() ||
|
||||
speedLimitControllerOffsetsKeys.find(key) != speedLimitControllerOffsetsKeys.end() ||
|
||||
speedLimitControllerQOLKeys.find(key) != speedLimitControllerQOLKeys.end() ||
|
||||
speedLimitControllerVisualKeys.find(key) != speedLimitControllerVisualKeys.end() ||
|
||||
standardPersonalityKeys.find(key) != standardPersonalityKeys.end() ||
|
||||
trafficPersonalityKeys.find(key) != trafficPersonalityKeys.end();
|
||||
|
||||
toggle->setVisible(!subToggles && tuningLevel >= frogpilotToggleLevels[key].toDouble());
|
||||
}
|
||||
|
||||
std::set<QString> toggleKeys = {"ConditionalExperimental", "CurveSpeedControl"};
|
||||
for (const QString &key : toggleKeys) {
|
||||
FrogPilotParamManageControl *control = static_cast<FrogPilotParamManageControl*>(toggles[key]);
|
||||
control->setVisibleButton(tuningLevel > frogpilotToggleLevels[key].toDouble());
|
||||
}
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void FrogPilotLongitudinalPanel::hideSubToggles() {
|
||||
if (customPersonalityOpen) {
|
||||
customPersonalityOpen = false;
|
||||
showToggles(customDrivingPersonalityKeys);
|
||||
} else if (slcOpen) {
|
||||
showToggles(speedLimitControllerKeys);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h"
|
||||
|
||||
class FrogPilotLongitudinalPanel : public FrogPilotListWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *parent);
|
||||
|
||||
signals:
|
||||
void openParentToggle();
|
||||
void openSubParentToggle();
|
||||
|
||||
private:
|
||||
void hideSubToggles();
|
||||
void hideToggles();
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void showToggles(const std::set<QString> &keys);
|
||||
void updateMetric(bool metric, bool bootRun);
|
||||
|
||||
bool customPersonalityOpen;
|
||||
bool hasDashSpeedLimits;
|
||||
bool hasPCMCruise;
|
||||
bool isGM;
|
||||
bool isHKGCanFd;
|
||||
bool isSubaru;
|
||||
bool isToyota;
|
||||
bool slcOpen;
|
||||
|
||||
int tuningLevel;
|
||||
|
||||
std::map<QString, AbstractControl*> toggles;
|
||||
|
||||
std::set<QString> aggressivePersonalityKeys = {"AggressiveFollow", "AggressiveJerkAcceleration", "AggressiveJerkDeceleration", "AggressiveJerkDanger", "AggressiveJerkSpeed", "AggressiveJerkSpeedDecrease", "ResetAggressivePersonality"};
|
||||
std::set<QString> conditionalExperimentalKeys = {"CESpeed", "CESpeedLead", "CECurves", "CELead", "CEModelStopTime", "CENavigation", "CESignalSpeed"};
|
||||
std::set<QString> curveSpeedKeys = {"CurveDetectionMethod", "CurveSensitivity", "HideCSCUI", "MTSCCurvatureCheck", "TurnAggressiveness"};
|
||||
std::set<QString> customDrivingPersonalityKeys = {"AggressivePersonalityProfile", "RelaxedPersonalityProfile", "StandardPersonalityProfile", "TrafficPersonalityProfile"};
|
||||
std::set<QString> experimentalModeActivationKeys = {"ExperimentalModeViaDistance", "ExperimentalModeViaLKAS", "ExperimentalModeViaTap"};
|
||||
std::set<QString> longitudinalTuneKeys = {"AccelerationProfile", "DecelerationProfile", "HumanAcceleration", "HumanFollowing", "LeadDetectionThreshold", "MaxDesiredAcceleration", "TacoTune"};
|
||||
std::set<QString> qolKeys = {"CustomCruise", "CustomCruiseLong", "ForceStandstill", "ForceStops", "IncreasedStoppedDistance", "MapGears", "ReverseCruise", "SetSpeedOffset"};
|
||||
std::set<QString> relaxedPersonalityKeys = {"RelaxedFollow", "RelaxedJerkAcceleration", "RelaxedJerkDeceleration", "RelaxedJerkDanger", "RelaxedJerkSpeed", "RelaxedJerkSpeedDecrease", "ResetRelaxedPersonality"};
|
||||
std::set<QString> speedLimitControllerKeys = {"SLCOffsets", "SLCFallback", "SLCOverride", "SLCPriority", "SLCQOL", "SLCVisuals"};
|
||||
std::set<QString> speedLimitControllerOffsetsKeys = {"Offset1", "Offset2", "Offset3", "Offset4"};
|
||||
std::set<QString> speedLimitControllerQOLKeys = {"ForceMPHDashboard", "SetSpeedLimit", "SLCConfirmation", "SLCLookaheadHigher", "SLCLookaheadLower"};
|
||||
std::set<QString> speedLimitControllerVisualKeys = {"ShowSLCOffset", "SpeedLimitSources"};
|
||||
std::set<QString> standardPersonalityKeys = {"StandardFollow", "StandardJerkAcceleration", "StandardJerkDeceleration", "StandardJerkDanger", "StandardJerkSpeed", "StandardJerkSpeedDecrease", "ResetStandardPersonality"};
|
||||
std::set<QString> trafficPersonalityKeys = {"TrafficFollow", "TrafficJerkAcceleration", "TrafficJerkDeceleration", "TrafficJerkDanger", "TrafficJerkSpeed", "TrafficJerkSpeedDecrease", "ResetTrafficPersonality"};
|
||||
|
||||
FrogPilotButtonToggleControl *curveDetectionToggle;
|
||||
|
||||
FrogPilotSettingsWindow *parent;
|
||||
|
||||
QJsonObject frogpilotToggleLevels;
|
||||
|
||||
Params params;
|
||||
Params params_default{"/dev/shm/params_default"};
|
||||
};
|
||||
@@ -0,0 +1,411 @@
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/model_settings.h"
|
||||
|
||||
FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
|
||||
const std::vector<std::tuple<QString, QString, QString, QString>> modelToggles {
|
||||
{"AutomaticallyUpdateModels", tr("Automatically Update and Download Models"), tr("Automatically downloads new models and updates existing ones if needed."), ""},
|
||||
|
||||
{"ModelRandomizer", tr("Model Randomizer"), tr("Randomly selects a model each drive and brings up a model review prompt at the end to help find your preferred model."), ""},
|
||||
{"ManageBlacklistedModels", tr("Manage Model Blacklist"), tr("Manage the blacklisted models that aren't being used with 'Model Randomizer'."), ""},
|
||||
{"ResetScores", tr("Reset Model Scores"), tr("Clear the ratings you've given to the driving models."), ""},
|
||||
{"ReviewScores", tr("Review Model Scores"), tr("View the ratings you've assigned to the driving models."), ""},
|
||||
|
||||
{"DeleteModel", tr("Delete Model"), tr("Delete driving models from your device."), ""},
|
||||
{"DownloadModel", tr("Download Model"), tr("Download new driving models."), ""},
|
||||
{"SelectModel", tr("Select Model"), tr("Select your preferred driving model."), ""},
|
||||
};
|
||||
|
||||
for (const auto &[param, title, desc, icon] : modelToggles) {
|
||||
AbstractControl *modelToggle;
|
||||
|
||||
if (param == "ModelRandomizer") {
|
||||
FrogPilotParamManageControl *modelRandomizerToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(modelRandomizerToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
modelRandomizerOpen = true;
|
||||
showToggles(modelRandomizerKeys);
|
||||
updateModelLabels();
|
||||
});
|
||||
modelToggle = modelRandomizerToggle;
|
||||
} else if (param == "ManageBlacklistedModels") {
|
||||
FrogPilotButtonsControl *blacklistBtn = new FrogPilotButtonsControl(title, desc, {tr("ADD"), tr("REMOVE"), tr("REMOVE ALL")});
|
||||
QObject::connect(blacklistBtn, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
|
||||
QStringList blacklistedModels = QString::fromStdString(params.get("BlacklistedModels")).split(",");
|
||||
blacklistedModels.removeAll("");
|
||||
|
||||
if (id == 0) {
|
||||
QStringList blacklistableModels;
|
||||
for (const QString &model : modelFileToNameMapProcessed.keys()) {
|
||||
if (!blacklistedModels.contains(model)) {
|
||||
blacklistableModels.append(modelFileToNameMapProcessed.value(model));
|
||||
}
|
||||
}
|
||||
|
||||
if (blacklistableModels.size() <= 1) {
|
||||
FrogPilotConfirmationDialog::toggleAlert(tr("There are no more models to blacklist! The only available model is \"%1\"!").arg(blacklistableModels.first()), tr("Ok"), this);
|
||||
} else {
|
||||
QString modelToBlacklist = MultiOptionDialog::getSelection(tr("Select a model to add to the blacklist"), blacklistableModels, "", this);
|
||||
if (!modelToBlacklist.isEmpty()) {
|
||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to add the '%1' model to the blacklist?").arg(modelToBlacklist), tr("Add"), this)) {
|
||||
blacklistedModels.append(modelFileToNameMapProcessed.key(modelToBlacklist));
|
||||
params.put("BlacklistedModels", blacklistedModels.join(",").toStdString());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (id == 1) {
|
||||
QStringList whitelistableModels;
|
||||
for (const QString &model : blacklistedModels) {
|
||||
QString modelName = modelFileToNameMapProcessed.value(model);
|
||||
if (!modelName.isEmpty()) {
|
||||
whitelistableModels.append(modelName);
|
||||
}
|
||||
}
|
||||
whitelistableModels.sort();
|
||||
|
||||
QString modelToWhitelist = MultiOptionDialog::getSelection(tr("Select a model to remove from the blacklist"), whitelistableModels, "", this);
|
||||
if (!modelToWhitelist.isEmpty()) {
|
||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to remove the '%1' model from the blacklist?").arg(modelToWhitelist), tr("Remove"), this)) {
|
||||
blacklistedModels.removeAll(modelFileToNameMapProcessed.key(modelToWhitelist));
|
||||
params.put("BlacklistedModels", blacklistedModels.join(",").toStdString());
|
||||
}
|
||||
}
|
||||
} else if (id == 2) {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to remove all of your blacklisted models?"), this)) {
|
||||
params.remove("BlacklistedModels");
|
||||
params_storage.remove("BlacklistedModels");
|
||||
}
|
||||
}
|
||||
});
|
||||
modelToggle = blacklistBtn;
|
||||
} else if (param == "ResetScores") {
|
||||
ButtonControl *resetScoresBtn = new ButtonControl(title, tr("RESET"), desc);
|
||||
QObject::connect(resetScoresBtn, &ButtonControl::clicked, [this]() {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to reset all of your model drives and scores?"), this)) {
|
||||
params.remove("ModelDrivesAndScores");
|
||||
params_storage.remove("ModelDrivesAndScores");
|
||||
updateModelLabels();
|
||||
}
|
||||
});
|
||||
modelToggle = resetScoresBtn;
|
||||
} else if (param == "ReviewScores") {
|
||||
ButtonControl *reviewScoresBtn = new ButtonControl(title, tr("REVIEW"), desc);
|
||||
QObject::connect(reviewScoresBtn, &ButtonControl::clicked, [this]() {
|
||||
openSubParentToggle();
|
||||
|
||||
for (LabelControl *labels : labelControls) {
|
||||
labels->setVisible(true);
|
||||
}
|
||||
|
||||
for (auto &[key, toggle] : toggles) {
|
||||
toggle->setVisible(false);
|
||||
}
|
||||
});
|
||||
modelToggle = reviewScoresBtn;
|
||||
|
||||
} else if (param == "DeleteModel") {
|
||||
deleteModelBtn = new FrogPilotButtonsControl(title, desc, {tr("DELETE"), tr("DELETE ALL")});
|
||||
QObject::connect(deleteModelBtn, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
|
||||
QStringList deletableModels;
|
||||
for (const QString &file : modelDir.entryList(QDir::Files)) {
|
||||
QString modelName = modelFileToNameMapProcessed.value(QFileInfo(file).baseName());
|
||||
if (!modelName.isEmpty()) {
|
||||
deletableModels.append(modelName);
|
||||
}
|
||||
}
|
||||
deletableModels.removeAll(processModelName(currentModel));
|
||||
deletableModels.removeAll(modelFileToNameMapProcessed.value(QString::fromStdString(params_default.get("Model"))));
|
||||
|
||||
if (id == 0) {
|
||||
QString modelToDelete = MultiOptionDialog::getSelection(tr("Select a driving model to delete"), deletableModels, "", this);
|
||||
if (!modelToDelete.isEmpty() && ConfirmationDialog::confirm(tr("Are you sure you want to delete the '%1' model?").arg(modelToDelete), tr("Delete"), this)) {
|
||||
QString modelFile = modelFileToNameMapProcessed.key(modelToDelete);
|
||||
for (const QString &file : modelDir.entryList(QDir::Files)) {
|
||||
if (QFileInfo(file).baseName() == modelFile) {
|
||||
QFile::remove(modelDir.filePath(file));
|
||||
break;
|
||||
}
|
||||
}
|
||||
allModelsDownloaded = false;
|
||||
}
|
||||
} else if (id == 1) {
|
||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to delete all of your downloaded driving models?"), tr("Delete"), this)) {
|
||||
for (const QString &file : modelDir.entryList(QDir::Files)) {
|
||||
QString modelName = modelFileToNameMapProcessed.value(QFileInfo(file).baseName());
|
||||
if (deletableModels.contains(modelName)) {
|
||||
QFile::remove(modelDir.filePath(file));
|
||||
}
|
||||
}
|
||||
allModelsDownloaded = false;
|
||||
noModelsDownloaded = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
modelToggle = deleteModelBtn;
|
||||
} else if (param == "DownloadModel") {
|
||||
downloadModelBtn = new FrogPilotButtonsControl(title, desc, {tr("DOWNLOAD"), tr("DOWNLOAD ALL")});
|
||||
QObject::connect(downloadModelBtn, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
|
||||
if (id == 0) {
|
||||
if (modelDownloading) {
|
||||
cancellingDownload = true;
|
||||
|
||||
params_memory.putBool("CancelModelDownload", true);
|
||||
} else {
|
||||
for (const QString &file : modelDir.entryList(QDir::Files)) {
|
||||
downloadableModels.removeAll(modelFileToNameMap.value(QFileInfo(file).baseName()));
|
||||
}
|
||||
|
||||
QString modelToDownload = MultiOptionDialog::getSelection(tr("Select a driving model to download"), downloadableModels, "", this);
|
||||
if (!modelToDownload.isEmpty()) {
|
||||
modelDownloading = true;
|
||||
|
||||
params_memory.put("ModelToDownload", modelFileToNameMap.key(modelToDownload).toStdString());
|
||||
params_memory.put("ModelDownloadProgress", "Downloading...");
|
||||
|
||||
downloadModelBtn->setValue("Downloading...");
|
||||
|
||||
downloadModelBtn->setVisibleButton(1, false);
|
||||
}
|
||||
}
|
||||
} else if (id == 1) {
|
||||
if (allModelsDownloading) {
|
||||
cancellingDownload = true;
|
||||
|
||||
params_memory.putBool("CancelModelDownload", true);
|
||||
} else {
|
||||
allModelsDownloading = true;
|
||||
|
||||
params_memory.putBool("DownloadAllModels", true);
|
||||
params_memory.put("ModelDownloadProgress", "Downloading...");
|
||||
|
||||
downloadModelBtn->setValue("Downloading...");
|
||||
|
||||
downloadModelBtn->setVisibleButton(0, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
modelToggle = downloadModelBtn;
|
||||
} else if (param == "SelectModel") {
|
||||
selectModelBtn = new ButtonControl(title, tr("SELECT"), desc);
|
||||
QObject::connect(selectModelBtn, &ButtonControl::clicked, [this]() {
|
||||
QStringList selectableModels;
|
||||
for (const QString &file : modelDir.entryList(QDir::Files)) {
|
||||
QString modelName = modelFileToNameMap.value(QFileInfo(file).baseName());
|
||||
if (!modelName.isEmpty() && !modelName.contains("(Default)")) {
|
||||
selectableModels.append(modelName);
|
||||
}
|
||||
}
|
||||
selectableModels.prepend(modelFileToNameMap.value(QString::fromStdString(params_default.get("Model"))));
|
||||
|
||||
QString modelToSelect = MultiOptionDialog::getSelection(tr("Select a model - 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC"), selectableModels, currentModel, this);
|
||||
if (!modelToSelect.isEmpty()) {
|
||||
currentModel = modelToSelect;
|
||||
params.put("Model", modelFileToNameMap.key(modelToSelect).toStdString());
|
||||
|
||||
if (started) {
|
||||
if (FrogPilotConfirmationDialog::toggleReboot(this)) {
|
||||
Hardware::reboot();
|
||||
}
|
||||
}
|
||||
selectModelBtn->setValue(modelToSelect);
|
||||
}
|
||||
});
|
||||
modelToggle = selectModelBtn;
|
||||
|
||||
} else {
|
||||
modelToggle = new ParamControl(param, title, desc, icon);
|
||||
}
|
||||
|
||||
addItem(modelToggle);
|
||||
toggles[param] = modelToggle;
|
||||
|
||||
if (FrogPilotParamManageControl *frogPilotManageToggle = qobject_cast<FrogPilotParamManageControl*>(modelToggle)) {
|
||||
QObject::connect(frogPilotManageToggle, &FrogPilotParamManageControl::manageButtonClicked, this, &FrogPilotModelPanel::openParentToggle);
|
||||
}
|
||||
}
|
||||
|
||||
QObject::connect(static_cast<ToggleControl*>(toggles["ModelRandomizer"]), &ToggleControl::toggleFlipped, [this](bool state) {
|
||||
if (state && !allModelsDownloaded) {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("The 'Model Randomizer' only works with downloaded models. Do you want to download all the driving models?"), this)) {
|
||||
allModelsDownloading = true;
|
||||
|
||||
params_memory.putBool("DownloadAllModels", true);
|
||||
params_memory.put("ModelDownloadProgress", "Downloading...");
|
||||
|
||||
downloadModelBtn->setValue("Downloading...");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotModelPanel::hideToggles);
|
||||
QObject::connect(parent, &FrogPilotSettingsWindow::closeSubParentToggle, this, &FrogPilotModelPanel::hideSubToggles);
|
||||
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotModelPanel::updateState);
|
||||
}
|
||||
|
||||
void FrogPilotModelPanel::showEvent(QShowEvent *event) {
|
||||
frogpilotToggleLevels = parent->frogpilotToggleLevels;
|
||||
tuningLevel = parent->tuningLevel;
|
||||
|
||||
availableModels = QString::fromStdString(params.get("AvailableModels")).split(",");
|
||||
availableModels.sort();
|
||||
availableModelNames = QString::fromStdString(params.get("AvailableModelNames")).split(",");
|
||||
availableModelNames.sort();
|
||||
|
||||
int size = qMin(availableModels.size(), availableModelNames.size());
|
||||
for (int i = 0; i < size; ++i) {
|
||||
modelFileToNameMap.insert(availableModels[i], availableModelNames[i]);
|
||||
modelFileToNameMapProcessed.insert(availableModels[i], processModelName(availableModelNames[i]));
|
||||
}
|
||||
|
||||
downloadableModels = availableModelNames;
|
||||
for (const QString &file : modelDir.entryList(QDir::Files)) {
|
||||
downloadableModels.removeAll(modelFileToNameMap.value(QFileInfo(file).baseName()));
|
||||
}
|
||||
allModelsDownloaded = downloadableModels.isEmpty();
|
||||
|
||||
QStringList deletableModels;
|
||||
for (const QString &file : modelDir.entryList(QDir::Files)) {
|
||||
QString modelName = modelFileToNameMapProcessed.value(QFileInfo(file).baseName());
|
||||
if (!modelName.isEmpty()) {
|
||||
deletableModels.append(modelName);
|
||||
}
|
||||
}
|
||||
deletableModels.removeAll(processModelName(currentModel));
|
||||
deletableModels.removeAll(modelFileToNameMapProcessed.value(QString::fromStdString(params_default.get("Model"))));
|
||||
noModelsDownloaded = deletableModels.isEmpty();
|
||||
|
||||
currentModel = modelFileToNameMap.value(QString::fromStdString(params.get("Model")));
|
||||
selectModelBtn->setValue(currentModel);
|
||||
|
||||
hideToggles();
|
||||
}
|
||||
|
||||
void FrogPilotModelPanel::updateState(const UIState &s) {
|
||||
if (!isVisible() || finalizingDownload) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (allModelsDownloading || modelDownloading) {
|
||||
QString progress = QString::fromStdString(params_memory.get("ModelDownloadProgress"));
|
||||
bool downloadFailed = progress.contains(QRegularExpression("cancelled|exists|failed|offline", QRegularExpression::CaseInsensitiveOption));
|
||||
|
||||
if (progress != "Downloading...") {
|
||||
downloadModelBtn->setValue(progress);
|
||||
}
|
||||
|
||||
if (progress == "All models downloaded!" && allModelsDownloading || progress == "Downloaded!" && modelDownloading || downloadFailed) {
|
||||
finalizingDownload = true;
|
||||
|
||||
QTimer::singleShot(2500, [this, progress]() {
|
||||
allModelsDownloaded = progress == "All models downloaded!";
|
||||
allModelsDownloading = false;
|
||||
cancellingDownload = false;
|
||||
finalizingDownload = false;
|
||||
modelDownloading = false;
|
||||
noModelsDownloaded = false;
|
||||
|
||||
params_memory.remove("CancelModelDownload");
|
||||
params_memory.remove("DownloadAllModels");
|
||||
params_memory.remove("ModelDownloadProgress");
|
||||
params_memory.remove("ModelToDownload");
|
||||
|
||||
downloadModelBtn->setEnabled(true);
|
||||
downloadModelBtn->setValue("");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool parked = !started || s.scene.parked || s.scene.frogs_go_moo;
|
||||
|
||||
deleteModelBtn->setEnabled(!(allModelsDownloading || modelDownloading || noModelsDownloaded));
|
||||
|
||||
downloadModelBtn->setText(0, modelDownloading ? tr("CANCEL") : tr("DOWNLOAD"));
|
||||
downloadModelBtn->setText(1, allModelsDownloading ? tr("CANCEL") : tr("DOWNLOAD ALL"));
|
||||
|
||||
downloadModelBtn->setEnabledButtons(0, !allModelsDownloaded && !allModelsDownloading && !cancellingDownload && s.scene.online && parked);
|
||||
downloadModelBtn->setEnabledButtons(1, !allModelsDownloaded && !modelDownloading && !cancellingDownload && s.scene.online && parked);
|
||||
|
||||
downloadModelBtn->setVisibleButton(0, !allModelsDownloading);
|
||||
downloadModelBtn->setVisibleButton(1, !modelDownloading);
|
||||
|
||||
started = s.scene.started;
|
||||
|
||||
parent->keepScreenOn = allModelsDownloading || modelDownloading;
|
||||
}
|
||||
|
||||
void FrogPilotModelPanel::updateModelLabels() {
|
||||
QString modelDrivesAndScoresJson = QString::fromStdString(params.get("ModelDrivesAndScores"));
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(modelDrivesAndScoresJson.toUtf8());
|
||||
QJsonObject modelDrivesAndScores = jsonDoc.object();
|
||||
|
||||
qDeleteAll(labelControls);
|
||||
labelControls.clear();
|
||||
|
||||
for (const QString &modelName : availableModelNames) {
|
||||
QJsonObject modelData = modelDrivesAndScores.value(processModelName(modelName)).toObject();
|
||||
|
||||
int drives = modelData.value("Drives").toInt(0);
|
||||
int score = modelData.value("Score").toInt(0);
|
||||
|
||||
QString drivesDisplay = drives == 1 ? QString("%1 Drive").arg(drives) : drives > 0 ? QString("%1 Drives").arg(drives) : "N/A";
|
||||
QString scoreDisplay = drives > 0 ? QString("Score: %1%").arg(score) : "N/A";
|
||||
|
||||
QString labelTitle = QStringLiteral("%1").arg(processModelName(modelName));
|
||||
QString labelText = QStringLiteral("%1 (%2)").arg(scoreDisplay, drivesDisplay);
|
||||
|
||||
LabelControl *labelControl = new LabelControl(labelTitle, labelText, "", this);
|
||||
labelControls.append(labelControl);
|
||||
addItem(labelControl);
|
||||
}
|
||||
|
||||
for (LabelControl *labels : labelControls) {
|
||||
labels->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
void FrogPilotModelPanel::showToggles(const std::set<QString> &keys) {
|
||||
setUpdatesEnabled(false);
|
||||
|
||||
for (auto &[key, toggle] : toggles) {
|
||||
toggle->setVisible(keys.find(key) != keys.end() && tuningLevel >= frogpilotToggleLevels[key].toDouble());
|
||||
}
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void FrogPilotModelPanel::hideToggles() {
|
||||
setUpdatesEnabled(false);
|
||||
|
||||
modelRandomizerOpen = false;
|
||||
|
||||
for (auto &[key, toggle] : toggles) {
|
||||
bool subToggles = modelRandomizerKeys.find(key) != modelRandomizerKeys.end();
|
||||
|
||||
toggle->setVisible(!subToggles && tuningLevel >= frogpilotToggleLevels[key].toDouble());
|
||||
}
|
||||
|
||||
for (LabelControl *labels : labelControls) {
|
||||
labels->setVisible(false);
|
||||
}
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void FrogPilotModelPanel::hideSubToggles() {
|
||||
setUpdatesEnabled(false);
|
||||
|
||||
if (modelRandomizerOpen) {
|
||||
for (auto &[key, toggle] : toggles) {
|
||||
toggle->setVisible(modelRandomizerKeys.find(key) != modelRandomizerKeys.end());
|
||||
}
|
||||
|
||||
for (LabelControl *labels : labelControls) {
|
||||
labels->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
|
||||
update();
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h"
|
||||
|
||||
class FrogPilotModelPanel : public FrogPilotListWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FrogPilotModelPanel(FrogPilotSettingsWindow *parent);
|
||||
|
||||
signals:
|
||||
void openParentToggle();
|
||||
void openSubParentToggle();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *event) override;
|
||||
|
||||
private:
|
||||
void hideSubToggles();
|
||||
void hideToggles();
|
||||
void showToggles(const std::set<QString> &keys);
|
||||
void updateModelLabels();
|
||||
void updateState(const UIState &s);
|
||||
|
||||
bool allModelsDownloaded;
|
||||
bool allModelsDownloading;
|
||||
bool cancellingDownload;
|
||||
bool finalizingDownload;
|
||||
bool modelDownloading;
|
||||
bool modelRandomizerOpen;
|
||||
bool noModelsDownloaded;
|
||||
bool started;
|
||||
|
||||
int tuningLevel;
|
||||
|
||||
std::map<QString, AbstractControl*> toggles;
|
||||
|
||||
std::set<QString> modelRandomizerKeys = {"ManageBlacklistedModels", "ResetScores", "ReviewScores"};
|
||||
|
||||
ButtonControl *selectModelBtn;
|
||||
|
||||
FrogPilotButtonsControl *deleteModelBtn;
|
||||
FrogPilotButtonsControl *downloadModelBtn;
|
||||
|
||||
FrogPilotSettingsWindow *parent;
|
||||
|
||||
Params params;
|
||||
Params params_default{"/dev/shm/params_default"};
|
||||
Params params_memory{"/dev/shm/params"};
|
||||
Params params_storage{"/persist/params"};
|
||||
|
||||
QDir modelDir{"/data/models/"};
|
||||
|
||||
QJsonObject frogpilotToggleLevels;
|
||||
|
||||
QList<LabelControl*> labelControls;
|
||||
|
||||
QMap<QString, QString> modelFileToNameMap;
|
||||
QMap<QString, QString> modelFileToNameMapProcessed;
|
||||
|
||||
QString currentModel;
|
||||
|
||||
QStringList availableModels;
|
||||
QStringList availableModelNames;
|
||||
QStringList downloadableModels;
|
||||
};
|
||||
@@ -0,0 +1,157 @@
|
||||
#include <filesystem>
|
||||
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h"
|
||||
|
||||
void playSound(const std::string &alert, int volume) {
|
||||
Params params_memory{"/dev/shm/params"};
|
||||
|
||||
std::string stockPath = "/data/openpilot/selfdrive/assets/sounds/" + alert + ".wav";
|
||||
std::string themePath = "/data/openpilot/selfdrive/frogpilot/assets/active_theme/sounds/" + alert + ".wav";
|
||||
|
||||
std::string filePath;
|
||||
if (std::filesystem::exists(themePath)) {
|
||||
filePath = themePath;
|
||||
} else if (std::filesystem::exists(stockPath)) {
|
||||
filePath = stockPath;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
params_memory.putBool("TestingSound", true);
|
||||
|
||||
std::system("pkill -f 'ffplay'");
|
||||
|
||||
volume = std::clamp(volume, 0, 100);
|
||||
std::string command = "ffplay -nodisp -autoexit -volume " + std::to_string(volume) + " \"" + filePath + "\"";
|
||||
std::system(command.c_str());
|
||||
|
||||
params_memory.putBool("TestingSound", false);
|
||||
}
|
||||
|
||||
FrogPilotSoundsPanel::FrogPilotSoundsPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
|
||||
const std::vector<std::tuple<QString, QString, QString, QString>> soundsToggles {
|
||||
{"AlertVolumeControl", tr("Alert Volume Controller"), tr("Control the volume level for each individual sound in openpilot."), "../frogpilot/assets/toggle_icons/icon_mute.png"},
|
||||
{"DisengageVolume", tr("Disengage Volume"), tr("Related alerts:\n\nAdaptive Cruise Disabled\nParking Brake Engaged\nBrake Pedal Pressed\nSpeed too Low"), ""},
|
||||
{"EngageVolume", tr("Engage Volume"), tr("Related alerts:\n\nNNFF Torque Controller loaded\nopenpilot engaged"), ""},
|
||||
{"PromptVolume", tr("Prompt Volume"), tr("Related alerts:\n\nCar Detected in Blindspot\nSpeed too Low\nSteer Unavailable Below 'X'\nTake Control, Turn Exceeds Steering Limit"), ""},
|
||||
{"PromptDistractedVolume", tr("Prompt Distracted Volume"), tr("Related alerts:\n\nPay Attention, Driver Distracted\nTouch Steering Wheel, Driver Unresponsive"), ""},
|
||||
{"RefuseVolume", tr("Refuse Volume"), tr("Related alerts:\n\nopenpilot Unavailable"), ""},
|
||||
{"WarningSoftVolume", tr("Warning Soft Volume"), tr("Related alerts:\n\nBRAKE!, Risk of Collision\nTAKE CONTROL IMMEDIATELY"), ""},
|
||||
{"WarningImmediateVolume", tr("Warning Immediate Volume"), tr("Related alerts:\n\nDISENGAGE IMMEDIATELY, Driver Distracted\nDISENGAGE IMMEDIATELY, Driver Unresponsive"), ""},
|
||||
|
||||
{"CustomAlerts", tr("Custom Alerts"), tr("Custom alerts for openpilot events."), "../frogpilot/assets/toggle_icons/icon_green_light.png"},
|
||||
{"GoatScream", tr("Goat Scream Steering Saturated Alert"), tr("Enables the famed 'Goat Scream' that has brought both joy and anger to FrogPilot users all around the world!"), ""},
|
||||
{"GreenLightAlert", tr("Green Light Alert"), tr("Plays an alert when a traffic light changes from red to green."), ""},
|
||||
{"LeadDepartingAlert", tr("Lead Departing Alert"), tr("Plays an alert when the lead vehicle starts starts to depart when at a standstill."), ""},
|
||||
{"LoudBlindspotAlert", tr("Loud Blindspot Alert"), tr("Plays a louder alert for when a vehicle is detected in the blindspot when attempting to change lanes."), ""},
|
||||
{"SpeedLimitChangedAlert", tr("Speed Limit Changed Alert"), tr("Plays an alert when the speed limit changes."), ""},
|
||||
};
|
||||
|
||||
for (const auto &[param, title, desc, icon] : soundsToggles) {
|
||||
AbstractControl *soundsToggle;
|
||||
|
||||
if (param == "AlertVolumeControl") {
|
||||
FrogPilotParamManageControl *alertVolumeControlToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(alertVolumeControlToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
showToggles(alertVolumeControlKeys);
|
||||
});
|
||||
soundsToggle = alertVolumeControlToggle;
|
||||
} else if (alertVolumeControlKeys.find(param) != alertVolumeControlKeys.end()) {
|
||||
std::map<int, QString> volumeLabels;
|
||||
for (int i = 0; i <= 101; ++i) {
|
||||
volumeLabels[i] = i == 101 ? tr("Auto") : i == 0 ? tr("Muted") : QString::number(i) + "%";
|
||||
}
|
||||
std::vector<QString> alertButton{"Test"};
|
||||
if (param == "WarningImmediateVolume") {
|
||||
soundsToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 25, 101, QString(), volumeLabels, 1, {}, alertButton, false, false);
|
||||
} else {
|
||||
soundsToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0, 101, QString(), volumeLabels, 1, {}, alertButton, false, false);
|
||||
}
|
||||
|
||||
} else if (param == "CustomAlerts") {
|
||||
FrogPilotParamManageControl *customAlertsToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(customAlertsToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
std::set<QString> modifiedCustomAlertsKeys = customAlertsKeys;
|
||||
|
||||
if (!hasBSM) {
|
||||
modifiedCustomAlertsKeys.erase("LoudBlindspotAlert");
|
||||
}
|
||||
|
||||
if (!(params.getBool("ShowSpeedLimits") || hasOpenpilotLongitudinal && params.getBool("SpeedLimitController"))) {
|
||||
modifiedCustomAlertsKeys.erase("SpeedLimitChangedAlert");
|
||||
}
|
||||
|
||||
showToggles(modifiedCustomAlertsKeys);
|
||||
});
|
||||
soundsToggle = customAlertsToggle;
|
||||
|
||||
} else {
|
||||
soundsToggle = new ParamControl(param, title, desc, icon);
|
||||
}
|
||||
|
||||
addItem(soundsToggle);
|
||||
toggles[param] = soundsToggle;
|
||||
|
||||
if (FrogPilotParamManageControl *frogPilotManageToggle = qobject_cast<FrogPilotParamManageControl*>(soundsToggle)) {
|
||||
QObject::connect(frogPilotManageToggle, &FrogPilotParamManageControl::manageButtonClicked, this, &FrogPilotSoundsPanel::openParentToggle);
|
||||
}
|
||||
}
|
||||
|
||||
for (const QString &key : alertVolumeControlKeys) {
|
||||
FrogPilotParamValueButtonControl *toggle = static_cast<FrogPilotParamValueButtonControl*>(toggles[key]);
|
||||
QObject::connect(toggle, &FrogPilotParamValueButtonControl::buttonClicked, [=]() {
|
||||
QString alertKey = key;
|
||||
alertKey.remove("Volume");
|
||||
QString snakeCaseKey;
|
||||
for (int i = 0; i < alertKey.size(); ++i) {
|
||||
QChar c = alertKey[i];
|
||||
if (c.isUpper() && i > 0) {
|
||||
snakeCaseKey += '_';
|
||||
}
|
||||
snakeCaseKey += c.toLower();
|
||||
}
|
||||
|
||||
std::thread([=]() {
|
||||
playSound(snakeCaseKey.toStdString(), params.getInt(key.toStdString()));
|
||||
}).detach();
|
||||
});
|
||||
}
|
||||
|
||||
QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotSoundsPanel::hideToggles);
|
||||
}
|
||||
|
||||
void FrogPilotSoundsPanel::showEvent(QShowEvent *event) {
|
||||
frogpilotToggleLevels = parent->frogpilotToggleLevels;
|
||||
hasBSM = parent->hasBSM;
|
||||
hasOpenpilotLongitudinal = parent->hasOpenpilotLongitudinal;
|
||||
tuningLevel = parent->tuningLevel;
|
||||
|
||||
hideToggles();
|
||||
}
|
||||
|
||||
void FrogPilotSoundsPanel::showToggles(const std::set<QString> &keys) {
|
||||
setUpdatesEnabled(false);
|
||||
|
||||
for (auto &[key, toggle] : toggles) {
|
||||
toggle->setVisible(keys.find(key) != keys.end() && tuningLevel >= frogpilotToggleLevels[key].toDouble());
|
||||
}
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void FrogPilotSoundsPanel::hideToggles() {
|
||||
setUpdatesEnabled(false);
|
||||
|
||||
for (auto &[key, toggle] : toggles) {
|
||||
bool subToggles = alertVolumeControlKeys.find(key) != alertVolumeControlKeys.end() ||
|
||||
customAlertsKeys.find(key) != customAlertsKeys.end();
|
||||
|
||||
toggle->setVisible(!subToggles && tuningLevel >= frogpilotToggleLevels[key].toDouble());
|
||||
}
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
|
||||
update();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h"
|
||||
|
||||
class FrogPilotSoundsPanel : public FrogPilotListWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FrogPilotSoundsPanel(FrogPilotSettingsWindow *parent);
|
||||
|
||||
signals:
|
||||
void openParentToggle();
|
||||
|
||||
private:
|
||||
void hideToggles();
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void showToggles(const std::set<QString> &keys);
|
||||
|
||||
bool hasBSM;
|
||||
bool hasOpenpilotLongitudinal;
|
||||
|
||||
int tuningLevel;
|
||||
|
||||
std::map<QString, AbstractControl*> toggles;
|
||||
|
||||
std::set<QString> alertVolumeControlKeys = {"DisengageVolume", "EngageVolume", "PromptDistractedVolume", "PromptVolume", "RefuseVolume", "WarningImmediateVolume", "WarningSoftVolume"};
|
||||
std::set<QString> customAlertsKeys = {"GoatScream", "GreenLightAlert", "LeadDepartingAlert", "LoudBlindspotAlert", "SpeedLimitChangedAlert"};
|
||||
|
||||
FrogPilotSettingsWindow *parent;
|
||||
|
||||
QJsonObject frogpilotToggleLevels;
|
||||
|
||||
Params params;
|
||||
};
|
||||
@@ -0,0 +1,615 @@
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/theme_settings.h"
|
||||
|
||||
void updateAssetParam(const QString &assetParam, Params ¶ms, const QString &value, bool add) {
|
||||
QStringList assets = QString::fromStdString(params.get(assetParam.toStdString())).split(",", QString::SkipEmptyParts);
|
||||
|
||||
if (add) {
|
||||
if (!assets.contains(value)) {
|
||||
assets.append(value);
|
||||
}
|
||||
} else {
|
||||
assets.removeAll(value);
|
||||
}
|
||||
|
||||
assets.sort();
|
||||
params.put(assetParam.toStdString(), assets.join(",").toStdString());
|
||||
}
|
||||
|
||||
void deleteThemeAsset(QDir &directory, const QString &subFolder, const QString &assetParam, const QString &themeToDelete, Params ¶ms) {
|
||||
bool useFiles = subFolder.isEmpty();
|
||||
|
||||
QString themeName = themeToDelete.toLower().replace(" (", "-").replace(")", "").replace(' ', '-');
|
||||
if (useFiles) {
|
||||
for (const QString &file : directory.entryList(QDir::Files)) {
|
||||
QString fileName = QFileInfo(file).baseName().toLower().replace('_', '-');
|
||||
if (fileName == themeName) {
|
||||
QFile::remove(directory.filePath(file));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QDir targetDir(directory.filePath(QDir(themeName).filePath(subFolder)));
|
||||
if (targetDir.exists()) {
|
||||
targetDir.removeRecursively();
|
||||
}
|
||||
}
|
||||
|
||||
updateAssetParam(assetParam, params, themeToDelete, true);
|
||||
}
|
||||
|
||||
void downloadThemeAsset(const QString &input, const std::string ¶mKey, const QString &assetParam, Params ¶ms, Params ¶ms_memory) {
|
||||
QString output = input.toLower().remove('(').remove(')');
|
||||
output.replace(' ', input.contains('(') ? '-' : '_');
|
||||
params_memory.put(paramKey, output.toStdString());
|
||||
|
||||
updateAssetParam(assetParam, params, input, false);
|
||||
}
|
||||
|
||||
QStringList getThemeList(const QDir &themePacksDirectory, const QString &subFolder, const QString &assetParam, Params ¶ms) {
|
||||
bool useFiles = subFolder.isEmpty();
|
||||
|
||||
QString currentAsset = QString::fromStdString(params.get(assetParam.toStdString()));
|
||||
QStringList themeList;
|
||||
for (const QFileInfo &entry : themePacksDirectory.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) {
|
||||
if (entry.baseName() == currentAsset) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (useFiles && entry.isDir()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!useFiles) {
|
||||
QString targetPath = QDir(entry.filePath()).filePath(subFolder);
|
||||
if (!QFileInfo(targetPath).exists()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
QChar delimiter = entry.baseName().contains('-') ? '-' : '_';
|
||||
QStringList parts = entry.baseName().split(delimiter, QString::SkipEmptyParts);
|
||||
for (QString &part : parts) {
|
||||
part[0] = part[0].toUpper();
|
||||
}
|
||||
|
||||
themeList.append(parts.size() <= 1 || useFiles ? parts.join(' ') : QString("%1 (%2)").arg(parts[0], parts.mid(1).join(' ')));
|
||||
}
|
||||
|
||||
return themeList;
|
||||
}
|
||||
|
||||
QString getThemeName(const std::string ¶mKey, Params ¶ms) {
|
||||
QString value = QString::fromStdString(params.get(paramKey));
|
||||
QChar delimiter = value.contains('-') ? '-' : '_';
|
||||
|
||||
QStringList parts = value.split(delimiter, QString::SkipEmptyParts);
|
||||
for (QString &part : parts) {
|
||||
part[0] = part[0].toUpper();
|
||||
}
|
||||
|
||||
if (value.contains('-') && parts.size() > 1) {
|
||||
return QString("%1 (%2)").arg(parts[0], parts.mid(1).join(' '));
|
||||
}
|
||||
return parts.join(' ');
|
||||
}
|
||||
|
||||
QString storeThemeName(const QString &input, const std::string ¶mKey, Params ¶ms) {
|
||||
QString output = input.toLower().remove('(').remove(')');
|
||||
output.replace(' ', input.contains('(') ? '-' : '_');
|
||||
params.put(paramKey, output.toStdString());
|
||||
return getThemeName(paramKey, params);
|
||||
}
|
||||
|
||||
FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
|
||||
const std::vector<std::tuple<QString, QString, QString, QString>> themeToggles {
|
||||
{"PersonalizeOpenpilot", tr("Custom Theme"), tr("Custom openpilot themes."), "../frogpilot/assets/toggle_icons/frog.png"},
|
||||
{"CustomColors", tr("Color Scheme"), tr("Changes out openpilot's color scheme.\n\nWant to submit your own color scheme? Share it in the 'custom-themes' channel on the FrogPilot Discord!"), ""},
|
||||
{"CustomDistanceIcons", "Distance Button", "Changes out openpilot's distance button icons.\n\nWant to submit your own icon pack? Share it in the 'custom-themes' channel on the FrogPilot Discord!", ""},
|
||||
{"CustomIcons", tr("Icon Pack"), tr("Changes out openpilot's icon pack.\n\nWant to submit your own icons? Share them in the 'custom-themes' channel on the FrogPilot Discord!"), ""},
|
||||
{"CustomSounds", tr("Sound Pack"), tr("Changes out openpilot's sound effects.\n\nWant to submit your own sounds? Share them in the 'custom-themes' channel on the FrogPilot Discord!"), ""},
|
||||
{"WheelIcon", tr("Steering Wheel"), tr("Enables a custom steering wheel icon in the top right of the screen."), ""},
|
||||
{"CustomSignals", tr("Turn Signal Animation"), tr("Enables themed turn signal animations.\n\nWant to submit your own animations? Share them in the 'custom-themes' channel on the FrogPilot Discord!"), ""},
|
||||
{"DownloadStatusLabel", tr("Download Status"), "", ""},
|
||||
|
||||
{"HolidayThemes", tr("Holiday Themes"), tr("Changes the openpilot theme based on the current holiday. Minor holidays last one day, while major holidays (Easter, Christmas, Halloween, etc.) last the entire week."), "../frogpilot/assets/toggle_icons/icon_calendar.png"},
|
||||
|
||||
{"RainbowPath", tr("Rainbow Path"), tr("Swap out the path in the onroad UI for a Mario Kart inspired 'Rainbow Path'."), "../frogpilot/assets/toggle_icons/icon_rainbow.png"},
|
||||
|
||||
{"RandomEvents", tr("Random Events"), tr("Enables random cosmetic events that happen during certain driving conditions. These events are purely for fun and don't affect driving controls!"), "../frogpilot/assets/toggle_icons/icon_random.png"},
|
||||
|
||||
{"StartupAlert", tr("Startup Alert"), tr("Controls the text of the 'Startup' alert message that appears when you start the drive."), "../frogpilot/assets/toggle_icons/icon_message.png"}
|
||||
};
|
||||
|
||||
for (const auto &[param, title, desc, icon] : themeToggles) {
|
||||
AbstractControl *themeToggle;
|
||||
|
||||
if (param == "PersonalizeOpenpilot") {
|
||||
FrogPilotParamManageControl *personalizeOpenpilotToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(personalizeOpenpilotToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
showToggles(customThemeKeys);
|
||||
});
|
||||
themeToggle = personalizeOpenpilotToggle;
|
||||
} else if (param == "CustomColors") {
|
||||
manageCustomColorsBtn = new FrogPilotButtonsControl(title, desc, {tr("DELETE"), tr("DOWNLOAD"), tr("SELECT")});
|
||||
QObject::connect(manageCustomColorsBtn, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
|
||||
QStringList colorSchemes = getThemeList(QDir(themePacksDirectory.path()), "colors", "CustomColors", params);
|
||||
|
||||
if (id == 0) {
|
||||
QString colorSchemeToDelete = MultiOptionDialog::getSelection(tr("Select a color scheme to delete"), colorSchemes, "", this);
|
||||
if (!colorSchemeToDelete.isEmpty() && ConfirmationDialog::confirm(tr("Are you sure you want to delete the '%1' color scheme?").arg(colorSchemeToDelete), tr("Delete"), this)) {
|
||||
colorsDownloaded = false;
|
||||
|
||||
deleteThemeAsset(themePacksDirectory, "colors", "DownloadableColors", colorSchemeToDelete, params);
|
||||
}
|
||||
} else if (id == 1) {
|
||||
if (colorDownloading) {
|
||||
cancellingDownload = true;
|
||||
|
||||
params_memory.putBool("CancelThemeDownload", true);
|
||||
|
||||
updateAssetParam("DownloadableColors", params, colorSchemeToDownload, true);
|
||||
|
||||
QTimer::singleShot(2500, [this]() {
|
||||
cancellingDownload = false;
|
||||
colorDownloading = false;
|
||||
themeDownloading = false;
|
||||
|
||||
params_memory.putBool("CancelThemeDownload", false);
|
||||
});
|
||||
} else {
|
||||
QStringList downloadableColorSchemes = QString::fromStdString(params.get("DownloadableColors")).split(",");
|
||||
colorSchemeToDownload = MultiOptionDialog::getSelection(tr("Select a color scheme to download"), downloadableColorSchemes, "", this);
|
||||
if (!colorSchemeToDownload.isEmpty()) {
|
||||
colorDownloading = true;
|
||||
themeDownloading = true;
|
||||
|
||||
params_memory.put("ThemeDownloadProgress", "Downloading...");
|
||||
|
||||
downloadThemeAsset(colorSchemeToDownload, "ColorToDownload", "DownloadableColors", params, params_memory);
|
||||
downloadStatusLabel->setText("Downloading...");
|
||||
}
|
||||
}
|
||||
} else if (id == 2) {
|
||||
colorSchemes.append("Stock");
|
||||
colorSchemes.sort();
|
||||
QString colorSchemeToSelect = MultiOptionDialog::getSelection(tr("Select a color scheme"), colorSchemes, getThemeName("CustomColors", params), this);
|
||||
if (!colorSchemeToSelect.isEmpty()) {
|
||||
manageCustomColorsBtn->setValue(storeThemeName(colorSchemeToSelect, "CustomColors", params));
|
||||
}
|
||||
}
|
||||
});
|
||||
manageCustomColorsBtn->setValue(getThemeName(param.toStdString(), params));
|
||||
themeToggle = manageCustomColorsBtn;
|
||||
} else if (param == "CustomDistanceIcons") {
|
||||
manageDistanceIconsBtn = new FrogPilotButtonsControl(title, desc, {tr("DELETE"), tr("DOWNLOAD"), tr("SELECT")});
|
||||
QObject::connect(manageDistanceIconsBtn, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
|
||||
QStringList distanceIconPacks = getThemeList(QDir(themePacksDirectory.path()), "distance_icons", "CustomDistanceIcons", params);
|
||||
|
||||
if (id == 0) {
|
||||
QString distanceIconPackToDelete = MultiOptionDialog::getSelection(tr("Select a distance icon pack to delete"), distanceIconPacks, "", this);
|
||||
if (!distanceIconPackToDelete.isEmpty() && ConfirmationDialog::confirm(tr("Are you sure you want to delete the '%1' distance icon pack?").arg(distanceIconPackToDelete), tr("Delete"), this)) {
|
||||
distanceIconsDownloaded = false;
|
||||
|
||||
deleteThemeAsset(themePacksDirectory, "distance_icons", "DownloadableDistanceIcons", distanceIconPackToDelete, params);
|
||||
}
|
||||
} else if (id == 1) {
|
||||
if (distanceIconDownloading) {
|
||||
cancellingDownload = true;
|
||||
|
||||
params_memory.putBool("CancelThemeDownload", true);
|
||||
|
||||
updateAssetParam("DownloadableDistanceIcons", params, distanceIconPackToDownload, true);
|
||||
|
||||
QTimer::singleShot(2500, [this]() {
|
||||
cancellingDownload = false;
|
||||
distanceIconDownloading = false;
|
||||
themeDownloading = false;
|
||||
|
||||
params_memory.putBool("CancelThemeDownload", false);
|
||||
});
|
||||
} else {
|
||||
QStringList downloadableDistanceIconPacks = QString::fromStdString(params.get("DownloadableDistanceIcons")).split(",");
|
||||
distanceIconPackToDownload = MultiOptionDialog::getSelection(tr("Select a distance icon pack to download"), downloadableDistanceIconPacks, "", this);
|
||||
if (!distanceIconPackToDownload.isEmpty()) {
|
||||
distanceIconDownloading = true;
|
||||
themeDownloading = true;
|
||||
|
||||
params_memory.put("ThemeDownloadProgress", "Downloading...");
|
||||
|
||||
downloadThemeAsset(distanceIconPackToDownload, "DistanceIconToDownload", "DownloadableDistanceIcons", params, params_memory);
|
||||
downloadStatusLabel->setText("Downloading...");
|
||||
}
|
||||
}
|
||||
} else if (id == 2) {
|
||||
distanceIconPacks.append("Stock");
|
||||
distanceIconPacks.sort();
|
||||
QString distanceIconPackToSelect = MultiOptionDialog::getSelection(tr("Select a distance icon pack"), distanceIconPacks, getThemeName("CustomDistanceIcons", params), this);
|
||||
if (!distanceIconPackToSelect.isEmpty()) {
|
||||
manageDistanceIconsBtn->setValue(storeThemeName(distanceIconPackToSelect, "CustomDistanceIcons", params));
|
||||
}
|
||||
}
|
||||
});
|
||||
manageDistanceIconsBtn->setValue(getThemeName(param.toStdString(), params));
|
||||
themeToggle = manageDistanceIconsBtn;
|
||||
} else if (param == "CustomIcons") {
|
||||
manageCustomIconsBtn = new FrogPilotButtonsControl(title, desc, {tr("DELETE"), tr("DOWNLOAD"), tr("SELECT")});
|
||||
QObject::connect(manageCustomIconsBtn, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
|
||||
QStringList iconPacks = getThemeList(QDir(themePacksDirectory.path()), "icons", "CustomIcons", params);
|
||||
|
||||
if (id == 0) {
|
||||
QString iconPackToDelete = MultiOptionDialog::getSelection(tr("Select an icon pack to delete"), iconPacks, "", this);
|
||||
if (!iconPackToDelete.isEmpty() && ConfirmationDialog::confirm(tr("Are you sure you want to delete the '%1' icon pack?").arg(iconPackToDelete), tr("Delete"), this)) {
|
||||
iconsDownloaded = false;
|
||||
|
||||
deleteThemeAsset(themePacksDirectory, "icons", "DownloadableIcons", iconPackToDelete, params);
|
||||
}
|
||||
} else if (id == 1) {
|
||||
if (iconDownloading) {
|
||||
cancellingDownload = true;
|
||||
|
||||
params_memory.putBool("CancelThemeDownload", true);
|
||||
|
||||
updateAssetParam("DownloadableIcons", params, colorSchemeToDownload, true);
|
||||
|
||||
QTimer::singleShot(2500, [this]() {
|
||||
cancellingDownload = false;
|
||||
iconDownloading = false;
|
||||
themeDownloading = false;
|
||||
|
||||
params_memory.putBool("CancelThemeDownload", false);
|
||||
});
|
||||
} else {
|
||||
QStringList downloadableIconPacks = QString::fromStdString(params.get("DownloadableIcons")).split(",");
|
||||
iconPackToDownload = MultiOptionDialog::getSelection(tr("Select an icon pack to download"), downloadableIconPacks, "", this);
|
||||
if (!iconPackToDownload.isEmpty()) {
|
||||
iconDownloading = true;
|
||||
themeDownloading = true;
|
||||
|
||||
params_memory.put("ThemeDownloadProgress", "Downloading...");
|
||||
|
||||
downloadThemeAsset(iconPackToDownload, "IconToDownload", "DownloadableIcons", params, params_memory);
|
||||
downloadStatusLabel->setText("Downloading...");
|
||||
}
|
||||
}
|
||||
} else if (id == 2) {
|
||||
iconPacks.append("Stock");
|
||||
iconPacks.sort();
|
||||
QString iconPackToSelect = MultiOptionDialog::getSelection(tr("Select an icon pack"), iconPacks, getThemeName("CustomIcons", params), this);
|
||||
if (!iconPackToSelect.isEmpty()) {
|
||||
manageCustomIconsBtn->setValue(storeThemeName(iconPackToSelect, "CustomIcons", params));
|
||||
}
|
||||
}
|
||||
});
|
||||
manageCustomIconsBtn->setValue(getThemeName(param.toStdString(), params));
|
||||
themeToggle = manageCustomIconsBtn;
|
||||
} else if (param == "CustomSignals") {
|
||||
manageCustomSignalsBtn = new FrogPilotButtonsControl(title, desc, {tr("DELETE"), tr("DOWNLOAD"), tr("SELECT")});
|
||||
QObject::connect(manageCustomSignalsBtn, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
|
||||
QStringList signalAnimations = getThemeList(QDir(themePacksDirectory.path()), "signals", "CustomSignals", params);
|
||||
|
||||
if (id == 0) {
|
||||
QString signalAnimationToDelete = MultiOptionDialog::getSelection(tr("Select a signal animation to delete"), signalAnimations, "", this);
|
||||
if (!signalAnimationToDelete.isEmpty() && ConfirmationDialog::confirm(tr("Are you sure you want to delete the '%1' signal animation?").arg(signalAnimationToDelete), tr("Delete"), this)) {
|
||||
signalsDownloaded = false;
|
||||
|
||||
deleteThemeAsset(themePacksDirectory, "signals", "DownloadableSignals", signalAnimationToDelete, params);
|
||||
}
|
||||
} else if (id == 1) {
|
||||
if (signalDownloading) {
|
||||
cancellingDownload = true;
|
||||
|
||||
params_memory.putBool("CancelThemeDownload", true);
|
||||
|
||||
updateAssetParam("DownloadableSignals", params, colorSchemeToDownload, true);
|
||||
|
||||
QTimer::singleShot(2500, [this]() {
|
||||
cancellingDownload = false;
|
||||
signalDownloading = false;
|
||||
themeDownloading = false;
|
||||
|
||||
params_memory.putBool("CancelThemeDownload", false);
|
||||
});
|
||||
} else {
|
||||
QStringList downloadableSignalAnimations = QString::fromStdString(params.get("DownloadableSignals")).split(",");
|
||||
signalAnimationToDownload = MultiOptionDialog::getSelection(tr("Select a signal animation to download"), downloadableSignalAnimations, "", this);
|
||||
if (!signalAnimationToDownload.isEmpty()) {
|
||||
signalDownloading = true;
|
||||
themeDownloading = true;
|
||||
|
||||
params_memory.put("ThemeDownloadProgress", "Downloading...");
|
||||
|
||||
downloadThemeAsset(signalAnimationToDownload, "SignalToDownload", "DownloadableSignals", params, params_memory);
|
||||
downloadStatusLabel->setText("Downloading...");
|
||||
}
|
||||
}
|
||||
} else if (id == 2) {
|
||||
signalAnimations.append("None");
|
||||
signalAnimations.sort();
|
||||
QString signalAnimationToSelect = MultiOptionDialog::getSelection(tr("Select a signal animation"), signalAnimations, getThemeName("CustomSignals", params), this);
|
||||
if (!signalAnimationToSelect.isEmpty()) {
|
||||
manageCustomSignalsBtn->setValue(storeThemeName(signalAnimationToSelect, "CustomSignals", params));
|
||||
}
|
||||
}
|
||||
});
|
||||
manageCustomSignalsBtn->setValue(getThemeName(param.toStdString(), params));
|
||||
themeToggle = manageCustomSignalsBtn;
|
||||
} else if (param == "CustomSounds") {
|
||||
manageCustomSoundsBtn = new FrogPilotButtonsControl(title, desc, {tr("DELETE"), tr("DOWNLOAD"), tr("SELECT")});
|
||||
QObject::connect(manageCustomSoundsBtn, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
|
||||
QStringList soundPacks = getThemeList(QDir(themePacksDirectory.path()), "sounds", "CustomSounds", params);
|
||||
|
||||
if (id == 0) {
|
||||
QString soundPackToDelete = MultiOptionDialog::getSelection(tr("Select a sound pack to delete"), soundPacks, "", this);
|
||||
if (!soundPackToDelete.isEmpty() && ConfirmationDialog::confirm(tr("Are you sure you want to delete the '%1' sound pack?").arg(soundPackToDelete), tr("Delete"), this)) {
|
||||
soundsDownloaded = false;
|
||||
|
||||
deleteThemeAsset(themePacksDirectory, "sounds", "DownloadableSounds", soundPackToDelete, params);
|
||||
}
|
||||
} else if (id == 1) {
|
||||
if (soundDownloading) {
|
||||
cancellingDownload = true;
|
||||
|
||||
params_memory.putBool("CancelThemeDownload", true);
|
||||
|
||||
updateAssetParam("DownloadableSounds", params, colorSchemeToDownload, true);
|
||||
|
||||
QTimer::singleShot(2500, [this]() {
|
||||
cancellingDownload = false;
|
||||
soundDownloading = false;
|
||||
themeDownloading = false;
|
||||
|
||||
params_memory.putBool("CancelThemeDownload", false);
|
||||
});
|
||||
} else {
|
||||
QStringList downloadableSoundPacks = QString::fromStdString(params.get("DownloadableSounds")).split(",");
|
||||
soundPackToDownload = MultiOptionDialog::getSelection(tr("Select a sound pack to download"), downloadableSoundPacks, "", this);
|
||||
if (!soundPackToDownload.isEmpty()) {
|
||||
soundDownloading = true;
|
||||
themeDownloading = true;
|
||||
|
||||
params_memory.put("ThemeDownloadProgress", "Downloading...");
|
||||
|
||||
downloadThemeAsset(soundPackToDownload, "SoundToDownload", "DownloadableSounds", params, params_memory);
|
||||
downloadStatusLabel->setText("Downloading...");
|
||||
}
|
||||
}
|
||||
} else if (id == 2) {
|
||||
soundPacks.append("Stock");
|
||||
soundPacks.sort();
|
||||
QString soundPackToSelect = MultiOptionDialog::getSelection(tr("Select a sound pack"), soundPacks, getThemeName("CustomSounds", params), this);
|
||||
if (!soundPackToSelect.isEmpty()) {
|
||||
manageCustomSoundsBtn->setValue(storeThemeName(soundPackToSelect, "CustomSounds", params));
|
||||
}
|
||||
}
|
||||
});
|
||||
manageCustomSoundsBtn->setValue(getThemeName(param.toStdString(), params));
|
||||
themeToggle = manageCustomSoundsBtn;
|
||||
} else if (param == "WheelIcon") {
|
||||
manageWheelIconsBtn = new FrogPilotButtonsControl(title, desc, {tr("DELETE"), tr("DOWNLOAD"), tr("SELECT")});
|
||||
QObject::connect(manageWheelIconsBtn, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
|
||||
QStringList wheelIcons = getThemeList(QDir(wheelsDirectory.path()), "", "WheelIcon", params);
|
||||
|
||||
if (id == 0) {
|
||||
QString wheelIconToDelete = MultiOptionDialog::getSelection(tr("Select a steering wheel to delete"), wheelIcons, "", this);
|
||||
if (!wheelIconToDelete.isEmpty() && ConfirmationDialog::confirm(tr("Are you sure you want to delete the '%1' steering wheel?").arg(wheelIconToDelete), tr("Delete"), this)) {
|
||||
wheelsDownloaded = false;
|
||||
|
||||
deleteThemeAsset(wheelsDirectory, "", "DownloadableWheels", wheelIconToDelete, params);
|
||||
}
|
||||
} else if (id == 1) {
|
||||
if (wheelDownloading) {
|
||||
cancellingDownload = true;
|
||||
|
||||
params_memory.putBool("CancelThemeDownload", true);
|
||||
|
||||
updateAssetParam("DownloadableWheels", params, colorSchemeToDownload, true);
|
||||
|
||||
QTimer::singleShot(2500, [this]() {
|
||||
cancellingDownload = false;
|
||||
wheelDownloading = false;
|
||||
themeDownloading = false;
|
||||
|
||||
params_memory.putBool("CancelThemeDownload", false);
|
||||
});
|
||||
} else {
|
||||
QStringList downloadableWheels = QString::fromStdString(params.get("DownloadableWheels")).split(",");
|
||||
wheelToDownload = MultiOptionDialog::getSelection(tr("Select a steering wheel to download"), downloadableWheels, "", this);
|
||||
if (!wheelToDownload.isEmpty()) {
|
||||
wheelDownloading = true;
|
||||
themeDownloading = true;
|
||||
|
||||
params_memory.put("ThemeDownloadProgress", "Downloading...");
|
||||
|
||||
downloadThemeAsset(wheelToDownload, "WheelToDownload", "DownloadableWheels", params, params_memory);
|
||||
downloadStatusLabel->setText("Downloading...");
|
||||
}
|
||||
}
|
||||
} else if (id == 2) {
|
||||
wheelIcons.append("None");
|
||||
wheelIcons.append("Stock");
|
||||
wheelIcons.sort();
|
||||
QString steeringWheelToSelect = MultiOptionDialog::getSelection(tr("Select a steering wheel"), wheelIcons, getThemeName("WheelIcon", params), this);
|
||||
if (!steeringWheelToSelect.isEmpty()) {
|
||||
manageWheelIconsBtn->setValue(storeThemeName(steeringWheelToSelect, "WheelIcon", params));
|
||||
}
|
||||
}
|
||||
});
|
||||
manageWheelIconsBtn->setValue(getThemeName(param.toStdString(), params));
|
||||
themeToggle = manageWheelIconsBtn;
|
||||
} else if (param == "DownloadStatusLabel") {
|
||||
downloadStatusLabel = new LabelControl(title, "Idle");
|
||||
themeToggle = downloadStatusLabel;
|
||||
} else if (param == "StartupAlert") {
|
||||
FrogPilotButtonsControl *startupAlertButton = new FrogPilotButtonsControl(title, desc, {tr("STOCK"), tr("FROGPILOT"), tr("CUSTOM"), tr("CLEAR")}, false, true, icon);
|
||||
QObject::connect(startupAlertButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
|
||||
int maxLengthTop = 35;
|
||||
int maxLengthBottom = 45;
|
||||
|
||||
QString stockTop = "Be ready to take over at any time";
|
||||
QString stockBottom = "Always keep hands on wheel and eyes on road";
|
||||
|
||||
QString frogpilotTop = "Hop in and buckle up!";
|
||||
QString frogpilotBottom = "Human-tested, frog-approved 🐸";
|
||||
|
||||
QString currentTop = QString::fromStdString(params.get("StartupMessageTop"));
|
||||
QString currentBottom = QString::fromStdString(params.get("StartupMessageBottom"));
|
||||
|
||||
if (id == 0) {
|
||||
params.put("StartupMessageTop", stockTop.toStdString());
|
||||
params.put("StartupMessageBottom", stockBottom.toStdString());
|
||||
} else if (id == 1) {
|
||||
params.put("StartupMessageTop", frogpilotTop.toStdString());
|
||||
params.put("StartupMessageBottom", frogpilotBottom.toStdString());
|
||||
} else if (id == 2) {
|
||||
QString newTop = InputDialog::getText(tr("Enter the text for the top half"), this, tr("Characters: 0/%1").arg(maxLengthTop), false, -1, currentTop, maxLengthTop).trimmed();
|
||||
if (newTop.length() > 0) {
|
||||
params.put("StartupMessageTop", newTop.toStdString());
|
||||
QString newBottom = InputDialog::getText(tr("Enter the text for the bottom half"), this, tr("Characters: 0/%1").arg(maxLengthBottom), false, -1, currentBottom, maxLengthBottom).trimmed();
|
||||
if (newBottom.length() > 0) {
|
||||
params.put("StartupMessageBottom", newBottom.toStdString());
|
||||
}
|
||||
}
|
||||
} else if (id == 3) {
|
||||
params.remove("StartupMessageTop");
|
||||
params.remove("StartupMessageBottom");
|
||||
}
|
||||
});
|
||||
themeToggle = startupAlertButton;
|
||||
|
||||
} else {
|
||||
themeToggle = new ParamControl(param, title, desc, icon);
|
||||
}
|
||||
|
||||
addItem(themeToggle);
|
||||
toggles[param] = themeToggle;
|
||||
|
||||
if (FrogPilotParamManageControl *frogPilotManageToggle = qobject_cast<FrogPilotParamManageControl*>(themeToggle)) {
|
||||
QObject::connect(frogPilotManageToggle, &FrogPilotParamManageControl::manageButtonClicked, this, &FrogPilotThemesPanel::openParentToggle);
|
||||
}
|
||||
}
|
||||
|
||||
QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotThemesPanel::hideToggles);
|
||||
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotThemesPanel::updateState);
|
||||
}
|
||||
|
||||
void FrogPilotThemesPanel::showEvent(QShowEvent *event) {
|
||||
colorsDownloaded = params.get("DownloadableColors").empty();
|
||||
distanceIconsDownloaded = params.get("DownloadableDistanceIcons").empty();
|
||||
iconsDownloaded = params.get("DownloadableIcons").empty();
|
||||
signalsDownloaded = params.get("DownloadableSignals").empty();
|
||||
soundsDownloaded = params.get("DownloadableSounds").empty();
|
||||
wheelsDownloaded = params.get("DownloadableWheels").empty();
|
||||
|
||||
frogpilotToggleLevels = parent->frogpilotToggleLevels;
|
||||
tuningLevel = parent->tuningLevel;
|
||||
|
||||
hideToggles();
|
||||
}
|
||||
|
||||
void FrogPilotThemesPanel::updateState(const UIState &s) {
|
||||
if (!isVisible() || finalizingDownload) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (themeDownloading) {
|
||||
QString progress = QString::fromStdString(params_memory.get("ThemeDownloadProgress"));
|
||||
bool downloadFailed = progress.contains(QRegularExpression("cancelled|exists|failed|offline", QRegularExpression::CaseInsensitiveOption));
|
||||
|
||||
if (progress != "Downloading...") {
|
||||
downloadStatusLabel->setText(progress);
|
||||
}
|
||||
|
||||
if (progress == "Downloaded!" || downloadFailed) {
|
||||
finalizingDownload = true;
|
||||
|
||||
QTimer::singleShot(2500, [this]() {
|
||||
colorDownloading = false;
|
||||
distanceIconDownloading = false;
|
||||
finalizingDownload = false;
|
||||
iconDownloading = false;
|
||||
signalDownloading = false;
|
||||
soundDownloading = false;
|
||||
themeDownloading = false;
|
||||
wheelDownloading = false;
|
||||
|
||||
colorsDownloaded = params.get("DownloadableColors").empty();
|
||||
distanceIconsDownloaded = params.get("DownloadableDistanceIcons").empty();
|
||||
iconsDownloaded = params.get("DownloadableIcons").empty();
|
||||
signalsDownloaded = params.get("DownloadableSignals").empty();
|
||||
soundsDownloaded = params.get("DownloadableSounds").empty();
|
||||
wheelsDownloaded = params.get("DownloadableWheels").empty();
|
||||
|
||||
params_memory.remove("CancelThemeDownload");
|
||||
params_memory.remove("ColorToDownload");
|
||||
params_memory.remove("DistanceIconToDownload");
|
||||
params_memory.remove("IconToDownload");
|
||||
params_memory.remove("SignalToDownload");
|
||||
params_memory.remove("SoundToDownload");
|
||||
params_memory.remove("ThemeDownloadProgress");
|
||||
params_memory.remove("WheelToDownload");
|
||||
|
||||
downloadStatusLabel->setText("Idle");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool parked = !s.scene.started || s.scene.parked || s.scene.frogs_go_moo;
|
||||
|
||||
manageCustomColorsBtn->setText(1, colorDownloading ? tr("CANCEL") : tr("DOWNLOAD"));
|
||||
manageCustomColorsBtn->setEnabledButtons(0, !themeDownloading);
|
||||
manageCustomColorsBtn->setEnabledButtons(1, s.scene.online && (!themeDownloading || colorDownloading) && !cancellingDownload && !colorsDownloaded && parked);
|
||||
manageCustomColorsBtn->setEnabledButtons(2, !themeDownloading);
|
||||
|
||||
manageCustomIconsBtn->setText(1, iconDownloading ? tr("CANCEL") : tr("DOWNLOAD"));
|
||||
manageCustomIconsBtn->setEnabledButtons(0, !themeDownloading);
|
||||
manageCustomIconsBtn->setEnabledButtons(1, s.scene.online && (!themeDownloading || iconDownloading) && !cancellingDownload && !iconsDownloaded && parked);
|
||||
manageCustomIconsBtn->setEnabledButtons(2, !themeDownloading);
|
||||
|
||||
manageCustomSignalsBtn->setText(1, signalDownloading ? tr("CANCEL") : tr("DOWNLOAD"));
|
||||
manageCustomSignalsBtn->setEnabledButtons(0, !themeDownloading);
|
||||
manageCustomSignalsBtn->setEnabledButtons(1, s.scene.online && (!themeDownloading || signalDownloading) && !cancellingDownload && !signalsDownloaded && parked);
|
||||
manageCustomSignalsBtn->setEnabledButtons(2, !themeDownloading);
|
||||
|
||||
manageCustomSoundsBtn->setText(1, soundDownloading ? tr("CANCEL") : tr("DOWNLOAD"));
|
||||
manageCustomSoundsBtn->setEnabledButtons(0, !themeDownloading);
|
||||
manageCustomSoundsBtn->setEnabledButtons(1, s.scene.online && (!themeDownloading || soundDownloading) && !cancellingDownload && !soundsDownloaded && parked);
|
||||
manageCustomSoundsBtn->setEnabledButtons(2, !themeDownloading);
|
||||
|
||||
manageDistanceIconsBtn->setText(1, distanceIconDownloading ? tr("CANCEL") : tr("DOWNLOAD"));
|
||||
manageDistanceIconsBtn->setEnabledButtons(0, !themeDownloading);
|
||||
manageDistanceIconsBtn->setEnabledButtons(1, s.scene.online && (!themeDownloading || distanceIconDownloading) && !cancellingDownload && !distanceIconsDownloaded && parked);
|
||||
manageDistanceIconsBtn->setEnabledButtons(2, !themeDownloading);
|
||||
|
||||
manageWheelIconsBtn->setText(1, wheelDownloading ? tr("CANCEL") : tr("DOWNLOAD"));
|
||||
manageWheelIconsBtn->setEnabledButtons(0, !themeDownloading);
|
||||
manageWheelIconsBtn->setEnabledButtons(1, s.scene.online && (!themeDownloading || wheelDownloading) && !cancellingDownload && !wheelsDownloaded && parked);
|
||||
manageWheelIconsBtn->setEnabledButtons(2, !themeDownloading);
|
||||
|
||||
parent->keepScreenOn = themeDownloading;
|
||||
}
|
||||
|
||||
void FrogPilotThemesPanel::showToggles(const std::set<QString> &keys) {
|
||||
setUpdatesEnabled(false);
|
||||
|
||||
for (auto &[key, toggle] : toggles) {
|
||||
toggle->setVisible(keys.find(key) != keys.end() && tuningLevel >= frogpilotToggleLevels[key].toDouble());
|
||||
}
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void FrogPilotThemesPanel::hideToggles() {
|
||||
setUpdatesEnabled(false);
|
||||
|
||||
for (auto &[key, toggle] : toggles) {
|
||||
bool subToggles = customThemeKeys.find(key) != customThemeKeys.end();
|
||||
|
||||
toggle->setVisible(!subToggles && tuningLevel >= frogpilotToggleLevels[key].toDouble());
|
||||
}
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
|
||||
update();
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h"
|
||||
|
||||
class FrogPilotThemesPanel : public FrogPilotListWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FrogPilotThemesPanel(FrogPilotSettingsWindow *parent);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *event) override;
|
||||
|
||||
signals:
|
||||
void openParentToggle();
|
||||
|
||||
private:
|
||||
void hideToggles();
|
||||
void showToggles(const std::set<QString> &keys);
|
||||
void updateState(const UIState &s);
|
||||
|
||||
bool cancellingDownload;
|
||||
bool colorDownloading;
|
||||
bool colorsDownloaded;
|
||||
bool distanceIconDownloading;
|
||||
bool distanceIconsDownloaded;
|
||||
bool finalizingDownload;
|
||||
bool iconDownloading;
|
||||
bool iconsDownloaded;
|
||||
bool signalDownloading;
|
||||
bool signalsDownloaded;
|
||||
bool soundDownloading;
|
||||
bool soundsDownloaded;
|
||||
bool themeDownloading;
|
||||
bool wheelDownloading;
|
||||
bool wheelsDownloaded;
|
||||
|
||||
int tuningLevel;
|
||||
|
||||
std::map<QString, AbstractControl*> toggles;
|
||||
|
||||
std::set<QString> customThemeKeys = {"CustomColors", "CustomDistanceIcons", "CustomIcons", "CustomSignals", "CustomSounds", "DownloadStatusLabel", "WheelIcon"};
|
||||
|
||||
FrogPilotButtonsControl *manageCustomColorsBtn;
|
||||
FrogPilotButtonsControl *manageCustomIconsBtn;
|
||||
FrogPilotButtonsControl *manageCustomSignalsBtn;
|
||||
FrogPilotButtonsControl *manageCustomSoundsBtn;
|
||||
FrogPilotButtonsControl *manageDistanceIconsBtn;
|
||||
FrogPilotButtonsControl *manageWheelIconsBtn;
|
||||
|
||||
FrogPilotSettingsWindow *parent;
|
||||
|
||||
LabelControl *downloadStatusLabel;
|
||||
|
||||
QDir themePacksDirectory{"/data/themes/theme_packs/"};
|
||||
QDir wheelsDirectory{"/data/themes/steering_wheels/"};
|
||||
|
||||
QJsonObject frogpilotToggleLevels;
|
||||
|
||||
QString colorSchemeToDownload;
|
||||
QString distanceIconPackToDownload;
|
||||
QString iconPackToDownload;
|
||||
QString signalAnimationToDownload;
|
||||
QString soundPackToDownload;
|
||||
QString wheelToDownload;
|
||||
|
||||
Params params;
|
||||
Params params_memory{"/dev/shm/params"};
|
||||
};
|
||||
@@ -0,0 +1,100 @@
|
||||
#include <filesystem>
|
||||
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/utilities.h"
|
||||
|
||||
FrogPilotUtilitiesPanel::FrogPilotUtilitiesPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
|
||||
ButtonControl *flashPandaBtn = new ButtonControl(tr("Flash Panda"), tr("FLASH"), tr("Flashes the Panda device's firmware if you're running into issues."));
|
||||
QObject::connect(flashPandaBtn, &ButtonControl::clicked, [=]() {
|
||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to flash the Panda?"), tr("Flash"), this)) {
|
||||
std::thread([=]() {
|
||||
parent->keepScreenOn = true;
|
||||
|
||||
flashPandaBtn->setEnabled(false);
|
||||
flashPandaBtn->setValue(tr("Flashing..."));
|
||||
|
||||
params_memory.putBool("FlashPanda", true);
|
||||
while (params_memory.getBool("FlashPanda")) {
|
||||
util::sleep_for(UI_FREQ);
|
||||
}
|
||||
|
||||
flashPandaBtn->setValue(tr("Flashed!"));
|
||||
util::sleep_for(2500);
|
||||
flashPandaBtn->setValue(tr("Rebooting..."));
|
||||
util::sleep_for(2500);
|
||||
Hardware::reboot();
|
||||
}).detach();
|
||||
}
|
||||
});
|
||||
addItem(flashPandaBtn);
|
||||
|
||||
FrogPilotButtonsControl *forceStartedBtn = new FrogPilotButtonsControl(tr("Force Started State"), tr("Forces openpilot either offroad or onroad."), {tr("OFFROAD"), tr("ONROAD"), tr("OFF")}, true);
|
||||
QObject::connect(forceStartedBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) {
|
||||
if (id == 0) {
|
||||
params_memory.putBool("ForceOffroad", true);
|
||||
params_memory.putBool("ForceOnroad", false);
|
||||
} else if (id == 1) {
|
||||
params_memory.putBool("ForceOffroad", false);
|
||||
params_memory.putBool("ForceOnroad", true);
|
||||
|
||||
util::sleep_for(1000);
|
||||
params.put("CarParams", params.get("CarParamsPersistent"));
|
||||
} else if (id == 2) {
|
||||
params_memory.putBool("ForceOffroad", false);
|
||||
params_memory.putBool("ForceOnroad", false);
|
||||
}
|
||||
forceStartedBtn->setCheckedButton(id);
|
||||
});
|
||||
forceStartedBtn->setCheckedButton(2);
|
||||
addItem(forceStartedBtn);
|
||||
|
||||
ButtonControl *reportIssueBtn = new ButtonControl(tr("Report a Bug or an Issue"), tr("REPORT"), tr("Let 'FrogsGoMoo' know about an issue you're facing."));
|
||||
QObject::connect(reportIssueBtn, &ButtonControl::clicked, [this]() {
|
||||
QStringList report_messages = {
|
||||
"I saw an alert that said 'openpilot crashed'",
|
||||
"I'm noticing harsh acceleration",
|
||||
"I'm noticing harsh braking",
|
||||
"I'm noticing unusual steering",
|
||||
"My car isn't staying in its lane",
|
||||
"Something else"
|
||||
};
|
||||
|
||||
QString selected_issue = MultiOptionDialog::getSelection(tr("What's going on?"), report_messages, "", this);
|
||||
if (selected_issue.isEmpty()) {
|
||||
return;
|
||||
} else if (selected_issue == "Something else") {
|
||||
selected_issue = InputDialog::getText(tr("Please describe what's happening"), this, tr("Send Report"), false, 10, "", 100).trimmed();
|
||||
}
|
||||
|
||||
QJsonObject reportData;
|
||||
reportData["Issue"] = selected_issue;
|
||||
reportData["DiscordUser"] = InputDialog::getText(tr("What's your Discord username?"), this, tr("Send Report"), false, -1, QString::fromStdString(params.get("DiscordUsername"))).trimmed();
|
||||
params.putNonBlocking("DiscordUsername", reportData["DiscordUser"].toString().toStdString());
|
||||
params_memory.put("IssueReported", QJsonDocument(reportData).toJson(QJsonDocument::Compact).toStdString());
|
||||
|
||||
FrogPilotConfirmationDialog::toggleAlert(tr("Thanks for letting us know! Your report has been submitted."), tr("Ok"), this);
|
||||
});
|
||||
addItem(reportIssueBtn);
|
||||
|
||||
ButtonControl *resetTogglesBtn = new ButtonControl(tr("Reset Toggles to Default"), tr("RESET"), tr("Reset your toggle settings back to their default settings."));
|
||||
QObject::connect(resetTogglesBtn, &ButtonControl::clicked, [=]() {
|
||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to completely reset all of your toggle settings?"), tr("Reset"), this)) {
|
||||
std::thread([=]() mutable {
|
||||
parent->keepScreenOn = true;
|
||||
|
||||
resetTogglesBtn->setEnabled(false);
|
||||
resetTogglesBtn->setValue(tr("Resetting..."));
|
||||
|
||||
params.putBool("DoToggleReset", true);
|
||||
|
||||
resetTogglesBtn->setValue(tr("Reset!"));
|
||||
|
||||
util::sleep_for(2500);
|
||||
resetTogglesBtn->setValue(tr("Rebooting..."));
|
||||
util::sleep_for(2500);
|
||||
|
||||
Hardware::reboot();
|
||||
}).detach();
|
||||
}
|
||||
});
|
||||
addItem(resetTogglesBtn);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h"
|
||||
|
||||
class FrogPilotUtilitiesPanel : public FrogPilotListWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FrogPilotUtilitiesPanel(FrogPilotSettingsWindow *parent);
|
||||
|
||||
private:
|
||||
FrogPilotSettingsWindow *parent;
|
||||
|
||||
Params params;
|
||||
Params params_memory{"/dev/shm/params"};
|
||||
};
|
||||
@@ -0,0 +1,313 @@
|
||||
#include <QRegularExpression>
|
||||
#include <QTextStream>
|
||||
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h"
|
||||
|
||||
QStringList getCarNames(const QString &carMake, QMap<QString, QString> &carModels) {
|
||||
static const QMap<QString, QString> makeMap = {
|
||||
{"acura", "honda"},
|
||||
{"audi", "volkswagen"},
|
||||
{"buick", "gm"},
|
||||
{"cadillac", "gm"},
|
||||
{"chevrolet", "gm"},
|
||||
{"chrysler", "chrysler"},
|
||||
{"cupra", "volkswagen"},
|
||||
{"dodge", "chrysler"},
|
||||
{"ford", "ford"},
|
||||
{"genesis", "hyundai"},
|
||||
{"gmc", "gm"},
|
||||
{"holden", "gm"},
|
||||
{"honda", "honda"},
|
||||
{"hyundai", "hyundai"},
|
||||
{"jeep", "chrysler"},
|
||||
{"kia", "hyundai"},
|
||||
{"lexus", "toyota"},
|
||||
{"lincoln", "ford"},
|
||||
{"man", "volkswagen"},
|
||||
{"mazda", "mazda"},
|
||||
{"nissan", "nissan"},
|
||||
{"ram", "chrysler"},
|
||||
{"seat", "volkswagen"},
|
||||
{"škoda", "volkswagen"},
|
||||
{"subaru", "subaru"},
|
||||
{"tesla", "tesla"},
|
||||
{"toyota", "toyota"},
|
||||
{"volkswagen", "volkswagen"}
|
||||
};
|
||||
|
||||
QStringList carNameList;
|
||||
QSet<QString> uniqueCarNames;
|
||||
|
||||
QString filePath = QString("../car/%1/values.py").arg(makeMap.value(carMake, carMake));
|
||||
QFile valuesFile(filePath);
|
||||
if (!valuesFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
return carNameList;
|
||||
}
|
||||
|
||||
QTextStream in(&valuesFile);
|
||||
QString fileContent = in.readAll();
|
||||
valuesFile.close();
|
||||
|
||||
fileContent.remove(QRegularExpression("#[^\n]*"));
|
||||
fileContent.remove(QRegularExpression("footnotes=\\[[^\\]]*\\],\\s*"));
|
||||
|
||||
static const QRegularExpression carNameRegex("CarDocs\\(\\s*\"([^\"]+)\"[^)]*\\)");
|
||||
static const QRegularExpression platformRegex("((\\w+)\\s*=\\s*\\w+\\s*\\(\\s*\\[([\\s\\S]*?)\\]\\s*,)");
|
||||
static const QRegularExpression validNameRegex("^[A-Za-z0-9 \u0160.()-]+$");
|
||||
|
||||
QRegularExpressionMatchIterator platformMatches = platformRegex.globalMatch(fileContent);
|
||||
while (platformMatches.hasNext()) {
|
||||
QRegularExpressionMatch platformMatch = platformMatches.next();
|
||||
QString platformName = platformMatch.captured(2);
|
||||
QString platformSection = platformMatch.captured(3);
|
||||
|
||||
QRegularExpressionMatchIterator carNameMatches = carNameRegex.globalMatch(platformSection);
|
||||
while (carNameMatches.hasNext()) {
|
||||
QString carName = carNameMatches.next().captured(1);
|
||||
if (carName.contains(validNameRegex) && carName.count(" ") >= 1) {
|
||||
QStringList carNameParts = carName.split(" ");
|
||||
for (const QString &part : carNameParts) {
|
||||
if (part.compare(carMake, Qt::CaseInsensitive) == 0) {
|
||||
if (!uniqueCarNames.contains(carName)) {
|
||||
uniqueCarNames.insert(carName);
|
||||
carNameList.append(carName);
|
||||
carModels[carName] = platformName;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
carNameList.sort();
|
||||
return carNameList;
|
||||
}
|
||||
|
||||
FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
addItem(mainLayout);
|
||||
|
||||
vehiclesLayout = new QStackedLayout();
|
||||
mainLayout->addLayout(vehiclesLayout);
|
||||
|
||||
FrogPilotListWidget *settingsList = new FrogPilotListWidget(this);
|
||||
|
||||
QStringList makes = {
|
||||
"Acura", "Audi", "Buick", "Cadillac", "Chevrolet", "Chrysler",
|
||||
"CUPRA", "Dodge", "Ford", "Genesis", "GMC", "Holden", "Honda",
|
||||
"Hyundai", "Jeep", "Kia", "Lexus", "Lincoln", "MAN", "Mazda",
|
||||
"Nissan", "Ram", "SEAT", "Škoda", "Subaru", "Tesla", "Toyota",
|
||||
"Volkswagen"
|
||||
};
|
||||
|
||||
ButtonControl *selectMakeButton = new ButtonControl(tr("Select Make"), tr("SELECT"));
|
||||
QObject::connect(selectMakeButton, &ButtonControl::clicked, [this, makes, selectMakeButton]() {
|
||||
QString makeSelection = MultiOptionDialog::getSelection(tr("Select a Make"), makes, "", this);
|
||||
if (!makeSelection.isEmpty()) {
|
||||
params.put("CarMake", makeSelection.toStdString());
|
||||
selectMakeButton->setValue(makeSelection);
|
||||
}
|
||||
});
|
||||
settingsList->addItem(selectMakeButton);
|
||||
|
||||
ButtonControl *selectModelButton = new ButtonControl(tr("Select Model"), tr("SELECT"));
|
||||
QObject::connect(selectModelButton, &ButtonControl::clicked, [this, selectModelButton]() {
|
||||
QString modelSelection = MultiOptionDialog::getSelection(tr("Select a Model"), getCarNames(QString::fromStdString(params.get("CarMake")).toLower(), carModels), "", this);
|
||||
if (!modelSelection.isEmpty()) {
|
||||
params.put("CarModel", carModels.value(modelSelection).toStdString());
|
||||
params.put("CarModelName", modelSelection.toStdString());
|
||||
selectModelButton->setValue(modelSelection);
|
||||
}
|
||||
});
|
||||
settingsList->addItem(selectModelButton);
|
||||
|
||||
ParamControl *forceFingerprint = new ParamControl("ForceFingerprint", tr("Disable Automatic Fingerprint Detection"), tr("Forces the selected fingerprint and prevents it from ever changing."), "");
|
||||
settingsList->addItem(forceFingerprint);
|
||||
|
||||
disableOpenpilotLong = new ParamControl("DisableOpenpilotLongitudinal", tr("Disable openpilot Longitudinal Control"), tr("Disables openpilot longitudinal control and uses the car's stock ACC instead."), "");
|
||||
QObject::connect(disableOpenpilotLong, &ToggleControl::toggleFlipped, [this, parent](bool state) {
|
||||
if (state) {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely disable openpilot longitudinal control?"), this)) {
|
||||
if (started) {
|
||||
if (FrogPilotConfirmationDialog::toggleReboot(this)) {
|
||||
Hardware::reboot();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
params.putBool("DisableOpenpilotLongitudinal", false);
|
||||
disableOpenpilotLong->refresh();
|
||||
}
|
||||
}
|
||||
|
||||
parent->updateVariables();
|
||||
updateToggles();
|
||||
});
|
||||
settingsList->addItem(disableOpenpilotLong);
|
||||
|
||||
FrogPilotListWidget *gmList = new FrogPilotListWidget(this);
|
||||
FrogPilotListWidget *hkgList = new FrogPilotListWidget(this);
|
||||
FrogPilotListWidget *toyotaList = new FrogPilotListWidget(this);
|
||||
|
||||
std::vector<std::tuple<QString, QString, QString, QString>> vehicleToggles {
|
||||
{"GMToggles", tr("General Motors Toggles"), tr("Toggles catered towards 'General Motors' vehicles."), ""},
|
||||
{"NewLongAPIGM", tr("Enable comma's New Longitudinal API"), tr("Enable comma's new longitudinal control system that has shown great improvement with acceleration and braking, but has issues on some GM vehicles."), ""},
|
||||
{"ExperimentalGMTune", tr("Enable FrogsGoMoo's Experimental Longitudinal Tune"), tr("Enable FrogsGoMoo's experimental GM longitudinal tune that is based on nothing but guesswork. Use at your own risk!"), ""},
|
||||
{"VoltSNG", tr("Enable Stop and Go Hack"), tr("Force stop and go for the 2017 Chevy Volt."), ""},
|
||||
{"LongPitch", tr("Smoothen Pedal Response While Going Downhill/Uphill"), tr("Smoothen the gas and brake response when driving downhill or uphill."), ""},
|
||||
|
||||
{"HKGToggles", tr("Hyundai/Kia/Genesis Toggles"), tr("Toggles catered towards 'Hyundai/Kia/Genesis' vehicles."), ""},
|
||||
{"NewLongAPI", tr("Enable comma's New Longitudinal API"), tr("Enable comma's new longitudinal control system that has shown great improvement with acceleration and braking, but has issues on some Hyundai/Kia/Genesis vehicles."), ""},
|
||||
|
||||
{"ToyotaToggles", tr("Toyota/Lexus Toggles"), tr("Toggles catered towards 'Toyota/Lexus' vehicles."), ""},
|
||||
{"ToyotaDoors", tr("Automatically Lock/Unlock Doors"), tr("Automatically lock the doors when in drive and unlock when in park."), ""},
|
||||
{"ClusterOffset", tr("Cluster Speed Offset"), tr("Set the cluster offset openpilot uses to try and match the speed displayed on the dash."), ""},
|
||||
{"FrogsGoMoosTweak", tr("Enable FrogsGoMoo's Personal Tweaks"), tr("FrogsGoMoo's personal tweaks that aim to take off faster and stop smoother."), ""},
|
||||
{"SNGHack", tr("Enable Stop and Go Hack"), tr("Force stop and go for vehicles without stock stop and go functionality."), ""},
|
||||
};
|
||||
|
||||
for (const auto &[param, title, desc, icon] : vehicleToggles) {
|
||||
AbstractControl *vehicleToggle;
|
||||
|
||||
if (param == "GMToggles") {
|
||||
ButtonControl *gmToggle = new ButtonControl(title, tr("MANAGE"), desc);
|
||||
QObject::connect(gmToggle, &ButtonControl::clicked, [this]() {
|
||||
vehiclesLayout->setCurrentIndex(1);
|
||||
openParentToggle();
|
||||
});
|
||||
vehicleToggle = gmToggle;
|
||||
|
||||
} else if (param == "HKGToggles") {
|
||||
ButtonControl *hkgToggle = new ButtonControl(title, tr("MANAGE"), desc);
|
||||
QObject::connect(hkgToggle, &ButtonControl::clicked, [this]() {
|
||||
vehiclesLayout->setCurrentIndex(2);
|
||||
openParentToggle();
|
||||
});
|
||||
vehicleToggle = hkgToggle;
|
||||
|
||||
} else if (param == "ToyotaToggles") {
|
||||
ButtonControl *toyotaToggle = new ButtonControl(title, tr("MANAGE"), desc);
|
||||
QObject::connect(toyotaToggle, &ButtonControl::clicked, [this]() {
|
||||
vehiclesLayout->setCurrentIndex(3);
|
||||
openParentToggle();
|
||||
});
|
||||
vehicleToggle = toyotaToggle;
|
||||
} else if (param == "ToyotaDoors") {
|
||||
std::vector<QString> lockToggles{"LockDoors", "UnlockDoors"};
|
||||
std::vector<QString> lockToggleNames{tr("Lock"), tr("Unlock")};
|
||||
vehicleToggle = new FrogPilotButtonToggleControl(param, title, desc, lockToggles, lockToggleNames);
|
||||
} else if (param == "ClusterOffset") {
|
||||
std::vector<QString> clusterOffsetButton{"Reset"};
|
||||
FrogPilotParamValueButtonControl *clusterOffsetToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 1.000, 1.050, "x", std::map<int, QString>(), 0.001, {}, clusterOffsetButton, false, false);
|
||||
QObject::connect(clusterOffsetToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this, clusterOffsetToggle]() {
|
||||
params.putFloat("ClusterOffset", params_default.getFloat("ClusterOffset"));
|
||||
clusterOffsetToggle->refresh();
|
||||
});
|
||||
vehicleToggle = clusterOffsetToggle;
|
||||
|
||||
} else {
|
||||
vehicleToggle = new ParamControl(param, title, desc, icon);
|
||||
}
|
||||
|
||||
toggles[param] = vehicleToggle;
|
||||
|
||||
if (gmKeys.find(param) != gmKeys.end()) {
|
||||
gmList->addItem(vehicleToggle);
|
||||
} else if (hkgKeys.find(param) != hkgKeys.end()) {
|
||||
hkgList->addItem(vehicleToggle);
|
||||
} else if (toyotaKeys.find(param) != toyotaKeys.end()) {
|
||||
toyotaList->addItem(vehicleToggle);
|
||||
} else {
|
||||
settingsList->addItem(vehicleToggle);
|
||||
}
|
||||
}
|
||||
|
||||
ScrollView *settingsPanel = new ScrollView(settingsList, this);
|
||||
vehiclesLayout->addWidget(settingsPanel);
|
||||
|
||||
ScrollView *gmPanel = new ScrollView(gmList, this);
|
||||
vehiclesLayout->addWidget(gmPanel);
|
||||
ScrollView *hkgPanel = new ScrollView(hkgList, this);
|
||||
vehiclesLayout->addWidget(hkgPanel);
|
||||
ScrollView *toyotaPanel = new ScrollView(toyotaList, this);
|
||||
vehiclesLayout->addWidget(toyotaPanel);
|
||||
|
||||
std::set<QString> rebootKeys = {"NewLongAPI", "NewLongAPIGM"};
|
||||
for (const QString &key : rebootKeys) {
|
||||
QObject::connect(static_cast<ToggleControl*>(toggles[key]), &ToggleControl::toggleFlipped, [this]() {
|
||||
if (started) {
|
||||
if (FrogPilotConfirmationDialog::toggleReboot(this)) {
|
||||
Hardware::reboot();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QObject::connect(uiState(), &UIState::offroadTransition, [this, selectMakeButton, selectModelButton]() {
|
||||
std::thread([this, selectMakeButton, selectModelButton]() {
|
||||
selectMakeButton->setValue(QString::fromStdString(params.get("CarMake", true)));
|
||||
selectModelButton->setValue(QString::fromStdString(params.get(params.get("CarModelName").empty() ? "CarModel" : "CarModelName")));
|
||||
}).detach();
|
||||
});
|
||||
|
||||
QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, [this] {vehiclesLayout->setCurrentIndex(0);});
|
||||
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotVehiclesPanel::updateState);
|
||||
}
|
||||
|
||||
void FrogPilotVehiclesPanel::showEvent(QShowEvent *event) {
|
||||
updateToggles();
|
||||
}
|
||||
|
||||
void FrogPilotVehiclesPanel::updateState(const UIState &s) {
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
started = s.scene.started;
|
||||
}
|
||||
|
||||
void FrogPilotVehiclesPanel::updateToggles() {
|
||||
toggles["GMToggles"]->setVisible(false);
|
||||
toggles["HKGToggles"]->setVisible(false);
|
||||
toggles["ToyotaToggles"]->setVisible(false);
|
||||
|
||||
for (auto &[key, toggle] : toggles) {
|
||||
bool setVisible = parent->tuningLevel >= parent->frogpilotToggleLevels[key].toDouble();
|
||||
|
||||
if (key == "GMToggles" || gmKeys.find(key) != gmKeys.end()) {
|
||||
setVisible = parent->isGM;
|
||||
} else if (key == "HKGToggles" || hkgKeys.find(key) != hkgKeys.end()) {
|
||||
setVisible = parent->isHKG;
|
||||
} else if (key == "ToyotaToggles" || toyotaKeys.find(key) != toyotaKeys.end()) {
|
||||
setVisible = parent->isToyota;
|
||||
}
|
||||
|
||||
if (longitudinalKeys.find(key) != longitudinalKeys.end()) {
|
||||
setVisible &= parent->hasOpenpilotLongitudinal;
|
||||
}
|
||||
|
||||
if (key == "SNGHack") {
|
||||
setVisible &= !parent->hasSNG;
|
||||
}
|
||||
|
||||
if (key == "VoltSNG") {
|
||||
setVisible &= parent->isVolt && !parent->hasSNG;
|
||||
}
|
||||
|
||||
toggle->setVisible(setVisible);
|
||||
|
||||
if (gmKeys.find(key) != gmKeys.end() && setVisible) {
|
||||
toggles["GMToggles"]->setVisible(true);
|
||||
}
|
||||
|
||||
if (hkgKeys.find(key) != hkgKeys.end() && setVisible) {
|
||||
toggles["HKGToggles"]->setVisible(true);
|
||||
}
|
||||
|
||||
if (toyotaKeys.find(key) != toyotaKeys.end() && setVisible) {
|
||||
toggles["ToyotaToggles"]->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
disableOpenpilotLong->setVisible((parent->hasOpenpilotLongitudinal || params.getBool("DisableOpenpilotLongitudinal")) && !parent->hasExperimentalOpenpilotLongitudinal);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h"
|
||||
|
||||
class FrogPilotVehiclesPanel : public FrogPilotListWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent);
|
||||
|
||||
signals:
|
||||
void openParentToggle();
|
||||
|
||||
private:
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void updateState(const UIState &s);
|
||||
void updateToggles();
|
||||
|
||||
bool started;
|
||||
|
||||
std::map<QString, AbstractControl*> toggles;
|
||||
|
||||
std::set<QString> gmKeys = {"ExperimentalGMTune", "LongPitch", "NewLongAPIGM", "VoltSNG"};
|
||||
std::set<QString> hkgKeys = {"NewLongAPI"};
|
||||
std::set<QString> longitudinalKeys = {"ExperimentalGMTune", "FrogsGoMoosTweak", "LongPitch", "NewLongAPI", "NewLongAPIGM", "SNGHack", "VoltSNG"};
|
||||
std::set<QString> toyotaKeys = {"ClusterOffset", "FrogsGoMoosTweak", "SNGHack", "ToyotaDoors"};
|
||||
|
||||
FrogPilotSettingsWindow *parent;
|
||||
|
||||
QMap<QString, QString> carModels;
|
||||
|
||||
QStackedLayout *vehiclesLayout;
|
||||
|
||||
ParamControl *disableOpenpilotLong;
|
||||
|
||||
Params params;
|
||||
Params params_default{"/dev/shm/params_default"};
|
||||
};
|
||||
@@ -0,0 +1,382 @@
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/visual_settings.h"
|
||||
|
||||
FrogPilotVisualsPanel::FrogPilotVisualsPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
|
||||
const std::vector<std::tuple<QString, QString, QString, QString>> visualToggles {
|
||||
{"QOLVisuals", tr("Accessibility"), tr("Visual features to improve your overall openpilot experience."), "../frogpilot/assets/toggle_icons/icon_accessibility.png"},
|
||||
{"CameraView", tr("Camera View"), tr("Changes the camera view display. This is purely a visual change and doesn't impact how openpilot drives."), ""},
|
||||
{"OnroadDistanceButton", tr("On Screen Personality Button"), tr("Displays the current driving personality on the screen. Tap to switch personalities, or long press for 2.5 seconds to activate 'Traffic' mode."), ""},
|
||||
{"DriverCamera", tr("Show Driver Camera When In Reverse"), tr("Displays the driver camera feed when the vehicle is in reverse."), ""},
|
||||
{"StandbyMode", tr("Standby Mode"), tr("Turns the screen off when driving and automatically wakes it up if engagement state changes or important alerts occur."), ""},
|
||||
{"StoppedTimer", tr("Stopped Timer"), tr("Activates a timer when stopped to indicate how long the vehicle has been stopped for."), ""},
|
||||
|
||||
{"AdvancedCustomUI", tr("Advanced UI Controls"), tr("Advanced features to fine tune your personalized UI."), "../frogpilot/assets/toggle_icons/icon_advanced_device.png"},
|
||||
{"HideSpeed", tr("Hide Current Speed"), tr("Hides the current speed."), ""},
|
||||
{"HideLeadMarker", tr("Hide Lead Marker"), tr("Hides the marker for the vehicle ahead."), ""},
|
||||
{"HideMapIcon", tr("Hide Map Icon"), tr("Hides the map icon."), ""},
|
||||
{"HideMaxSpeed", tr("Hide Max Speed"), tr("Hides the max speed."), ""},
|
||||
{"HideAlerts", tr("Hide Non-Critical Alerts"), tr("Hides non-critical alerts."), ""},
|
||||
{"HideSpeedLimit", tr("Hide Speed Limits"), tr("Hides the speed limits."), ""},
|
||||
{"WheelSpeed", tr("Use Wheel Speed"), tr("Uses the wheel speed instead of the cluster speed. This is purely a visual change and doesn't impact how openpilot drives."), ""},
|
||||
|
||||
{"DeveloperUI", tr("Developer UI"), tr("Show detailed information about openpilot's internal operations."), "../assets/offroad/icon_shell.png"},
|
||||
{"DeveloperMetrics", tr("Developer Metrics"), tr("Show detailed information about openpilot's internal operations."), ""},
|
||||
{"BorderMetrics", tr("Border Metrics"), tr("Displays performance metrics around the edge of the screen while driving."), ""},
|
||||
{"FPSCounter", tr("FPS Display"), tr("Displays the 'Frames Per Second' (FPS) at the bottom of the screen while driving."), ""},
|
||||
{"LateralMetrics", tr("Lateral Metrics"), tr("Displays metrics related to steering control at the top of the screen while driving."), ""},
|
||||
{"LongitudinalMetrics", tr("Longitudinal Metrics"), tr("Displays metrics related to acceleration, speed, and desired following distance at the top of the screen while driving."), ""},
|
||||
{"NumericalTemp", tr("Numerical Temperature Gauge"), tr("Shows exact temperature readings instead of status labels like 'GOOD', 'OK', or 'HIGH' in the sidebar."), ""},
|
||||
{"SidebarMetrics", tr("Sidebar"), tr("Displays system information like CPU, GPU, RAM usage, IP address, and storage space in the sidebar."), ""},
|
||||
{"UseSI", tr("Use International System of Units"), tr("Displays measurements using the 'International System of Units' (SI)."), ""},
|
||||
{"DeveloperWidgets", tr("Developer Widgets"), tr("Show detailed information about openpilot's internal operations."), ""},
|
||||
{"ShowCEMStatus", tr("'Conditional Experimental Mode' Status"), tr("Show 'Conditional Experimental Mode''s current status in the onroad UI."), ""},
|
||||
{"ShowStoppingPoint", tr("Model Stopping Point"), tr("Displays an image on the screen where openpilot is wanting to stop."), ""},
|
||||
|
||||
{"ModelUI", tr("Model UI"), tr("Customize the model visualizations on the screen."), "../frogpilot/assets/toggle_icons/icon_vtc.png"},
|
||||
{"DynamicPathWidth", tr("Dynamic Path Width"), tr("Automatically adjusts the width of the driving path display based on the current engagement state:\n\nFully engaged = 100%\nAlways On Lateral Active = 75%\nFully disengaged = 50%"), ""},
|
||||
{"LaneLinesWidth", tr("Lane Lines Width"), tr("Controls the thickness the lane lines appear on the display.\n\nDefault matches the MUTCD standard of 4 inches."), ""},
|
||||
{"PathEdgeWidth", tr("Path Edges Width"), tr("Controls the width of the edges of the driving path to represent different modes and statuses.\n\nDefault is 20% of the total path width.\n\nColor Guide:\n\n- Blue: Navigation\n- Light Blue: 'Always On Lateral'\n- Green: Default\n- Orange: 'Experimental Mode'\n- Red: 'Traffic Mode'\n- Yellow: 'Conditional Experimental Mode' Overridden"), ""},
|
||||
{"PathWidth", tr("Path Width"), tr("Controls how wide the driving path appears on your screen.\n\nDefault (6.1 feet / 1.9 meters) matches the width of a 2019 Lexus ES 350."), ""},
|
||||
{"RoadEdgesWidth", tr("Road Edges Width"), tr("Controls how thick the road edges appear on the display.\n\nDefault matches half of the MUTCD standard lane line width of 4 inches."), ""},
|
||||
{"UnlimitedLength", tr("'Unlimited' Road UI"), tr("Extends the display of the path, lane lines, and road edges as far as the model can see."), ""},
|
||||
|
||||
{"NavigationUI", tr("Navigation Widgets"), tr("Wwidgets focused around navigation."), "../frogpilot/assets/toggle_icons/icon_map.png"},
|
||||
{"BigMap", tr("Larger Map Display"), tr("Increases the size of the map for easier navigation readings."), ""},
|
||||
{"MapStyle", tr("Map Style"), tr("Swaps out the stock map style for community created ones."), ""},
|
||||
{"RoadNameUI", tr("Road Name"), tr("Displays the current road name at the bottom of the screen using data from 'OpenStreetMap'."), ""},
|
||||
{"ShowSpeedLimits", tr("Show Speed Limits"), tr("Displays the currently detected speed limit in the top left corner of the onroad UI. Uses data from your car's dashboard (if supported) and data from 'OpenStreetMaps'."), ""},
|
||||
{"UseVienna", tr("Use Vienna-Style Speed Signs"), tr("Forces Vienna-style (EU) speed limit signs instead of MUTCD (US)."), ""},
|
||||
|
||||
{"CustomUI", tr("Onroad Screen Widgets"), tr("Custom FrogPilot widgets used in the onroad user interface."), "../assets/offroad/icon_road.png"},
|
||||
{"AccelerationPath", tr("Acceleration Path"), tr("Projects a path based on openpilot's current desired acceleration or deceleration."), ""},
|
||||
{"AdjacentPath", tr("Adjacent Lanes"), tr("Projects paths for the adjascent lanes."), ""},
|
||||
{"BlindSpotPath", tr("Blind Spot Path"), tr("Projects a red path when vehicles are detected in the blind spot for the respective lane."), ""},
|
||||
{"Compass", tr("Compass"), tr("Displays a compass to show the current driving direction."), ""},
|
||||
{"PedalsOnUI", tr("Gas / Brake Pedal Indicators"), tr("Displays pedal indicators to indicate when either of the pedals are currently being used."), ""},
|
||||
{"RotatingWheel", tr("Rotating Steering Wheel"), tr("Rotates the steering wheel in the onroad UI rotates along with your steering wheel movements."), ""}
|
||||
};
|
||||
|
||||
for (const auto &[param, title, desc, icon] : visualToggles) {
|
||||
AbstractControl *visualToggle;
|
||||
|
||||
if (param == "QOLVisuals") {
|
||||
FrogPilotParamManageControl *qolToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(qolToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
std::set<QString> modifiedAccessibilityKeys = accessibilityKeys;
|
||||
|
||||
if (!hasOpenpilotLongitudinal) {
|
||||
modifiedAccessibilityKeys.erase("OnroadDistanceButton");
|
||||
}
|
||||
|
||||
showToggles(modifiedAccessibilityKeys);
|
||||
});
|
||||
visualToggle = qolToggle;
|
||||
} else if (param == "CameraView") {
|
||||
std::vector<QString> cameraOptions{tr("Auto"), tr("Driver"), tr("Standard"), tr("Wide")};
|
||||
ButtonParamControl *preferredCamera = new ButtonParamControl(param, title, desc, icon, cameraOptions);
|
||||
visualToggle = preferredCamera;
|
||||
|
||||
} else if (param == "AdvancedCustomUI") {
|
||||
FrogPilotParamManageControl *advancedCustomUIToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(advancedCustomUIToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
std::set<QString> modifiedAdvancedCustomOnroadUIKeys = advancedCustomOnroadUIKeys;
|
||||
|
||||
if (!hasOpenpilotLongitudinal) {
|
||||
modifiedAdvancedCustomOnroadUIKeys.erase("HideLeadMarker");
|
||||
}
|
||||
|
||||
showToggles(modifiedAdvancedCustomOnroadUIKeys);
|
||||
});
|
||||
visualToggle = advancedCustomUIToggle;
|
||||
|
||||
} else if (param == "DeveloperUI") {
|
||||
FrogPilotParamManageControl *developerUIToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(developerUIToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
std::set<QString> modifiedDeveloperUIKeys = developerUIKeys;
|
||||
|
||||
if (!hasOpenpilotLongitudinal) {
|
||||
modifiedDeveloperUIKeys.erase("DeveloperWidgets");
|
||||
}
|
||||
|
||||
showToggles(modifiedDeveloperUIKeys);
|
||||
});
|
||||
visualToggle = developerUIToggle;
|
||||
} else if (param == "DeveloperMetrics") {
|
||||
ButtonControl *developerMetricsToggle = new ButtonControl(title, tr("MANAGE"), desc);
|
||||
QObject::connect(developerMetricsToggle, &ButtonControl::clicked, [this]() {
|
||||
developerUIOpen = true;
|
||||
|
||||
openSubParentToggle();
|
||||
|
||||
borderMetricsBtn->setVisibleButton(0, hasBSM);
|
||||
lateralMetricsBtn->setVisibleButton(1, hasAutoTune);
|
||||
longitudinalMetricsBtn->setVisibleButton(0, hasRadar);
|
||||
|
||||
std::set<QString> modifiedDeveloperMetricKeys = developerMetricKeys;
|
||||
|
||||
if (!hasOpenpilotLongitudinal) {
|
||||
modifiedDeveloperMetricKeys.erase("LongitudinalMetrics");
|
||||
}
|
||||
|
||||
showToggles(modifiedDeveloperMetricKeys);
|
||||
});
|
||||
visualToggle = 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")};
|
||||
borderMetricsBtn = new FrogPilotButtonToggleControl(param, title, desc, borderToggles, borderToggleNames);
|
||||
visualToggle = borderMetricsBtn;
|
||||
} else if (param == "LateralMetrics") {
|
||||
std::vector<QString> lateralToggles{"AdjacentPathMetrics", "TuningInfo"};
|
||||
std::vector<QString> lateralToggleNames{tr("Adjacent Path Metrics"), tr("Auto Tune")};
|
||||
lateralMetricsBtn = new FrogPilotButtonToggleControl(param, title, desc, lateralToggles, lateralToggleNames);
|
||||
visualToggle = lateralMetricsBtn;
|
||||
} else if (param == "LongitudinalMetrics") {
|
||||
std::vector<QString> longitudinalToggles{"AdjacentLeadsUI", "LeadInfo", "JerkInfo"};
|
||||
std::vector<QString> longitudinalToggleNames{tr("Adjacent Leads"), tr("Lead Info"), tr("Jerk Values")};
|
||||
longitudinalMetricsBtn = new FrogPilotButtonToggleControl(param, title, desc, longitudinalToggles, longitudinalToggleNames);
|
||||
visualToggle = longitudinalMetricsBtn;
|
||||
} else if (param == "NumericalTemp") {
|
||||
std::vector<QString> temperatureToggles{"Fahrenheit"};
|
||||
std::vector<QString> temperatureToggleNames{tr("Fahrenheit")};
|
||||
visualToggle = new FrogPilotButtonToggleControl(param, title, desc, temperatureToggles, temperatureToggleNames);
|
||||
} else if (param == "SidebarMetrics") {
|
||||
std::vector<QString> 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")};
|
||||
FrogPilotButtonToggleControl *sidebarMetricsToggle = new FrogPilotButtonToggleControl(param, title, desc, sidebarMetricsToggles, sidebarMetricsToggleNames, false, false, 150);
|
||||
QObject::connect(sidebarMetricsToggle, &FrogPilotButtonToggleControl::buttonClicked, [sidebarMetricsToggle, this](int index) {
|
||||
if (index == 0) {
|
||||
params.putBool("ShowGPU", false);
|
||||
} else if (index == 1) {
|
||||
params.putBool("ShowCPU", false);
|
||||
} else if (index == 3) {
|
||||
params.putBool("ShowStorageLeft", false);
|
||||
params.putBool("ShowStorageUsed", false);
|
||||
} else if (index == 4) {
|
||||
params.putBool("ShowMemoryUsage", false);
|
||||
params.putBool("ShowStorageUsed", false);
|
||||
} else if (index == 5) {
|
||||
params.putBool("ShowMemoryUsage", false);
|
||||
params.putBool("ShowStorageLeft", false);
|
||||
}
|
||||
sidebarMetricsToggle->refresh();
|
||||
});
|
||||
visualToggle = sidebarMetricsToggle;
|
||||
} else if (param == "DeveloperWidgets") {
|
||||
ButtonControl *developerWidgetsToggle = new ButtonControl(title, tr("MANAGE"), desc);
|
||||
QObject::connect(developerWidgetsToggle, &ButtonControl::clicked, [this]() {
|
||||
developerUIOpen = true;
|
||||
|
||||
openSubParentToggle();
|
||||
|
||||
std::set<QString> modifiedDeveloperWidgetKeys = developerWidgetKeys;
|
||||
|
||||
if (!hasOpenpilotLongitudinal) {
|
||||
modifiedDeveloperWidgetKeys.erase("ShowCEMStatus");
|
||||
modifiedDeveloperWidgetKeys.erase("ShowStoppingPoint");
|
||||
}
|
||||
|
||||
if (!params.getBool("ConditionalExperimental")) {
|
||||
modifiedDeveloperWidgetKeys.erase("ShowCEMStatus");
|
||||
}
|
||||
|
||||
showToggles(modifiedDeveloperWidgetKeys);
|
||||
});
|
||||
visualToggle = developerWidgetsToggle;
|
||||
} else if (param == "ShowStoppingPoint") {
|
||||
std::vector<QString> stoppingPointToggles{"ShowStoppingPointMetrics"};
|
||||
std::vector<QString> stoppingPointToggleNames{tr("Show Distance")};
|
||||
visualToggle = new FrogPilotButtonToggleControl(param, title, desc, stoppingPointToggles, stoppingPointToggleNames);
|
||||
|
||||
} else if (param == "ModelUI") {
|
||||
FrogPilotParamManageControl *modelUIToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(modelUIToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
showToggles(modelUIKeys);
|
||||
});
|
||||
visualToggle = modelUIToggle;
|
||||
} else if (param == "LaneLinesWidth" || param == "RoadEdgesWidth") {
|
||||
visualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 24, tr(" inches"));
|
||||
} else if (param == "PathEdgeWidth") {
|
||||
visualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, tr("%"));
|
||||
} else if (param == "PathWidth") {
|
||||
visualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 10, tr(" feet"), std::map<int, QString>(), 0.1);
|
||||
|
||||
} else if (param == "NavigationUI") {
|
||||
FrogPilotParamManageControl *customUIToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(customUIToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
std::set<QString> modifiedNavigationUIKeys = navigationUIKeys;
|
||||
|
||||
if (params.getBool("SpeedLimitController")) {
|
||||
modifiedNavigationUIKeys.erase("ShowSpeedLimits");
|
||||
}
|
||||
|
||||
showToggles(modifiedNavigationUIKeys);
|
||||
});
|
||||
visualToggle = customUIToggle;
|
||||
} else if (param == "BigMap") {
|
||||
std::vector<QString> mapToggles{"FullMap"};
|
||||
std::vector<QString> mapToggleNames{tr("Full Map")};
|
||||
visualToggle = new FrogPilotButtonToggleControl(param, title, desc, mapToggles, mapToggleNames);
|
||||
} else if (param == "MapStyle") {
|
||||
QMap<int, QString> styleMap {
|
||||
{0, tr("Stock")},
|
||||
{1, tr("Mapbox Streets")},
|
||||
{2, tr("Mapbox Outdoors")},
|
||||
{3, tr("Mapbox Light")},
|
||||
{4, tr("Mapbox Dark")},
|
||||
{5, tr("Mapbox Satellite")},
|
||||
{6, tr("Mapbox Satellite Streets")},
|
||||
{7, tr("Mapbox Navigation Day")},
|
||||
{8, tr("Mapbox Navigation Night")},
|
||||
{9, tr("Mapbox Traffic Night")},
|
||||
{10, tr("mike854's (Satellite hybrid)")}
|
||||
};
|
||||
|
||||
ButtonControl *mapStyleButton = new ButtonControl(title, tr("SELECT"), desc);
|
||||
QObject::connect(mapStyleButton, &ButtonControl::clicked, [this, mapStyleButton, styleMap]() {
|
||||
QString selection = MultiOptionDialog::getSelection(tr("Select a map style"), styleMap.values(), "", this);
|
||||
if (!selection.isEmpty()) {
|
||||
int selectedStyle = styleMap.key(selection);
|
||||
params.putInt("MapStyle", selectedStyle);
|
||||
mapStyleButton->setValue(selection);
|
||||
}
|
||||
});
|
||||
int currentStyle = params.getInt("MapStyle");
|
||||
mapStyleButton->setValue(styleMap[currentStyle]);
|
||||
|
||||
visualToggle = mapStyleButton;
|
||||
|
||||
} else if (param == "CustomUI") {
|
||||
FrogPilotParamManageControl *customUIToggle = new FrogPilotParamManageControl(param, title, desc, icon);
|
||||
QObject::connect(customUIToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() {
|
||||
std::set<QString> modifiedCustomOnroadUIKeys = customOnroadUIKeys;
|
||||
|
||||
if (!hasBSM) {
|
||||
modifiedCustomOnroadUIKeys.erase("BlindSpotPath");
|
||||
}
|
||||
|
||||
if (!hasOpenpilotLongitudinal) {
|
||||
modifiedCustomOnroadUIKeys.erase("AccelerationPath");
|
||||
modifiedCustomOnroadUIKeys.erase("PedalsOnUI");
|
||||
}
|
||||
|
||||
showToggles(modifiedCustomOnroadUIKeys);
|
||||
});
|
||||
visualToggle = customUIToggle;
|
||||
} else if (param == "PedalsOnUI") {
|
||||
std::vector<QString> pedalsToggles{"DynamicPedalsOnUI", "StaticPedalsOnUI"};
|
||||
std::vector<QString> pedalsToggleNames{tr("Dynamic"), tr("Static")};
|
||||
FrogPilotButtonToggleControl *pedalsToggle = new FrogPilotButtonToggleControl(param, title, desc, pedalsToggles, pedalsToggleNames, true);
|
||||
QObject::connect(pedalsToggle, &FrogPilotButtonToggleControl::buttonClicked, [this](int index) {
|
||||
if (index == 0) {
|
||||
params.putBool("StaticPedalsOnUI", false);
|
||||
} else if (index == 1) {
|
||||
params.putBool("DynamicPedalsOnUI", false);
|
||||
}
|
||||
});
|
||||
visualToggle = pedalsToggle;
|
||||
|
||||
} else {
|
||||
visualToggle = new ParamControl(param, title, desc, icon);
|
||||
}
|
||||
|
||||
addItem(visualToggle);
|
||||
toggles[param] = visualToggle;
|
||||
|
||||
if (FrogPilotParamManageControl *frogPilotManageToggle = qobject_cast<FrogPilotParamManageControl*>(visualToggle)) {
|
||||
QObject::connect(frogPilotManageToggle, &FrogPilotParamManageControl::manageButtonClicked, this, &FrogPilotVisualsPanel::openParentToggle);
|
||||
}
|
||||
}
|
||||
|
||||
QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotVisualsPanel::hideToggles);
|
||||
QObject::connect(parent, &FrogPilotSettingsWindow::closeSubParentToggle, this, &FrogPilotVisualsPanel::hideSubToggles);
|
||||
QObject::connect(parent, &FrogPilotSettingsWindow::updateMetric, this, &FrogPilotVisualsPanel::updateMetric);
|
||||
}
|
||||
|
||||
void FrogPilotVisualsPanel::showEvent(QShowEvent *event) {
|
||||
frogpilotToggleLevels = parent->frogpilotToggleLevels;
|
||||
hasAutoTune = parent->hasAutoTune;
|
||||
hasBSM = parent->hasBSM;
|
||||
hasOpenpilotLongitudinal = parent->hasOpenpilotLongitudinal;
|
||||
hasRadar = parent->hasRadar;
|
||||
tuningLevel = parent->tuningLevel;
|
||||
|
||||
hideToggles();
|
||||
}
|
||||
|
||||
void FrogPilotVisualsPanel::updateMetric(bool metric, bool bootRun) {
|
||||
static bool previousMetric;
|
||||
if (metric != previousMetric && !bootRun) {
|
||||
double smallDistanceConversion = metric ? INCH_TO_CM : CM_TO_INCH;
|
||||
double distanceConversion = metric ? FOOT_TO_METER : METER_TO_FOOT;
|
||||
|
||||
params.putFloatNonBlocking("LaneLinesWidth", params.getFloat("LaneLinesWidth") * smallDistanceConversion);
|
||||
params.putFloatNonBlocking("RoadEdgesWidth", params.getFloat("RoadEdgesWidth") * smallDistanceConversion);
|
||||
|
||||
params.putFloatNonBlocking("PathWidth", params.getFloat("PathWidth") * distanceConversion);
|
||||
}
|
||||
previousMetric = metric;
|
||||
|
||||
FrogPilotParamValueControl *laneLinesWidthToggle = static_cast<FrogPilotParamValueControl*>(toggles["LaneLinesWidth"]);
|
||||
FrogPilotParamValueControl *pathWidthToggle = static_cast<FrogPilotParamValueControl*>(toggles["PathWidth"]);
|
||||
FrogPilotParamValueControl *roadEdgesWidthToggle = static_cast<FrogPilotParamValueControl*>(toggles["RoadEdgesWidth"]);
|
||||
|
||||
if (metric) {
|
||||
laneLinesWidthToggle->setDescription(tr("Adjust how thick the lane lines appear on the display.\n\nDefault matches the Vienna standard of 10 centimeters."));
|
||||
roadEdgesWidthToggle->setDescription(tr("Adjust how thick the road edges appear on the display.\n\nDefault matches half of the Vienna standard of 10 centimeters."));
|
||||
|
||||
laneLinesWidthToggle->updateControl(0, 60, tr(" centimeters"));
|
||||
roadEdgesWidthToggle->updateControl(0, 60, tr(" centimeters"));
|
||||
|
||||
pathWidthToggle->updateControl(0, 3, tr(" meters"));
|
||||
} else {
|
||||
laneLinesWidthToggle->setDescription(tr("Adjust how thick the lane lines appear on the display.\n\nDefault matches the MUTCD standard of 4 inches."));
|
||||
roadEdgesWidthToggle->setDescription(tr("Adjust how thick the road edges appear on the display.\n\nDefault matches half of the MUTCD standard of 4 inches."));
|
||||
|
||||
laneLinesWidthToggle->updateControl(0, 24, tr(" inches"));
|
||||
roadEdgesWidthToggle->updateControl(0, 24, tr(" inches"));
|
||||
|
||||
pathWidthToggle->updateControl(0, 10, tr(" feet"));
|
||||
}
|
||||
}
|
||||
|
||||
void FrogPilotVisualsPanel::showToggles(const std::set<QString> &keys) {
|
||||
setUpdatesEnabled(false);
|
||||
|
||||
for (auto &[key, toggle] : toggles) {
|
||||
toggle->setVisible(keys.find(key) != keys.end() && tuningLevel >= frogpilotToggleLevels[key].toDouble());
|
||||
}
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void FrogPilotVisualsPanel::hideToggles() {
|
||||
setUpdatesEnabled(false);
|
||||
|
||||
for (auto &[key, toggle] : toggles) {
|
||||
bool subToggles = accessibilityKeys.find(key) != accessibilityKeys.end() ||
|
||||
advancedCustomOnroadUIKeys.find(key) != advancedCustomOnroadUIKeys.end() ||
|
||||
customOnroadUIKeys.find(key) != customOnroadUIKeys.end() ||
|
||||
developerMetricKeys.find(key) != developerMetricKeys.end() ||
|
||||
developerUIKeys.find(key) != developerUIKeys.end() ||
|
||||
developerWidgetKeys.find(key) != developerWidgetKeys.end() ||
|
||||
modelUIKeys.find(key) != modelUIKeys.end() ||
|
||||
navigationUIKeys.find(key) != navigationUIKeys.end();
|
||||
|
||||
toggle->setVisible(!subToggles && tuningLevel >= frogpilotToggleLevels[key].toDouble());
|
||||
}
|
||||
|
||||
toggles["QOLVisuals"]->setVisible(toggles["QOLVisuals"]->isVisible() || hasOpenpilotLongitudinal);
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void FrogPilotVisualsPanel::hideSubToggles() {
|
||||
if (developerUIOpen) {
|
||||
developerUIOpen = false;
|
||||
showToggles(developerUIKeys);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h"
|
||||
|
||||
class FrogPilotVisualsPanel : public FrogPilotListWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FrogPilotVisualsPanel(FrogPilotSettingsWindow *parent);
|
||||
|
||||
signals:
|
||||
void openParentToggle();
|
||||
void openSubParentToggle();
|
||||
|
||||
private:
|
||||
void hideSubToggles();
|
||||
void hideToggles();
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void showToggles(const std::set<QString> &keys);
|
||||
void updateMetric(bool metric, bool bootRun);
|
||||
|
||||
bool developerUIOpen;
|
||||
bool hasAutoTune;
|
||||
bool hasBSM;
|
||||
bool hasOpenpilotLongitudinal;
|
||||
bool hasRadar;
|
||||
|
||||
int tuningLevel;
|
||||
|
||||
std::map<QString, AbstractControl*> toggles;
|
||||
|
||||
std::set<QString> accessibilityKeys = {"CameraView", "DriverCamera", "OnroadDistanceButton", "StandbyMode", "StoppedTimer"};
|
||||
std::set<QString> advancedCustomOnroadUIKeys = {"HideAlerts", "HideLeadMarker", "HideMapIcon", "HideMaxSpeed", "HideSpeed", "HideSpeedLimit", "WheelSpeed"};
|
||||
std::set<QString> customOnroadUIKeys = {"AccelerationPath", "AdjacentPath", "BlindSpotPath", "Compass", "PedalsOnUI", "RotatingWheel"};
|
||||
std::set<QString> developerMetricKeys = {"BorderMetrics", "FPSCounter", "LateralMetrics", "LongitudinalMetrics", "NumericalTemp", "SidebarMetrics", "UseSI"};
|
||||
std::set<QString> developerUIKeys = {"DeveloperMetrics", "DeveloperWidgets"};
|
||||
std::set<QString> developerWidgetKeys = {"ShowCEMStatus", "ShowStoppingPoint"};
|
||||
std::set<QString> modelUIKeys = {"DynamicPathWidth", "LaneLinesWidth", "PathEdgeWidth", "PathWidth", "RoadEdgesWidth", "UnlimitedLength"};
|
||||
std::set<QString> navigationUIKeys = {"BigMap", "MapStyle", "RoadNameUI", "ShowSpeedLimits", "UseVienna"};
|
||||
|
||||
FrogPilotButtonToggleControl *borderMetricsBtn;
|
||||
FrogPilotButtonToggleControl *lateralMetricsBtn;
|
||||
FrogPilotButtonToggleControl *longitudinalMetricsBtn;
|
||||
|
||||
FrogPilotSettingsWindow *parent;
|
||||
|
||||
Params params;
|
||||
|
||||
QJsonObject frogpilotToggleLevels;
|
||||
};
|
||||
@@ -0,0 +1,106 @@
|
||||
#include "selfdrive/ui/qt/request_repeater.h"
|
||||
#include "selfdrive/ui/qt/util.h"
|
||||
|
||||
#include "selfdrive/frogpilot/ui/qt/widgets/drive_stats.h"
|
||||
|
||||
static QLabel *newLabel(const QString &text, const QString &type) {
|
||||
QLabel *label = new QLabel(text);
|
||||
label->setProperty("type", type);
|
||||
return label;
|
||||
}
|
||||
|
||||
DriveStats::DriveStats(QWidget *parent) : QFrame(parent) {
|
||||
metric = params.getBool("IsMetric");
|
||||
|
||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||
main_layout->setContentsMargins(50, 25, 50, 20);
|
||||
|
||||
addStatsLayouts(tr("ALL TIME"), all);
|
||||
addStatsLayouts(tr("PAST WEEK"), week);
|
||||
addStatsLayouts(tr("FROGPILOT"), frogPilot, true);
|
||||
|
||||
std::optional<QString> dongleId = getDongleId();
|
||||
if (dongleId.has_value()) {
|
||||
QString url = CommaApi::BASE_URL + "/v1.1/devices/" + dongleId.value() + "/stats";
|
||||
RequestRepeater *repeater = new RequestRepeater(this, url, "ApiCache_DriveStats", 30);
|
||||
QObject::connect(repeater, &RequestRepeater::requestDone, this, &DriveStats::parseResponse);
|
||||
}
|
||||
|
||||
setStyleSheet(R"(
|
||||
DriveStats {
|
||||
background-color: #333333;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
QLabel[type="title"] { font-size: 50px; font-weight: 500; }
|
||||
QLabel[type="frogpilot_title"] { font-size: 50px; font-weight: 500; color: #178643; }
|
||||
QLabel[type="number"] { font-size: 65px; font-weight: 400; }
|
||||
QLabel[type="unit"] { font-size: 50px; font-weight: 300; color: #A0A0A0; }
|
||||
)");
|
||||
}
|
||||
|
||||
void DriveStats::addStatsLayouts(const QString &title, StatsLabels &labels, bool FrogPilot) {
|
||||
QGridLayout *grid_layout = new QGridLayout;
|
||||
grid_layout->setVerticalSpacing(10);
|
||||
grid_layout->setContentsMargins(0, 10, 0, 10);
|
||||
|
||||
int row = 0;
|
||||
grid_layout->addWidget(newLabel(title, FrogPilot ? "frogpilot_title" : "title"), row++, 0, 1, 3);
|
||||
grid_layout->addItem(new QSpacerItem(0, 10), row++, 0, 1, 1);
|
||||
|
||||
grid_layout->addWidget(labels.routes = newLabel("0", "number"), row, 0, Qt::AlignLeft);
|
||||
grid_layout->addWidget(labels.distance = newLabel("0", "number"), row, 1, Qt::AlignLeft);
|
||||
grid_layout->addWidget(labels.hours = newLabel("0", "number"), row, 2, Qt::AlignLeft);
|
||||
|
||||
grid_layout->addWidget(newLabel(tr("Drives"), "unit"), row + 1, 0, Qt::AlignLeft);
|
||||
grid_layout->addWidget(labels.distance_unit = newLabel(getDistanceUnit(), "unit"), row + 1, 1, Qt::AlignLeft);
|
||||
grid_layout->addWidget(newLabel(tr("Hours"), "unit"), row + 1, 2, Qt::AlignLeft);
|
||||
|
||||
QVBoxLayout *main_layout = static_cast<QVBoxLayout *>(layout());
|
||||
main_layout->addLayout(grid_layout);
|
||||
main_layout->addStretch(1);
|
||||
}
|
||||
|
||||
void DriveStats::updateStatsForLabel(const QJsonObject &obj, StatsLabels &labels) {
|
||||
labels.routes->setText(QString::number((int)obj["routes"].toDouble()));
|
||||
labels.distance->setText(QString::number(int(obj["distance"].toDouble() * (metric ? MILE_TO_KM : 1))));
|
||||
labels.distance_unit->setText(getDistanceUnit());
|
||||
labels.hours->setText(QString::number((int)(obj["minutes"].toDouble() / 60)));
|
||||
}
|
||||
|
||||
void DriveStats::updateFrogPilotStats(const QJsonObject &obj, StatsLabels &labels) {
|
||||
labels.routes->setText(QString::number(paramsTracking.getInt("FrogPilotDrives")));
|
||||
labels.distance->setText(QString::number(int(paramsTracking.getFloat("FrogPilotKilometers") * (metric ? 1 : KM_TO_MILE))));
|
||||
labels.distance_unit->setText(getDistanceUnit());
|
||||
labels.hours->setText(QString::number(int(paramsTracking.getFloat("FrogPilotMinutes") / 60)));
|
||||
}
|
||||
|
||||
void DriveStats::updateStats() {
|
||||
QJsonObject json = stats.object();
|
||||
|
||||
updateFrogPilotStats(json["frogpilot"].toObject(), frogPilot);
|
||||
updateStatsForLabel(json["all"].toObject(), all);
|
||||
updateStatsForLabel(json["week"].toObject(), week);
|
||||
|
||||
int all_time_minutes = (int)(json["all"].toObject()["minutes"].toDouble());
|
||||
params.put("openpilotMinutes", QString::number(all_time_minutes).toStdString());
|
||||
}
|
||||
|
||||
void DriveStats::parseResponse(const QString &response, bool success) {
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(response.trimmed().toUtf8());
|
||||
if (doc.isNull()) {
|
||||
qDebug() << "JSON Parse failed on getting past drives statistics";
|
||||
return;
|
||||
}
|
||||
stats = doc;
|
||||
updateStats();
|
||||
}
|
||||
|
||||
void DriveStats::showEvent(QShowEvent *event) {
|
||||
metric = params.getBool("IsMetric");
|
||||
updateStats();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "selfdrive/ui/ui.h"
|
||||
|
||||
class DriveStats : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DriveStats(QWidget *parent = 0);
|
||||
|
||||
private:
|
||||
inline QString getDistanceUnit() const { return metric ? tr("KM") : tr("Miles"); }
|
||||
|
||||
struct StatsLabels {
|
||||
QLabel *routes;
|
||||
QLabel *distance;
|
||||
QLabel *distance_unit;
|
||||
QLabel *hours;
|
||||
};
|
||||
|
||||
void addStatsLayouts(const QString &title, StatsLabels &labels, bool FrogPilot = false);
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void updateStats();
|
||||
void updateStatsForLabel(const QJsonObject &obj, StatsLabels &labels);
|
||||
void updateFrogPilotStats(const QJsonObject &obj, StatsLabels &labels);
|
||||
|
||||
Params params;
|
||||
Params paramsTracking{"/persist/tracking"};
|
||||
|
||||
bool metric;
|
||||
|
||||
QJsonDocument stats;
|
||||
|
||||
StatsLabels all, week, frogPilot;
|
||||
|
||||
private slots:
|
||||
void parseResponse(const QString &response, bool success);
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonParseError>
|
||||
|
||||
#include "selfdrive/ui/ui.h"
|
||||
|
||||
void updateFrogPilotToggles() {
|
||||
static Params params_memory{"/dev/shm/params"};
|
||||
params_memory.putBool("FrogPilotTogglesUpdated", true);
|
||||
}
|
||||
|
||||
QColor loadThemeColors(const QString &colorKey, bool clearCache) {
|
||||
static QJsonObject cachedColorData;
|
||||
|
||||
if (clearCache) {
|
||||
QFile file("../frogpilot/assets/active_theme/colors/colors.json");
|
||||
|
||||
while (!file.exists()) {
|
||||
util::sleep_for(100);
|
||||
}
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
return QColor();
|
||||
}
|
||||
|
||||
cachedColorData = QJsonDocument::fromJson(file.readAll()).object();
|
||||
}
|
||||
|
||||
if (cachedColorData.isEmpty()) {
|
||||
return QColor();
|
||||
}
|
||||
|
||||
QJsonObject colorObj = cachedColorData.value(colorKey).toObject();
|
||||
return QColor(
|
||||
colorObj.value("red").toInt(255),
|
||||
colorObj.value("green").toInt(255),
|
||||
colorObj.value("blue").toInt(255),
|
||||
colorObj.value("alpha").toInt(255)
|
||||
);
|
||||
}
|
||||
|
||||
bool FrogPilotConfirmationDialog::toggleAlert(const QString &prompt_text, const QString &button_text, QWidget *parent, const bool isLong) {
|
||||
ConfirmationDialog d(prompt_text, button_text, "", false, parent, isLong);
|
||||
return d.exec();
|
||||
}
|
||||
|
||||
bool FrogPilotConfirmationDialog::toggleReboot(QWidget *parent) {
|
||||
ConfirmationDialog d(tr("Reboot required to take effect."), tr("Reboot Now"), tr("Reboot Later"), false, parent);
|
||||
return d.exec();
|
||||
}
|
||||
|
||||
bool FrogPilotConfirmationDialog::yesorno(const QString &prompt_text, QWidget *parent) {
|
||||
ConfirmationDialog d(prompt_text, tr("Yes"), tr("No"), false, parent);
|
||||
return d.exec();
|
||||
}
|
||||
@@ -0,0 +1,551 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QTimer>
|
||||
|
||||
#include "selfdrive/ui/qt/widgets/controls.h"
|
||||
|
||||
QColor loadThemeColors(const QString &colorKey, const bool clearCache = false);
|
||||
|
||||
void updateFrogPilotToggles();
|
||||
|
||||
inline QString processModelName(const QString &modelName) {
|
||||
QString modelCleaned = modelName;
|
||||
modelCleaned = modelCleaned.remove(QRegularExpression("[🗺️👀📡]")).simplified();
|
||||
modelCleaned = modelCleaned.replace("(Default)", "");
|
||||
return modelCleaned;
|
||||
}
|
||||
|
||||
const QString buttonStyle = R"(
|
||||
QPushButton {
|
||||
padding: 0px 25px 0px 25px;
|
||||
border-radius: 50px;
|
||||
font-size: 35px;
|
||||
font-weight: 500;
|
||||
height: 100px;
|
||||
color: #E4E4E4;
|
||||
background-color: #393939;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #4a4a4a;
|
||||
}
|
||||
QPushButton:checked:enabled {
|
||||
background-color: #33Ab4C;
|
||||
}
|
||||
QPushButton:disabled {
|
||||
color: #33E4E4E4;
|
||||
}
|
||||
)";
|
||||
|
||||
class FrogPilotConfirmationDialog : public ConfirmationDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FrogPilotConfirmationDialog(const QString &prompt_text, const QString &confirm_text,
|
||||
const QString &cancel_text, const bool rich, QWidget *parent);
|
||||
static bool toggleAlert(const QString &prompt_text, const QString &button_text, QWidget *parent, const bool isLong=false);
|
||||
static bool toggleReboot(QWidget *parent);
|
||||
static bool yesorno(const QString &prompt_text, QWidget *parent);
|
||||
};
|
||||
|
||||
class FrogPilotListWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FrogPilotListWidget(QWidget *parent = 0) : QWidget(parent), outer_layout(this) {
|
||||
outer_layout.setMargin(0);
|
||||
outer_layout.setSpacing(0);
|
||||
outer_layout.addLayout(&inner_layout);
|
||||
inner_layout.setMargin(0);
|
||||
inner_layout.setSpacing(25); // default spacing is 25
|
||||
outer_layout.addStretch();
|
||||
}
|
||||
inline void addItem(QWidget *w) {
|
||||
w->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
inner_layout.addWidget(w);
|
||||
}
|
||||
inline void addItem(QLayout *layout) { inner_layout.addLayout(layout); }
|
||||
inline void setSpacing(int spacing) { inner_layout.setSpacing(spacing); }
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *) override {
|
||||
QPainter p(this);
|
||||
p.setPen(Qt::gray);
|
||||
for (int i = 0; i < inner_layout.count() - 1; ++i) {
|
||||
QWidget *widget = inner_layout.itemAt(i)->widget();
|
||||
|
||||
QWidget *nextWidget = nullptr;
|
||||
for (int j = i + 1; j < inner_layout.count(); ++j) {
|
||||
nextWidget = inner_layout.itemAt(j)->widget();
|
||||
if (nextWidget != nullptr && nextWidget->isVisible()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((widget == nullptr || widget->isVisible()) && nextWidget != nullptr && nextWidget->isVisible()) {
|
||||
QRect r = inner_layout.itemAt(i)->geometry();
|
||||
int bottom = r.bottom() + inner_layout.spacing() / 2;
|
||||
p.drawLine(r.left() + 40, bottom, r.right() - 40, bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
QVBoxLayout outer_layout;
|
||||
QVBoxLayout inner_layout;
|
||||
};
|
||||
|
||||
class FrogPilotButtonsControl : public AbstractControl {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FrogPilotButtonsControl(const QString &title, const QString &desc,
|
||||
const std::vector<QString> &buttonLabels,
|
||||
const bool checkable = false, const bool exclusive = true, const QString &icon = "",
|
||||
const int minimumButtonWidth = 225)
|
||||
: AbstractControl(title, desc, icon), buttonGroup(new QButtonGroup(this)) {
|
||||
buttonGroup->setExclusive(exclusive);
|
||||
for (int i = 0; i < buttonLabels.size(); ++i) {
|
||||
QPushButton *button = new QPushButton(buttonLabels[i], this);
|
||||
button->setCheckable(checkable);
|
||||
button->setStyleSheet(buttonStyle);
|
||||
button->setMinimumWidth(minimumButtonWidth);
|
||||
hlayout->addWidget(button);
|
||||
buttonGroup->addButton(button, i);
|
||||
}
|
||||
|
||||
QObject::connect(buttonGroup, QOverload<int>::of(&QButtonGroup::buttonClicked), [=](int id) {
|
||||
emit buttonClicked(id);
|
||||
});
|
||||
}
|
||||
|
||||
void setEnabled(bool enable) {
|
||||
for (QAbstractButton *btn : buttonGroup->buttons()) {
|
||||
btn->setEnabled(enable);
|
||||
}
|
||||
}
|
||||
|
||||
void setCheckedButton(int id, bool status = true) {
|
||||
if (QAbstractButton *button = buttonGroup->button(id)) {
|
||||
button->setChecked(status);
|
||||
}
|
||||
}
|
||||
|
||||
void setEnabledButtons(int id, bool enable) {
|
||||
if (QAbstractButton *button = buttonGroup->button(id)) {
|
||||
button->setEnabled(enable);
|
||||
}
|
||||
}
|
||||
|
||||
void setVisibleButton(int id, bool visible) {
|
||||
if (QAbstractButton *button = buttonGroup->button(id)) {
|
||||
button->setVisible(visible);
|
||||
}
|
||||
}
|
||||
|
||||
void setText(int id, const QString &text) {
|
||||
if (QAbstractButton *button = buttonGroup->button(id)) {
|
||||
button->setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
signals:
|
||||
void buttonClicked(int id);
|
||||
|
||||
private:
|
||||
QButtonGroup *buttonGroup;
|
||||
};
|
||||
|
||||
class FrogPilotButtonToggleControl : public ParamControl {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FrogPilotButtonToggleControl(const QString ¶m, const QString &title, const QString &desc,
|
||||
const std::vector<QString> &buttonParams, const std::vector<QString> &buttonLabels,
|
||||
const bool exclusive = false, const bool hideToggle = false, const int minimumButtonWidth = 225, QWidget *parent = nullptr)
|
||||
: ParamControl(param, title, desc, "", parent),
|
||||
key(param.toStdString()), buttonParams(buttonParams), buttonGroup(new QButtonGroup(this)), hideToggle(hideToggle) {
|
||||
buttonGroup->setExclusive(exclusive);
|
||||
|
||||
for (int i = 0; i < buttonLabels.size(); ++i) {
|
||||
QPushButton *button = new QPushButton(buttonLabels[i], this);
|
||||
button->setCheckable(true);
|
||||
button->setStyleSheet(buttonStyle);
|
||||
button->setMinimumWidth(minimumButtonWidth);
|
||||
hlayout->addWidget(button);
|
||||
buttonGroup->addButton(button, i);
|
||||
button->installEventFilter(this);
|
||||
}
|
||||
|
||||
hlayout->addWidget(&toggle);
|
||||
|
||||
QObject::connect(buttonGroup, QOverload<int>::of(&QButtonGroup::buttonClicked), [=](int id) {
|
||||
bool checked = buttonGroup->button(id)->isChecked();
|
||||
params.putBool(buttonParams[id].toStdString(), checked);
|
||||
emit buttonClicked(id);
|
||||
});
|
||||
|
||||
if (hideToggle) {
|
||||
toggle.hide();
|
||||
}
|
||||
|
||||
QObject::connect(this, &ToggleControl::toggleFlipped, this, &FrogPilotButtonToggleControl::refresh);
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
bool state = params.getBool(key) || hideToggle;
|
||||
if (state != toggle.on) {
|
||||
toggle.togglePosition();
|
||||
}
|
||||
|
||||
const QList<QAbstractButton *> buttons = buttonGroup->buttons();
|
||||
for (int i = 0; i < buttons.size(); ++i) {
|
||||
QAbstractButton *button = buttons[i];
|
||||
if (button) {
|
||||
button->setEnabled(state);
|
||||
button->setChecked(params.getBool(buttonParams[i].toStdString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setEnabledButtons(int id, bool enable) {
|
||||
if (QAbstractButton *button = buttonGroup->button(id)) {
|
||||
button->setEnabled(enable);
|
||||
}
|
||||
}
|
||||
|
||||
void setVisibleButton(int id, bool visible) {
|
||||
if (QAbstractButton *button = buttonGroup->button(id)) {
|
||||
button->setVisible(visible);
|
||||
}
|
||||
}
|
||||
|
||||
signals:
|
||||
void buttonClicked(int id);
|
||||
void disabledButtonClicked(int id);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override {
|
||||
if (event->type() == QEvent::MouseButtonPress) {
|
||||
QPushButton *button = qobject_cast<QPushButton *>(obj);
|
||||
if (button && !button->isEnabled()) {
|
||||
emit disabledButtonClicked(buttonGroup->id(button));
|
||||
}
|
||||
}
|
||||
return AbstractControl::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
void showEvent(QShowEvent *event) override {
|
||||
refresh();
|
||||
QObject::connect(this, &ToggleControl::toggleFlipped, this, &FrogPilotButtonToggleControl::refresh);
|
||||
}
|
||||
|
||||
private:
|
||||
Params params;
|
||||
|
||||
QButtonGroup *buttonGroup;
|
||||
|
||||
bool hideToggle;
|
||||
|
||||
std::string key;
|
||||
std::vector<QString> buttonParams;
|
||||
};
|
||||
|
||||
class FrogPilotParamManageControl : public ParamControl {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FrogPilotParamManageControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon, QWidget *parent = nullptr)
|
||||
: ParamControl(param, title, desc, icon, parent),
|
||||
key(param.toStdString()),
|
||||
manageButton(new ButtonControl("", tr("MANAGE"))) {
|
||||
|
||||
hlayout->insertWidget(hlayout->indexOf(&toggle) - 1, manageButton);
|
||||
|
||||
QObject::connect(manageButton, &ButtonControl::clicked, this, &FrogPilotParamManageControl::manageButtonClicked);
|
||||
QObject::connect(this, &ToggleControl::toggleFlipped, this, &FrogPilotParamManageControl::refresh);
|
||||
}
|
||||
|
||||
void setEnabled(bool enabled) {
|
||||
manageButton->setEnabled(enabled && params.getBool(key));
|
||||
|
||||
toggle.setEnabled(enabled);
|
||||
toggle.update();
|
||||
}
|
||||
|
||||
void setVisibleButton(bool visible) {
|
||||
manageButton->setVisible(visible);
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
manageButton->setEnabled(params.getBool(key));
|
||||
}
|
||||
|
||||
signals:
|
||||
void manageButtonClicked();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *event) override {
|
||||
ParamControl::showEvent(event);
|
||||
refresh();
|
||||
}
|
||||
|
||||
private:
|
||||
Params params;
|
||||
|
||||
ButtonControl *manageButton;
|
||||
|
||||
std::string key;
|
||||
};
|
||||
|
||||
class FrogPilotParamValueControl : public AbstractControl {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QLabel *valueLabel;
|
||||
|
||||
FrogPilotParamValueControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon,
|
||||
const float minValue, const float maxValue, const QString &label = "", const std::map<int, QString> &valueLabels = {},
|
||||
const float interval = 1.0f, const bool compactSize = false)
|
||||
: AbstractControl(title, desc, icon), key(param.toStdString()), minValue(minValue), maxValue(maxValue),
|
||||
labelText(label), interval(interval), valueLabels(valueLabels),
|
||||
decimalPlaces(std::ceil(-std::log10(interval))), factor(std::pow(10.0f, decimalPlaces)) {
|
||||
|
||||
setupButton(decrementButton, "-");
|
||||
setupButton(incrementButton, "+");
|
||||
|
||||
valueLabel = new QLabel(this);
|
||||
valueLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||
valueLabel->setStyleSheet("QLabel { color: #E0E879; }");
|
||||
if (compactSize) {
|
||||
valueLabel->setFixedSize(175, 100);
|
||||
} else {
|
||||
valueLabel->setFixedSize(350, 100);
|
||||
}
|
||||
|
||||
hlayout->addWidget(valueLabel);
|
||||
hlayout->addWidget(&decrementButton);
|
||||
hlayout->addWidget(&incrementButton);
|
||||
|
||||
QObject::connect(&decrementButton, &QPushButton::pressed, this, &FrogPilotParamValueControl::onDecrementPressed);
|
||||
QObject::connect(&incrementButton, &QPushButton::pressed, this, &FrogPilotParamValueControl::onIncrementPressed);
|
||||
QObject::connect(&decrementButton, &QPushButton::released, this, &FrogPilotParamValueControl::onButtonReleased);
|
||||
QObject::connect(&incrementButton, &QPushButton::released, this, &FrogPilotParamValueControl::onButtonReleased);
|
||||
}
|
||||
|
||||
void updateControl(float newMinValue, float newMaxValue, const QString &newLabel = "") {
|
||||
minValue = newMinValue;
|
||||
maxValue = newMaxValue;
|
||||
labelText = newLabel;
|
||||
refresh();
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
value = params.getFloat(key);
|
||||
updateValueDisplay();
|
||||
}
|
||||
|
||||
signals:
|
||||
void valueChanged(float value);
|
||||
|
||||
protected:
|
||||
void hideEvent(QHideEvent *event) override {
|
||||
params.putFloat(key, value);
|
||||
}
|
||||
|
||||
void showEvent(QShowEvent *event) override {
|
||||
refresh();
|
||||
}
|
||||
|
||||
private slots:
|
||||
void onIncrementPressed() {
|
||||
adjustValue(interval);
|
||||
}
|
||||
|
||||
void onDecrementPressed() {
|
||||
adjustValue(-interval);
|
||||
}
|
||||
|
||||
void onButtonReleased() {
|
||||
params.putFloat(key, value);
|
||||
|
||||
float lastValue = value;
|
||||
QTimer::singleShot(50, this, [this, lastValue]() {
|
||||
if (lastValue != value) {
|
||||
return;
|
||||
}
|
||||
|
||||
previousDelta = false;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
void adjustValue(float delta) {
|
||||
float modResult = fmod(value, 5.0f * interval);
|
||||
if (modResult < interval) {
|
||||
if (previousDelta) {
|
||||
delta *= 5;
|
||||
}
|
||||
previousDelta = true;
|
||||
}
|
||||
|
||||
value = qBound(minValue, value + delta, maxValue);
|
||||
value = std::round(value * factor) / factor;
|
||||
|
||||
updateValueDisplay();
|
||||
|
||||
emit valueChanged(value);
|
||||
}
|
||||
|
||||
void updateValueDisplay() {
|
||||
int intValue = static_cast<int>(value / interval);
|
||||
if (valueLabels.count(intValue)) {
|
||||
valueLabel->setText(valueLabels.at(intValue));
|
||||
} else {
|
||||
valueLabel->setText(QString::number(value, 'f', decimalPlaces) + labelText);
|
||||
}
|
||||
}
|
||||
|
||||
void setupButton(QPushButton &button, const QString &text) {
|
||||
button.setFixedSize(150, 100);
|
||||
button.setText(text);
|
||||
button.setAutoRepeat(true);
|
||||
button.setAutoRepeatInterval(150);
|
||||
button.setAutoRepeatDelay(500);
|
||||
button.setStyleSheet(R"(
|
||||
QPushButton {
|
||||
border-radius: 50px;
|
||||
font-size: 50px;
|
||||
font-weight: 500;
|
||||
height: 100px;
|
||||
padding: 0 25px;
|
||||
color: #E4E4E4;
|
||||
background-color: #393939;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #4a4a4a;
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
Params params;
|
||||
|
||||
QPushButton decrementButton;
|
||||
QPushButton incrementButton;
|
||||
|
||||
QString labelText;
|
||||
|
||||
bool previousDelta;
|
||||
|
||||
int decimalPlaces;
|
||||
|
||||
float factor;
|
||||
float interval;
|
||||
float minValue;
|
||||
float maxValue;
|
||||
float value;
|
||||
|
||||
std::map<int, QString> valueLabels;
|
||||
|
||||
std::string key;
|
||||
};
|
||||
|
||||
class FrogPilotParamValueButtonControl : public FrogPilotParamValueControl {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FrogPilotParamValueButtonControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon,
|
||||
const float minValue, const float maxValue, const QString &label = "", const std::map<int, QString> &valueLabels = {},
|
||||
const float interval = 1.0f,
|
||||
const std::vector<QString> &buttonParams = {}, const std::vector<QString> &buttonLabels = {},
|
||||
const bool leftButton = false, const bool checkable = true, const int minimumButtonWidth = 225)
|
||||
: FrogPilotParamValueControl(param, title, desc, icon, minValue, maxValue, label, valueLabels, interval, true),
|
||||
buttonParams(buttonParams),
|
||||
buttonGroup(new QButtonGroup(this)),
|
||||
checkable(checkable) {
|
||||
buttonGroup->setExclusive(false);
|
||||
|
||||
for (int i = 0; i < buttonLabels.size(); ++i) {
|
||||
QPushButton *button = new QPushButton(buttonLabels[i], this);
|
||||
button->setCheckable(checkable);
|
||||
button->setStyleSheet(buttonStyle);
|
||||
button->setMinimumWidth(minimumButtonWidth);
|
||||
|
||||
if (leftButton) {
|
||||
hlayout->insertWidget(hlayout->indexOf(valueLabel) - 1, button);
|
||||
} else {
|
||||
hlayout->addWidget(button);
|
||||
}
|
||||
buttonGroup->addButton(button, i);
|
||||
}
|
||||
|
||||
QObject::connect(buttonGroup, QOverload<int>::of(&QButtonGroup::buttonClicked), [=](int id) {
|
||||
if (checkable) {
|
||||
bool checked = buttonGroup->button(id)->isChecked();
|
||||
params.putBool(buttonParams[id].toStdString(), checked);
|
||||
}
|
||||
emit buttonClicked(id);
|
||||
});
|
||||
|
||||
QObject::connect(this, &FrogPilotParamValueControl::valueChanged, this, &FrogPilotParamValueButtonControl::refresh);
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
if (checkable) {
|
||||
const QList<QAbstractButton *> buttons = buttonGroup->buttons();
|
||||
for (int i = 0; i < buttons.size(); ++i) {
|
||||
QAbstractButton *button = buttons[i];
|
||||
if (button) {
|
||||
button->setChecked(params.getBool(buttonParams[i].toStdString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
FrogPilotParamValueControl::refresh();
|
||||
}
|
||||
|
||||
signals:
|
||||
void buttonClicked(int id);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *event) override {
|
||||
FrogPilotParamValueControl::showEvent(event);
|
||||
refresh();
|
||||
}
|
||||
|
||||
private:
|
||||
Params params;
|
||||
|
||||
QButtonGroup *buttonGroup;
|
||||
|
||||
bool checkable;
|
||||
|
||||
std::vector<QString> buttonParams;
|
||||
};
|
||||
|
||||
class FrogPilotDualParamControl : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FrogPilotDualParamControl(FrogPilotParamValueControl *control1, FrogPilotParamValueControl *control2, QWidget *parent = nullptr)
|
||||
: QFrame(parent), control1(control1), control2(control2) {
|
||||
QHBoxLayout *hlayout = new QHBoxLayout(this);
|
||||
hlayout->addWidget(control1);
|
||||
hlayout->addWidget(control2);
|
||||
|
||||
control1->setObjectName("control1");
|
||||
control2->setObjectName("control2");
|
||||
}
|
||||
|
||||
void updateControl(float newMinValue, float newMaxValue, const QString &newLabel = "") {
|
||||
control1->updateControl(newMinValue, newMaxValue, newLabel);
|
||||
control2->updateControl(newMinValue, newMaxValue, newLabel);
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
control1->refresh();
|
||||
control2->refresh();
|
||||
}
|
||||
|
||||
private:
|
||||
FrogPilotParamValueControl *control1;
|
||||
FrogPilotParamValueControl *control2;
|
||||
};
|
||||
@@ -0,0 +1,244 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "selfdrive/frogpilot/ui/qt/widgets/model_reviewer.h"
|
||||
|
||||
ModelReview::ModelReview(QWidget *parent) : QFrame(parent) {
|
||||
mainLayout = new QStackedLayout(this);
|
||||
|
||||
QVBoxLayout *ratingLayout = new QVBoxLayout();
|
||||
ratingLayout->setContentsMargins(50, 25, 50, 20);
|
||||
|
||||
questionLabel = addLabel(ratingLayout, "What would you rate that drive?", "question");
|
||||
|
||||
QHBoxLayout *ratingButtonsLayout = new QHBoxLayout();
|
||||
QStringList emojis = {"🤩", "🙂", "🤔", "🙁", "🤕"};
|
||||
QList<int> scores = {100, 80, 60, 40, 20};
|
||||
|
||||
for (int i = 0; i < emojis.size(); ++i) {
|
||||
QPushButton *ratingButton = createButton(emojis[i], "rating_button", scores[i], 150, 150);
|
||||
ratingButtonsLayout->addWidget(ratingButton);
|
||||
QObject::connect(ratingButton, &QPushButton::clicked, this, &ModelReview::onRatingButtonClicked);
|
||||
}
|
||||
|
||||
ratingLayout->addLayout(ratingButtonsLayout);
|
||||
|
||||
blacklistButton = createButton("Blacklist this model", "blacklist_button", 0, 600, 100);
|
||||
QObject::connect(blacklistButton, &QPushButton::clicked, this, &ModelReview::onBlacklistButtonClicked);
|
||||
ratingLayout->addWidget(blacklistButton, 0, Qt::AlignCenter);
|
||||
|
||||
QWidget *ratingWidget = new QWidget(this);
|
||||
ratingWidget->setLayout(ratingLayout);
|
||||
mainLayout->addWidget(ratingWidget);
|
||||
|
||||
QVBoxLayout *modelInfoLayout = new QVBoxLayout();
|
||||
modelInfoLayout->setContentsMargins(50, 25, 50, 20);
|
||||
|
||||
titleLabel = addLabel(modelInfoLayout, "The model used during that drive was:", "title");
|
||||
modelLabel = addLabel(modelInfoLayout, "", "model");
|
||||
|
||||
modelInfoLayout->addItem(new QSpacerItem(20, 75, QSizePolicy::Minimum, QSizePolicy::Fixed));
|
||||
|
||||
QVBoxLayout *bottomLayout = new QVBoxLayout();
|
||||
modelScoreLabel = addLabel(bottomLayout, "Current Model Score: 0", "score");
|
||||
modelRankLabel = addLabel(bottomLayout, "Current Model Rank: 0", "rank");
|
||||
totalDrivesLabel = addLabel(bottomLayout, "Total Model Drives: 0", "drives");
|
||||
totalOverallDrivesLabel = addLabel(bottomLayout, "Total Overall Model Drives: 0", "drives");
|
||||
blacklistMessageLabel = addLabel(bottomLayout, "", "blacklist_message");
|
||||
|
||||
modelInfoLayout->addLayout(bottomLayout);
|
||||
|
||||
QWidget *modelInfoWidget = new QWidget(this);
|
||||
modelInfoWidget->setLayout(modelInfoLayout);
|
||||
mainLayout->addWidget(modelInfoWidget);
|
||||
|
||||
setStyleSheet(R"(
|
||||
ModelReview {
|
||||
background-color: #333333;
|
||||
border-radius: 5px;
|
||||
}
|
||||
QLabel[type="drives"], QLabel[type="question"], QLabel[type="rank"], QLabel[type="score"], QLabel[type="title"] {
|
||||
font-size: 50px;
|
||||
font-weight: semi-bold;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
QLabel[type="model"] {
|
||||
font-size: 75px;
|
||||
font-weight: bold;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
QLabel[type="blacklist_message"] {
|
||||
font-size: 40px;
|
||||
font-weight: bold;
|
||||
color: #C92231;
|
||||
}
|
||||
QPushButton[type="rating_button"] {
|
||||
font-size: 75px;
|
||||
font-weight: bold;
|
||||
padding: 10px;
|
||||
color: #FFFFFF;
|
||||
background-color: #555555;
|
||||
border: 2px solid #FFFFFF;
|
||||
border-radius: 5px;
|
||||
}
|
||||
QPushButton[type="rating_button"]:hover {
|
||||
background-color: #777777;
|
||||
}
|
||||
QPushButton[type="blacklist_button"] {
|
||||
font-size: 50px;
|
||||
font-weight: bold;
|
||||
padding: 10px;
|
||||
color: #C92231;
|
||||
background-color: #000000;
|
||||
border: 2px solid #FFFFFF;
|
||||
border-radius: 5px;
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
QLabel *ModelReview::addLabel(QVBoxLayout *layout, const QString &text, const QString &type) {
|
||||
QLabel *label = new QLabel(text, this);
|
||||
label->setProperty("type", type);
|
||||
label->setAlignment(Qt::AlignCenter);
|
||||
layout->addWidget(label);
|
||||
return label;
|
||||
}
|
||||
|
||||
QPushButton *ModelReview::createButton(const QString &text, const QString &type, int rating, int width, int height) {
|
||||
QPushButton *button = new QPushButton(text, this);
|
||||
button->setProperty("type", type);
|
||||
button->setProperty("rating", rating);
|
||||
button->setFixedSize(width, height);
|
||||
return button;
|
||||
}
|
||||
|
||||
void ModelReview::showEvent(QShowEvent *event) {
|
||||
QStringList availableModels = QString::fromStdString(params.get("AvailableModels")).split(",");
|
||||
QStringList availableModelNames = QString::fromStdString(params.get("AvailableModelNames")).split(",");
|
||||
|
||||
QMap<QString, QString> modelFileToNameMap;
|
||||
for (int i = 0; i < qMin(availableModels.size(), availableModelNames.size()); ++i) {
|
||||
modelFileToNameMap.insert(availableModels[i], processModelName(availableModelNames[i]));
|
||||
}
|
||||
|
||||
currentModel = uiState()->scene.model;
|
||||
currentModelFiltered = modelFileToNameMap.value(currentModel);
|
||||
|
||||
mainLayout->setCurrentIndex(modelRated ? 1 : 0);
|
||||
|
||||
checkBlacklistButtonVisibility();
|
||||
}
|
||||
|
||||
void ModelReview::mousePressEvent(QMouseEvent *e) {
|
||||
if (mainLayout->currentIndex() != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit driveRated();
|
||||
}
|
||||
|
||||
void ModelReview::updateLabel() {
|
||||
totalDrivesLabel->setText(QString("Total Model Drives: %1").arg(totalDrives));
|
||||
modelLabel->setText(currentModelFiltered);
|
||||
modelRankLabel->setText(QString("Current Model Rank: %1").arg(getModelRank()));
|
||||
modelScoreLabel->setText(QString("Current Model Score: %1").arg(finalRating));
|
||||
totalOverallDrivesLabel->setText(QString("Total Overall Drives: %1").arg(totalOverallDrives));
|
||||
|
||||
mainLayout->setCurrentIndex(1);
|
||||
|
||||
QTimer::singleShot(30000, [this]() {
|
||||
emit driveRated();
|
||||
modelRated = false;
|
||||
});
|
||||
}
|
||||
|
||||
void ModelReview::onRatingButtonClicked() {
|
||||
int newRating = qobject_cast<QPushButton*>(sender())->property("rating").toInt();
|
||||
|
||||
QString jsonString = QString::fromStdString(params.get("ModelDrivesAndScores"));
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonString.toUtf8());
|
||||
QJsonObject jsonObject = jsonDoc.isObject() ? jsonDoc.object() : QJsonObject();
|
||||
|
||||
QJsonObject modelData = jsonObject.value(currentModelFiltered).toObject();
|
||||
int modelDrives = modelData.value("Drives").toInt();
|
||||
int modelScore = modelData.value("Score").toInt();
|
||||
|
||||
totalDrives = modelDrives + 1;
|
||||
finalRating = ((modelScore * modelDrives) + newRating) / totalDrives;
|
||||
|
||||
modelData["Drives"] = totalDrives;
|
||||
modelData["Score"] = finalRating;
|
||||
jsonObject[currentModelFiltered] = modelData;
|
||||
|
||||
params.put("ModelDrivesAndScores", QString(QJsonDocument(jsonObject).toJson(QJsonDocument::Compact)).toStdString());
|
||||
|
||||
modelRated = true;
|
||||
updateLabel();
|
||||
}
|
||||
|
||||
void ModelReview::onBlacklistButtonClicked() {
|
||||
QString jsonString = QString::fromStdString(params.get("ModelDrivesAndScores"));
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonString.toUtf8());
|
||||
QJsonObject jsonObject = jsonDoc.isObject() ? jsonDoc.object() : QJsonObject();
|
||||
|
||||
QJsonObject modelData = jsonObject.value(currentModelFiltered).toObject();
|
||||
int modelDrives = modelData.value("Drives").toInt();
|
||||
|
||||
totalDrives = modelDrives + 1;
|
||||
modelData["Drives"] = totalDrives;
|
||||
modelData["Score"] = 0;
|
||||
jsonObject[currentModelFiltered] = modelData;
|
||||
|
||||
if (!blacklistedModels.contains(currentModel)) {
|
||||
blacklistedModels.append(currentModel);
|
||||
params.put("BlacklistedModels", blacklistedModels.join(",").toStdString());
|
||||
}
|
||||
|
||||
params.put("ModelDrivesAndScores", QString(QJsonDocument(jsonObject).toJson(QJsonDocument::Compact)).toStdString());
|
||||
|
||||
blacklistMessageLabel->setText("Model successfully blacklisted!");
|
||||
updateLabel();
|
||||
}
|
||||
|
||||
void ModelReview::checkBlacklistButtonVisibility() {
|
||||
QStringList availableModels = QString::fromStdString(params.get("AvailableModels")).split(",");
|
||||
blacklistedModels = QString::fromStdString(params.get("BlacklistedModels")).split(",", QString::SkipEmptyParts);
|
||||
|
||||
blacklistButton->setVisible(availableModels.size() > blacklistedModels.size());
|
||||
}
|
||||
|
||||
int ModelReview::getModelRank() {
|
||||
QString jsonString = QString::fromStdString(params.get("ModelDrivesAndScores"));
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonString.toUtf8());
|
||||
QJsonObject jsonObject = jsonDoc.isObject() ? jsonDoc.object() : QJsonObject();
|
||||
|
||||
QList<QPair<QString, int>> modelScores;
|
||||
totalOverallDrives = 0;
|
||||
|
||||
QStringList availableModels = QString::fromStdString(params.get("AvailableModelNames")).split(",");
|
||||
for (const QString &model : availableModels) {
|
||||
QString processedModel = processModelName(model);
|
||||
QJsonObject modelData = jsonObject.value(processedModel).toObject();
|
||||
|
||||
int modelDrives = modelData.value("Drives").toInt();
|
||||
int modelScore = modelData.value("Score").toInt();
|
||||
|
||||
totalOverallDrives += modelDrives;
|
||||
|
||||
if (modelScore > 0) {
|
||||
modelScores.append(qMakePair(processedModel, modelScore));
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(modelScores.begin(), modelScores.end(), [](QPair<QString, int> &a, QPair<QString, int> &b) {
|
||||
return a.second > b.second;
|
||||
});
|
||||
|
||||
for (int i = 0; i < modelScores.size(); ++i) {
|
||||
if (modelScores[i].first == currentModelFiltered) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include "selfdrive/ui/ui.h"
|
||||
|
||||
class ModelReview : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ModelReview(QWidget *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void driveRated();
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void showEvent(QShowEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void onBlacklistButtonClicked();
|
||||
void onRatingButtonClicked();
|
||||
|
||||
private:
|
||||
int getModelRank();
|
||||
|
||||
QLabel *addLabel(QVBoxLayout *layout, const QString &text, const QString &type);
|
||||
|
||||
QPushButton *createButton(const QString &text, const QString &type, int rating, int width, int height);
|
||||
|
||||
void checkBlacklistButtonVisibility();
|
||||
void updateLabel();
|
||||
|
||||
QStackedLayout *mainLayout;
|
||||
|
||||
QLabel *blacklistMessageLabel;
|
||||
QLabel *modelLabel;
|
||||
QLabel *modelRankLabel;
|
||||
QLabel *modelScoreLabel;
|
||||
QLabel *questionLabel;
|
||||
QLabel *titleLabel;
|
||||
QLabel *totalDrivesLabel;
|
||||
QLabel *totalOverallDrivesLabel;
|
||||
|
||||
QPushButton *blacklistButton;
|
||||
|
||||
Params params;
|
||||
Params params_memory{"/dev/shm/params"};
|
||||
|
||||
QString currentModel;
|
||||
QString currentModelFiltered;
|
||||
|
||||
QStringList blacklistedModels;
|
||||
|
||||
bool modelRated;
|
||||
|
||||
int finalRating;
|
||||
int totalDrives;
|
||||
int totalOverallDrives;
|
||||
};
|
||||
Reference in New Issue
Block a user