mirror of
https://github.com/infiniteCable2/openpilot.git
synced 2026-09-06 00:03:42 +08:00
Merge branch 'master-new' of https://github.com/sunnypilot/sunnypilot into master-new
This commit is contained in:
@@ -122,10 +122,6 @@ jobs:
|
||||
fi
|
||||
PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --disable
|
||||
|
||||
- name: Build Panda
|
||||
run: |
|
||||
scons -j$(nproc) cache_dir=${{env.SCONS_CACHE_DIR}} ${{ github.workspace }}/panda
|
||||
|
||||
- name: Build Main Project
|
||||
run: |
|
||||
export PYTHONPATH="$BUILD_DIR"
|
||||
|
||||
@@ -132,6 +132,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},
|
||||
|
||||
@@ -5,13 +5,17 @@ from collections import deque
|
||||
from typing import Any
|
||||
|
||||
import capnp
|
||||
from cereal import messaging, log, car
|
||||
from cereal import messaging, log, car, custom
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.realtime import DT_MDL, Priority, config_realtime_process
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.common.simple_kalman import KF1D
|
||||
|
||||
from opendbc.car import structs
|
||||
from opendbc.car.hyundai.values import HyundaiFlags
|
||||
from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP
|
||||
|
||||
|
||||
# Default lead acceleration decay set to 50% at 1s
|
||||
_LEAD_ACCEL_TAU = 1.5
|
||||
@@ -157,7 +161,7 @@ def get_RadarState_from_vision(lead_msg: capnp._DynamicStructReader, v_ego: floa
|
||||
|
||||
|
||||
def get_lead(v_ego: float, ready: bool, tracks: dict[int, Track], lead_msg: capnp._DynamicStructReader,
|
||||
model_v_ego: float, low_speed_override: bool = True) -> dict[str, Any]:
|
||||
model_v_ego: float, CP: structs.CarParams, CP_SP: structs.CarParamsSP, low_speed_override: bool = True) -> dict[str, Any]:
|
||||
# Determine leads, this is where the essential logic happens
|
||||
if len(tracks) > 0 and ready and lead_msg.prob > .5:
|
||||
track = match_vision_to_track(v_ego, lead_msg, tracks)
|
||||
@@ -167,6 +171,7 @@ def get_lead(v_ego: float, ready: bool, tracks: dict[int, Track], lead_msg: capn
|
||||
lead_dict = {'status': False}
|
||||
if track is not None:
|
||||
lead_dict = track.get_RadarState(lead_msg.prob)
|
||||
lead_dict = get_custom_yrel(CP, CP_SP, lead_dict, lead_msg)
|
||||
elif (track is None) and ready and (lead_msg.prob > .5):
|
||||
lead_dict = get_RadarState_from_vision(lead_msg, v_ego, model_v_ego)
|
||||
|
||||
@@ -182,8 +187,20 @@ def get_lead(v_ego: float, ready: bool, tracks: dict[int, Track], lead_msg: capn
|
||||
return lead_dict
|
||||
|
||||
|
||||
def get_custom_yrel(CP: structs.CarParams, CP_SP: structs.CarParamsSP, lead_dict: dict[str, Any],
|
||||
lead_msg: capnp._DynamicStructReader) -> dict[str, Any]:
|
||||
if CP.brand == "hyundai" and (CP_SP.flags & HyundaiFlagsSP.ENHANCED_SCC or
|
||||
CP.flags & (HyundaiFlags.CANFD_CAMERA_SCC | HyundaiFlags.CAMERA_SCC)):
|
||||
lead_dict['yRel'] = float(-lead_msg.y[0])
|
||||
|
||||
return lead_dict
|
||||
|
||||
|
||||
class RadarD:
|
||||
def __init__(self, delay: float = 0.0):
|
||||
def __init__(self, CP: structs.CarParams, CP_SP: structs.CarParams, delay: float = 0.0):
|
||||
self.CP = CP
|
||||
self.CP_SP = CP_SP
|
||||
|
||||
self.current_time = 0.0
|
||||
|
||||
self.tracks: dict[int, Track] = {}
|
||||
@@ -239,8 +256,8 @@ class RadarD:
|
||||
model_v_ego = self.v_ego
|
||||
leads_v3 = sm['modelV2'].leadsV3
|
||||
if len(leads_v3) > 1:
|
||||
self.radar_state.leadOne = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[0], model_v_ego, low_speed_override=True)
|
||||
self.radar_state.leadTwo = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[1], model_v_ego, low_speed_override=False)
|
||||
self.radar_state.leadOne = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[0], model_v_ego, self.CP, self.CP_SP, low_speed_override=True)
|
||||
self.radar_state.leadTwo = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[1], model_v_ego, self.CP, self.CP_SP, low_speed_override=False)
|
||||
|
||||
def publish(self, pm: messaging.PubMaster):
|
||||
assert self.radar_state is not None
|
||||
@@ -260,11 +277,15 @@ def main() -> None:
|
||||
CP = messaging.log_from_bytes(Params().get("CarParams", block=True), car.CarParams)
|
||||
cloudlog.info("radard got CarParams")
|
||||
|
||||
cloudlog.info("radard is waiting for CarParamsSP")
|
||||
CP_SP = messaging.log_from_bytes(Params().get("CarParamsSP", block=True), custom.CarParamsSP)
|
||||
cloudlog.info("radard got CarParamsSP")
|
||||
|
||||
# *** setup messaging
|
||||
sm = messaging.SubMaster(['modelV2', 'carState', 'liveTracks'], poll='modelV2')
|
||||
pm = messaging.PubMaster(['radarState'])
|
||||
|
||||
RD = RadarD(CP.radarDelay)
|
||||
RD = RadarD(CP, CP_SP, CP.radarDelay)
|
||||
|
||||
while 1:
|
||||
sm.update()
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -31,15 +31,16 @@ DevicePanelSP::DevicePanelSP(SettingsWindowSP *parent) : DevicePanel(parent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto *btn = new PushButtonSP(text, 720, this, param);
|
||||
auto *btn = new PushButtonSP(text, 750, this, param);
|
||||
btn->setObjectName(id);
|
||||
|
||||
device_grid_layout->addWidget(btn, row, col);
|
||||
buttons[id] = btn;
|
||||
|
||||
col++;
|
||||
if (col > 1) {
|
||||
col = 0;
|
||||
if (col==0) {
|
||||
device_grid_layout->addWidget(btn, row, col, Qt::AlignLeft);
|
||||
col++;
|
||||
} else {
|
||||
device_grid_layout->addWidget(btn, row, col, Qt::AlignRight);
|
||||
col=0;
|
||||
row++;
|
||||
}
|
||||
}
|
||||
@@ -74,19 +75,24 @@ 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
|
||||
|
||||
QHBoxLayout *power_layout = new QHBoxLayout();
|
||||
power_layout->setSpacing(5);
|
||||
power_layout->setSpacing(25);
|
||||
|
||||
PushButtonSP *rebootBtn = new PushButtonSP(tr("Reboot"), 720, this);
|
||||
PushButtonSP *rebootBtn = new PushButtonSP(tr("Reboot"), 750, this);
|
||||
rebootBtn->setStyleSheet(rebootButtonStyle);
|
||||
power_layout->addWidget(rebootBtn);
|
||||
QObject::connect(rebootBtn, &PushButtonSP::clicked, this, &DevicePanelSP::reboot);
|
||||
|
||||
PushButtonSP *poweroffBtn = new PushButtonSP(tr("Power Off"), 720, this);
|
||||
PushButtonSP *poweroffBtn = new PushButtonSP(tr("Power Off"), 750, this);
|
||||
poweroffBtn->setStyleSheet(powerOffButtonStyle);
|
||||
power_layout->addWidget(poweroffBtn);
|
||||
QObject::connect(poweroffBtn, &PushButtonSP::clicked, this, &DevicePanelSP::poweroff);
|
||||
@@ -100,7 +106,7 @@ DevicePanelSP::DevicePanelSP(SettingsWindowSP *parent) : DevicePanel(parent) {
|
||||
QObject::connect(offroadBtn, &PushButtonSP::clicked, this, &DevicePanelSP::setOffroadMode);
|
||||
|
||||
QVBoxLayout *power_group_layout = new QVBoxLayout();
|
||||
power_group_layout->setSpacing(30);
|
||||
power_group_layout->setSpacing(25);
|
||||
power_group_layout->addWidget(offroadBtn, 0, Qt::AlignHCenter);
|
||||
power_group_layout->addLayout(power_layout);
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -85,7 +85,7 @@ SunnylinkPanel::SunnylinkPanel(QWidget *parent) : QFrame(parent) {
|
||||
});
|
||||
|
||||
// Backup Settings
|
||||
backupSettings = new PushButtonSP(tr("Backup Settings"), 730, this);
|
||||
backupSettings = new PushButtonSP(tr("Backup Settings"), 750, this);
|
||||
backupSettings->setObjectName("backup_btn");
|
||||
connect(backupSettings, &QPushButton::clicked, [=]() {
|
||||
backupSettings->setEnabled(false);
|
||||
@@ -96,7 +96,7 @@ SunnylinkPanel::SunnylinkPanel(QWidget *parent) : QFrame(parent) {
|
||||
});
|
||||
|
||||
// Restore Settings
|
||||
restoreSettings = new PushButtonSP(tr("Restore Settings"), 730, this);
|
||||
restoreSettings = new PushButtonSP(tr("Restore Settings"), 750, this);
|
||||
restoreSettings->setObjectName("restore_btn");
|
||||
connect(restoreSettings, &QPushButton::clicked, [=]() {
|
||||
restoreSettings->setEnabled(false);
|
||||
@@ -108,10 +108,9 @@ SunnylinkPanel::SunnylinkPanel(QWidget *parent) : QFrame(parent) {
|
||||
// Settings Restore and Settings Backup in the same horizontal space
|
||||
auto settings_layout = new QHBoxLayout;
|
||||
settings_layout->setContentsMargins(0, 0, 0, 30);
|
||||
settings_layout->addWidget(backupSettings);
|
||||
settings_layout->addWidget(backupSettings, 0, Qt::AlignLeft);
|
||||
settings_layout->addSpacing(10);
|
||||
settings_layout->addWidget(restoreSettings);
|
||||
settings_layout->setAlignment(Qt::AlignLeft);
|
||||
settings_layout->addWidget(restoreSettings, 0, Qt::AlignRight);
|
||||
list->addItem(settings_layout);
|
||||
|
||||
QObject::connect(uiState(), &UIState::offroadTransition, this, &SunnylinkPanel::updatePanel);
|
||||
|
||||
@@ -132,10 +132,7 @@ AbstractControlSP_SELECTOR::AbstractControlSP_SELECTOR(const QString &title, con
|
||||
|
||||
if (isVisible && spacingItem) {
|
||||
main_layout->removeItem(spacingItem);
|
||||
delete spacingItem;
|
||||
spacingItem = nullptr;
|
||||
} else if (!isVisible && spacingItem == nullptr) {
|
||||
spacingItem = new QSpacerItem(44, 44, QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
} else if (!isVisible && spacingItem != nullptr && main_layout->indexOf(spacingItem) == -1) {
|
||||
main_layout->insertItem(main_layout->indexOf(description), spacingItem);
|
||||
}
|
||||
}
|
||||
@@ -145,8 +142,7 @@ AbstractControlSP_SELECTOR::AbstractControlSP_SELECTOR(const QString &title, con
|
||||
}
|
||||
|
||||
main_layout->addLayout(hlayout);
|
||||
if (!desc.isEmpty() && spacingItem == nullptr) {
|
||||
spacingItem = new QSpacerItem(44, 44, QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
if (!desc.isEmpty() && spacingItem != nullptr && main_layout->indexOf(spacingItem) == -1) {
|
||||
main_layout->insertItem(main_layout->count(), spacingItem);
|
||||
}
|
||||
|
||||
@@ -166,8 +162,7 @@ void AbstractControlSP_SELECTOR::hideEvent(QHideEvent *e) {
|
||||
description->hide();
|
||||
}
|
||||
|
||||
if (spacingItem == nullptr) {
|
||||
spacingItem = new QSpacerItem(44, 44, QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
if (spacingItem != nullptr && main_layout->indexOf(spacingItem) == -1) {
|
||||
main_layout->insertItem(main_layout->indexOf(description), spacingItem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,11 +96,10 @@ class AbstractControlSP_SELECTOR : public AbstractControlSP {
|
||||
Q_OBJECT
|
||||
|
||||
protected:
|
||||
QSpacerItem *spacingItem = new QSpacerItem(44, 44, QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
AbstractControlSP_SELECTOR(const QString &title, const QString &desc = "", const QString &icon = "", QWidget *parent = nullptr);
|
||||
void hideEvent(QHideEvent *e) override;
|
||||
|
||||
private:
|
||||
QSpacerItem *spacingItem = nullptr;
|
||||
};
|
||||
|
||||
// widget to display a value
|
||||
@@ -417,14 +416,29 @@ class OptionControlSP : public AbstractControlSP_SELECTOR {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
bool isInlineLayout;
|
||||
QHBoxLayout *optionSelectorLayout = isInlineLayout ? new QHBoxLayout() : hlayout;
|
||||
|
||||
struct MinMaxValue {
|
||||
int min_value;
|
||||
int max_value;
|
||||
};
|
||||
|
||||
int getParamValue() {
|
||||
const auto param_value = QString::fromStdString(params.get(key));
|
||||
const auto result = valueMap != nullptr ? valueMap->key(param_value) : param_value;
|
||||
return result.toInt();
|
||||
}
|
||||
|
||||
// Although the method is not static, and thus has access to the value property, I prefer to be explicit about the value.
|
||||
void setParamValue(const int new_value) {
|
||||
const auto value_str = valueMap != nullptr ? valueMap->value(QString::number(new_value)) : QString::number(new_value);
|
||||
params.put(key, value_str.toStdString());
|
||||
}
|
||||
|
||||
public:
|
||||
OptionControlSP(const QString ¶m, const QString &title, const QString &desc, const QString &icon,
|
||||
const MinMaxValue &range, const int per_value_change = 1) : _title(title), AbstractControlSP_SELECTOR(title, desc, icon) {
|
||||
const MinMaxValue &range, const int per_value_change = 1, const bool inline_layout = false, const QMap<QString, QString> *valMap = nullptr) : AbstractControlSP_SELECTOR(title, desc, icon, nullptr), _title(title), valueMap(valMap), isInlineLayout(inline_layout) {
|
||||
const QString style = R"(
|
||||
QPushButton {
|
||||
border-radius: 20px;
|
||||
@@ -444,14 +458,27 @@ public:
|
||||
}
|
||||
)";
|
||||
|
||||
if (inline_layout) {
|
||||
optionSelectorLayout->setMargin(0);
|
||||
optionSelectorLayout->setSpacing(0);
|
||||
if (!title.isEmpty()) {
|
||||
main_layout->removeWidget(title_label);
|
||||
hlayout->addWidget(title_label, 1);
|
||||
}
|
||||
if (spacingItem != nullptr && main_layout->indexOf(spacingItem) != -1) {
|
||||
main_layout->removeItem(spacingItem);
|
||||
spacingItem = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
label.setStyleSheet(label_enabled_style);
|
||||
label.setFixedWidth(300);
|
||||
label.setFixedWidth(inline_layout ? 350 : 300);
|
||||
label.setAlignment(Qt::AlignCenter);
|
||||
|
||||
const std::vector<QString> button_texts{"-", "+"};
|
||||
|
||||
key = param.toStdString();
|
||||
value = atoi(params.get(key).c_str());
|
||||
value = getParamValue();
|
||||
|
||||
button_group = new QButtonGroup(this);
|
||||
button_group->setExclusive(true);
|
||||
@@ -459,19 +486,18 @@ public:
|
||||
QPushButton *button = new QPushButton(button_texts[i], this);
|
||||
button->setStyleSheet(style + ((i == 0) ? "QPushButton { text-align: left; }" :
|
||||
"QPushButton { text-align: right; }"));
|
||||
hlayout->addWidget(button, 0, ((i == 0) ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignVCenter);
|
||||
optionSelectorLayout->addWidget(button, 0, ((i == 0) ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignVCenter);
|
||||
if (i == 0) {
|
||||
hlayout->addWidget(&label, 0, Qt::AlignCenter);
|
||||
optionSelectorLayout->addWidget(&label, 0, Qt::AlignCenter);
|
||||
}
|
||||
button_group->addButton(button, i);
|
||||
|
||||
QObject::connect(button, &QPushButton::clicked, [=]() {
|
||||
int change_value = (i == 0) ? -per_value_change : per_value_change;
|
||||
key = param.toStdString();
|
||||
value = atoi(params.get(key).c_str());
|
||||
value = getParamValue(); // in case it changed externally, we need to get the latest value.
|
||||
value += change_value;
|
||||
value = std::clamp(value, range.min_value, range.max_value);
|
||||
params.put(key, QString::number(value).toStdString());
|
||||
setParamValue(value);
|
||||
|
||||
button_group->button(0)->setEnabled(!(value <= range.min_value));
|
||||
button_group->button(1)->setEnabled(!(value >= range.max_value));
|
||||
@@ -484,7 +510,13 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
hlayout->setAlignment(Qt::AlignLeft);
|
||||
optionSelectorLayout->setAlignment(Qt::AlignLeft);
|
||||
if (isInlineLayout) {
|
||||
QFrame *container = new QFrame;
|
||||
container->setLayout(optionSelectorLayout);
|
||||
container->setStyleSheet("background-color: #393939; border-radius: 20px;");
|
||||
hlayout->addWidget(container);
|
||||
}
|
||||
}
|
||||
|
||||
void setUpdateOtherToggles(bool _update) {
|
||||
@@ -506,6 +538,10 @@ public:
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override {
|
||||
if (isInlineLayout) {
|
||||
return;
|
||||
}
|
||||
|
||||
QPainter p(this);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
@@ -513,8 +549,8 @@ protected:
|
||||
int w = 0;
|
||||
int h = 150;
|
||||
|
||||
for (int i = 0; i < hlayout->count(); ++i) {
|
||||
QWidget *widget = qobject_cast<QWidget *>(hlayout->itemAt(i)->widget());
|
||||
for (int i = 0; i < optionSelectorLayout->count(); ++i) {
|
||||
QWidget *widget = qobject_cast<QWidget *>(optionSelectorLayout->itemAt(i)->widget());
|
||||
if (widget) {
|
||||
w += widget->width();
|
||||
}
|
||||
@@ -544,6 +580,7 @@ private:
|
||||
std::map<QString, QString> option_label = {};
|
||||
bool request_update = false;
|
||||
QString _title = "";
|
||||
const QMap<QString, QString> *valueMap;
|
||||
|
||||
const QString label_enabled_style = "font-size: 50px; font-weight: 450; color: #FFFFFF;";
|
||||
const QString label_disabled_style = "font-size: 50px; font-weight: 450; color: #5C5C5C;";
|
||||
|
||||
@@ -5,16 +5,18 @@ 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.
|
||||
"""
|
||||
|
||||
from cereal import car, log, custom
|
||||
from cereal import log, custom
|
||||
|
||||
from opendbc.car import structs
|
||||
from opendbc.car.hyundai.values import HyundaiFlags
|
||||
from openpilot.sunnypilot.mads.state import StateMachine, GEARS_ALLOW_PAUSED_SILENT
|
||||
|
||||
State = custom.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState
|
||||
ButtonType = car.CarState.ButtonEvent.Type
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
EventName = log.OnroadEvent.EventName
|
||||
EventNameSP = custom.OnroadEventSP.EventName
|
||||
SafetyModel = car.CarParams.SafetyModel
|
||||
GearShifter = structs.CarState.GearShifter
|
||||
SafetyModel = structs.CarParams.SafetyModel
|
||||
|
||||
SET_SPEED_BUTTONS = (ButtonType.accelCruise, ButtonType.resumeCruise, ButtonType.decelCruise, ButtonType.setCruise)
|
||||
IGNORED_SAFETY_MODES = (SafetyModel.silent, SafetyModel.noOutput)
|
||||
@@ -48,7 +50,7 @@ class ModularAssistiveDrivingSystem:
|
||||
self.main_enabled_toggle = self.params.get_bool("MadsMainCruiseAllowed")
|
||||
self.unified_engagement_mode = self.params.get_bool("MadsUnifiedEngagementMode")
|
||||
|
||||
def update_events(self, CS: car.CarState):
|
||||
def update_events(self, CS: structs.CarState):
|
||||
def update_unified_engagement_mode():
|
||||
uem_blocked = self.enabled or (self.selfdrive.enabled and self.selfdrive.enabled_prev)
|
||||
if (self.unified_engagement_mode and uem_blocked) or not self.unified_engagement_mode:
|
||||
@@ -70,7 +72,7 @@ class ModularAssistiveDrivingSystem:
|
||||
if self.events.has(EventName.seatbeltNotLatched):
|
||||
replace_event(EventName.seatbeltNotLatched, EventNameSP.silentSeatbeltNotLatched)
|
||||
transition_paused_state()
|
||||
if self.events.has(EventName.wrongGear):
|
||||
if self.events.has(EventName.wrongGear) and (CS.standstill or CS.gearShifter == GearShifter.reverse):
|
||||
replace_event(EventName.wrongGear, EventNameSP.silentWrongGear)
|
||||
transition_paused_state()
|
||||
if self.events.has(EventName.reverseGear):
|
||||
@@ -133,7 +135,7 @@ class ModularAssistiveDrivingSystem:
|
||||
else:
|
||||
self.events.remove(EventName.wrongCarMode)
|
||||
|
||||
def update(self, CS: car.CarState):
|
||||
def update(self, CS: structs.CarState):
|
||||
if not self.enabled_toggle:
|
||||
return
|
||||
|
||||
|
||||
@@ -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