mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-12 03:03:51 +08:00
Device: Customizable Max Time Offroad (#796)
* Max Time Offroad * Refactor & Fix param * Error Handling * rename SP variable * Update selfdrive/ui/sunnypilot/qt/widgets/controls.h Co-authored-by: DevTekVE <devtekve@gmail.com> * Update selfdrive/ui/sunnypilot/qt/widgets/controls.h Co-authored-by: DevTekVE <devtekve@gmail.com> * Update selfdrive/ui/sunnypilot/qt/widgets/controls.h Co-authored-by: DevTekVE <devtekve@gmail.com> * Update selfdrive/ui/sunnypilot/qt/widgets/controls.h Co-authored-by: DevTekVE <devtekve@gmail.com> * Update selfdrive/ui/sunnypilot/qt/widgets/controls.cc Co-authored-by: DevTekVE <devtekve@gmail.com> * Update selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/max_time_offroad.h Co-authored-by: DevTekVE <devtekve@gmail.com> * Update selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/max_time_offroad.cc Co-authored-by: DevTekVE <devtekve@gmail.com> * UI layout changes for better alignment selector is not big enough -> make it bigger ;) OptionControlSP now includes a QMap argument to allow actual values to be set in param directly * Rebase & resolve reviews * change default to be closer to OP default * me dumb * MaxTimeOffroad: Add support for 30h limit and improve label formatting * power_monitoring: Refactor MaxTimeOffroad parameter handling for clarity * test: Add unit tests for MaxTimeOffroad parameter handling * power_monitoring: Update MaxTimeOffroad handling to use seconds and improve shutdown logic * power_monitoring: Improve exception handling and remove redundant shutdown check for MaxTimeOffroad --------- Co-authored-by: DevTekVE <devtekve@gmail.com> Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
This commit is contained in:
@@ -127,6 +127,7 @@ inline static std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"CarParamsSPPersistent", PERSISTENT},
|
||||
{"CarPlatformBundle", PERSISTENT},
|
||||
{"EnableGithubRunner", PERSISTENT | BACKUP},
|
||||
{"MaxTimeOffroad", PERSISTENT | BACKUP},
|
||||
{"ModelRunnerTypeCache", CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"OffroadMode", CLEAR_ON_MANAGER_START},
|
||||
{"OffroadMode_Status", CLEAR_ON_MANAGER_START},
|
||||
|
||||
@@ -21,6 +21,7 @@ qt_src = [
|
||||
"sunnypilot/qt/offroad/offroad_home.cc",
|
||||
"sunnypilot/qt/offroad/settings/device_panel.cc",
|
||||
"sunnypilot/qt/offroad/settings/lateral_panel.cc",
|
||||
"sunnypilot/qt/offroad/settings/max_time_offroad.cc",
|
||||
"sunnypilot/qt/offroad/settings/settings.cc",
|
||||
"sunnypilot/qt/offroad/settings/software_panel.cc",
|
||||
"sunnypilot/qt/offroad/settings/sunnylink_panel.cc",
|
||||
|
||||
@@ -75,6 +75,11 @@ DevicePanelSP::DevicePanelSP(SettingsWindowSP *parent) : DevicePanel(parent) {
|
||||
|
||||
connect(buttons["resetParams"], &PushButtonSP::clicked, this, &DevicePanelSP::resetSettings);
|
||||
|
||||
// Max Time Offroad
|
||||
maxTimeOffroad = new MaxTimeOffroad();
|
||||
connect(maxTimeOffroad, &OptionControlSP::updateLabels, maxTimeOffroad, &MaxTimeOffroad::refresh);
|
||||
addItem(maxTimeOffroad);
|
||||
|
||||
addItem(device_grid_layout);
|
||||
|
||||
// offroad mode and power buttons
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/max_time_offroad.h"
|
||||
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
|
||||
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
|
||||
|
||||
@@ -23,6 +24,7 @@ public:
|
||||
private:
|
||||
std::map<QString, PushButtonSP*> buttons;
|
||||
PushButtonSP *offroadBtn;
|
||||
MaxTimeOffroad *maxTimeOffroad;
|
||||
|
||||
const QString alwaysOffroadStyle = R"(
|
||||
PushButtonSP {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
|
||||
*
|
||||
* This file is part of sunnypilot and is licensed under the MIT License.
|
||||
* See the LICENSE.md file in the root directory for more details.
|
||||
*/
|
||||
|
||||
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/max_time_offroad.h"
|
||||
|
||||
// Map of Max Offroad Time Options (Minutes)
|
||||
const QMap<QString, QString> MaxTimeOffroad::offroad_time_options = {
|
||||
{"0", "0"}, // Always On
|
||||
{"1", "5"},
|
||||
{"2", "10"},
|
||||
{"3", "15"},
|
||||
{"4", "30"},
|
||||
{"5", "60"},
|
||||
{"6", "120"},
|
||||
{"7", "180"},
|
||||
{"8", "300"},
|
||||
{"9", "600"},
|
||||
{"10", "1440"},
|
||||
{"11", "1800"}
|
||||
};
|
||||
|
||||
MaxTimeOffroad::MaxTimeOffroad() : OptionControlSP(
|
||||
"MaxTimeOffroad",
|
||||
tr("Max Time Offroad"),
|
||||
tr("Device will automatically shutdown after set time once the engine is turned off.<br/>(30h is the default)"),
|
||||
"../assets/offroad/icon_blank.png",
|
||||
{0, 11}, 1, true, &offroad_time_options) {
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void MaxTimeOffroad::refresh() {
|
||||
const int maxOffroadInMinutes = QString::fromStdString(params.get("MaxTimeOffroad")).toInt();
|
||||
const bool useHours = maxOffroadInMinutes >= 60;
|
||||
|
||||
QString label;
|
||||
if (maxOffroadInMinutes == 0) {
|
||||
label = tr("Always On");
|
||||
} else {
|
||||
const int value = useHours ? maxOffroadInMinutes / 60 : maxOffroadInMinutes;
|
||||
label = QString("%1%2").arg(value).arg(useHours ? tr("h") : tr("m"));
|
||||
}
|
||||
|
||||
if (maxOffroadInMinutes == 1800) {
|
||||
label += tr(" (default)");
|
||||
}
|
||||
|
||||
setLabel(label);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
|
||||
*
|
||||
* This file is part of sunnypilot and is licensed under the MIT License.
|
||||
* See the LICENSE.md file in the root directory for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "selfdrive/ui/sunnypilot/ui.h"
|
||||
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
|
||||
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
|
||||
|
||||
class MaxTimeOffroad : public OptionControlSP {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static const QMap<QString, QString> offroad_time_options;
|
||||
|
||||
MaxTimeOffroad();
|
||||
void refresh();
|
||||
|
||||
private:
|
||||
Params params;
|
||||
};
|
||||
@@ -106,7 +106,23 @@ class PowerMonitoring:
|
||||
def get_car_battery_capacity(self) -> int:
|
||||
return int(self.car_battery_capacity_uWh)
|
||||
|
||||
# See if we need to shutdown
|
||||
# Max Time Offroad
|
||||
def max_time_offroad_exceeded(self, offroad_time):
|
||||
"""
|
||||
Check if the max time offroad has been exceeded. If the value is 0, it means no limit.
|
||||
:param offroad_time: Time spent offroad in seconds
|
||||
:return: True if the max time offroad has been exceeded, False otherwise
|
||||
"""
|
||||
try:
|
||||
param = self.params.get("MaxTimeOffroad", encoding="utf8")
|
||||
sp_max_time_val_s = int(param) * 60 if param is not None and int(param) >= 0 else MAX_TIME_OFFROAD_S
|
||||
except Exception:
|
||||
sp_max_time_val_s = MAX_TIME_OFFROAD_S
|
||||
|
||||
return sp_max_time_val_s > 0 and offroad_time >= sp_max_time_val_s
|
||||
|
||||
|
||||
# See if we need to shutdown
|
||||
def should_shutdown(self, ignition: bool, in_car: bool, offroad_timestamp: float | None, started_seen: bool):
|
||||
if offroad_timestamp is None:
|
||||
return False
|
||||
@@ -116,7 +132,7 @@ class PowerMonitoring:
|
||||
offroad_time = (now - offroad_timestamp)
|
||||
low_voltage_shutdown = (self.car_voltage_mV < (VBATT_PAUSE_CHARGING * 1e3) and
|
||||
offroad_time > VOLTAGE_SHUTDOWN_MIN_OFFROAD_TIME_S)
|
||||
should_shutdown |= offroad_time > MAX_TIME_OFFROAD_S
|
||||
should_shutdown |= self.max_time_offroad_exceeded(offroad_time)
|
||||
should_shutdown |= low_voltage_shutdown
|
||||
should_shutdown |= (self.car_battery_capacity_uWh <= 0)
|
||||
should_shutdown &= not ignition
|
||||
|
||||
@@ -2,7 +2,7 @@ import pytest
|
||||
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.system.hardware.power_monitoring import PowerMonitoring, CAR_BATTERY_CAPACITY_uWh, \
|
||||
CAR_CHARGING_RATE_W, VBATT_PAUSE_CHARGING, DELAY_SHUTDOWN_TIME_S
|
||||
CAR_CHARGING_RATE_W, VBATT_PAUSE_CHARGING, DELAY_SHUTDOWN_TIME_S, MAX_TIME_OFFROAD_S
|
||||
|
||||
# Create fake time
|
||||
ssb = 0.
|
||||
@@ -197,3 +197,38 @@ class TestPowerMonitoring:
|
||||
offroad_timestamp,
|
||||
started_seen), \
|
||||
f"Should shutdown after {DELAY_SHUTDOWN_TIME_S} seconds offroad time"
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"max_time_offroad, offroad_time_min, expected_result",
|
||||
[
|
||||
# No max time set – fallback to default (30 hours)
|
||||
(None, 0, False),
|
||||
(None, MAX_TIME_OFFROAD_S + 1, True), # exceeds 30h (1800+ mins)
|
||||
|
||||
# Valid max time values (in minutes)
|
||||
("60", 59, False), # under limit
|
||||
("60", 120, True), # over limit
|
||||
("10", 8, False), # under limit
|
||||
("10", 11, True), # over limit
|
||||
|
||||
# Edge case: max time is zero → no limit enforced
|
||||
("0", 0, False),
|
||||
("0", 400, False),
|
||||
|
||||
# Invalid max time formats or negative values → fallback to 30 hours
|
||||
("invalid", 100, False), # should fallback to 30h
|
||||
("-1", MAX_TIME_OFFROAD_S + 1, True), # should fallback to 30h, and exceed it
|
||||
]
|
||||
)
|
||||
def test_max_time_offroad_exceeded(self, max_time_offroad, offroad_time_min, expected_result):
|
||||
# Set the parameter if provided
|
||||
if max_time_offroad is not None:
|
||||
self.params.put("MaxTimeOffroad", max_time_offroad)
|
||||
|
||||
# Convert offroad time from minutes to seconds
|
||||
offroad_time_s = offroad_time_min * 60
|
||||
|
||||
pm = PowerMonitoring()
|
||||
result = pm.max_time_offroad_exceeded(offroad_time_s)
|
||||
|
||||
assert result == expected_result
|
||||
|
||||
@@ -50,6 +50,7 @@ def manager_init() -> None:
|
||||
("MadsMainCruiseAllowed", "1"),
|
||||
("MadsPauseLateralOnBrake", "0"),
|
||||
("MadsUnifiedEngagementMode", "1"),
|
||||
("MaxTimeOffroad", "1800"),
|
||||
("ModelManager_LastSyncTime", "0"),
|
||||
("ModelManager_ModelsCache", ""),
|
||||
("NeuralNetworkLateralControl", "0"),
|
||||
|
||||
Reference in New Issue
Block a user