diff --git a/notes b/notes index 201f52c..010afa8 100644 --- a/notes +++ b/notes @@ -39,6 +39,7 @@ hyundai/interface ENABLE_BUTTONS = (Buttons.RES_ACCEL, Buttons.SET_DECEL, Button - car location tracker automatic upload / api of car location - can i get the cabin temprature from the comma??? display on home screen? - review "BBOTCRASH" - rewrite the crash handler +- show map illustration, and drive stats, on route completion https://github.com/pfeiferj/pfeifer-pilot-patches/tree/pfeifer-openpilot-patches/speed-limit-control https://github.com/pfeiferj/pfeifer-pilot-patches/tree/pfeifer-openpilot-patches/disable-dcam-upload https://github.com/pfeiferj/pfeifer-pilot-patches/tree/pfeifer-openpilot-patches/disable-registration diff --git a/selfdrive/oscarpilot/buttons.py b/selfdrive/oscarpilot/buttons.py new file mode 100644 index 0000000..2e789b2 --- /dev/null +++ b/selfdrive/oscarpilot/buttons.py @@ -0,0 +1,21 @@ +import cereal.messaging as messaging +import numpy as np + +from openpilot.common.conversions import Conversions as CV +from openpilot.common.numpy_fast import clip, interp +from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N, V_CRUISE_MAX +from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import T_IDXS as T_IDXS_MPC +from openpilot.selfdrive.controls.lib.longitudinal_planner import A_CRUISE_MIN, A_CRUISE_MAX_VALS, A_CRUISE_MAX_BP, get_max_accel +from openpilot.selfdrive.modeld.constants import ModelConstants + +from openpilot.selfdrive.frogpilot.functions.conditional_experimental_mode import ConditionalExperimentalMode +from openpilot.selfdrive.frogpilot.functions.map_turn_speed_controller import MapTurnSpeedController +from openpilot.selfdrive.frogpilot.functions.speed_limit_controller import SpeedLimitController + +# Class for intercepting when buttons are pressed, or dispatching buttons to be pressed + + +class OscarPilotButtons: + def __init__(self, params, params_memory): + self.params_memory = params_memory + \ No newline at end of file diff --git a/selfdrive/oscarpilot/events.py b/selfdrive/oscarpilot/events.py new file mode 100644 index 0000000..c5818e3 --- /dev/null +++ b/selfdrive/oscarpilot/events.py @@ -0,0 +1,40 @@ +import cereal.messaging as messaging +import numpy as np + +from openpilot.common.conversions import Conversions as CV +from openpilot.common.numpy_fast import clip, interp +from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N, V_CRUISE_MAX +from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import T_IDXS as T_IDXS_MPC +from openpilot.selfdrive.controls.lib.longitudinal_planner import A_CRUISE_MIN, A_CRUISE_MAX_VALS, A_CRUISE_MAX_BP, get_max_accel +from openpilot.selfdrive.modeld.constants import ModelConstants + +from openpilot.selfdrive.frogpilot.functions.conditional_experimental_mode import ConditionalExperimentalMode +from openpilot.selfdrive.frogpilot.functions.map_turn_speed_controller import MapTurnSpeedController +from openpilot.selfdrive.frogpilot.functions.speed_limit_controller import SpeedLimitController + +# class FrogPilotPlanner: +# def __init__(self, params, params_memory): +# self.params_memory = params_memory +# self.cem = ConditionalExperimentalMode() +# self.mtsc = MapTurnSpeedController() + +# self.override_slc = False + +# self.overridden_speed = 0 +# self.mtsc_target = 0 +# self.slc_target = 0 +# self.v_cruise = 0 +# self.vtsc_target = 0 + +# self.x_desired_trajectory = np.zeros(CONTROL_N) + +# self.update_frogpilot_params(params, params_memory) + +# def update(self, sm, mpc): +# carState, controlsState, modelData = sm['carState'], sm['controlsState'], sm['modelV2'] + +# enabled = controlsState.enabled + +# v_cruise_kph = min(controlsState.vCruise, V_CRUISE_MAX) +# v_cruise = v_cruise_kph * CV.KPH_TO_MS +# v_ego = carState.vEgo \ No newline at end of file diff --git a/selfdrive/oscarpilot/settings/advanced.cc b/selfdrive/oscarpilot/settings/advanced.cc new file mode 100644 index 0000000..8eb1354 --- /dev/null +++ b/selfdrive/oscarpilot/settings/advanced.cc @@ -0,0 +1 @@ +// Should show a UI showing the settings menu for stock frogpilot \ No newline at end of file diff --git a/selfdrive/oscarpilot/settings/advanced.h b/selfdrive/oscarpilot/settings/advanced.h new file mode 100644 index 0000000..e69de29 diff --git a/selfdrive/oscarpilot/settings/basic.cc b/selfdrive/oscarpilot/settings/basic.cc new file mode 100644 index 0000000..3cb18a2 --- /dev/null +++ b/selfdrive/oscarpilot/settings/basic.cc @@ -0,0 +1,242 @@ +#include "selfdrive/frogpilot/ui/visual_settings.h" +#include "selfdrive/ui/ui.h" + +FrogPilotVisualsPanel::FrogPilotVisualsPanel(OscarSettingsWindow *parent) : FrogPilotListWidget(parent) { + const std::vector> visualToggles { + {"CustomTheme", "Custom Themes", "Enable the ability to use custom themes.", "../frogpilot/assets/wheel_images/frog.png"}, + {"CustomColors", "Color Theme", "Switch out the standard openpilot color scheme with a custom color scheme.\n\nWant to submit your own color scheme? Post it in the 'feature-request' channel in the FrogPilot Discord!", ""}, + {"CustomIcons", "Icon Pack", "Switch out the standard openpilot icons with a set of custom icons.\n\nWant to submit your own icon pack? Post it in the 'feature-request' channel in the FrogPilot Discord!", ""}, + {"CustomSignals", "Turn Signals", "Add custom animations for your turn signals for a personal touch!\n\nWant to submit your own turn signal animation? Post it in the 'feature-request' channel in the FrogPilot Discord!", ""}, + {"CustomSounds", "Sound Pack", "Switch out the standard openpilot sounds with a set of custom sounds.\n\nWant to submit your own sound pack? Post it in the 'feature-request' channel in the FrogPilot Discord!", ""}, + + {"CameraView", "Camera View", "Choose your preferred camera view for the onroad UI. This is a visual change only and doesn't impact openpilot.", "../frogpilot/assets/toggle_icons/icon_camera.png"}, + {"Compass", "Compass", "Add a compass to your onroad UI.", "../frogpilot/assets/toggle_icons/icon_compass.png"}, + + {"CustomUI", "Custom Onroad UI", "Customize the Onroad UI with some additional visual functions.", "../assets/offroad/icon_road.png"}, + {"AdjacentPath", "Adjacent Paths", "Display paths to the left and right of your car, visualizing where the model detects lanes.", ""}, + {"BlindSpotPath", "Blind Spot Path", "Visualize your blind spots with a red path when another vehicle is detected nearby.", ""}, + {"ShowFPS", "FPS Counter", "Display the Frames Per Second (FPS) of your onroad UI for monitoring system performance.", ""}, + {"LeadInfo", "Lead Info and Logics", "Get detailed information about the vehicle ahead, including speed and distance, and the logic behind your following distance.", ""}, + {"RoadNameUI", "Road Name", "See the name of the road you're on at the bottom of your screen. Sourced from OpenStreetMap.", ""}, + {"UseVienna", "Use Vienna Speed Limit Signs", "Use the Vienna (EU) speed limit style signs as opposed to MUTCD (US).", ""}, + + {"DriverCamera", "Driver Camera On Reverse", "Show the driver's camera feed when you shift to reverse.", "../assets/img_driver_face_static.png"}, + {"GreenLightAlert", "Green Light Alert", "Get an alert when a traffic light changes from red to green.", "../frogpilot/assets/toggle_icons/icon_green_light.png"}, + + {"ModelUI", "Model UI", "Personalize how the model's visualizations appear on your screen.", "../assets/offroad/icon_calibration.png"}, + {"AccelerationPath", "Acceleration Path", "Visualize the car's intended acceleration or deceleration with a color-coded path.", ""}, + {"LaneLinesWidth", "Lane Lines", "Adjust the visual thickness of lane lines on your display.\n\nDefault matches the MUTCD average of 4 inches.", ""}, + {"PathEdgeWidth", "Path Edges", "Adjust the width of the path edges shown on your UI to represent different driving modes and statuses.\n\nDefault is 20% of the total path.\n\nBlue = Navigation\nLight Blue = Always On Lateral\nGreen = Default with 'FrogPilot Colors'\nLight Green = Default with stock colors\nOrange = Experimental Mode Active\nYellow = Conditional Overriden", ""}, + {"PathWidth", "Path Width", "Customize the width of the driving path shown on your UI.\n\nDefault matches the width of a 2019 Lexus ES 350.", ""}, + {"RoadEdgesWidth", "Road Edges", "Adjust the visual thickness of road edges on your display.\n\nDefault is 1/2 of the MUTCD average lane line width of 4 inches.", ""}, + {"UnlimitedLength", "'Unlimited' Road UI Length", "Extend the display of the path, lane lines, and road edges as far as the system can detect, providing a more expansive view of the road ahead.", ""}, + + {"QOLVisuals", "Quality of Life", "Miscellaneous quality of life changes to improve your overall openpilot experience.", "../frogpilot/assets/toggle_icons/quality_of_life.png"}, + {"DriveStats", "Drive Stats In Home Screen", "Display your device's drive stats in the home screen.", ""}, + {"HideSpeed", "Hide Speed", "Hide the speed indicator in the onroad UI.", ""}, + {"ShowSLCOffset", "Show Speed Limit Offset", "Show the speed limit offset seperated from the speed limit in the onroad UI when using 'Speed Limit Controller'.", ""}, + + {"RandomEvents", "Random Events", "Enjoy a bit of unpredictability with random events that can occur during certain driving conditions.", "../frogpilot/assets/toggle_icons/icon_random.png"}, + {"ScreenBrightness", "Screen Brightness", "Customize your screen brightness.", "../frogpilot/assets/toggle_icons/icon_light.png"}, + {"SilentMode", "Silent Mode", "Mute openpilot sounds for a quieter driving experience.", "../frogpilot/assets/toggle_icons/icon_mute.png"}, + {"WheelIcon", "Steering Wheel Icon", "Replace the default steering wheel icon with a custom design, adding a unique touch to your interface.", "../assets/offroad/icon_openpilot.png"}, + }; + + for (const auto &[param, title, desc, icon] : visualToggles) { + ParamControl *toggle; + + if (param == "CameraView") { + std::vector cameraOptions{tr("Auto"), tr("Standard"), tr("Wide"), tr("Driver")}; + FrogPilotButtonParamControl *preferredCamera = new FrogPilotButtonParamControl(param, title, desc, icon, cameraOptions); + toggle = preferredCamera; + + } else if (param == "CustomTheme") { + FrogPilotParamManageControl *customThemeToggle = new FrogPilotParamManageControl(param, title, desc, icon, this); + QObject::connect(customThemeToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() { + parentToggleClicked(); + for (auto &[key, toggle] : toggles) { + toggle->setVisible(customThemeKeys.find(key.c_str()) != customThemeKeys.end()); + } + }); + toggle = customThemeToggle; + } else if (param == "CustomColors" || param == "CustomIcons" || param == "CustomSignals" || param == "CustomSounds") { + std::vector themeOptions{tr("Stock"), tr("Frog"), tr("Tesla"), tr("Stalin")}; + FrogPilotButtonParamControl *themeSelection = new FrogPilotButtonParamControl(param, title, desc, icon, themeOptions); + toggle = themeSelection; + + if (param == "CustomSounds") { + QObject::connect(static_cast(toggle), &FrogPilotButtonParamControl::buttonClicked, [this](int id) { + if (id == 1) { + if (FrogPilotConfirmationDialog::yesorno("Do you want to enable the bonus 'Goat' sound effect?", this)) { + params.putBool("GoatScream", true); + } else { + params.putBool("GoatScream", false); + } + } + }); + } + + } else if (param == "CustomUI") { + FrogPilotParamManageControl *customUIToggle = new FrogPilotParamManageControl(param, title, desc, icon, this); + QObject::connect(customUIToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() { + parentToggleClicked(); + for (auto &[key, toggle] : toggles) { + toggle->setVisible(customOnroadUIKeys.find(key.c_str()) != customOnroadUIKeys.end()); + } + }); + toggle = customUIToggle; + } else if (param == "LeadInfo") { + std::vector leadInfoToggles{tr("UseSI")}; + std::vector leadInfoToggleNames{tr("Use SI Values")}; + toggle = new FrogPilotParamToggleControl(param, title, desc, icon, leadInfoToggles, leadInfoToggleNames); + + } else if (param == "ModelUI") { + FrogPilotParamManageControl *modelUIToggle = new FrogPilotParamManageControl(param, title, desc, icon, this); + QObject::connect(modelUIToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() { + parentToggleClicked(); + for (auto &[key, toggle] : toggles) { + toggle->setVisible(modelUIKeys.find(key.c_str()) != modelUIKeys.end()); + } + }); + toggle = modelUIToggle; + } else if (param == "LaneLinesWidth" || param == "RoadEdgesWidth") { + toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 24, std::map(), this, false, " inches"); + } else if (param == "PathEdgeWidth") { + toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, std::map(), this, false, "%"); + } else if (param == "PathWidth") { + toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, std::map(), this, false, " feet", 10); + + } else if (param == "QOLVisuals") { + FrogPilotParamManageControl *qolToggle = new FrogPilotParamManageControl(param, title, desc, icon, this); + QObject::connect(qolToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() { + parentToggleClicked(); + for (auto &[key, toggle] : toggles) { + toggle->setVisible(qolKeys.find(key.c_str()) != qolKeys.end()); + } + }); + toggle = qolToggle; + + } else if (param == "ScreenBrightness") { + std::map brightnessLabels; + for (int i = 0; i <= 101; ++i) { + brightnessLabels[i] = i == 0 ? "Screen Off" : i == 101 ? "Auto" : QString::number(i) + "%"; + } + toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 101, brightnessLabels, this, false); + + } else if (param == "WheelIcon") { + std::vector wheelToggles{"RotatingWheel"}; + std::vector wheelToggleNames{tr("Rotating")}; + std::map steeringWheelLabels = {{0, "Stock"}, {1, "Lexus"}, {2, "Toyota"}, {3, "Frog"}, {4, "Rocket"}, {5, "Hyundai"}, {6, "Stalin"}}; + toggle = new FrogPilotParamValueToggleControl(param, title, desc, icon, 0, 6, steeringWheelLabels, this, true, "", 1, wheelToggles, wheelToggleNames); + + } else { + toggle = new ParamControl(param, title, desc, icon, this); + } + + addItem(toggle); + toggles[param.toStdString()] = toggle; + + QObject::connect(toggle, &ToggleControl::toggleFlipped, [this]() { + updateToggles(); + }); + + QObject::connect(static_cast(toggle), &FrogPilotParamValueControl::buttonPressed, [this]() { + updateToggles(); + }); + + QObject::connect(toggle, &AbstractControl::showDescriptionEvent, [this]() { + update(); + }); + + QObject::connect(static_cast(toggle), &FrogPilotParamManageControl::manageButtonClicked, [this]() { + update(); + }); + } + + std::set rebootKeys = {"DriveStats"}; + for (const std::string &key : rebootKeys) { + QObject::connect(toggles[key], &ToggleControl::toggleFlipped, [this]() { + if (FrogPilotConfirmationDialog::toggle("Reboot required to take effect.", "Reboot Now", this)) { + Hardware::reboot(); + } + }); + } + + customOnroadUIKeys = {"AdjacentPath", "BlindSpotPath", "ShowFPS", "LeadInfo", "RoadNameUI", "UseVienna"}; + customThemeKeys = {"CustomColors", "CustomIcons", "CustomSignals", "CustomSounds"}; + modelUIKeys = {"AccelerationPath", "LaneLinesWidth", "PathEdgeWidth", "PathWidth", "RoadEdgesWidth", "UnlimitedLength"}; + qolKeys = {"DriveStats", "HideSpeed", "ShowSLCOffset"}; + + QObject::connect(device(), &Device::interactiveTimeout, this, &FrogPilotVisualsPanel::hideSubToggles); + QObject::connect(parent, &OscarSettingsWindow::closeParentToggle, this, &FrogPilotVisualsPanel::hideSubToggles); + QObject::connect(parent, &OscarSettingsWindow::updateMetric, this, &FrogPilotVisualsPanel::updateMetric); + + hideSubToggles(); + updateMetric(); +} + +void FrogPilotVisualsPanel::updateToggles() { + std::thread([this]() { + paramsMemory.putBool("FrogPilotTogglesUpdated", true); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + paramsMemory.putBool("FrogPilotTogglesUpdated", false); + }).detach(); +} + +void FrogPilotVisualsPanel::updateMetric() { + bool previousIsMetric = isMetric; + isMetric = params.getBool("IsMetric"); + + if (isMetric != previousIsMetric) { + double distanceConversion = isMetric ? INCH_TO_CM : CM_TO_INCH; + double speedConversion = isMetric ? FOOT_TO_METER : METER_TO_FOOT; + params.putInt("LaneLinesWidth", std::nearbyint(params.getInt("LaneLinesWidth") * distanceConversion)); + params.putInt("RoadEdgesWidth", std::nearbyint(params.getInt("RoadEdgesWidth") * distanceConversion)); + params.putInt("PathWidth", std::nearbyint(params.getInt("PathWidth") * speedConversion)); + } + + FrogPilotParamValueControl *laneLinesWidthToggle = static_cast(toggles["LaneLinesWidth"]); + FrogPilotParamValueControl *roadEdgesWidthToggle = static_cast(toggles["RoadEdgesWidth"]); + FrogPilotParamValueControl *pathWidthToggle = static_cast(toggles["PathWidth"]); + + if (isMetric) { + laneLinesWidthToggle->setDescription("Customize the lane line width.\n\nDefault matches the Vienna average of 10 centimeters."); + roadEdgesWidthToggle->setDescription("Customize the road edges width.\n\nDefault is 1/2 of the Vienna average lane line width of 10 centimeters."); + + laneLinesWidthToggle->updateControl(0, 60, " centimeters"); + roadEdgesWidthToggle->updateControl(0, 60, " centimeters"); + pathWidthToggle->updateControl(0, 30, " meters"); + } else { + laneLinesWidthToggle->setDescription("Customize the lane line width.\n\nDefault matches the MUTCD average of 4 inches."); + roadEdgesWidthToggle->setDescription("Customize the road edges width.\n\nDefault is 1/2 of the MUTCD average lane line width of 4 inches."); + + laneLinesWidthToggle->updateControl(0, 24, " inches"); + roadEdgesWidthToggle->updateControl(0, 24, " inches"); + pathWidthToggle->updateControl(0, 100, " feet"); + } + + laneLinesWidthToggle->refresh(); + roadEdgesWidthToggle->refresh(); + + previousIsMetric = isMetric; +} + +void FrogPilotVisualsPanel::parentToggleClicked() { + this->openParentToggle(); +} + +void FrogPilotVisualsPanel::hideSubToggles() { + for (auto &[key, toggle] : toggles) { + bool subToggles = modelUIKeys.find(key.c_str()) != modelUIKeys.end() || + customOnroadUIKeys.find(key.c_str()) != customOnroadUIKeys.end() || + customThemeKeys.find(key.c_str()) != customThemeKeys.end() || + qolKeys.find(key.c_str()) != qolKeys.end(); + toggle->setVisible(!subToggles); + } + + this->closeParentToggle(); +} + +void FrogPilotVisualsPanel::hideEvent(QHideEvent *event) { + hideSubToggles(); +} diff --git a/selfdrive/oscarpilot/settings/basic.h b/selfdrive/oscarpilot/settings/basic.h new file mode 100644 index 0000000..3bd7374 --- /dev/null +++ b/selfdrive/oscarpilot/settings/basic.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include "selfdrive/frogpilot/ui/frogpilot_functions.h" +#include "selfdrive/ui/qt/offroad/settings.h" + +class FrogPilotVisualsPanel : public FrogPilotListWidget { + Q_OBJECT + +public: + explicit FrogPilotVisualsPanel(OscarSettingsWindow *parent); + +signals: + void closeParentToggle(); + void openParentToggle(); + +private: + void hideEvent(QHideEvent *event); + void hideSubToggles(); + void parentToggleClicked(); + void updateMetric(); + void updateToggles(); + + std::set customOnroadUIKeys; + std::set customThemeKeys; + std::set modelUIKeys; + std::set qolKeys; + + std::map toggles; + + Params params; + Params paramsMemory{"/dev/shm/params"}; + + bool isMetric = params.getBool("IsMetric"); +}; diff --git a/selfdrive/oscarpilot/settings/defaults.cc b/selfdrive/oscarpilot/settings/defaults.cc new file mode 100644 index 0000000..e69de29 diff --git a/selfdrive/oscarpilot/settings/defaults.h b/selfdrive/oscarpilot/settings/defaults.h new file mode 100644 index 0000000..e69de29 diff --git a/selfdrive/oscarpilot/settings/notes b/selfdrive/oscarpilot/settings/notes new file mode 100644 index 0000000..c5abc07 --- /dev/null +++ b/selfdrive/oscarpilot/settings/notes @@ -0,0 +1 @@ +Need a reset to defaults option \ No newline at end of file diff --git a/selfdrive/oscarpilot/settings/settings.cc b/selfdrive/oscarpilot/settings/settings.cc new file mode 100644 index 0000000..5dac050 --- /dev/null +++ b/selfdrive/oscarpilot/settings/settings.cc @@ -0,0 +1,147 @@ +#include "selfdrive/ui/qt/offroad/settings.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include "selfdrive/ui/qt/network/networking.h" + +#include "common/params.h" +#include "common/watchdog.h" +#include "common/util.h" +#include "system/hardware/hw.h" +#include "selfdrive/ui/qt/widgets/controls.h" +#include "selfdrive/ui/qt/widgets/input.h" +#include "selfdrive/ui/qt/widgets/scrollview.h" +#include "selfdrive/ui/qt/widgets/ssh_keys.h" +#include "selfdrive/ui/qt/widgets/toggle.h" +#include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/qt/qt_window.h" + +#include "selfdrive/frogpilot/navigation/ui/navigation_settings.h" +#include "selfdrive/frogpilot/ui/control_settings.h" +#include "selfdrive/frogpilot/ui/vehicle_settings.h" +#include "selfdrive/frogpilot/ui/visual_settings.h" + + +void OscarSettingsWindow::showEvent(QShowEvent *event) { + setCurrentPanel(0); +} + +void OscarSettingsWindow::setCurrentPanel(int index, const QString ¶m) { + panel_widget->setCurrentIndex(index); + nav_btns->buttons()[index]->setChecked(true); + if (!param.isEmpty()) { + emit expandToggleDescription(param); + } +} + +OscarSettingsWindow::OscarSettingsWindow(QWidget *parent) : QFrame(parent) { + + // setup two main layouts + sidebar_widget = new QWidget; + QVBoxLayout *sidebar_layout = new QVBoxLayout(sidebar_widget); + sidebar_layout->setMargin(0); + panel_widget = new QStackedWidget(); + + // close button + QPushButton *close_btn = new QPushButton(tr("← Back")); + close_btn->setStyleSheet(R"( + QPushButton { + font-size: 50px; + padding-bottom: 0px; + border 1px grey solid; + border-radius: 25px; + background-color: #292929; + font-weight: 500; + } + QPushButton:pressed { + background-color: #3B3B3B; + } + )"); + close_btn->setFixedSize(300, 125); + sidebar_layout->addSpacing(10); + sidebar_layout->addWidget(close_btn, 0, Qt::AlignRight); + QObject::connect(close_btn, &QPushButton::clicked, [this]() { + if (frogPilotTogglesOpen) { + frogPilotTogglesOpen = false; + this->closeParentToggle(); + } else { + this->closeSettings(); + } + }); + + QList> panels = { + {tr("Basic"), basic}, + {tr("Advanced"), basic}, + {tr("Logging"), basic}, // Log / Upload driver cam, Routes + {tr("System"), basic}, // Debugging + {tr("Status"), basic}, // Report on stuff like connectivity, free space, detected features + {tr("Extra"), basic}, // Custom cloud services, QOL automations + }; + + nav_btns = new QButtonGroup(this); + for (auto &[name, panel] : panels) { + QPushButton *btn = new QPushButton(name); + btn->setCheckable(true); + btn->setChecked(nav_btns->buttons().size() == 0); + btn->setStyleSheet(); + btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + nav_btns->addButton(btn); + sidebar_layout->addWidget(btn, 0, Qt::AlignRight); + + const int lr_margin = name != tr("Network") ? 50 : 0; // Network panel handles its own margins + panel->setContentsMargins(lr_margin, 25, lr_margin, 25); + + ScrollView *panel_frame = new ScrollView(panel, this); + panel_widget->addWidget(panel_frame); + + if (name == tr("Controls") || name == tr("Visuals")) { + QScrollBar *scrollbar = panel_frame->verticalScrollBar(); + + QObject::connect(scrollbar, &QScrollBar::valueChanged, this, [this](int value) { + if (!frogPilotTogglesOpen) { + previousScrollPosition = value; + } + }); + + QObject::connect(scrollbar, &QScrollBar::rangeChanged, this, [this, panel_frame]() { + panel_frame->restorePosition(previousScrollPosition); + }); + } + + QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() { + previousScrollPosition = 0; + btn->setChecked(true); + panel_widget->setCurrentWidget(w); + }); + } + sidebar_layout->setContentsMargins(50, 50, 100, 50); + + // main settings layout, sidebar + main panel + QHBoxLayout *main_layout = new QHBoxLayout(this); + + sidebar_widget->setFixedWidth(500); + main_layout->addWidget(sidebar_widget); + main_layout->addWidget(panel_widget); + + setStyleSheet(R"( + * { + color: white; + font-size: 50px; + } + OscarSettingsWindow { + background-color: black; + } + QStackedWidget, ScrollView { + background-color: #292929; + border-radius: 30px; + } + )"); +} diff --git a/selfdrive/oscarpilot/settings/settings.h b/selfdrive/oscarpilot/settings/settings.h new file mode 100644 index 0000000..1b8844a --- /dev/null +++ b/selfdrive/oscarpilot/settings/settings.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/qt/widgets/controls.h" + +// ********** settings window + top-level panels ********** +class OscarSettingsWindow : public QFrame { + Q_OBJECT + +public: + explicit OscarSettingsWindow(QWidget *parent = 0); + void setCurrentPanel(int index, const QString ¶m = ""); + +protected: + void showEvent(QShowEvent *event) override; + +signals: + void closeSettings(); +// void showDriverView(); + void expandToggleDescription(const QString ¶m); + + // FrogPilot signals + void closeParentToggle(); + void updateMetric(); +private: + QPushButton *sidebar_alert_widget; + QWidget *sidebar_widget; + QButtonGroup *nav_btns; + QStackedWidget *panel_widget; + + // FrogPilot variables + bool frogPilotTogglesOpen; + int previousScrollPosition; +}; + diff --git a/selfdrive/oscarpilot/settings/style.cc b/selfdrive/oscarpilot/settings/style.cc new file mode 100644 index 0000000..6ccf74a --- /dev/null +++ b/selfdrive/oscarpilot/settings/style.cc @@ -0,0 +1,42 @@ +#include + +#include "selfdrive/frogpilot/ui/frogpilot_functions.h" +#include "selfdrive/ui/ui.h" + +bool FrogPilotConfirmationDialog::toggle(const QString &prompt_text, const QString &confirm_text, QWidget *parent) { + ConfirmationDialog d = ConfirmationDialog(prompt_text, confirm_text, tr("Reboot Later"), false, parent); + return d.exec(); +} + +bool FrogPilotConfirmationDialog::toggleAlert(const QString &prompt_text, const QString &button_text, QWidget *parent) { + ConfirmationDialog d = ConfirmationDialog(prompt_text, button_text, "", false, parent); + return d.exec(); +} + +bool FrogPilotConfirmationDialog::yesorno(const QString &prompt_text, QWidget *parent) { + ConfirmationDialog d = ConfirmationDialog(prompt_text, tr("Yes"), tr("No"), false, parent); + return d.exec(); +} + +FrogPilotButtonIconControl::FrogPilotButtonIconControl(const QString &title, const QString &text, const QString &desc, const QString &icon, QWidget *parent) : AbstractControl(title, desc, icon, parent) { + btn.setText(text); + btn.setStyleSheet(R"( + QPushButton { + padding: 0; + border-radius: 50px; + font-size: 35px; + font-weight: 500; + color: #E4E4E4; + background-color: #393939; + } + QPushButton:pressed { + background-color: #4a4a4a; + } + QPushButton:disabled { + color: #33E4E4E4; + } + )"); + btn.setFixedSize(250, 100); + QObject::connect(&btn, &QPushButton::clicked, this, &FrogPilotButtonIconControl::clicked); + hlayout->addWidget(&btn); +} diff --git a/selfdrive/oscarpilot/settings/style.h b/selfdrive/oscarpilot/settings/style.h new file mode 100644 index 0000000..0d2161a --- /dev/null +++ b/selfdrive/oscarpilot/settings/style.h @@ -0,0 +1,16 @@ + +const button_style = R"( + QPushButton { + color: grey; + border: none; + background: none; + font-size: 65px; + font-weight: 500; + } + QPushButton:checked { + color: white; + } + QPushButton:pressed { + color: #ADADAD; + } +)"; \ No newline at end of file diff --git a/selfdrive/oscarpilot/settings_sync.py b/selfdrive/oscarpilot/settings_sync.py new file mode 100644 index 0000000..e69de29 diff --git a/selfdrive/oscarpilot/stocklong.py b/selfdrive/oscarpilot/stocklong.py new file mode 100644 index 0000000..e69de29 diff --git a/selfdrive/oscarpilot/weather.cc b/selfdrive/oscarpilot/weather.cc new file mode 100644 index 0000000..e69de29 diff --git a/selfdrive/oscarpilot/weather.h b/selfdrive/oscarpilot/weather.h new file mode 100644 index 0000000..e69de29 diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index b82559d..bde9942 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -29,9 +29,6 @@ #include "selfdrive/frogpilot/ui/vehicle_settings.h" #include "selfdrive/frogpilot/ui/visual_settings.h" -// Bbot notes: -// This is the main settings page. All other settings pages attach themselves to this page. - TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { // param, title, desc, icon std::vector> toggle_defs{ @@ -447,16 +444,14 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::openParentToggle, this, [this]() {frogPilotTogglesOpen = true;}); QList> panels = { - {tr("Basic"), toggles}, - {tr("Interface"), frogpilotVisuals}, - {tr("Advanced"), frogpilotControls}, - {tr("System"), new Networking(this)}, - {tr("Software"), new SoftwarePanel(this)}, - // {tr("Extras"), device}, - // {tr("Navigation"), new FrogPilotNavigationPanel(this)}, - // {tr("Vehicles"), new FrogPilotVehiclesPanel(this)}, - // {tr("Visuals"), frogpilotVisuals}, {tr("Device"), device}, + {tr("Network"), new Networking(this)}, + {tr("Toggles"), toggles}, + {tr("Software"), new SoftwarePanel(this)}, + {tr("Controls"), frogpilotControls}, + {tr("Navigation"), new FrogPilotNavigationPanel(this)}, + {tr("Vehicles"), new FrogPilotVehiclesPanel(this)}, + {tr("Visuals"), frogpilotVisuals}, }; nav_btns = new QButtonGroup(this); diff --git a/selfdrive/ui/qt/offroad/settings.cc.org b/selfdrive/ui/qt/offroad/settings.cc.org deleted file mode 100644 index 5cd34da..0000000 --- a/selfdrive/ui/qt/offroad/settings.cc.org +++ /dev/null @@ -1,533 +0,0 @@ -#include "selfdrive/ui/qt/offroad/settings.h" - -#include -#include -#include -#include -#include - -#include -#include - -#include "selfdrive/ui/qt/network/networking.h" - -#include "common/params.h" -#include "common/watchdog.h" -#include "common/util.h" -#include "system/hardware/hw.h" -#include "selfdrive/ui/qt/widgets/controls.h" -#include "selfdrive/ui/qt/widgets/input.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" -#include "selfdrive/ui/qt/widgets/ssh_keys.h" -#include "selfdrive/ui/qt/widgets/toggle.h" -#include "selfdrive/ui/ui.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/qt_window.h" - -#include "selfdrive/frogpilot/navigation/ui/navigation_settings.h" -#include "selfdrive/frogpilot/ui/control_settings.h" -#include "selfdrive/frogpilot/ui/vehicle_settings.h" -#include "selfdrive/frogpilot/ui/visual_settings.h" - -// Bbot notes: -// This is the main settings page. All other settings pages attach themselves to this page. - -TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { - // param, title, desc, icon - std::vector> toggle_defs{ - { - "OpenpilotEnabledToggle", - tr("Enable openpilot"), - tr("Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off."), - "../assets/offroad/icon_openpilot.png", - }, - { - "ExperimentalLongitudinalEnabled", - tr("openpilot Longitudinal Control (Alpha)"), - QString("%1

%2") - .arg(tr("WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).")) - .arg(tr("On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. " - "Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.")), - "../assets/offroad/icon_speed_limit.png", - }, - { - "ExperimentalMode", - tr("Experimental Mode"), - "", - "../assets/img_experimental_white.svg", - }, - { - "DisengageOnAccelerator", - tr("Disengage on Accelerator Pedal"), - tr("When enabled, pressing the accelerator pedal will disengage openpilot."), - "../assets/offroad/icon_disengage_on_accelerator.svg", - }, - { - "IsLdwEnabled", - tr("Enable Lane Departure Warnings"), - tr("Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h)."), - "../assets/offroad/icon_warning.png", - }, - { - "RecordFront", - tr("Record and Upload Driver Camera"), - tr("Upload data from the driver facing camera and help improve the driver monitoring algorithm."), - "../assets/offroad/icon_monitoring.png", - }, - { - "IsMetric", - tr("Use Metric System"), - tr("Display speed in km/h instead of mph."), - "../assets/offroad/icon_metric.png", - }, -#ifdef ENABLE_MAPS - { - "NavSettingTime24h", - tr("Show ETA in 24h Format"), - tr("Use 24h format instead of am/pm"), - "../assets/offroad/icon_metric.png", - }, - { - "NavSettingLeftSide", - tr("Show Map on Left Side of UI"), - tr("Show map on left side when in split screen view."), - "../assets/offroad/icon_road.png", - }, -#endif - }; - - - std::vector longi_button_texts{tr("Aggressive"), tr("Standard"), tr("Relaxed")}; - long_personality_setting = new ButtonParamControl("LongitudinalPersonality", tr("Driving Personality"), - tr("Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. " - "In relaxed mode openpilot will stay further away from lead cars."), - "../assets/offroad/icon_speed_limit.png", - longi_button_texts); - for (auto &[param, title, desc, icon] : toggle_defs) { - auto toggle = new ParamControl(param, title, desc, icon, this); - - bool locked = params.getBool((param + "Lock").toStdString()); - toggle->setEnabled(!locked); - - addItem(toggle); - toggles[param.toStdString()] = toggle; - - // insert longitudinal personality after NDOG toggle - if (param == "DisengageOnAccelerator" && !params.getInt("AdjustablePersonalities")) { - addItem(long_personality_setting); - } - } - - // Toggles with confirmation dialogs - toggles["ExperimentalMode"]->setActiveIcon("../assets/img_experimental.svg"); - toggles["ExperimentalMode"]->setConfirmation(true, true); - toggles["ExperimentalLongitudinalEnabled"]->setConfirmation(true, false); - - connect(toggles["ExperimentalLongitudinalEnabled"], &ToggleControl::toggleFlipped, [=]() { - updateToggles(); - }); - - connect(toggles["IsMetric"], &ToggleControl::toggleFlipped, [=]() { - updateMetric(); - }); -} - -void TogglesPanel::expandToggleDescription(const QString ¶m) { - toggles[param.toStdString()]->showDescription(); -} - -void TogglesPanel::showEvent(QShowEvent *event) { - updateToggles(); -} - -void TogglesPanel::updateToggles() { - auto experimental_mode_toggle = toggles["ExperimentalMode"]; - auto op_long_toggle = toggles["ExperimentalLongitudinalEnabled"]; - const QString e2e_description = QString("%1
" - "

%2


" - "%3
" - "

%4


" - "%5
" - "

%6


" - "%7") - .arg(tr("openpilot defaults to driving in chill mode. Experimental mode enables alpha-level features that aren't ready for chill mode. Experimental features are listed below:")) - .arg(tr("End-to-End Longitudinal Control")) - .arg(tr("Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. " - "Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; " - "mistakes should be expected.")) - .arg(tr("Navigate on openpilot")) - .arg(tr("When navigation has a destination, openpilot will input the map information into the model. This provides useful context for the model and allows openpilot to keep left or right " - "appropriately at forks/exits. Lane change behavior is unchanged and still activated by the driver. This is an alpha quality feature; mistakes should be expected, particularly around " - "exits and forks. These mistakes can include unintended laneline crossings, late exit taking, driving towards dividing barriers in the gore areas, etc.")) - .arg(tr("New Driving Visualization")) - .arg(tr("The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. " - "When a navigation destination is set and the driving model is using it as input, the driving path on the map will turn green.")); - - const bool is_release = params.getBool("IsReleaseBranch"); - auto cp_bytes = params.get("CarParamsPersistent"); - if (!cp_bytes.empty()) { - AlignedBuffer aligned_buf; - capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); - cereal::CarParams::Reader CP = cmsg.getRoot(); - - if (!CP.getExperimentalLongitudinalAvailable() || is_release) { - params.remove("ExperimentalLongitudinalEnabled"); - } - op_long_toggle->setVisible(CP.getExperimentalLongitudinalAvailable() && !is_release); - if (hasLongitudinalControl(CP)) { - // normal description and toggle - experimental_mode_toggle->setEnabled(!params.getBool("ConditionalExperimental")); - experimental_mode_toggle->setDescription(e2e_description); - long_personality_setting->setEnabled(true); - } else { - // no long for now - experimental_mode_toggle->setEnabled(false); - long_personality_setting->setEnabled(false); - params.remove("ExperimentalMode"); - - const QString unavailable = tr("Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control."); - - QString long_desc = unavailable + " " + \ - tr("openpilot longitudinal control may come in a future update."); - if (CP.getExperimentalLongitudinalAvailable()) { - if (is_release) { - long_desc = unavailable + " " + tr("An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches."); - } else { - long_desc = tr("Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode."); - } - } - experimental_mode_toggle->setDescription("" + long_desc + "

" + e2e_description); - } - - experimental_mode_toggle->refresh(); - } else { - experimental_mode_toggle->setDescription(e2e_description); - op_long_toggle->setVisible(false); - } -} - -DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) { - setSpacing(50); - addItem(new LabelControl(tr("Dongle ID"), getDongleId().value_or(tr("N/A")))); - addItem(new LabelControl(tr("Serial"), params.get("HardwareSerial").c_str())); - - // offroad-only buttons - - auto dcamBtn = new ButtonControl(tr("Driver Camera"), tr("PREVIEW"), - tr("Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)")); - connect(dcamBtn, &ButtonControl::clicked, [=]() { emit showDriverView(); }); - addItem(dcamBtn); - - auto resetCalibBtn = new ButtonControl(tr("Reset Calibration"), tr("RESET"), ""); - connect(resetCalibBtn, &ButtonControl::showDescriptionEvent, this, &DevicePanel::updateCalibDescription); - connect(resetCalibBtn, &ButtonControl::clicked, [&]() { - if (ConfirmationDialog::confirm(tr("Are you sure you want to reset calibration?"), tr("Reset"), this)) { - params.remove("CalibrationParams"); - params.remove("LiveTorqueParameters"); - } - }); - addItem(resetCalibBtn); - - if (!params.getBool("Passive")) { - auto retrainingBtn = new ButtonControl(tr("Review Training Guide"), tr("REVIEW"), tr("Review the rules, features, and limitations of openpilot")); - connect(retrainingBtn, &ButtonControl::clicked, [=]() { - if (ConfirmationDialog::confirm(tr("Are you sure you want to review the training guide?"), tr("Review"), this)) { - emit reviewTrainingGuide(); - } - }); - addItem(retrainingBtn); - } - - if (Hardware::TICI()) { - auto regulatoryBtn = new ButtonControl(tr("Regulatory"), tr("VIEW"), ""); - connect(regulatoryBtn, &ButtonControl::clicked, [=]() { - const std::string txt = util::read_file("../assets/offroad/fcc.html"); - ConfirmationDialog::rich(QString::fromStdString(txt), this); - }); - addItem(regulatoryBtn); - } - - auto translateBtn = new ButtonControl(tr("Change Language"), tr("CHANGE"), ""); - connect(translateBtn, &ButtonControl::clicked, [=]() { - QMap langs = getSupportedLanguages(); - QString selection = MultiOptionDialog::getSelection(tr("Select a language"), langs.keys(), langs.key(uiState()->language), this); - if (!selection.isEmpty()) { - // put language setting, exit Qt UI, and trigger fast restart - params.put("LanguageSetting", langs[selection].toStdString()); - qApp->exit(18); - watchdog_kick(0); - } - }); - addItem(translateBtn); - - // Delete driving footage button - auto deleteFootageBtn = new ButtonControl(tr("Delete Driving Data"), tr("DELETE"), tr("This button provides a swift and secure way to permanently delete all " - "stored driving footage and data from your device. Ideal for maintaining privacy or freeing up space.") - ); - connect(deleteFootageBtn, &ButtonControl::clicked, [this]() { - if (!ConfirmationDialog::confirm(tr("Are you sure you want to permanently delete all of your driving footage and data?"), tr("Delete"), this)) return; - std::thread([&] { - std::system("rm -rf /data/media/0/realdata"); - }).detach(); - }); - addItem(deleteFootageBtn); - - // Panda flashing button - auto flashPandaBtn = new ButtonControl(tr("Flash Panda"), tr("FLASH"), "Use this button to troubleshoot and update the Panda device's firmware."); - connect(flashPandaBtn, &ButtonControl::clicked, [this]() { - if (!ConfirmationDialog::confirm(tr("Are you sure you want to flash the Panda?"), tr("Flash"), this)) return; - QProcess process; - // Get Panda type - SubMaster &sm = *(uiState()->sm); - auto pandaStates = sm["pandaStates"].getPandaStates(); - // Choose recovery script based on Panda type - if (pandaStates.size() != 0) { - auto pandaType = pandaStates[0].getPandaType(); - bool isRedPanda = (pandaType == cereal::PandaState::PandaType::RED_PANDA || - pandaType == cereal::PandaState::PandaType::RED_PANDA_V2); - QString recoveryScript = isRedPanda ? "./recover.sh" : "./recover.py"; - // Run recovery script and flash Panda - process.setWorkingDirectory("/data/openpilot/panda/board"); - process.start("/bin/sh", QStringList{"-c", recoveryScript}); - process.waitForFinished(); - } - // Run the killall script as a redundancy - process.setWorkingDirectory("/data/openpilot/panda"); - process.start("/bin/sh", QStringList{"-c", "pkill -f boardd; PYTHONPATH=.. python -c \"from panda import Panda; Panda().flash()\""}); - process.waitForFinished(); - Hardware::reboot(); - }); - addItem(flashPandaBtn); - - QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { - for (auto btn : findChildren()) { - btn->setEnabled(offroad); - } - }); - - // power buttons - QHBoxLayout *power_layout = new QHBoxLayout(); - power_layout->setSpacing(30); - - QPushButton *reboot_btn = new QPushButton(tr("Reboot")); - reboot_btn->setObjectName("reboot_btn"); - power_layout->addWidget(reboot_btn); - QObject::connect(reboot_btn, &QPushButton::clicked, this, &DevicePanel::reboot); - - QPushButton *poweroff_btn = new QPushButton(tr("Power Off")); - poweroff_btn->setObjectName("poweroff_btn"); - power_layout->addWidget(poweroff_btn); - QObject::connect(poweroff_btn, &QPushButton::clicked, this, &DevicePanel::poweroff); - - if (!Hardware::PC()) { - connect(uiState(), &UIState::offroadTransition, poweroff_btn, &QPushButton::setVisible); - } - - setStyleSheet(R"( - #reboot_btn { height: 120px; border-radius: 15px; background-color: #393939; } - #reboot_btn:pressed { background-color: #4a4a4a; } - #poweroff_btn { height: 120px; border-radius: 15px; background-color: #E22C2C; } - #poweroff_btn:pressed { background-color: #FF2424; } - )"); - addItem(power_layout); -} - -void DevicePanel::updateCalibDescription() { - QString desc = - tr("openpilot requires the device to be mounted within 4° left or right and " - "within 5° up or 9° down. openpilot is continuously calibrating, resetting is rarely required."); - std::string calib_bytes = params.get("CalibrationParams"); - if (!calib_bytes.empty()) { - try { - AlignedBuffer aligned_buf; - capnp::FlatArrayMessageReader cmsg(aligned_buf.align(calib_bytes.data(), calib_bytes.size())); - auto calib = cmsg.getRoot().getLiveCalibration(); - if (calib.getCalStatus() != cereal::LiveCalibrationData::Status::UNCALIBRATED) { - double pitch = calib.getRpyCalib()[1] * (180 / M_PI); - double yaw = calib.getRpyCalib()[2] * (180 / M_PI); - desc += tr(" Your device is pointed %1° %2 and %3° %4.") - .arg(QString::number(std::abs(pitch), 'g', 1), pitch > 0 ? tr("down") : tr("up"), - QString::number(std::abs(yaw), 'g', 1), yaw > 0 ? tr("left") : tr("right")); - } - } catch (kj::Exception) { - qInfo() << "invalid CalibrationParams"; - } - } - qobject_cast(sender())->setDescription(desc); -} - -void DevicePanel::reboot() { - if (!uiState()->engaged()) { - if (ConfirmationDialog::confirm(tr("Are you sure you want to reboot?"), tr("Reboot"), this)) { - // Check engaged again in case it changed while the dialog was open - if (!uiState()->engaged()) { - params.putBool("DoReboot", true); - } - } - } else { - ConfirmationDialog::alert(tr("Disengage to Reboot"), this); - } -} - -void DevicePanel::poweroff() { - if (!uiState()->engaged()) { - if (ConfirmationDialog::confirm(tr("Are you sure you want to power off?"), tr("Power Off"), this)) { - // Check engaged again in case it changed while the dialog was open - if (!uiState()->engaged()) { - params.putBool("DoShutdown", true); - } - } - } else { - ConfirmationDialog::alert(tr("Disengage to Power Off"), this); - } -} - -void SettingsWindow::showEvent(QShowEvent *event) { - setCurrentPanel(0); -} - -void SettingsWindow::setCurrentPanel(int index, const QString ¶m) { - panel_widget->setCurrentIndex(index); - nav_btns->buttons()[index]->setChecked(true); - if (!param.isEmpty()) { - emit expandToggleDescription(param); - } -} - -SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { - - // setup two main layouts - sidebar_widget = new QWidget; - QVBoxLayout *sidebar_layout = new QVBoxLayout(sidebar_widget); - sidebar_layout->setMargin(0); - panel_widget = new QStackedWidget(); - - // close button - QPushButton *close_btn = new QPushButton(tr("← Back")); - close_btn->setStyleSheet(R"( - QPushButton { - font-size: 50px; - padding-bottom: 0px; - border 1px grey solid; - border-radius: 25px; - background-color: #292929; - font-weight: 500; - } - QPushButton:pressed { - background-color: #3B3B3B; - } - )"); - close_btn->setFixedSize(300, 125); - sidebar_layout->addSpacing(10); - sidebar_layout->addWidget(close_btn, 0, Qt::AlignRight); - QObject::connect(close_btn, &QPushButton::clicked, [this]() { - if (frogPilotTogglesOpen) { - frogPilotTogglesOpen = false; - this->closeParentToggle(); - } else { - this->closeSettings(); - } - }); - - // setup panels - DevicePanel *device = new DevicePanel(this); - QObject::connect(device, &DevicePanel::reviewTrainingGuide, this, &SettingsWindow::reviewTrainingGuide); - QObject::connect(device, &DevicePanel::showDriverView, this, &SettingsWindow::showDriverView); - - TogglesPanel *toggles = new TogglesPanel(this); - QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription); - QObject::connect(toggles, &TogglesPanel::updateMetric, this, &SettingsWindow::updateMetric); - - FrogPilotControlsPanel *frogpilotControls = new FrogPilotControlsPanel(this); - QObject::connect(frogpilotControls, &FrogPilotControlsPanel::closeParentToggle, this, [this]() {frogPilotTogglesOpen = false;}); - QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openParentToggle, this, [this]() {frogPilotTogglesOpen = true;}); - - FrogPilotVisualsPanel *frogpilotVisuals = new FrogPilotVisualsPanel(this); - QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::closeParentToggle, this, [this]() {frogPilotTogglesOpen = false;}); - QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::openParentToggle, this, [this]() {frogPilotTogglesOpen = true;}); - - QList> panels = { - {tr("Basic"), toggles}, - {tr("Customization"), frogpilotVisuals}, - {tr("Advanced"), frogpilotControls}, - {tr("System"), new Networking(this)}, - {tr("Extras"), device}, - // {tr("Navigation"), new FrogPilotNavigationPanel(this)}, - // {tr("Vehicles"), new FrogPilotVehiclesPanel(this)}, - // {tr("Visuals"), frogpilotVisuals}, - // {tr("Device"), device}, - }; - - nav_btns = new QButtonGroup(this); - for (auto &[name, panel] : panels) { - QPushButton *btn = new QPushButton(name); - btn->setCheckable(true); - btn->setChecked(nav_btns->buttons().size() == 0); - btn->setStyleSheet(R"( - QPushButton { - color: grey; - border: none; - background: none; - font-size: 65px; - font-weight: 500; - } - QPushButton:checked { - color: white; - } - QPushButton:pressed { - color: #ADADAD; - } - )"); - btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); - nav_btns->addButton(btn); - sidebar_layout->addWidget(btn, 0, Qt::AlignRight); - - const int lr_margin = name != tr("Network") ? 50 : 0; // Network panel handles its own margins - panel->setContentsMargins(lr_margin, 25, lr_margin, 25); - - ScrollView *panel_frame = new ScrollView(panel, this); - panel_widget->addWidget(panel_frame); - - if (name == tr("Controls") || name == tr("Visuals")) { - QScrollBar *scrollbar = panel_frame->verticalScrollBar(); - - QObject::connect(scrollbar, &QScrollBar::valueChanged, this, [this](int value) { - if (!frogPilotTogglesOpen) { - previousScrollPosition = value; - } - }); - - QObject::connect(scrollbar, &QScrollBar::rangeChanged, this, [this, panel_frame]() { - panel_frame->restorePosition(previousScrollPosition); - }); - } - - QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() { - previousScrollPosition = 0; - btn->setChecked(true); - panel_widget->setCurrentWidget(w); - }); - } - sidebar_layout->setContentsMargins(50, 50, 100, 50); - - // main settings layout, sidebar + main panel - QHBoxLayout *main_layout = new QHBoxLayout(this); - - sidebar_widget->setFixedWidth(500); - main_layout->addWidget(sidebar_widget); - main_layout->addWidget(panel_widget); - - setStyleSheet(R"( - * { - color: white; - font-size: 50px; - } - SettingsWindow { - background-color: black; - } - QStackedWidget, ScrollView { - background-color: #292929; - border-radius: 30px; - } - )"); -} diff --git a/selfdrive/ui/qt/window.h b/selfdrive/ui/qt/window.h index f1389c2..9f9e44c 100644 --- a/selfdrive/ui/qt/window.h +++ b/selfdrive/ui/qt/window.h @@ -6,6 +6,7 @@ #include "selfdrive/ui/qt/home.h" #include "selfdrive/ui/qt/offroad/onboarding.h" #include "selfdrive/ui/qt/offroad/settings.h" +#include "selfdrive/oscarpilot/settings/settings.h" class MainWindow : public QWidget { Q_OBJECT