diff --git a/common/params_keys.h b/common/params_keys.h index ca779a5b5c..3fd4e1b6ab 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -93,6 +93,7 @@ inline static std::unordered_map keys = { {"Offroad_TemperatureTooHigh", CLEAR_ON_MANAGER_START}, {"Offroad_UnofficialHardware", CLEAR_ON_MANAGER_START}, {"Offroad_UpdateFailed", CLEAR_ON_MANAGER_START}, + {"OnroadCycleRequested", CLEAR_ON_MANAGER_START}, {"OpenpilotEnabledToggle", PERSISTENT}, {"PandaHeartbeatLost", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION}, {"PandaSomResetTriggered", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION}, diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index ed06734f0f..baac582645 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -17,49 +17,56 @@ #include "selfdrive/ui/qt/offroad/firehose.h" TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { - // param, title, desc, icon - std::vector> toggle_defs{ + // param, title, desc, icon, restart needed + 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."), + 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."), "../assets/icons/chffr_wheel.png", + true, }, { "ExperimentalMode", tr("Experimental Mode"), "", "../assets/icons/experimental_white.svg", + false, }, { "DisengageOnAccelerator", tr("Disengage on Accelerator Pedal"), tr("When enabled, pressing the accelerator pedal will disengage openpilot."), "../assets/icons/disengage_on_accelerator.svg", + false, }, { "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/icons/warning.png", + false, }, { "AlwaysOnDM", tr("Always-On Driver Monitoring"), tr("Enable driver monitoring even when openpilot is not engaged."), "../assets/icons/monitoring.png", + false, }, { "RecordFront", tr("Record and Upload Driver Camera"), tr("Upload data from the driver facing camera and help improve the driver monitoring algorithm."), "../assets/icons/monitoring.png", + true, }, { "IsMetric", tr("Use Metric System"), tr("Display speed in km/h instead of mph."), "../assets/icons/metric.png", + false, }, }; @@ -75,12 +82,24 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { // set up uiState update for personality setting QObject::connect(uiState(), &UIState::uiUpdate, this, &TogglesPanel::updateState); - for (auto &[param, title, desc, icon] : toggle_defs) { + for (auto &[param, title, desc, icon, needs_restart] : toggle_defs) { auto toggle = new ParamControl(param, title, desc, icon, this); bool locked = params.getBool((param + "Lock").toStdString()); toggle->setEnabled(!locked); + if (needs_restart && !locked) { + toggle->setDescription(toggle->getDescription() + " " + tr("Changing this setting will restart openpilot if the car is powered on.")); + + QObject::connect(uiState(), &UIState::engagedChanged, [toggle](bool engaged) { + toggle->setEnabled(!engaged); + }); + + QObject::connect(toggle, &ParamControl::toggleFlipped, [=](bool state) { + params.putBool("OnroadCycleRequested", true); + }); + } + addItem(toggle); toggles[param.toStdString()] = toggle; diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index ec3d40961d..79a245a0e7 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -78,6 +78,11 @@ void UIState::updateStatus() { } } + if (engaged() != engaged_prev) { + engaged_prev = engaged(); + emit engagedChanged(engaged()); + } + // Handle onroad/offroad transition if (scene.started != started_prev || sm->frame == 1) { if (scene.started) { diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index 6ff67cacde..fd2aee771e 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -81,6 +81,7 @@ public: signals: void uiUpdate(const UIState &s); void offroadTransition(bool offroad); + void engagedChanged(bool engaged); private slots: void update(); @@ -88,6 +89,7 @@ private slots: private: QTimer *timer; bool started_prev = false; + bool engaged_prev = false; }; UIState *uiState(); diff --git a/system/hardware/hardwared.py b/system/hardware/hardwared.py index b6de91818e..15a191adc4 100755 --- a/system/hardware/hardwared.py +++ b/system/hardware/hardwared.py @@ -34,6 +34,7 @@ CURRENT_TAU = 15. # 15s time constant TEMP_TAU = 5. # 5s time constant DISCONNECT_TIMEOUT = 5. # wait 5 seconds before going offroad after disconnect so you get an alert PANDA_STATES_TIMEOUT = round(1000 / SERVICE_LIST['pandaStates'].frequency * 1.5) # 1.5x the expected pandaState frequency +ONROAD_CYCLE_TIME = 1 # seconds to wait offroad after requesting an onroad cycle ThermalBand = namedtuple("ThermalBand", ['min_temp', 'max_temp']) HardwareState = namedtuple("HardwareState", ['network_type', 'network_info', 'network_strength', 'network_stats', @@ -170,6 +171,7 @@ def hardware_thread(end_event, hw_queue) -> None: onroad_conditions: dict[str, bool] = { "ignition": False, + "not_onroad_cycle": True, } startup_conditions: dict[str, bool] = {} startup_conditions_prev: dict[str, bool] = {} @@ -195,6 +197,7 @@ def hardware_thread(end_event, hw_queue) -> None: should_start_prev = False in_car = False engaged_prev = False + offroad_cycle_count = 0 params = Params() power_monitor = PowerMonitoring() @@ -211,6 +214,12 @@ def hardware_thread(end_event, hw_queue) -> None: peripheralState = sm['peripheralState'] peripheral_panda_present = peripheralState.pandaType != log.PandaState.PandaType.unknown + # handle requests to cycle system started state + if params.get_bool("OnroadCycleRequested"): + params.put_bool("OnroadCycleRequested", False) + offroad_cycle_count = sm.frame + onroad_conditions["not_onroad_cycle"] = (sm.frame - offroad_cycle_count) >= ONROAD_CYCLE_TIME * SERVICE_LIST['pandaStates'].frequency + if sm.updated['pandaStates'] and len(pandaStates) > 0: # Set ignition based on any panda connected @@ -231,7 +240,7 @@ def hardware_thread(end_event, hw_queue) -> None: cloudlog.error("panda timed out onroad") # Run at 2Hz, plus either edge of ignition - ign_edge = (started_ts is not None) != onroad_conditions["ignition"] + ign_edge = (started_ts is not None) != all(onroad_conditions.values()) if (sm.frame % round(SERVICE_LIST['pandaStates'].frequency * DT_HW) != 0) and not ign_edge: continue