From 7a286d2f52c15ee797752b61ef32779192b8e9e4 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Fri, 17 May 2024 04:57:44 +0000 Subject: [PATCH 1/2] Controlsd: Remove unused code and `leadDistanceBar` parsing with SP values --- selfdrive/controls/controlsd.py | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 32407f97a0..48eddc7b84 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -209,34 +209,6 @@ class Controls: if any(ps.controlsAllowed for ps in self.sm['pandaStates']): self.state = State.enabled - def get_road_edge(self, carstate, model_v2): - # Lane detection by FrogAi - one_blinker = carstate.leftBlinker != carstate.rightBlinker - if not self.edge_toggle: - self.road_edge = False - elif one_blinker: - # Set the minimum lane threshold to 3.0 meters - min_lane_threshold = 3.0 - # Set the blinker index based on which signal is on - blinker_index = 0 if carstate.leftBlinker else 1 - desired_edge = model_v2.roadEdges[blinker_index] - current_lane = model_v2.laneLines[blinker_index + 1] - # Check if both the desired lane and the current lane have valid x and y values - if all([desired_edge.x, desired_edge.y, current_lane.x, current_lane.y]) and len(desired_edge.x) == len(current_lane.x): - # Interpolate the x and y values to the same length - x = np.linspace(desired_edge.x[0], desired_edge.x[-1], num=len(desired_edge.x)) - lane_y = np.interp(x, current_lane.x, current_lane.y) - desired_y = np.interp(x, desired_edge.x, desired_edge.y) - # Calculate the width of the lane we're wanting to change into - lane_width = np.abs(desired_y - lane_y) - # Set road_edge to False if the lane width is not larger than the threshold - self.road_edge = not (np.amax(lane_width) > min_lane_threshold) - else: - self.road_edge = True - else: - # Default to setting "road_edge" to False - self.road_edge = False - def update_events(self, CS): """Compute onroadEvents from carState""" @@ -795,7 +767,7 @@ class Controls: hudControl.speedVisible = self.enabled_long hudControl.lanesVisible = self.enabled hudControl.leadVisible = self.sm['longitudinalPlan'].hasLead - hudControl.leadDistanceBars = self.personality + 1 + hudControl.leadDistanceBars = PERSONALITY_MAPPING.get(self.personality, log.LongitudinalPersonality.standard) + 1 hudControl.rightLaneVisible = True hudControl.leftLaneVisible = True From 9842d68f4c415cdf1cc5e6e19628c9f790c51ede Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Fri, 17 May 2024 06:00:29 +0000 Subject: [PATCH 2/2] ui: Force offroad from settings when the car is on --- CHANGELOGS.md | 3 ++ common/params.cc | 2 + selfdrive/boardd/boardd.cc | 6 +-- selfdrive/controls/lib/alerts_offroad.json | 4 ++ selfdrive/thermald/thermald.py | 6 +++ selfdrive/ui/qt/offroad/settings.cc | 55 +++++++++++++++++++++- selfdrive/ui/qt/offroad/settings.h | 5 ++ selfdrive/ui/ui.cc | 3 +- 8 files changed, 79 insertions(+), 5 deletions(-) diff --git a/CHANGELOGS.md b/CHANGELOGS.md index 8631ce8d75..01ae83beba 100644 --- a/CHANGELOGS.md +++ b/CHANGELOGS.md @@ -24,6 +24,9 @@ sunnypilot - 0.9.7.0 (2024-05-xx) * comma Prime support * Personal Mapbox/Amap/Google Maps token support * Instructions on how to set up your iOS Siri Shortcuts: https://routinehub.co/shortcut/17677/ +* NEW❗: Forced Offroad mode + * Force sunnypilot in the offroad state even when the car is on + * When Forced Offroad mode is on, allows changing offroad-only settings even when the car is turned on * UPDATED: Hyundai CAN-FD Camera-based SCC * NEW❗: Parse lead info for camera-based SCC platforms with longitudinal support * Improve lead tracking when using openpilot longitudinal diff --git a/common/params.cc b/common/params.cc index 76b9a1fbb1..920dd564da 100644 --- a/common/params.cc +++ b/common/params.cc @@ -255,6 +255,7 @@ std::unordered_map keys = { {"EnhancedScc", PERSISTENT | BACKUP}, {"FeatureStatus", PERSISTENT | BACKUP}, {"FleetManagerPin", PERSISTENT}, + {"ForceOffroad", CLEAR_ON_MANAGER_START}, {"GmapKey", PERSISTENT | BACKUP}, {"HandsOnWheelMonitoring", PERSISTENT | BACKUP}, {"HideVEgoUi", PERSISTENT | BACKUP}, @@ -322,6 +323,7 @@ std::unordered_map keys = { {"VisionCurveLaneless", PERSISTENT | BACKUP}, {"VwAccType", PERSISTENT | BACKUP}, {"VwCCOnly", PERSISTENT | BACKUP}, + {"Offroad_ForceStatus", CLEAR_ON_MANAGER_START}, {"Offroad_SupersededUpdate", PERSISTENT}, {"SunnylinkCache_Users", PERSISTENT}, diff --git a/selfdrive/boardd/boardd.cc b/selfdrive/boardd/boardd.cc index fcbf58999e..eb301b1f00 100644 --- a/selfdrive/boardd/boardd.cc +++ b/selfdrive/boardd/boardd.cc @@ -229,7 +229,7 @@ void can_recv_thread(std::vector pandas) { } } -std::optional send_panda_states(PubMaster *pm, const std::vector &pandas, bool spoofing_started) { +std::optional send_panda_states(PubMaster *pm, const std::vector &pandas, bool spoofing_started, Params ¶ms) { bool ignition_local = false; const uint32_t pandas_cnt = pandas.size(); @@ -277,7 +277,7 @@ std::optional send_panda_states(PubMaster *pm, const std::vector health.ignition_line_pkt = 0; } - ignition_local |= ((health.ignition_line_pkt != 0) || (health.ignition_can_pkt != 0)); + ignition_local |= ((health.ignition_line_pkt != 0) || (health.ignition_can_pkt != 0)) && !params.getBool("ForceOffroad"); pandaStates.push_back(health); } @@ -432,7 +432,7 @@ void panda_state_thread(std::vector pandas, bool spoofing_started) { send_peripheral_state(&pm, peripheral_panda); } - auto ignition_opt = send_panda_states(&pm, pandas, spoofing_started); + auto ignition_opt = send_panda_states(&pm, pandas, spoofing_started, params); if (!ignition_opt) { LOGE("Failed to get ignition_opt"); diff --git a/selfdrive/controls/lib/alerts_offroad.json b/selfdrive/controls/lib/alerts_offroad.json index 0293a655fa..4bb7fd1736 100644 --- a/selfdrive/controls/lib/alerts_offroad.json +++ b/selfdrive/controls/lib/alerts_offroad.json @@ -52,5 +52,9 @@ "Offroad_OSMUpdateRequired": { "text": "OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display.\n\n%1", "severity": 0 + }, + "Offroad_ForceStatus": { + "text": "sunnypilot is now in Forced Offroad mode. sunnypilot won't start until Forced Offroad mode is disabled. Go to \"Settings\" -> \"Device\" -> \"Unforce Offroad\" to exit Force Offroad mode.", + "severity": 1 } } diff --git a/selfdrive/thermald/thermald.py b/selfdrive/thermald/thermald.py index 10d0c13883..57b9914239 100755 --- a/selfdrive/thermald/thermald.py +++ b/selfdrive/thermald/thermald.py @@ -311,6 +311,12 @@ def thermald_thread(end_event, hw_queue) -> None: # ensure device is fully booted startup_conditions["device_booted"] = startup_conditions.get("device_booted", False) or HARDWARE.booted() + # user-forced status + force_offroad = params.get_bool("ForceOffroad") + startup_conditions["not_force_offroad"] = not force_offroad + onroad_conditions["not_force_offroad"] = not force_offroad + set_offroad_alert("Offroad_ForceStatus", force_offroad) + # if the temperature enters the danger zone, go offroad to cool down onroad_conditions["device_temp_good"] = thermal_status < ThermalStatus.danger extra_text = f"{offroad_comp_temp:.1f}C" diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index 886653bd4b..4c9a2e9085 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -420,13 +420,24 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) { connect(uiState(), &UIState::offroadTransition, poweroff_btn, &QPushButton::setVisible); } + offroad_btn = new QPushButton(tr("Toggle Onroad/Offroad")); + offroad_btn->setObjectName("offroad_btn"); + QObject::connect(offroad_btn, &QPushButton::clicked, this, &DevicePanel::forceoffroad); + + QVBoxLayout *buttons_layout = new QVBoxLayout(); + buttons_layout->setSpacing(24); + buttons_layout->addLayout(power_layout); + buttons_layout->addWidget(offroad_btn); + 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); + addItem(buttons_layout); + + updateLabels(); } void DevicePanel::onPinFileChanged(const QString &file_path) { @@ -501,9 +512,51 @@ void DevicePanel::poweroff() { } } +void DevicePanel::forceoffroad() { + if (!uiState()->engaged()) { + if (params.getBool("ForceOffroad")) { + if (ConfirmationDialog::confirm(tr("Are you sure you want to unforce offroad?"), tr("Unforce"), this)) { + // Check engaged again in case it changed while the dialog was open + if (!uiState()->engaged()) { + params.remove("ForceOffroad"); + } + } + } else { + if (ConfirmationDialog::confirm(tr("Are you sure you want to force offroad?"), tr("Force"), this)) { + // Check engaged again in case it changed while the dialog was open + if (!uiState()->engaged()) { + params.putBool("ForceOffroad", true); + } + } + } + } else { + ConfirmationDialog::alert(tr("Disengage to Force Offroad"), this); + } + + updateLabels(); +} + void DevicePanel::showEvent(QShowEvent *event) { pair_device->setVisible(uiState()->primeType() == PrimeType::UNPAIRED); ListWidget::showEvent(event); + updateLabels(); +} + +void DevicePanel::updateLabels() { + if (!isVisible()) { + return; + } + + bool force_offroad_param = params.getBool("ForceOffroad"); + QString offroad_btn_style = force_offroad_param ? "#393939" : "#E22C2C"; + QString offroad_btn_pressed_style = force_offroad_param ? "#4a4a4a" : "#FF2424"; + QString btn_common_style = QString("QPushButton { height: 120px; border-radius: 15px; background-color: %1; }" + "QPushButton:pressed { background-color: %2; }") + .arg(offroad_btn_style, + offroad_btn_pressed_style); + + offroad_btn->setText(force_offroad_param ? tr("Unforce Offroad") : tr("Force Offroad")); + offroad_btn->setStyleSheet(btn_common_style + offroad_btn_style + offroad_btn_pressed_style); } void SettingsWindow::showEvent(QShowEvent *event) { diff --git a/selfdrive/ui/qt/offroad/settings.h b/selfdrive/ui/qt/offroad/settings.h index b1ee32f39d..0e550850e8 100644 --- a/selfdrive/ui/qt/offroad/settings.h +++ b/selfdrive/ui/qt/offroad/settings.h @@ -63,6 +63,9 @@ private slots: void updateCalibDescription(); void onPinFileChanged(const QString &file_path); void refreshPin(); + void forceoffroad(); + + void updateLabels(); private: Params params; @@ -72,6 +75,8 @@ private: QString pin_title = tr("Fleet Manager PIN:") + " "; QString pin = "OFF"; QFileSystemWatcher *fs_watch; + + QPushButton *offroad_btn; }; class TogglesPanel : public ListWidget { diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 11ac3ee650..b7b37ec9de 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -165,6 +165,7 @@ static void update_sockets(UIState *s) { static void update_state(UIState *s) { SubMaster &sm = *(s->sm); UIScene &scene = s->scene; + auto params = Params(); if (sm.updated("liveCalibration")) { auto live_calib = sm["liveCalibration"].getLiveCalibration(); @@ -214,7 +215,7 @@ static void update_state(UIState *s) { float scale = (cam_state.getSensor() == cereal::FrameData::ImageSensor::AR0231) ? 6.0f : 1.0f; scene.light_sensor = std::max(100.0f - scale * cam_state.getExposureValPercent(), 0.0f); } - scene.started = sm["deviceState"].getDeviceState().getStarted() && scene.ignition; + scene.started = sm["deviceState"].getDeviceState().getStarted() && scene.ignition && !params.getBool("ForceOffroad"); scene.world_objects_visible = scene.world_objects_visible || (scene.started &&