Merge branch 'master' of https://github.com/infiniteCable2/openpilot into simple_long

This commit is contained in:
infiniteCable2
2025-10-09 22:25:04 +02:00
16 changed files with 231 additions and 64 deletions
+1
View File
@@ -181,6 +181,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"QuietMode", {PERSISTENT | BACKUP, BOOL, "0"}},
{"RainbowMode", {PERSISTENT | BACKUP, BOOL, "0"}},
{"ShowAdvancedControls", {PERSISTENT | BACKUP, BOOL, "0"}},
{"ShowTurnSignals", {PERSISTENT | BACKUP, BOOL, "0"}},
{"StandstillTimer", {PERSISTENT | BACKUP, BOOL, "0"}},
{"TrueVEgoUI", {PERSISTENT | BACKUP, BOOL, "0"}},
+1 -1
View File
@@ -57,7 +57,7 @@ def convert_carControlSP(struct: capnp.lib.capnp._DynamicStructReader) -> struct
struct_dataclass = structs.CarControlSP(**remove_deprecated({k: v for k, v in struct_dict.items() if not isinstance(k, dict)}))
struct_dataclass.mads = structs.ModularAssistiveDrivingSystem(**remove_deprecated(struct_dict.get('mads', {})))
struct_dataclass.params = [structs.CarControlSP.Param(**remove_deprecated(p)) for p in struct_dict.get('params', [])]
# struct_dataclass.params = [structs.CarControlSP.Param(**remove_deprecated(p)) for p in struct_dict.get('params', [])]
struct_dataclass.leadOne = structs.LeadData(**remove_deprecated(struct_dict.get('leadOne', {})))
struct_dataclass.leadTwo = structs.LeadData(**remove_deprecated(struct_dict.get('leadTwo', {})))
struct_dataclass.intelligentCruiseButtonManagement = structs.IntelligentCruiseButtonManagement(
-9
View File
@@ -5,9 +5,6 @@
#include <map>
#include "selfdrive/ui/qt/util.h"
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/ui.h"
#endif
void OnroadAlerts::updateState(const UIState &s) {
Alert a = getAlert(*(s.sm), s.scene.started_frame);
@@ -79,12 +76,6 @@ void OnroadAlerts::paintEvent(QPaintEvent *event) {
}
QRect r = QRect(0 + margin, height() - h + margin, width() - margin*2, h - margin*2);
#ifdef SUNNYPILOT
const int dev_ui_info = uiStateSP()->scene.dev_ui_info;
const int adjustment = dev_ui_info > 1 && alert.size != cereal::SelfdriveState::AlertSize::FULL ? 30 : 0;
r = QRect(0 + margin, height() - h + margin - adjustment, width() - margin*2, h - margin*2);
#endif
QPainter p(this);
// draw background + gradient
+2
View File
@@ -4,8 +4,10 @@
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.h"
#include "selfdrive/ui/sunnypilot/qt/onroad/alerts.h"
#define UIState UIStateSP
#define AnnotatedCameraWidget AnnotatedCameraWidgetSP
#define OnroadAlerts OnroadAlertsSP
#else
#include "selfdrive/ui/qt/onroad/annotated_camera.h"
#endif
+1
View File
@@ -38,6 +38,7 @@ qt_src = [
"sunnypilot/qt/offroad/settings/trips_panel.cc",
"sunnypilot/qt/offroad/settings/vehicle_panel.cc",
"sunnypilot/qt/offroad/settings/visuals_panel.cc",
"sunnypilot/qt/onroad/alerts.cc",
"sunnypilot/qt/onroad/annotated_camera.cc",
"sunnypilot/qt/onroad/buttons.cc",
"sunnypilot/qt/onroad/developer_ui/developer_ui.cc",
@@ -79,10 +79,17 @@ VisualsPanel::VisualsPanel(QWidget *parent) : QWidget(parent) {
{
"HideVEgoUI",
tr("Speedometer: Hide from Onroad Screen"),
tr("Always display the true vehicle current speed from wheel speed sensors."),
tr("When enabled, the speedometer on the onroad screen is not displayed."),
"",
false,
}
},
{
"ShowTurnSignals",
tr("Display Turn Signals"),
tr("When enabled, visual turn indicators are drawn on the HUD."),
"",
false,
},
};
// Add regular toggles first
+102
View File
@@ -0,0 +1,102 @@
/**
* 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/onroad/alerts.h"
#include <QPainter>
#include <map>
#include <QRect>
#include <QFont>
OnroadAlerts::Alert OnroadAlertsSP::getAlert(const SubMaster &sm, uint64_t started_frame) {
OnroadAlerts::Alert alert = OnroadAlerts::getAlert(sm, started_frame);
alert.text1.replace("openpilot", "sunnypilot");
alert.text2.replace("openpilot", "sunnypilot");
return alert;
}
void OnroadAlertsSP::paintEvent(QPaintEvent *event) {
if (alert.size == cereal::SelfdriveState::AlertSize::NONE) {
return;
} else if (alert.size == cereal::SelfdriveState::AlertSize::FULL) {
OnroadAlerts::paintEvent(event);
return;
}
static std::map<cereal::SelfdriveState::AlertSize, const int> alert_heights = {
{cereal::SelfdriveState::AlertSize::SMALL, 271},
{cereal::SelfdriveState::AlertSize::MID, 420}
};
int h = alert_heights[alert.size];
QPainter p(this);
QFont topFont;
QFont bottomFont;
QRect topTextBoundingRect;
QRect bottomTextBoundingRect;
QRect rect;
int margin = 40;
int radius = 30;
const int dev_ui_info = uiStateSP()->scene.dev_ui_info;
const int v_adjustment = dev_ui_info > 1 && alert.size != cereal::SelfdriveState::AlertSize::FULL ? 40 : 0;
const int h_adjustment = dev_ui_info > 0 && alert.size != cereal::SelfdriveState::AlertSize::FULL ? 230 : 0;
if (alert.size == cereal::SelfdriveState::AlertSize::SMALL) {
topFont = InterFont(74, QFont::DemiBold);
QFontMetrics fmTop(topFont);
topTextBoundingRect = fmTop.boundingRect(
QRect(0 + margin, height() - h + margin - v_adjustment, width() - margin * 2 - h_adjustment, 0), Qt::TextWordWrap,
alert.text1);
h = topTextBoundingRect.height();
rect = QRect(0 + margin, height() - h - margin * 2 - v_adjustment, width() - margin * 2 - h_adjustment, h + margin);
} else if (alert.size == cereal::SelfdriveState::AlertSize::MID) {
topFont = InterFont(88, QFont::Bold);
bottomFont = InterFont(66);
QFontMetrics fmTop(topFont);
QFontMetrics fmBotton(bottomFont);
topTextBoundingRect = fmTop.boundingRect(
QRect(0 + margin, height() - h + margin - v_adjustment, width() - margin * 2 - h_adjustment, 0), Qt::TextWordWrap,
alert.text1);
bottomTextBoundingRect = fmBotton.boundingRect(
QRect(0 + margin, height() - h + margin - v_adjustment + topTextBoundingRect.height(),
width() - margin * 2 - h_adjustment, 0), Qt::TextWordWrap, alert.text2);
h = topTextBoundingRect.height() + bottomTextBoundingRect.height() + margin * 2;
rect = QRect(0 + margin, height() - h - margin * 2 - v_adjustment, width() - margin * 2 - h_adjustment, h + margin);
}
// draw background + gradient
// draw background + gradient
p.setPen(Qt::NoPen);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
p.setBrush(QBrush(alert_colors[alert.status]));
p.drawRoundedRect(rect, radius, radius);
QLinearGradient g(0, rect.y(), 0, rect.bottom());
g.setColorAt(0, QColor::fromRgbF(0, 0, 0, 0.05));
g.setColorAt(1, QColor::fromRgbF(0, 0, 0, 0.35));
p.setCompositionMode(QPainter::CompositionMode_DestinationOver);
p.setBrush(QBrush(g));
p.drawRoundedRect(rect, radius, radius);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
// text
p.setPen(QColor(0xff, 0xff, 0xff));
p.setRenderHint(QPainter::TextAntialiasing);
p.setFont(topFont);
if (alert.size == cereal::SelfdriveState::AlertSize::SMALL) {
p.drawText(rect, Qt::AlignCenter | Qt::TextWordWrap, alert.text1);
} else if (alert.size == cereal::SelfdriveState::AlertSize::MID) {
QRect topText = QRect(rect.x(), rect.top() + margin, rect.width(), topTextBoundingRect.height());
p.drawText(topText, Qt::AlignHCenter | Qt::AlignTop | Qt::TextWordWrap | Qt::AlignCenter, alert.text1);
p.setFont(bottomFont);
p.drawText(QRect(rect.x(), topText.bottom() + margin, rect.width(), bottomTextBoundingRect.height()),
Qt::AlignHCenter | Qt::TextWordWrap | Qt::AlignCenter, alert.text2);
}
}
@@ -0,0 +1,22 @@
/**
* 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/qt/onroad/alerts.h"
#include "selfdrive/ui/sunnypilot/ui.h"
class OnroadAlertsSP : public OnroadAlerts {
Q_OBJECT
public:
OnroadAlertsSP(QWidget *parent = 0) : OnroadAlerts(parent) {}
protected:
void paintEvent(QPaintEvent *) override;
Alert getAlert(const SubMaster &sm, uint64_t started_frame);
};
+81
View File
@@ -122,6 +122,12 @@ void HudRendererSP::updateState(const UIState &s) {
float v_ego = (v_ego_cluster_seen && !s.scene.trueVEgoUI) ? car_state.getVEgoCluster() : car_state.getVEgo();
speed = std::max<float>(0.0f, v_ego * (is_metric ? MS_TO_KPH : MS_TO_MPH));
hideVEgoUI = s.scene.hideVEgoUI;
leftBlinkerOn = car_state.getLeftBlinker();
rightBlinkerOn = car_state.getRightBlinker();
leftBlindspot = car_state.getLeftBlindspot();
rightBlindspot = car_state.getRightBlindspot();
showTurnSignals = s.scene.turn_signals;
}
void HudRendererSP::draw(QPainter &p, const QRect &surface_rect) {
@@ -239,6 +245,11 @@ void HudRendererSP::draw(QPainter &p, const QRect &surface_rect) {
} else {
e2eAlertFrame = 0;
}
// Blinker
if (showTurnSignals) {
drawBlinker(p, surface_rect);
}
}
p.restore();
@@ -752,3 +763,73 @@ void HudRendererSP::drawCurrentSpeedSP(QPainter &p, const QRect &surface_rect) {
p.setFont(InterFont(66));
HudRenderer::drawText(p, surface_rect.center().x(), 290, is_metric ? tr("km/h") : tr("mph"), 200);
}
void HudRendererSP::drawBlinker(QPainter &p, const QRect &surface_rect) {
if (!leftBlinkerOn && !rightBlinkerOn) {
blinkerFrameCounter = 0;
return;
}
++blinkerFrameCounter;
const int circleRadius = 44;
const int arrowLength = 44;
const int x_gap = 180;
const int y_offset = 272;
const int centerX = surface_rect.center().x();
const bool hazard = leftBlinkerOn && rightBlinkerOn;
const QPen bgBorder(Qt::white, 5);
const QPen arrowPen(Qt::NoPen);
p.save();
auto drawArrow = [&](int cx, int cy, int dir, const QBrush &arrowBrush) {
const int bodyLength = arrowLength / 2;
const int bodyWidth = arrowLength / 2;
const int headLength = arrowLength / 2;
const int headWidth = arrowLength;
QPolygon arrow;
arrow.reserve(7);
arrow << QPoint(cx - dir * bodyLength, cy - bodyWidth / 2)
<< QPoint(cx, cy - bodyWidth / 2)
<< QPoint(cx, cy - headWidth / 2)
<< QPoint(cx + dir * headLength, cy)
<< QPoint(cx, cy + headWidth / 2)
<< QPoint(cx, cy + bodyWidth / 2)
<< QPoint(cx - dir * bodyLength, cy + bodyWidth / 2);
p.setPen(arrowPen);
p.setBrush(arrowBrush);
p.drawPolygon(arrow);
};
auto drawCircle = [&](int cx, int cy, const QBrush &bgBrush) {
p.setPen(bgBorder);
p.setBrush(bgBrush);
p.drawEllipse(QPoint(cx, cy), circleRadius, circleRadius);
};
struct BlinkerSide { bool on; int dir; bool blocked; int cx; };
const std::array<BlinkerSide, 2> sides = {{
{leftBlinkerOn, -1, hazard ? true : (leftBlinkerOn && leftBlindspot), centerX - x_gap},
{rightBlinkerOn, 1, hazard ? true : (rightBlinkerOn && rightBlindspot), centerX + x_gap},
}};
for (const auto &s: sides) {
if (!s.on) continue;
QColor bgColor = s.blocked ? QColor(135, 23, 23) : QColor(23, 134, 68);
QColor arrowColor = s.blocked ? QColor(66, 12, 12) : QColor(12, 67, 34);
if (pulseElement(blinkerFrameCounter)) arrowColor = Qt::white;
const QBrush bgBrush(bgColor);
const QBrush arrowBrush(arrowColor);
drawCircle(s.cx, y_offset, bgBrush);
drawArrow(s.cx, y_offset, s.dir, arrowBrush);
}
p.restore();
}
+7
View File
@@ -38,6 +38,7 @@ private:
void drawSetSpeedSP(QPainter &p, const QRect &surface_rect);
void drawE2eAlert(QPainter &p, const QRect &surface_rect);
void drawCurrentSpeedSP(QPainter &p, const QRect &surface_rect);
void drawBlinker(QPainter &p, const QRect &surface_rect);
bool lead_status;
float lead_d_rel;
@@ -109,4 +110,10 @@ private:
QString alert_text;
QPixmap alert_img;
bool hideVEgoUI;
bool leftBlinkerOn;
bool rightBlinkerOn;
bool leftBlindspot;
bool rightBlindspot;
int blinkerFrameCounter;
bool showTurnSignals;
};
+1 -2
View File
@@ -95,13 +95,12 @@ QStringList searchFromList(const QString &query, const QStringList &list) {
return list;
}
QStringList search_terms = query.simplified().toLower().split(" ", QString::SkipEmptyParts);
QStringList search_terms = query.simplified().toLower().replace(QRegularExpression("[^a-zA-Z0-9\\s]"), " ").split(" ", QString::SkipEmptyParts);
QStringList search_results;
for (const QString &element : list) {
if (std::all_of(search_terms.begin(), search_terms.end(), [&](const QString &term) {
QString normalized_term = term.normalized(QString::NormalizationForm_KD).toLower();
normalized_term.remove(QRegularExpression("[^a-zA-Z0-9\\s]"));
QString normalized_element = element.normalized(QString::NormalizationForm_KD).toLower();
return normalized_element.contains(normalized_term, Qt::CaseInsensitive);
})) {
+2
View File
@@ -72,6 +72,8 @@ void ui_update_params_sp(UIStateSP *s) {
s->scene.onroadScreenOffControl = params.getBool("OnroadScreenOffControl");
s->scene.onroadScreenOffTimerParam = std::atoi(params.get("OnroadScreenOffTimer").c_str());
s->reset_onroad_sleep_timer();
s->scene.turn_signals = params.getBool("ShowTurnSignals");
}
void UIStateSP::reset_onroad_sleep_timer() {
+1
View File
@@ -17,4 +17,5 @@ typedef struct UISceneSP : UIScene {
int onroadScreenOffTimerParam;
bool trueVEgoUI;
bool hideVEgoUI;
bool turn_signals = false;
} UISceneSP;
@@ -10,7 +10,6 @@ from cereal import log, custom
from opendbc.car import structs
from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
from openpilot.sunnypilot.selfdrive.controls.lib.param_store import ParamStore
from openpilot.sunnypilot.selfdrive.controls.lib.blinker_pause_lateral import BlinkerPauseLateral
@@ -19,7 +18,6 @@ class ControlsExt:
self.CP = CP
self.params = params
self.blinker_pause_lateral = BlinkerPauseLateral()
self.param_store = ParamStore(self.CP)
self.get_params_sp()
cloudlog.info("controlsd_ext is waiting for CarParamsSP")
@@ -30,7 +28,6 @@ class ControlsExt:
self.pm_services_ext = ['carControlSP']
def get_params_sp(self) -> None:
self.param_store.update(self.params)
self.blinker_pause_lateral.get_params()
def get_lat_active(self, sm: messaging.SubMaster) -> bool:
@@ -73,8 +70,6 @@ class ControlsExt:
# MADS state
CC_SP.mads = sm['selfdriveStateSP'].mads
CC_SP.params = self.param_store.param_list
CC_SP.intelligentCruiseButtonManagement = sm['selfdriveStateSP'].intelligentCruiseButtonManagement
return CC_SP
@@ -1,44 +0,0 @@
"""
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.
"""
from cereal import custom
from opendbc.car import structs
from openpilot.common.params import Params
from sunnypilot.sunnylink.utils import get_param_as_byte
class ParamStore:
keys: list[str]
_params: dict[str, custom.CarControlSP.Param]
def __init__(self, CP: structs.CarParams):
universal_params: list[str] = []
brand_params: list[str] = []
self.keys = universal_params + brand_params
self._params = {}
self.frame = -1
def update(self, params: Params) -> None:
self.frame += 1
if self.frame % 300 != 0:
return
for key in self.keys:
param_type = params.get_type(key).name.lower() # Using string instead of number because its "loose" dependency, and could change by OP at anytime.
# Over engineering opportunity: It's possible this conversion is slow, we may check the value as params returns it for cache purposes. Not today.
param_value = get_param_as_byte(key, params)
if (existing_param := self._params.get(key)) is not None and existing_param.value == param_value:
continue
self._params[key] = custom.CarControlSP.Param(key=key, value=param_value, type=param_type)
@property
def param_list(self) -> list[custom.CarControlSP.Param]:
return [v for k,v in self._params.items()]