mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-06-20 01:02:07 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9e131b4a69 | |||
| f402487f9e | |||
| 2c922afa12 | |||
| 342abcd45a | |||
| 6e8586e566 |
+1
-25
@@ -1,30 +1,6 @@
|
|||||||
sunnypilot Version 2025.003.000 (20xx-xx-xx)
|
sunnypilot Version 2025.002.000 (2025-xx-xx)
|
||||||
========================
|
========================
|
||||||
|
|
||||||
sunnypilot Version 2025.002.000 (2025-11-06)
|
|
||||||
========================
|
|
||||||
* What's Changed (sunnypilot/sunnypilot)
|
|
||||||
* models: bump model json to v8 by @Discountchubbs
|
|
||||||
* Bug: Model UI Crash Fix by @nayan8teen
|
|
||||||
* controlsd: add `CP_SP` to `get_pid_accel_limits` by @THERoenPR
|
|
||||||
* sunnylink: update uploader button logic to support novice tier and above by @devtekve
|
|
||||||
* Tesla: Coop Steering by @AmyJeanes
|
|
||||||
* ui: update discord references and add forum widget by @devtekve
|
|
||||||
* ui: Fix spacing in sunnylink panel by @devtekve
|
|
||||||
* docs: Update README installation branches and discord links by @mpurnell1 in
|
|
||||||
* stats: sunnylink integration by @devtekve
|
|
||||||
* bug: Fix initial registration for sunnylink by @devtekve
|
|
||||||
* What's Changed (sunnypilot/opendbc)
|
|
||||||
* Honda: add brake hold messages for Clarity by @mvl-boston
|
|
||||||
* interface: add `CP_SP` to `get_pid_accel_limits` method signature by @roenthomas
|
|
||||||
* Honda: use fixed accel min/max constants for Gas Interceptor by @roenthomas
|
|
||||||
* Tesla: Coop Steering by @AmyJeanes
|
|
||||||
* New Contributors (sunnypilot/sunnypilot)
|
|
||||||
* @THERoenPR made their first contribution in "controlsd: add `CP_SP` to `get_pid_accel_limits`"
|
|
||||||
* @AmyJeanes made their first contribution in "Tesla: Coop Steering"
|
|
||||||
* @mpurnell1 made their first contribution in "docs: Update README installation branches and discord links"
|
|
||||||
* Full Changelog: https://github.com/sunnypilot/sunnypilot/compare/v2025.001.000...v2025.002.000
|
|
||||||
|
|
||||||
sunnypilot Version 2025.001.000 (2025-10-25)
|
sunnypilot Version 2025.001.000 (2025-10-25)
|
||||||
========================
|
========================
|
||||||
* 🛠️ Major rewrite
|
* 🛠️ Major rewrite
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
#define DEFAULT_MODEL "Firehose (Default)"
|
#define DEFAULT_MODEL "TCPv3 + gWMv9 (Default)"
|
||||||
|
|||||||
@@ -172,14 +172,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"ShowTurnSignals", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"ShowTurnSignals", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"StandstillTimer", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"StandstillTimer", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"TrueVEgoUI", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"TrueVEgoUI", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"VisualRadarTracks", {PERSISTENT | BACKUP, BOOL, "0"}},
|
|
||||||
{"VisualRadarTracksDelay", {PERSISTENT | BACKUP, FLOAT, "0.0"}},
|
|
||||||
{"VisualWideCam", {PERSISTENT | BACKUP, BOOL, "0"}},
|
|
||||||
{"VisualStyle", {PERSISTENT | BACKUP, INT, "0"}},
|
|
||||||
{"VisualStyleZoom", {PERSISTENT | BACKUP, BOOL, "0"}},
|
|
||||||
{"VisualStyleOverhead", {PERSISTENT | BACKUP, BOOL, "0"}},
|
|
||||||
{"VisualStyleOverheadZoom", {PERSISTENT | BACKUP, BOOL, "0"}},
|
|
||||||
{"VisualStyleOverheadThreshold", {PERSISTENT | BACKUP, INT, "20"}},
|
|
||||||
|
|
||||||
// MADS params
|
// MADS params
|
||||||
{"Mads", {PERSISTENT | BACKUP, BOOL, "1"}},
|
{"Mads", {PERSISTENT | BACKUP, BOOL, "1"}},
|
||||||
|
|||||||
+1
-1
Submodule opendbc_repo updated: c32e79f3c6...c7126f8ba6
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:ebb38a934d6472c061cc6010f46d9720ca132d631a47e585a893bdd41ade2419
|
oid sha256:76e7beaa7822219b019a4352ab111d0898abf72bd4b0d9520bd8dc68174e8c31
|
||||||
size 12343535
|
size 13926460
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:befac016a247b7ad5dc5b55d339d127774ed7bd2b848f1583f72aa4caee37781
|
oid sha256:8f16d548ea4eb5d01518a9e90d4527cd97c31a84bcaf6f695dead8f0015fecc4
|
||||||
size 46271991
|
size 46271942
|
||||||
|
|||||||
@@ -25,11 +25,6 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
|
|||||||
// update engageability/experimental mode button
|
// update engageability/experimental mode button
|
||||||
experimental_btn->updateState(s);
|
experimental_btn->updateState(s);
|
||||||
dmon.updateState(s);
|
dmon.updateState(s);
|
||||||
if (s.scene.visual_style == 0) {
|
|
||||||
setBackgroundColor(bg_colors[STATUS_DISENGAGED]);
|
|
||||||
} else {
|
|
||||||
setBackgroundColor(QColor(0, 0, 0));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnnotatedCameraWidget::initializeGL() {
|
void AnnotatedCameraWidget::initializeGL() {
|
||||||
@@ -40,12 +35,7 @@ void AnnotatedCameraWidget::initializeGL() {
|
|||||||
qInfo() << "OpenGL language version:" << QString((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION));
|
qInfo() << "OpenGL language version:" << QString((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION));
|
||||||
|
|
||||||
prev_draw_t = millis_since_boot();
|
prev_draw_t = millis_since_boot();
|
||||||
auto *s = uiState();
|
setBackgroundColor(bg_colors[STATUS_DISENGAGED]);
|
||||||
if (s->scene.visual_style == 0) {
|
|
||||||
setBackgroundColor(bg_colors[STATUS_DISENGAGED]);
|
|
||||||
} else {
|
|
||||||
setBackgroundColor(QColor(0, 0, 0));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mat4 AnnotatedCameraWidget::calcFrameMatrix() {
|
mat4 AnnotatedCameraWidget::calcFrameMatrix() {
|
||||||
@@ -128,13 +118,7 @@ void AnnotatedCameraWidget::paintGL() {
|
|||||||
} else if (v_ego > 15) {
|
} else if (v_ego > 15) {
|
||||||
wide_cam_requested = false;
|
wide_cam_requested = false;
|
||||||
}
|
}
|
||||||
if (s->scene.visual_wide_cam == 1) {
|
wide_cam_requested = wide_cam_requested && sm["selfdriveState"].getSelfdriveState().getExperimentalMode();
|
||||||
wide_cam_requested = true;
|
|
||||||
} else if (s->scene.visual_wide_cam == 2) {
|
|
||||||
wide_cam_requested = false;
|
|
||||||
} else {
|
|
||||||
wide_cam_requested = wide_cam_requested && sm["selfdriveState"].getSelfdriveState().getExperimentalMode();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
CameraWidget::setStreamType(wide_cam_requested ? VISION_STREAM_WIDE_ROAD : VISION_STREAM_ROAD);
|
CameraWidget::setStreamType(wide_cam_requested ? VISION_STREAM_WIDE_ROAD : VISION_STREAM_ROAD);
|
||||||
CameraWidget::setFrameId(sm["modelV2"].getModelV2().getFrameId());
|
CameraWidget::setFrameId(sm["modelV2"].getModelV2().getFrameId());
|
||||||
|
|||||||
+16
-241
@@ -1,5 +1,4 @@
|
|||||||
#include "selfdrive/ui/qt/onroad/model.h"
|
#include "selfdrive/ui/qt/onroad/model.h"
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
void ModelRenderer::draw(QPainter &painter, const QRect &surface_rect) {
|
void ModelRenderer::draw(QPainter &painter, const QRect &surface_rect) {
|
||||||
auto *s = uiState();
|
auto *s = uiState();
|
||||||
@@ -50,14 +49,8 @@ void ModelRenderer::update_leads(const cereal::RadarState::Reader &radar_state,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ModelRenderer::update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead) {
|
void ModelRenderer::update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead) {
|
||||||
auto *s = uiState();
|
|
||||||
const auto &model_position = model.getPosition();
|
const auto &model_position = model.getPosition();
|
||||||
float max_distance;
|
float max_distance = std::clamp(*(model_position.getX().end() - 1), MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE);
|
||||||
if (s->scene.visual_style == 0) {
|
|
||||||
max_distance = std::clamp(*(model_position.getX().end() - 1), MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE);
|
|
||||||
} else {
|
|
||||||
max_distance = std::clamp(*(model_position.getX().end() - 1), MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// update lane lines
|
// update lane lines
|
||||||
const auto &lane_lines = model.getLaneLines();
|
const auto &lane_lines = model.getLaneLines();
|
||||||
@@ -65,11 +58,7 @@ void ModelRenderer::update_model(const cereal::ModelDataV2::Reader &model, const
|
|||||||
int max_idx = get_path_length_idx(lane_lines[0], max_distance);
|
int max_idx = get_path_length_idx(lane_lines[0], max_distance);
|
||||||
for (int i = 0; i < std::size(lane_line_vertices); i++) {
|
for (int i = 0; i < std::size(lane_line_vertices); i++) {
|
||||||
lane_line_probs[i] = line_probs[i];
|
lane_line_probs[i] = line_probs[i];
|
||||||
if (s->scene.visual_style == 2) {
|
mapLineToPolygon(lane_lines[i], 0.025 * lane_line_probs[i], 0, &lane_line_vertices[i], max_idx);
|
||||||
mapLineToPolygon(lane_lines[i], 0.075 * lane_line_probs[i], 0, &lane_line_vertices[i], max_idx);
|
|
||||||
} else {
|
|
||||||
mapLineToPolygon(lane_lines[i], 0.025 * lane_line_probs[i], 0, &lane_line_vertices[i], max_idx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update road edges
|
// update road edges
|
||||||
@@ -77,11 +66,7 @@ void ModelRenderer::update_model(const cereal::ModelDataV2::Reader &model, const
|
|||||||
const auto &edge_stds = model.getRoadEdgeStds();
|
const auto &edge_stds = model.getRoadEdgeStds();
|
||||||
for (int i = 0; i < std::size(road_edge_vertices); i++) {
|
for (int i = 0; i < std::size(road_edge_vertices); i++) {
|
||||||
road_edge_stds[i] = edge_stds[i];
|
road_edge_stds[i] = edge_stds[i];
|
||||||
if (s->scene.visual_style == 2) {
|
mapLineToPolygon(road_edges[i], 0.025, 0, &road_edge_vertices[i], max_idx);
|
||||||
mapLineToPolygon(road_edges[i], 0.1, 0, &road_edge_vertices[i], max_idx);
|
|
||||||
} else {
|
|
||||||
mapLineToPolygon(road_edges[i], 0.025, 0, &road_edge_vertices[i], max_idx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update path
|
// update path
|
||||||
@@ -94,112 +79,16 @@ void ModelRenderer::update_model(const cereal::ModelDataV2::Reader &model, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ModelRenderer::drawLaneLines(QPainter &painter) {
|
void ModelRenderer::drawLaneLines(QPainter &painter) {
|
||||||
auto *s = uiState();
|
// lanelines
|
||||||
if (s->scene.visual_style == 2) {
|
for (int i = 0; i < std::size(lane_line_vertices); ++i) {
|
||||||
QRectF r = clip_region;
|
painter.setBrush(QColor::fromRgbF(1.0, 1.0, 1.0, std::clamp<float>(lane_line_probs[i], 0.0, 0.7)));
|
||||||
|
painter.drawPolygon(lane_line_vertices[i]);
|
||||||
|
}
|
||||||
|
|
||||||
qreal horizonY = r.bottom();
|
// road edges
|
||||||
if (!road_edge_vertices[0].isEmpty() || !road_edge_vertices[1].isEmpty()) {
|
for (int i = 0; i < std::size(road_edge_vertices); ++i) {
|
||||||
qreal leftH = r.top();
|
painter.setBrush(QColor::fromRgbF(1.0, 0, 0, std::clamp<float>(1.0 - road_edge_stds[i], 0.0, 1.0)));
|
||||||
qreal rightH = r.top();
|
painter.drawPolygon(road_edge_vertices[i]);
|
||||||
|
|
||||||
if (!road_edge_vertices[0].isEmpty()) {
|
|
||||||
leftH = std::numeric_limits<qreal>::max();
|
|
||||||
for (const QPointF &pt : road_edge_vertices[0]) {
|
|
||||||
if (pt.y() < leftH) leftH = pt.y();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!road_edge_vertices[1].isEmpty()) {
|
|
||||||
rightH = std::numeric_limits<qreal>::max();
|
|
||||||
for (const QPointF &pt : road_edge_vertices[1]) {
|
|
||||||
if (pt.y() < rightH) rightH = pt.y();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
horizonY = std::max(leftH, rightH);
|
|
||||||
}
|
|
||||||
|
|
||||||
painter.fillRect(QRectF(r.left(), horizonY + 0, r.width(), r.bottom() - (horizonY + 0)), QColor("#111111"));
|
|
||||||
|
|
||||||
auto buildFill = [&](const QPolygonF &edgeRibbon, bool isLeftSide) -> QPolygonF {
|
|
||||||
if (edgeRibbon.isEmpty()) return {};
|
|
||||||
|
|
||||||
QMap<int, QPointF> byY;
|
|
||||||
for (const QPointF &pt : edgeRibbon) {
|
|
||||||
int yi = int(std::round(pt.y()));
|
|
||||||
if (!byY.contains(yi)) {
|
|
||||||
byY[yi] = pt;
|
|
||||||
} else {
|
|
||||||
if (isLeftSide) {
|
|
||||||
if (pt.x() > byY[yi].x()) byY[yi] = pt;
|
|
||||||
} else {
|
|
||||||
if (pt.x() < byY[yi].x()) byY[yi] = pt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (byY.isEmpty()) return {};
|
|
||||||
|
|
||||||
QPolygonF curve;
|
|
||||||
for (auto it = byY.cbegin(); it != byY.cend(); ++it) {
|
|
||||||
curve << it.value();
|
|
||||||
}
|
|
||||||
if (curve.size() < 2) return {};
|
|
||||||
|
|
||||||
const qreal topY = curve.first().y();
|
|
||||||
QPolygonF fill;
|
|
||||||
if (isLeftSide) {
|
|
||||||
fill << QPointF(r.left(), topY);
|
|
||||||
for (const QPointF &pt : curve) fill << pt;
|
|
||||||
fill << QPointF(r.left(), r.bottom());
|
|
||||||
} else {
|
|
||||||
fill << QPointF(r.right(), topY);
|
|
||||||
for (const QPointF &pt : curve) fill << pt;
|
|
||||||
fill << QPointF(r.right(), r.bottom());
|
|
||||||
}
|
|
||||||
return fill;
|
|
||||||
};
|
|
||||||
|
|
||||||
QPolygonF leftFill = buildFill(road_edge_vertices[0], true);
|
|
||||||
QPolygonF rightFill = buildFill(road_edge_vertices[1], false);
|
|
||||||
|
|
||||||
if (!leftFill.isEmpty()) {
|
|
||||||
painter.setBrush(QColor("#222222"));
|
|
||||||
painter.drawPolygon(leftFill);
|
|
||||||
}
|
|
||||||
if (!rightFill.isEmpty()) {
|
|
||||||
painter.setBrush(QColor("#222222"));
|
|
||||||
painter.drawPolygon(rightFill);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < std::size(lane_line_vertices); ++i) {
|
|
||||||
painter.setBrush(QColor::fromRgbF(0.902, 0.902, 0.902, std::clamp<float>(lane_line_probs[i], 0.0, 0.7)));
|
|
||||||
painter.drawPolygon(lane_line_vertices[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < std::size(road_edge_vertices); ++i) {
|
|
||||||
painter.setBrush(QColor(0x55, 0x55, 0x55, 255));
|
|
||||||
painter.drawPolygon(road_edge_vertices[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
QLinearGradient bgGrad(r.left(), horizonY - 100, r.left(), horizonY + 100);
|
|
||||||
bgGrad.setColorAt(0.0, QColor("#000000"));
|
|
||||||
bgGrad.setColorAt(0.5, QColor("#111111"));
|
|
||||||
bgGrad.setColorAt(1.0, QColor("#111111"));
|
|
||||||
painter.fillRect(QRectF(r.left(), horizonY - 200, r.width(), 200), bgGrad);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// lanelines
|
|
||||||
for (int i = 0; i < std::size(lane_line_vertices); ++i) {
|
|
||||||
painter.setBrush(QColor::fromRgbF(1.0, 1.0, 1.0, std::clamp<float>(lane_line_probs[i], 0.0, 0.7)));
|
|
||||||
painter.drawPolygon(lane_line_vertices[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// road edges
|
|
||||||
for (int i = 0; i < std::size(road_edge_vertices); ++i) {
|
|
||||||
painter.setBrush(QColor::fromRgbF(1.0, 0, 0, std::clamp<float>(1.0 - road_edge_stds[i], 0.0, 1.0)));
|
|
||||||
painter.drawPolygon(road_edge_vertices[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,7 +175,6 @@ QColor ModelRenderer::blendColors(const QColor &start, const QColor &end, float
|
|||||||
|
|
||||||
void ModelRenderer::drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data,
|
void ModelRenderer::drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data,
|
||||||
const QPointF &vd, const QRect &surface_rect) {
|
const QPointF &vd, const QRect &surface_rect) {
|
||||||
auto *s = uiState();
|
|
||||||
const float speedBuff = 10.;
|
const float speedBuff = 10.;
|
||||||
const float leadBuff = 40.;
|
const float leadBuff = 40.;
|
||||||
const float d_rel = lead_data.getDRel();
|
const float d_rel = lead_data.getDRel();
|
||||||
@@ -309,133 +197,20 @@ void ModelRenderer::drawLead(QPainter &painter, const cereal::RadarState::LeadDa
|
|||||||
float g_yo = sz / 10;
|
float g_yo = sz / 10;
|
||||||
|
|
||||||
QPointF glow[] = {{x + (sz * 1.35) + g_xo, y + sz + g_yo}, {x, y - g_yo}, {x - (sz * 1.35) - g_xo, y + sz + g_yo}};
|
QPointF glow[] = {{x + (sz * 1.35) + g_xo, y + sz + g_yo}, {x, y - g_yo}, {x - (sz * 1.35) - g_xo, y + sz + g_yo}};
|
||||||
if (s->scene.visual_style == 2) {
|
painter.setBrush(QColor(218, 202, 37, 255));
|
||||||
painter.setBrush(QColor(0xE6, 0xE6, 0xE6, 255));
|
|
||||||
} else {
|
|
||||||
painter.setBrush(QColor(218, 202, 37, 255));
|
|
||||||
}
|
|
||||||
painter.drawPolygon(glow, std::size(glow));
|
painter.drawPolygon(glow, std::size(glow));
|
||||||
|
|
||||||
// chevron
|
// chevron
|
||||||
QPointF chevron[] = {{x + (sz * 1.25), y + sz}, {x, y}, {x - (sz * 1.25), y + sz}};
|
QPointF chevron[] = {{x + (sz * 1.25), y + sz}, {x, y}, {x - (sz * 1.25), y + sz}};
|
||||||
if (s->scene.visual_style == 2) {
|
painter.setBrush(QColor(201, 34, 49, fillAlpha));
|
||||||
painter.setBrush(QColor(0, 0, 0, fillAlpha));
|
|
||||||
} else {
|
|
||||||
painter.setBrush(QColor(201, 34, 49, fillAlpha));
|
|
||||||
}
|
|
||||||
painter.drawPolygon(chevron, std::size(chevron));
|
painter.drawPolygon(chevron, std::size(chevron));
|
||||||
}
|
}
|
||||||
|
|
||||||
float mapRange(float x, float in_min, float in_max, float out_min, float out_max) {
|
// Projects a point in car to space to the corresponding point in full frame image space.
|
||||||
if (in_min < in_max) {
|
|
||||||
x = std::clamp(x, in_min, in_max);
|
|
||||||
} else {
|
|
||||||
x = std::clamp(x, in_max, in_min);
|
|
||||||
}
|
|
||||||
return out_min + (x - in_min) * (out_max - out_min) / (in_max - in_min);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Projects a point in car space to the corresponding point in full frame image space.
|
|
||||||
bool ModelRenderer::mapToScreen(float in_x, float in_y, float in_z, QPointF *out) {
|
bool ModelRenderer::mapToScreen(float in_x, float in_y, float in_z, QPointF *out) {
|
||||||
auto *s = uiState();
|
|
||||||
auto &sm = *(s->sm);
|
|
||||||
float blend_speed_mph = fabsf(sm["carState"].getCarState().getVEgo() * 2.23694f);
|
|
||||||
|
|
||||||
Eigen::Vector3f input(in_x, in_y, in_z);
|
Eigen::Vector3f input(in_x, in_y, in_z);
|
||||||
|
|
||||||
if ((s->scene.visual_style_zoom == 1 || s->scene.visual_style_zoom == 2) && s->scene.visual_style != 0) {
|
|
||||||
float zoom_start = 20.0f;
|
|
||||||
float zoom_end = 50.0f;
|
|
||||||
|
|
||||||
if (s->scene.visual_style_zoom == 2) {
|
|
||||||
std::swap(zoom_start, zoom_end);
|
|
||||||
}
|
|
||||||
|
|
||||||
float IN_X_OFFSET = mapRange(blend_speed_mph, zoom_start, zoom_end, 0.0f, 24.0f);
|
|
||||||
float IN_Y_OFFSET = mapRange(blend_speed_mph, zoom_start, zoom_end, 1.0f, 2.0f);
|
|
||||||
float IN_Z_OFFSET = mapRange(blend_speed_mph, zoom_start, zoom_end, 0.0f, 5.0f);
|
|
||||||
float PITCH_DEG = mapRange(blend_speed_mph, zoom_start, zoom_end, 0.0f, 5.0f);
|
|
||||||
|
|
||||||
input = Eigen::Vector3f(in_x + IN_X_OFFSET, in_y / IN_Y_OFFSET, in_z + IN_Z_OFFSET);
|
|
||||||
Eigen::AngleAxisf pitch_rot(PITCH_DEG * M_PI / 180.0f, Eigen::Vector3f::UnitY());
|
|
||||||
input = pitch_rot * input;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto pt = car_space_transform * input;
|
auto pt = car_space_transform * input;
|
||||||
bool normal_valid = (pt.z() > 1e-3f &&
|
*out = QPointF(pt.x() / pt.z(), pt.y() / pt.z());
|
||||||
std::isfinite(pt.x()) && std::isfinite(pt.y()));
|
|
||||||
QPointF normal_view;
|
|
||||||
if (normal_valid) {
|
|
||||||
normal_view = QPointF(pt.x() / pt.z(), pt.y() / pt.z());
|
|
||||||
}
|
|
||||||
|
|
||||||
const float base_scale_x = 20.0f;
|
|
||||||
const float base_scale_y = 15.0f;
|
|
||||||
const float y_offset = 450.0f;
|
|
||||||
|
|
||||||
float factor_scale_x = 0.0f;
|
|
||||||
if (blend_speed_mph > 0.0f) {
|
|
||||||
if (s->scene.visual_style_overhead_zoom == 1) {
|
|
||||||
factor_scale_x = mapRange(blend_speed_mph, 0.0f, 50.0f, 30.0f, 0.0f);
|
|
||||||
} else if (s->scene.visual_style_overhead_zoom == 2) {
|
|
||||||
factor_scale_x = mapRange(blend_speed_mph, 50.0f, 0.0f, 30.0f, 0.0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float scale_x = base_scale_x + factor_scale_x;
|
|
||||||
float scale_y = base_scale_y;
|
|
||||||
|
|
||||||
QPointF topdown_view(
|
|
||||||
clip_region.center().x() + in_y * scale_x,
|
|
||||||
(clip_region.bottom() - y_offset) - in_x * scale_y
|
|
||||||
);
|
|
||||||
|
|
||||||
if ((s->scene.visual_style_overhead == 1 || s->scene.visual_style_overhead == 2) && s->scene.visual_style != 0) {
|
|
||||||
static float blend = 0.0f;
|
|
||||||
static float target_blend = 0.0f;
|
|
||||||
static double last_t = millis_since_boot();
|
|
||||||
|
|
||||||
const bool inverted = (s->scene.visual_style_overhead == 2);
|
|
||||||
const float threshold = s->scene.visual_style_overhead_threshold;
|
|
||||||
const float hysteresis = 5.0f;
|
|
||||||
|
|
||||||
if (!inverted) {
|
|
||||||
if (target_blend < 0.5f && blend_speed_mph > threshold) {
|
|
||||||
target_blend = 1.0f;
|
|
||||||
} else if (target_blend > 0.5f && blend_speed_mph < threshold - hysteresis) {
|
|
||||||
target_blend = 0.0f;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (target_blend < 0.5f && blend_speed_mph < threshold) {
|
|
||||||
target_blend = 1.0f;
|
|
||||||
} else if (target_blend > 0.5f && blend_speed_mph > threshold + hysteresis) {
|
|
||||||
target_blend = 0.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double now = millis_since_boot();
|
|
||||||
double dt = (now - last_t) / 1000.0;
|
|
||||||
last_t = now;
|
|
||||||
|
|
||||||
const float transition_time = 1.50f;
|
|
||||||
float step = dt / transition_time;
|
|
||||||
|
|
||||||
if (blend < target_blend) {
|
|
||||||
blend = std::min(blend + step, target_blend);
|
|
||||||
} else if (blend > target_blend) {
|
|
||||||
blend = std::max(blend - step, target_blend);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!normal_valid) return false;
|
|
||||||
*out = QPointF(
|
|
||||||
(1 - blend) * normal_view.x() + blend * topdown_view.x(),
|
|
||||||
(1 - blend) * normal_view.y() + blend * topdown_view.y()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
if (!normal_valid) return false;
|
|
||||||
*out = normal_view;
|
|
||||||
}
|
|
||||||
|
|
||||||
return clip_region.contains(*out);
|
return clip_region.contains(*out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -196,7 +196,6 @@ mat4 CameraWidget::calcFrameMatrix() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CameraWidget::paintGL() {
|
void CameraWidget::paintGL() {
|
||||||
auto *s = uiState();
|
|
||||||
glClearColor(bg.redF(), bg.greenF(), bg.blueF(), bg.alphaF());
|
glClearColor(bg.redF(), bg.greenF(), bg.blueF(), bg.alphaF());
|
||||||
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
@@ -249,9 +248,7 @@ void CameraWidget::paintGL() {
|
|||||||
|
|
||||||
glUniformMatrix4fv(program->uniformLocation("uTransform"), 1, GL_TRUE, frame_mat.v);
|
glUniformMatrix4fv(program->uniformLocation("uTransform"), 1, GL_TRUE, frame_mat.v);
|
||||||
glEnableVertexAttribArray(0);
|
glEnableVertexAttribArray(0);
|
||||||
if (s->scene.visual_style == 0) {
|
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (const void *)0);
|
||||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (const void *)0);
|
|
||||||
}
|
|
||||||
glDisableVertexAttribArray(0);
|
glDisableVertexAttribArray(0);
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|||||||
@@ -11,18 +11,6 @@ VisualsPanel::VisualsPanel(QWidget *parent) : QWidget(parent) {
|
|||||||
param_watcher = new ParamWatcher(this);
|
param_watcher = new ParamWatcher(this);
|
||||||
connect(param_watcher, &ParamWatcher::paramChanged, [=](const QString ¶m_name, const QString ¶m_value) {
|
connect(param_watcher, &ParamWatcher::paramChanged, [=](const QString ¶m_name, const QString ¶m_value) {
|
||||||
paramsRefresh();
|
paramsRefresh();
|
||||||
if (param_name == "VisualStyle") {
|
|
||||||
visual_style_value = param_value.toInt();
|
|
||||||
} else if (param_name == "VisualStyleOverhead") {
|
|
||||||
visual_style_overhead_value = param_value.toInt();
|
|
||||||
} else if (param_name == "VisualRadarTracks") {
|
|
||||||
bool radar_tracks_enabled = param_value.toInt() != 0;
|
|
||||||
visual_radar_tracks_delay_settings->setVisible(radar_tracks_enabled);
|
|
||||||
}
|
|
||||||
visual_style_zoom_settings->setVisible(visual_style_value != 0);
|
|
||||||
visual_style_overhead_settings->setVisible(visual_style_value != 0);
|
|
||||||
visual_style_overhead_zoom_settings->setVisible(visual_style_value != 0 && visual_style_overhead_value != 0);
|
|
||||||
visual_style_overhead_threshold_settings->setVisible(visual_style_value != 0 && visual_style_overhead_value != 0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
main_layout = new QStackedLayout(this);
|
main_layout = new QStackedLayout(this);
|
||||||
@@ -102,13 +90,6 @@ VisualsPanel::VisualsPanel(QWidget *parent) : QWidget(parent) {
|
|||||||
"",
|
"",
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"VisualRadarTracks",
|
|
||||||
tr("Show Radar Tracks"),
|
|
||||||
tr("Shows what the cars radar sees."),
|
|
||||||
"",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add regular toggles first
|
// Add regular toggles first
|
||||||
@@ -135,111 +116,6 @@ VisualsPanel::VisualsPanel(QWidget *parent) : QWidget(parent) {
|
|||||||
param_watcher->addParam(param);
|
param_watcher->addParam(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visuals: Radar Tracks Delay
|
|
||||||
visual_radar_tracks_delay_settings = new OptionControlSP("VisualRadarTracksDelay", tr("Adjust Visual Radar Tracks Delay"),
|
|
||||||
tr("Delays radar tracks to better match what you see through the camera."),
|
|
||||||
"", {0, 100}, 10, false, nullptr, true);
|
|
||||||
|
|
||||||
connect(visual_radar_tracks_delay_settings, &OptionControlSP::updateLabels, [=]() {
|
|
||||||
float radar_tracks_delay_value = QString::fromStdString(params.get("VisualRadarTracksDelay")).toFloat();
|
|
||||||
visual_radar_tracks_delay_settings->setLabel(QString::number(radar_tracks_delay_value, 'f', 1) + " s");
|
|
||||||
});
|
|
||||||
|
|
||||||
float radar_tracks_delay_value = QString::fromStdString(params.get("VisualRadarTracksDelay")).toFloat();
|
|
||||||
visual_radar_tracks_delay_settings->setLabel(QString::number(radar_tracks_delay_value, 'f', 1) + " s");
|
|
||||||
|
|
||||||
list->addItem(visual_radar_tracks_delay_settings);
|
|
||||||
|
|
||||||
// Wide Cam
|
|
||||||
std::vector<QString> visual_wide_cam_settings_texts{tr("Auto"), tr("On"), tr("Off")};
|
|
||||||
visual_wide_cam_settings = new ButtonParamControlSP(
|
|
||||||
"VisualWideCam", tr("Wide Cam"), tr("Override the wide cam view regardless of experimental mode status."),
|
|
||||||
"",
|
|
||||||
visual_wide_cam_settings_texts,
|
|
||||||
250);
|
|
||||||
list->addItem(visual_wide_cam_settings);
|
|
||||||
|
|
||||||
// Visual Style
|
|
||||||
std::vector<QString> visual_style_settings_texts{tr("Default"), tr("Minimal"), tr("Vision")};
|
|
||||||
visual_style_settings = new ButtonParamControlSP(
|
|
||||||
"VisualStyle", tr("Visual Style"),
|
|
||||||
tr(
|
|
||||||
"Switch between different on-road visualization layouts."
|
|
||||||
"<ul style='margin-left: 10px; margin-top: 4px;'>"
|
|
||||||
"<li><b>Default:</b> Standard OpenPilot layout with camera and path view.</li>"
|
|
||||||
"<li><b>Minimal:</b> Clean interface without camera feed or extra elements.</li>"
|
|
||||||
"<li><b>Vision:</b> Experimental layout that focuses on model perception and environment.</li>"
|
|
||||||
"</ul>"
|
|
||||||
),
|
|
||||||
"",
|
|
||||||
visual_style_settings_texts,
|
|
||||||
380);
|
|
||||||
list->addItem(visual_style_settings);
|
|
||||||
|
|
||||||
// Visual Style Zoom
|
|
||||||
std::vector<QString> visual_style_zoom_settings_texts{tr("Disabled"), tr("Enabled"), tr("Inverted")};
|
|
||||||
visual_style_zoom_settings = new ButtonParamControlSP(
|
|
||||||
"VisualStyleZoom", tr("Visual Style Zoom"),
|
|
||||||
tr(
|
|
||||||
"Enables dynamic zooming based on driving speed in the selected visual style."
|
|
||||||
"<ul style='margin-left: 10px; margin-top: 4px;'>"
|
|
||||||
"<li><b>Disabled:</b> Keeps the zoom fixed.</li>"
|
|
||||||
"<li><b>Enabled:</b> Zooms in at low speed and out at high speed.</li>"
|
|
||||||
"<li><b>Inverted:</b> Reverses the zoom behavior.</li>"
|
|
||||||
"</ul>"
|
|
||||||
),
|
|
||||||
"",
|
|
||||||
visual_style_zoom_settings_texts,
|
|
||||||
380);
|
|
||||||
list->addItem(visual_style_zoom_settings);
|
|
||||||
|
|
||||||
// Visual Style Overhead
|
|
||||||
std::vector<QString> visual_style_overhead_settings_texts{tr("Disabled"), tr("Enabled"), tr("Inverted")};
|
|
||||||
visual_style_overhead_settings = new ButtonParamControlSP(
|
|
||||||
"VisualStyleOverhead", tr("Visual Style Overhead"),
|
|
||||||
tr(
|
|
||||||
"Toggles an overhead (top-down) camera view for a 2D-style perspective."
|
|
||||||
"<ul style='margin-left: 10px; margin-top: 4px;'>"
|
|
||||||
"<li><b>Disabled:</b> Keeps the standard forward 3D view.</li>"
|
|
||||||
"<li><b>Enabled:</b> Switches to overhead view when active.</li>"
|
|
||||||
"<li><b>Inverted:</b> Reverses when the transition happens.</li>"
|
|
||||||
"</ul>"
|
|
||||||
),
|
|
||||||
"",
|
|
||||||
visual_style_overhead_settings_texts,
|
|
||||||
380);
|
|
||||||
list->addItem(visual_style_overhead_settings);
|
|
||||||
|
|
||||||
// Visual Style Overhead Zoom
|
|
||||||
std::vector<QString> visual_style_overhead_zoom_settings_texts{tr("Disabled"), tr("Enabled"), tr("Inverted")};
|
|
||||||
visual_style_overhead_zoom_settings = new ButtonParamControlSP(
|
|
||||||
"VisualStyleOverheadZoom", tr("Visual Style Overhead Zoom"),
|
|
||||||
tr(
|
|
||||||
"Controls zooming behavior while in overhead mode."
|
|
||||||
"<ul style='margin-left: 10px; margin-top: 4px;'>"
|
|
||||||
"<li><b>Disabled:</b> Keeps a fixed zoom level in overhead mode.</li>"
|
|
||||||
"<li><b>Enabled:</b> Zooms dynamically based on speed while overhead.</li>"
|
|
||||||
"<li><b>Inverted:</b> Opposite zoom direction.</li>"
|
|
||||||
"</ul>"
|
|
||||||
),
|
|
||||||
"",
|
|
||||||
visual_style_overhead_zoom_settings_texts,
|
|
||||||
380);
|
|
||||||
list->addItem(visual_style_overhead_zoom_settings);
|
|
||||||
|
|
||||||
// Visual Style Overhead Threshold
|
|
||||||
visual_style_overhead_threshold_settings = new OptionControlSP(
|
|
||||||
"VisualStyleOverheadThreshold", tr("Visual Style Overhead Threshold"),
|
|
||||||
tr("Sets the speed (in mph) where the display transitions between normal and overhead view."),
|
|
||||||
"", {10, 80}, 5, false, nullptr, false);
|
|
||||||
auto updateThresholdLabel = [=]() {
|
|
||||||
int mph = QString::fromStdString(params.get("VisualStyleOverheadThreshold")).toInt();
|
|
||||||
visual_style_overhead_threshold_settings->setLabel(QString("%1 mph").arg(mph));
|
|
||||||
};
|
|
||||||
connect(visual_style_overhead_threshold_settings, &OptionControlSP::updateLabels, updateThresholdLabel);
|
|
||||||
updateThresholdLabel();
|
|
||||||
list->addItem(visual_style_overhead_threshold_settings);
|
|
||||||
|
|
||||||
// Visuals: Display Metrics below Chevron
|
// Visuals: Display Metrics below Chevron
|
||||||
std::vector<QString> chevron_info_settings_texts{tr("Off"), tr("Distance"), tr("Speed"), tr("Time"), tr("All")};
|
std::vector<QString> chevron_info_settings_texts{tr("Off"), tr("Distance"), tr("Speed"), tr("Time"), tr("All")};
|
||||||
chevron_info_settings = new ButtonParamControlSP(
|
chevron_info_settings = new ButtonParamControlSP(
|
||||||
@@ -260,19 +136,6 @@ VisualsPanel::VisualsPanel(QWidget *parent) : QWidget(parent) {
|
|||||||
380);
|
380);
|
||||||
list->addItem(dev_ui_settings);
|
list->addItem(dev_ui_settings);
|
||||||
|
|
||||||
bool radar_tracks_enabled = QString::fromStdString(params.get("VisualRadarTracks")).toInt() != 0;
|
|
||||||
visual_radar_tracks_delay_settings->setVisible(radar_tracks_enabled);
|
|
||||||
param_watcher->addParam("VisualRadarTracks");
|
|
||||||
|
|
||||||
visual_style_value = QString::fromStdString(params.get("VisualStyle")).toInt();
|
|
||||||
visual_style_overhead_value = QString::fromStdString(params.get("VisualStyleOverhead")).toInt();
|
|
||||||
visual_style_zoom_settings->setVisible(visual_style_value != 0);
|
|
||||||
visual_style_overhead_settings->setVisible(visual_style_value != 0);
|
|
||||||
visual_style_overhead_zoom_settings->setVisible(visual_style_value != 0 && visual_style_overhead_value != 0);
|
|
||||||
visual_style_overhead_threshold_settings->setVisible(visual_style_value != 0 && visual_style_overhead_value != 0);
|
|
||||||
param_watcher->addParam("VisualStyle");
|
|
||||||
param_watcher->addParam("VisualStyleOverhead");
|
|
||||||
|
|
||||||
sunnypilotScroller = new ScrollViewSP(list, this);
|
sunnypilotScroller = new ScrollViewSP(list, this);
|
||||||
vlayout->addWidget(sunnypilotScroller);
|
vlayout->addWidget(sunnypilotScroller);
|
||||||
|
|
||||||
@@ -328,19 +191,4 @@ void VisualsPanel::paramsRefresh() {
|
|||||||
if (dev_ui_settings) {
|
if (dev_ui_settings) {
|
||||||
dev_ui_settings->refresh();
|
dev_ui_settings->refresh();
|
||||||
}
|
}
|
||||||
if (visual_wide_cam_settings) {
|
|
||||||
visual_wide_cam_settings->refresh();
|
|
||||||
}
|
|
||||||
if (visual_style_settings) {
|
|
||||||
visual_style_settings->refresh();
|
|
||||||
}
|
|
||||||
if (visual_style_zoom_settings) {
|
|
||||||
visual_style_zoom_settings->refresh();
|
|
||||||
}
|
|
||||||
if (visual_style_overhead_settings) {
|
|
||||||
visual_style_overhead_settings->refresh();
|
|
||||||
}
|
|
||||||
if (visual_style_overhead_zoom_settings) {
|
|
||||||
visual_style_overhead_zoom_settings->refresh();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,14 +32,4 @@ protected:
|
|||||||
ButtonParamControlSP *dev_ui_settings;
|
ButtonParamControlSP *dev_ui_settings;
|
||||||
|
|
||||||
bool has_longitudinal_control = false;
|
bool has_longitudinal_control = false;
|
||||||
|
|
||||||
OptionControlSP *visual_radar_tracks_delay_settings;
|
|
||||||
ButtonParamControlSP *visual_wide_cam_settings;
|
|
||||||
int visual_style_value = 0;
|
|
||||||
int visual_style_overhead_value = 0;
|
|
||||||
ButtonParamControlSP *visual_style_settings;
|
|
||||||
ButtonParamControlSP *visual_style_zoom_settings;
|
|
||||||
ButtonParamControlSP *visual_style_overhead_settings;
|
|
||||||
ButtonParamControlSP *visual_style_overhead_zoom_settings;
|
|
||||||
OptionControlSP *visual_style_overhead_threshold_settings;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,12 +8,6 @@
|
|||||||
#include "selfdrive/ui/sunnypilot/qt/onroad/model.h"
|
#include "selfdrive/ui/sunnypilot/qt/onroad/model.h"
|
||||||
|
|
||||||
|
|
||||||
void ModelRendererSP::drawRadarPoint(QPainter &painter, const QPointF &pos, float v_rel, float radius) {
|
|
||||||
painter.setBrush(QColor(255, 255, 255, 200));
|
|
||||||
painter.setPen(Qt::NoPen);
|
|
||||||
painter.drawEllipse(pos, radius, radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModelRendererSP::update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead) {
|
void ModelRendererSP::update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead) {
|
||||||
ModelRenderer::update_model(model, lead);
|
ModelRenderer::update_model(model, lead);
|
||||||
const auto &model_position = model.getPosition();
|
const auto &model_position = model.getPosition();
|
||||||
@@ -73,26 +67,6 @@ void ModelRendererSP::draw(QPainter &painter, const QRect &surface_rect) {
|
|||||||
const bool right_blindspot = car_state.getRightBlindspot();
|
const bool right_blindspot = car_state.getRightBlindspot();
|
||||||
drawBlindspot(painter, surface_rect, left_blindspot, right_blindspot);
|
drawBlindspot(painter, surface_rect, left_blindspot, right_blindspot);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->scene.visual_radar_tracks) {
|
|
||||||
if (sm.alive("liveTracks") && sm.rcv_frame("liveTracks") >= s->scene.started_frame) {
|
|
||||||
const auto &tracks = sm["liveTracks"].getLiveTracks().getPoints();
|
|
||||||
for (const auto &track : tracks) {
|
|
||||||
if (!std::isfinite(track.getDRel()) || !std::isfinite(track.getYRel())) continue;
|
|
||||||
float t_lag = s->scene.visual_radar_tracks_delay;
|
|
||||||
float d_pred = track.getDRel();
|
|
||||||
float y_pred = track.getYRel();
|
|
||||||
if (t_lag > 0.0f) {
|
|
||||||
d_pred += track.getVRel() * t_lag + 0.5f * track.getARel() * t_lag * t_lag;
|
|
||||||
}
|
|
||||||
QPointF screen_pt;
|
|
||||||
if (mapToScreen(d_pred, -y_pred, path_offset_z, &screen_pt)) {
|
|
||||||
drawRadarPoint(painter, screen_pt, track.getVRel(), 10.0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
drawLeadStatus(painter, surface_rect.height(), surface_rect.width());
|
drawLeadStatus(painter, surface_rect.height(), surface_rect.width());
|
||||||
|
|
||||||
painter.restore();
|
painter.restore();
|
||||||
|
|||||||
@@ -28,6 +28,4 @@ private:
|
|||||||
|
|
||||||
// Lead status animation
|
// Lead status animation
|
||||||
float lead_status_alpha = 0.0f;
|
float lead_status_alpha = 0.0f;
|
||||||
|
|
||||||
void drawRadarPoint(QPainter &painter, const QPointF &pos, float v_rel, float radius = 10.0f);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ UIStateSP::UIStateSP(QObject *parent) : UIState(parent) {
|
|||||||
"wideRoadCameraState", "managerState", "selfdriveState", "longitudinalPlan",
|
"wideRoadCameraState", "managerState", "selfdriveState", "longitudinalPlan",
|
||||||
"modelManagerSP", "selfdriveStateSP", "longitudinalPlanSP", "backupManagerSP",
|
"modelManagerSP", "selfdriveStateSP", "longitudinalPlanSP", "backupManagerSP",
|
||||||
"carControl", "gpsLocationExternal", "gpsLocation", "liveTorqueParameters",
|
"carControl", "gpsLocationExternal", "gpsLocation", "liveTorqueParameters",
|
||||||
"carStateSP", "liveParameters", "liveMapDataSP", "carParamsSP", "liveTracks"
|
"carStateSP", "liveParameters", "liveMapDataSP", "carParamsSP"
|
||||||
});
|
});
|
||||||
|
|
||||||
// update timer
|
// update timer
|
||||||
@@ -44,14 +44,6 @@ UIStateSP::UIStateSP(QObject *parent) : UIState(parent) {
|
|||||||
});
|
});
|
||||||
param_watcher->addParam("DevUIInfo");
|
param_watcher->addParam("DevUIInfo");
|
||||||
param_watcher->addParam("StandstillTimer");
|
param_watcher->addParam("StandstillTimer");
|
||||||
param_watcher->addParam("VisualRadarTracks");
|
|
||||||
param_watcher->addParam("VisualRadarTracksDelay");
|
|
||||||
param_watcher->addParam("VisualWideCam");
|
|
||||||
param_watcher->addParam("VisualStyle");
|
|
||||||
param_watcher->addParam("VisualStyleZoom");
|
|
||||||
param_watcher->addParam("VisualStyleOverhead");
|
|
||||||
param_watcher->addParam("VisualStyleOverheadZoom");
|
|
||||||
param_watcher->addParam("VisualStyleOverheadThreshold");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method overrides completely the update method from the parent class intentionally.
|
// This method overrides completely the update method from the parent class intentionally.
|
||||||
@@ -84,17 +76,6 @@ void ui_update_params_sp(UIStateSP *s) {
|
|||||||
s->scene.chevron_info = std::atoi(params.get("ChevronInfo").c_str());
|
s->scene.chevron_info = std::atoi(params.get("ChevronInfo").c_str());
|
||||||
s->scene.blindspot_ui = params.getBool("BlindSpot");
|
s->scene.blindspot_ui = params.getBool("BlindSpot");
|
||||||
s->scene.rainbow_mode = params.getBool("RainbowMode");
|
s->scene.rainbow_mode = params.getBool("RainbowMode");
|
||||||
|
|
||||||
s->scene.visual_radar_tracks = QString::fromStdString(params.get("VisualRadarTracks")).toInt();
|
|
||||||
s->scene.visual_radar_tracks_delay = QString::fromStdString(params.get("VisualRadarTracksDelay")).toFloat();
|
|
||||||
|
|
||||||
s->scene.visual_wide_cam = QString::fromStdString(params.get("VisualWideCam")).toInt();
|
|
||||||
|
|
||||||
s->scene.visual_style = QString::fromStdString(params.get("VisualStyle")).toInt();
|
|
||||||
s->scene.visual_style_zoom = QString::fromStdString(params.get("VisualStyleZoom")).toInt();
|
|
||||||
s->scene.visual_style_overhead = QString::fromStdString(params.get("VisualStyleOverhead")).toInt();
|
|
||||||
s->scene.visual_style_overhead_zoom = QString::fromStdString(params.get("VisualStyleOverheadZoom")).toInt();
|
|
||||||
s->scene.visual_style_overhead_threshold = QString::fromStdString(params.get("VisualStyleOverheadThreshold")).toInt();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIStateSP::reset_onroad_sleep_timer(OnroadTimerStatusToggle toggleTimerStatus) {
|
void UIStateSP::reset_onroad_sleep_timer(OnroadTimerStatusToggle toggleTimerStatus) {
|
||||||
|
|||||||
@@ -21,12 +21,4 @@ typedef struct UISceneSP : UIScene {
|
|||||||
int chevron_info;
|
int chevron_info;
|
||||||
bool blindspot_ui;
|
bool blindspot_ui;
|
||||||
bool rainbow_mode;
|
bool rainbow_mode;
|
||||||
int visual_radar_tracks = 0;
|
|
||||||
float visual_radar_tracks_delay = 0;
|
|
||||||
int visual_wide_cam = 0;
|
|
||||||
int visual_style = 0;
|
|
||||||
int visual_style_zoom = 0;
|
|
||||||
int visual_style_overhead = 0;
|
|
||||||
int visual_style_overhead_zoom = 0;
|
|
||||||
int visual_style_overhead_threshold = 20.0;
|
|
||||||
} UISceneSP;
|
} UISceneSP;
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#define SUNNYPILOT_VERSION "2025.003.000"
|
#define SUNNYPILOT_VERSION "2025.002.000"
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
70406ab4dd66d0e384734a8a56632ae4a62bc9670c2e630a0f71588c4e212cd8
|
5584d697233d147e0b6402e485b7cbf8fdddb70bde4b9e3b2f6919ed5f69475f
|
||||||
@@ -15,8 +15,6 @@ from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit.helpers import set_
|
|||||||
|
|
||||||
import openpilot.system.sentry as sentry
|
import openpilot.system.sentry as sentry
|
||||||
|
|
||||||
from sunnypilot.sunnylink.statsd import STATSLOGSP
|
|
||||||
|
|
||||||
|
|
||||||
def log_fingerprint(CP: structs.CarParams) -> None:
|
def log_fingerprint(CP: structs.CarParams) -> None:
|
||||||
if CP.carFingerprint == "MOCK":
|
if CP.carFingerprint == "MOCK":
|
||||||
@@ -102,9 +100,6 @@ def setup_interfaces(CI: CarInterfaceBase, params: Params = None) -> None:
|
|||||||
_initialize_torque_lateral_control(CI, CP, enforce_torque, nnlc_enabled)
|
_initialize_torque_lateral_control(CI, CP, enforce_torque, nnlc_enabled)
|
||||||
_cleanup_unsupported_params(CP, CP_SP)
|
_cleanup_unsupported_params(CP, CP_SP)
|
||||||
|
|
||||||
STATSLOGSP.raw('sunnypilot.car_params', CP.to_dict())
|
|
||||||
# STATSLOGSP.raw('sunnypilot_params.car_params_sp', CP_SP.to_dict()) # https://github.com/sunnypilot/opendbc/pull/361
|
|
||||||
|
|
||||||
|
|
||||||
def initialize_params(params) -> list[dict[str, Any]]:
|
def initialize_params(params) -> list[dict[str, Any]]:
|
||||||
keys: list = []
|
keys: list = []
|
||||||
|
|||||||
@@ -16,9 +16,8 @@ from functools import partial
|
|||||||
from openpilot.common.params import Params
|
from openpilot.common.params import Params
|
||||||
from openpilot.common.realtime import set_core_affinity
|
from openpilot.common.realtime import set_core_affinity
|
||||||
from openpilot.common.swaglog import cloudlog
|
from openpilot.common.swaglog import cloudlog
|
||||||
from openpilot.system.hardware.hw import Paths
|
|
||||||
from openpilot.system.athena.athenad import ws_send, jsonrpc_handler, \
|
from openpilot.system.athena.athenad import ws_send, jsonrpc_handler, \
|
||||||
recv_queue, UploadQueueCache, upload_queue, cur_upload_items, backoff, ws_manage, log_handler, start_local_proxy_shim, upload_handler, stat_handler
|
recv_queue, UploadQueueCache, upload_queue, cur_upload_items, backoff, ws_manage, log_handler, start_local_proxy_shim, upload_handler
|
||||||
from websocket import (ABNF, WebSocket, WebSocketException, WebSocketTimeoutException,
|
from websocket import (ABNF, WebSocket, WebSocketException, WebSocketTimeoutException,
|
||||||
create_connection, WebSocketConnectionClosedException)
|
create_connection, WebSocketConnectionClosedException)
|
||||||
|
|
||||||
@@ -34,6 +33,9 @@ SUNNYLINK_RECONNECT_TIMEOUT_S = 70 # FYI changing this will also would require
|
|||||||
DISALLOW_LOG_UPLOAD = threading.Event()
|
DISALLOW_LOG_UPLOAD = threading.Event()
|
||||||
|
|
||||||
params = Params()
|
params = Params()
|
||||||
|
sunnylink_dongle_id = params.get("SunnylinkDongleId")
|
||||||
|
sunnylink_api = SunnylinkApi(sunnylink_dongle_id)
|
||||||
|
|
||||||
|
|
||||||
def handle_long_poll(ws: WebSocket, exit_event: threading.Event | None) -> None:
|
def handle_long_poll(ws: WebSocket, exit_event: threading.Event | None) -> None:
|
||||||
cloudlog.info("sunnylinkd.handle_long_poll started")
|
cloudlog.info("sunnylinkd.handle_long_poll started")
|
||||||
@@ -49,7 +51,7 @@ def handle_long_poll(ws: WebSocket, exit_event: threading.Event | None) -> None:
|
|||||||
threading.Thread(target=ws_queue, args=(end_event,), name='ws_queue'),
|
threading.Thread(target=ws_queue, args=(end_event,), name='ws_queue'),
|
||||||
threading.Thread(target=upload_handler, args=(end_event,), name='upload_handler'),
|
threading.Thread(target=upload_handler, args=(end_event,), name='upload_handler'),
|
||||||
# threading.Thread(target=sunny_log_handler, args=(end_event, comma_prime_cellular_end_event), name='log_handler'),
|
# threading.Thread(target=sunny_log_handler, args=(end_event, comma_prime_cellular_end_event), name='log_handler'),
|
||||||
threading.Thread(target=stat_handler, args=(end_event, Paths.stats_sp_root(), True), name='stat_handler'),
|
# threading.Thread(target=stat_handler, args=(end_event,), name='stat_handler'),
|
||||||
] + [
|
] + [
|
||||||
threading.Thread(target=jsonrpc_handler, args=(end_event, partial(startLocalProxy, end_event),), name=f'worker_{x}')
|
threading.Thread(target=jsonrpc_handler, args=(end_event, partial(startLocalProxy, end_event),), name=f'worker_{x}')
|
||||||
for x in range(HANDLER_THREADS)
|
for x in range(HANDLER_THREADS)
|
||||||
@@ -130,8 +132,6 @@ def ws_ping(ws: WebSocket, end_event: threading.Event) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def ws_queue(end_event: threading.Event) -> None:
|
def ws_queue(end_event: threading.Event) -> None:
|
||||||
sunnylink_dongle_id = params.get("SunnylinkDongleId")
|
|
||||||
sunnylink_api = SunnylinkApi(sunnylink_dongle_id)
|
|
||||||
resume_requested = False
|
resume_requested = False
|
||||||
tries = 0
|
tries = 0
|
||||||
|
|
||||||
@@ -233,9 +233,6 @@ def saveParams(params_to_update: dict[str, str], compression: bool = False) -> N
|
|||||||
|
|
||||||
|
|
||||||
def startLocalProxy(global_end_event: threading.Event, remote_ws_uri: str, local_port: int) -> dict[str, int]:
|
def startLocalProxy(global_end_event: threading.Event, remote_ws_uri: str, local_port: int) -> dict[str, int]:
|
||||||
sunnylink_dongle_id = params.get("SunnylinkDongleId")
|
|
||||||
sunnylink_api = SunnylinkApi(sunnylink_dongle_id)
|
|
||||||
|
|
||||||
cloudlog.debug("athena.startLocalProxy.starting")
|
cloudlog.debug("athena.startLocalProxy.starting")
|
||||||
ws = create_connection(
|
ws = create_connection(
|
||||||
remote_ws_uri,
|
remote_ws_uri,
|
||||||
@@ -257,8 +254,6 @@ def main(exit_event: threading.Event = None):
|
|||||||
cloudlog.info("Waiting for sunnylink registration to complete")
|
cloudlog.info("Waiting for sunnylink registration to complete")
|
||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
|
|
||||||
sunnylink_dongle_id = params.get("SunnylinkDongleId")
|
|
||||||
sunnylink_api = SunnylinkApi(sunnylink_dongle_id)
|
|
||||||
UploadQueueCache.initialize(upload_queue)
|
UploadQueueCache.initialize(upload_queue)
|
||||||
|
|
||||||
ws_uri = f"{SUNNYLINK_ATHENA_HOST}"
|
ws_uri = f"{SUNNYLINK_ATHENA_HOST}"
|
||||||
|
|||||||
@@ -1,278 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import base64
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import threading
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
import zmq
|
|
||||||
import time
|
|
||||||
import uuid
|
|
||||||
from pathlib import Path
|
|
||||||
from collections import defaultdict
|
|
||||||
from datetime import datetime, UTC
|
|
||||||
|
|
||||||
from openpilot.common.params import Params
|
|
||||||
from cereal.messaging import SubMaster
|
|
||||||
from openpilot.system.hardware.hw import Paths
|
|
||||||
from openpilot.common.swaglog import cloudlog
|
|
||||||
from openpilot.system.hardware import HARDWARE
|
|
||||||
from openpilot.common.file_helpers import atomic_write_in_dir
|
|
||||||
from openpilot.system.version import get_build_metadata
|
|
||||||
from openpilot.system.loggerd.config import STATS_DIR_FILE_LIMIT, STATS_SOCKET, STATS_FLUSH_TIME_S
|
|
||||||
from openpilot.system.statsd import METRIC_TYPE, StatLogSP
|
|
||||||
from openpilot.common.realtime import Ratekeeper
|
|
||||||
|
|
||||||
STATSLOGSP = StatLogSP(intercept=False)
|
|
||||||
|
|
||||||
def sp_stats(end_event):
|
|
||||||
"""Collect sunnypilot-specific statistics and send as raw metrics."""
|
|
||||||
rk = Ratekeeper(.1, print_delay_threshold=None)
|
|
||||||
statlogsp = STATSLOGSP
|
|
||||||
params = Params()
|
|
||||||
|
|
||||||
def flatten_dict(d, parent_key='', sep='.'):
|
|
||||||
items = {}
|
|
||||||
if isinstance(d, dict):
|
|
||||||
for k, v in d.items():
|
|
||||||
new_key = f"{parent_key}{sep}{k}" if parent_key else k
|
|
||||||
items.update(flatten_dict(v, new_key, sep=sep))
|
|
||||||
elif isinstance(d, (list, tuple)):
|
|
||||||
for i, v in enumerate(d):
|
|
||||||
new_key = f"{parent_key}[{i}]"
|
|
||||||
items.update(flatten_dict(v, new_key, sep=sep))
|
|
||||||
else:
|
|
||||||
items[parent_key] = d
|
|
||||||
return items
|
|
||||||
|
|
||||||
# Collect sunnypilot parameters
|
|
||||||
stats_dict = {}
|
|
||||||
|
|
||||||
param_keys = [
|
|
||||||
'SunnylinkEnabled',
|
|
||||||
'AutoLaneChangeBsmDelay',
|
|
||||||
'AutoLaneChangeTimer',
|
|
||||||
'CarPlatformBundle',
|
|
||||||
'CurrentRoute',
|
|
||||||
'DevUIInfo',
|
|
||||||
'EnableCopyparty',
|
|
||||||
'IntelligentCruiseButtonManagement',
|
|
||||||
'QuietMode',
|
|
||||||
'RainbowMode',
|
|
||||||
'ShowAdvancedControls',
|
|
||||||
'Mads',
|
|
||||||
'MadsMainCruiseAllowed',
|
|
||||||
'MadsSteeringMode',
|
|
||||||
'MadsUnifiedEngagementMode',
|
|
||||||
'ModelManager_ActiveBundle',
|
|
||||||
'ModelManager_Favs',
|
|
||||||
'EnableSunnylinkUploader',
|
|
||||||
'SunnylinkEnabled',
|
|
||||||
'InstallDate',
|
|
||||||
'UptimeOffroad',
|
|
||||||
'UptimeOnroad',
|
|
||||||
]
|
|
||||||
|
|
||||||
while not end_event.is_set():
|
|
||||||
try:
|
|
||||||
for key in param_keys:
|
|
||||||
|
|
||||||
try:
|
|
||||||
value = params.get(key)
|
|
||||||
except Exception as e:
|
|
||||||
stats_dict[key] = e
|
|
||||||
continue
|
|
||||||
|
|
||||||
if value is None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if isinstance(value, (dict, list, tuple)):
|
|
||||||
stats_dict.update(flatten_dict(value, key))
|
|
||||||
else:
|
|
||||||
stats_dict[key] = value
|
|
||||||
|
|
||||||
if stats_dict:
|
|
||||||
statlogsp.raw('sunnypilot.device_params', stats_dict)
|
|
||||||
except Exception as e:
|
|
||||||
cloudlog.error(f"Exception {e}")
|
|
||||||
finally:
|
|
||||||
rk.keep_time()
|
|
||||||
|
|
||||||
|
|
||||||
def stats_main(end_event):
|
|
||||||
comma_dongle_id = Params().get("DongleId")
|
|
||||||
sunnylink_dongle_id = Params().get("SunnylinkDongleId")
|
|
||||||
|
|
||||||
def get_influxdb_line(measurement: str, value: float | dict[str, float], timestamp: datetime, tags: dict) -> str:
|
|
||||||
res = f"{measurement}"
|
|
||||||
for k, v in tags.items():
|
|
||||||
res += f",{k}={str(v)}"
|
|
||||||
res += " "
|
|
||||||
|
|
||||||
if isinstance(value, float):
|
|
||||||
value = {'value': value}
|
|
||||||
|
|
||||||
for k, v in value.items():
|
|
||||||
res += f"{k}={str(v)},"
|
|
||||||
|
|
||||||
res += f"sunnylink_dongle_id=\"{sunnylink_dongle_id}\",comma_dongle_id=\"{comma_dongle_id}\" {int(timestamp.timestamp() * 1e9)}\n"
|
|
||||||
return res
|
|
||||||
|
|
||||||
def get_influxdb_line_raw(measurement: str, value: dict, timestamp: datetime, tags: dict) -> str:
|
|
||||||
res = f"{measurement}"
|
|
||||||
try:
|
|
||||||
custom_tags = ""
|
|
||||||
for k, v in tags.items():
|
|
||||||
custom_tags += f",{k}={str(v)}"
|
|
||||||
res += custom_tags
|
|
||||||
|
|
||||||
fields = ""
|
|
||||||
for k, v in value.items():
|
|
||||||
# Skip complex types - only keep simple scalar values
|
|
||||||
if isinstance(v, (dict, list, bytes, bytearray)):
|
|
||||||
continue
|
|
||||||
|
|
||||||
fields += f"{k}={json.dumps(v)},"
|
|
||||||
|
|
||||||
res += f" {fields}"
|
|
||||||
except Exception as e:
|
|
||||||
cloudlog.error(f"Unable to get influxdb line for: {value}")
|
|
||||||
res += f",invalid=1 reason={e},"
|
|
||||||
|
|
||||||
res += f"sunnylink_dongle_id=\"{sunnylink_dongle_id}\",comma_dongle_id=\"{comma_dongle_id}\" {int(timestamp.timestamp() * 1e9)}\n"
|
|
||||||
return res
|
|
||||||
|
|
||||||
# open statistics socket
|
|
||||||
ctx = zmq.Context.instance()
|
|
||||||
sock = ctx.socket(zmq.PULL)
|
|
||||||
sock.bind(f"{STATS_SOCKET}_sp")
|
|
||||||
|
|
||||||
STATS_DIR = Paths.stats_sp_root()
|
|
||||||
|
|
||||||
# initialize stats directory
|
|
||||||
Path(STATS_DIR).mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
build_metadata = get_build_metadata()
|
|
||||||
|
|
||||||
# initialize tags
|
|
||||||
tags = {
|
|
||||||
'started': False,
|
|
||||||
'version': build_metadata.openpilot.version,
|
|
||||||
'branch': build_metadata.channel,
|
|
||||||
'dirty': build_metadata.openpilot.is_dirty,
|
|
||||||
'origin': build_metadata.openpilot.git_normalized_origin,
|
|
||||||
'deviceType': HARDWARE.get_device_type(),
|
|
||||||
}
|
|
||||||
|
|
||||||
# subscribe to deviceState for started state
|
|
||||||
sm = SubMaster(['deviceState'])
|
|
||||||
|
|
||||||
idx = 0
|
|
||||||
boot_uid = str(uuid.uuid4())[:8]
|
|
||||||
last_flush_time = time.monotonic()
|
|
||||||
gauges = {}
|
|
||||||
samples: dict[str, list[float]] = defaultdict(list)
|
|
||||||
raws: dict = defaultdict()
|
|
||||||
try:
|
|
||||||
while not end_event.is_set():
|
|
||||||
started_prev = sm['deviceState'].started
|
|
||||||
sm.update()
|
|
||||||
|
|
||||||
# Update metrics
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
metric = sock.recv_string(zmq.NOBLOCK)
|
|
||||||
try:
|
|
||||||
metric_type = metric.split('|')[1]
|
|
||||||
metric_name = metric.split(':')[0]
|
|
||||||
metric_value_raw = metric.split('|')[0].split(':')[1]
|
|
||||||
|
|
||||||
if metric_type == METRIC_TYPE.GAUGE:
|
|
||||||
metric_value = float(metric_value_raw)
|
|
||||||
gauges[metric_name] = metric_value
|
|
||||||
elif metric_type == METRIC_TYPE.SAMPLE:
|
|
||||||
metric_value = float(metric_value_raw)
|
|
||||||
samples[metric_name].append(metric_value)
|
|
||||||
elif metric_type == METRIC_TYPE.RAW:
|
|
||||||
raws[metric_name] = metric_value_raw
|
|
||||||
else:
|
|
||||||
cloudlog.event("unknown metric type", metric_type=metric_type)
|
|
||||||
except Exception:
|
|
||||||
print(traceback.format_exc())
|
|
||||||
cloudlog.event("malformed metric", metric=metric)
|
|
||||||
except zmq.error.Again:
|
|
||||||
break
|
|
||||||
|
|
||||||
# flush when started state changes or after FLUSH_TIME_S
|
|
||||||
if (time.monotonic() > last_flush_time + STATS_FLUSH_TIME_S) or (sm['deviceState'].started != started_prev):
|
|
||||||
result = ""
|
|
||||||
current_time = datetime.now(UTC)
|
|
||||||
tags['started'] = sm['deviceState'].started
|
|
||||||
|
|
||||||
for key, value in raws.items():
|
|
||||||
decoded_value = json.loads(base64.b64decode(value).decode('utf-8'))
|
|
||||||
result += get_influxdb_line_raw(key, decoded_value, current_time, tags)
|
|
||||||
|
|
||||||
for key, value in gauges.items():
|
|
||||||
result += get_influxdb_line(f"gauge.{key}", value, current_time, tags)
|
|
||||||
|
|
||||||
for key, values in samples.items():
|
|
||||||
values.sort()
|
|
||||||
sample_count = len(values)
|
|
||||||
sample_sum = sum(values)
|
|
||||||
|
|
||||||
stats = {
|
|
||||||
'count': sample_count,
|
|
||||||
'min': values[0],
|
|
||||||
'max': values[-1],
|
|
||||||
'mean': sample_sum / sample_count,
|
|
||||||
}
|
|
||||||
for percentile in [0.05, 0.5, 0.95]:
|
|
||||||
value = values[int(round(percentile * (sample_count - 1)))]
|
|
||||||
stats[f"p{int(percentile * 100)}"] = value
|
|
||||||
|
|
||||||
result += get_influxdb_line(f"sample.{key}", stats, current_time, tags)
|
|
||||||
|
|
||||||
# clear intermediate data
|
|
||||||
gauges.clear()
|
|
||||||
samples.clear()
|
|
||||||
last_flush_time = time.monotonic()
|
|
||||||
|
|
||||||
# check that we aren't filling up the drive
|
|
||||||
if len(os.listdir(STATS_DIR)) < STATS_DIR_FILE_LIMIT:
|
|
||||||
if len(result) > 0:
|
|
||||||
stats_path = os.path.join(STATS_DIR, f"{boot_uid}_{idx}")
|
|
||||||
with atomic_write_in_dir(stats_path) as f:
|
|
||||||
f.write(result)
|
|
||||||
idx += 1
|
|
||||||
else:
|
|
||||||
cloudlog.error("stats dir full")
|
|
||||||
finally:
|
|
||||||
sock.close()
|
|
||||||
ctx.term()
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
rk = Ratekeeper(1, print_delay_threshold=None)
|
|
||||||
end_event = threading.Event()
|
|
||||||
|
|
||||||
threads = [
|
|
||||||
threading.Thread(target=stats_main, args=(end_event,)),
|
|
||||||
threading.Thread(target=sp_stats, args=(end_event,)),
|
|
||||||
]
|
|
||||||
|
|
||||||
for t in threads:
|
|
||||||
t.start()
|
|
||||||
|
|
||||||
try:
|
|
||||||
while all(t.is_alive() for t in threads):
|
|
||||||
rk.keep_time()
|
|
||||||
finally:
|
|
||||||
end_event.set()
|
|
||||||
|
|
||||||
for t in threads:
|
|
||||||
t.join()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -744,40 +744,26 @@ def log_handler(end_event: threading.Event, log_attr_name=LOG_ATTR_NAME) -> None
|
|||||||
cloudlog.exception("athena.log_handler.exception")
|
cloudlog.exception("athena.log_handler.exception")
|
||||||
|
|
||||||
|
|
||||||
def stat_handler(end_event: threading.Event, stats_dir=None, is_sunnylink=False) -> None:
|
def stat_handler(end_event: threading.Event) -> None:
|
||||||
stats_dir = stats_dir or Paths.stats_root()
|
STATS_DIR = Paths.stats_root()
|
||||||
last_scan = 0.0
|
last_scan = 0.0
|
||||||
|
|
||||||
while not end_event.is_set():
|
while not end_event.is_set():
|
||||||
curr_scan = time.monotonic()
|
curr_scan = time.monotonic()
|
||||||
try:
|
try:
|
||||||
if curr_scan - last_scan > 10:
|
if curr_scan - last_scan > 10:
|
||||||
stat_filenames = list(filter(lambda name: not name.startswith(tempfile.gettempprefix()), os.listdir(stats_dir)))
|
stat_filenames = list(filter(lambda name: not name.startswith(tempfile.gettempprefix()), os.listdir(STATS_DIR)))
|
||||||
if len(stat_filenames) > 0:
|
if len(stat_filenames) > 0:
|
||||||
stat_path = os.path.join(stats_dir, stat_filenames[0])
|
stat_path = os.path.join(STATS_DIR, stat_filenames[0])
|
||||||
with open(stat_path) as f:
|
with open(stat_path) as f:
|
||||||
payload = f.read()
|
|
||||||
is_compressed = False
|
|
||||||
|
|
||||||
# Log the current size of the file
|
|
||||||
if is_sunnylink:
|
|
||||||
# Compress and encode the data if it exceeds the maximum size
|
|
||||||
compressed_data = gzip.compress(payload.encode())
|
|
||||||
payload = base64.b64encode(compressed_data).decode()
|
|
||||||
is_compressed = True
|
|
||||||
|
|
||||||
jsonrpc = {
|
jsonrpc = {
|
||||||
"method": "storeStats",
|
"method": "storeStats",
|
||||||
"params": {
|
"params": {
|
||||||
"stats": payload
|
"stats": f.read()
|
||||||
},
|
},
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": stat_filenames[0]
|
"id": stat_filenames[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_sunnylink and is_compressed:
|
|
||||||
jsonrpc["params"]["compressed"] = is_compressed
|
|
||||||
|
|
||||||
low_priority_send_queue.put_nowait(json.dumps(jsonrpc))
|
low_priority_send_queue.put_nowait(json.dumps(jsonrpc))
|
||||||
os.remove(stat_path)
|
os.remove(stat_path)
|
||||||
last_scan = curr_scan
|
last_scan = curr_scan
|
||||||
|
|||||||
@@ -55,13 +55,6 @@ class Paths:
|
|||||||
else:
|
else:
|
||||||
return "/data/stats/"
|
return "/data/stats/"
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def stats_sp_root() -> str:
|
|
||||||
if PC:
|
|
||||||
return str(Path(Paths.comma_home()) / "stats")
|
|
||||||
else:
|
|
||||||
return "/data/stats_sp/"
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def config_root() -> str:
|
def config_root() -> str:
|
||||||
if PC:
|
if PC:
|
||||||
|
|||||||
@@ -164,7 +164,6 @@ procs = [
|
|||||||
# sunnylink <3
|
# sunnylink <3
|
||||||
DaemonProcess("manage_sunnylinkd", "sunnypilot.sunnylink.athena.manage_sunnylinkd", "SunnylinkdPid"),
|
DaemonProcess("manage_sunnylinkd", "sunnypilot.sunnylink.athena.manage_sunnylinkd", "SunnylinkdPid"),
|
||||||
PythonProcess("sunnylink_registration_manager", "sunnypilot.sunnylink.registration_manager", sunnylink_need_register_shim),
|
PythonProcess("sunnylink_registration_manager", "sunnypilot.sunnylink.registration_manager", sunnylink_need_register_shim),
|
||||||
PythonProcess("statsd_sp", "sunnypilot.sunnylink.statsd", and_(always_run, sunnylink_ready_shim)),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# sunnypilot
|
# sunnypilot
|
||||||
|
|||||||
+4
-55
@@ -1,15 +1,11 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import base64
|
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
from decimal import Decimal
|
|
||||||
|
|
||||||
import zmq
|
import zmq
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from datetime import datetime, UTC, date
|
from datetime import datetime, UTC
|
||||||
from typing import NoReturn
|
from typing import NoReturn
|
||||||
|
|
||||||
from openpilot.common.params import Params
|
from openpilot.common.params import Params
|
||||||
@@ -25,21 +21,18 @@ from openpilot.system.loggerd.config import STATS_DIR_FILE_LIMIT, STATS_SOCKET,
|
|||||||
class METRIC_TYPE:
|
class METRIC_TYPE:
|
||||||
GAUGE = 'g'
|
GAUGE = 'g'
|
||||||
SAMPLE = 'sa'
|
SAMPLE = 'sa'
|
||||||
RAW = 'r'
|
|
||||||
|
|
||||||
|
|
||||||
class StatLog:
|
class StatLog:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.pid = None
|
self.pid = None
|
||||||
self.zctx = None
|
self.zctx = None
|
||||||
self.sock = None
|
self.sock = None
|
||||||
self.stats_socket = STATS_SOCKET
|
|
||||||
|
|
||||||
def connect(self) -> None:
|
def connect(self) -> None:
|
||||||
self.zctx = zmq.Context.instance() or zmq.Context()
|
self.zctx = zmq.Context()
|
||||||
self.sock = self.zctx.socket(zmq.PUSH)
|
self.sock = self.zctx.socket(zmq.PUSH)
|
||||||
self.sock.setsockopt(zmq.LINGER, 10)
|
self.sock.setsockopt(zmq.LINGER, 10)
|
||||||
self.sock.connect(self.stats_socket)
|
self.sock.connect(STATS_SOCKET)
|
||||||
self.pid = os.getpid()
|
self.pid = os.getpid()
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
@@ -67,50 +60,6 @@ class StatLog:
|
|||||||
self._send(f"{name}:{value}|{METRIC_TYPE.SAMPLE}")
|
self._send(f"{name}:{value}|{METRIC_TYPE.SAMPLE}")
|
||||||
|
|
||||||
|
|
||||||
class StatLogSP(StatLog):
|
|
||||||
def __init__(self, intercept=True):
|
|
||||||
"""
|
|
||||||
Initializes the class instance with an optional parameter to determine
|
|
||||||
if statistical logging should be configured or not.
|
|
||||||
|
|
||||||
:param intercept: A boolean flag that indicates whether to initialize
|
|
||||||
the `comma_statlog`. If True, the `comma_statlog` attribute is
|
|
||||||
instantiated as a `StatLog` object. Defaults to True.
|
|
||||||
"""
|
|
||||||
super().__init__()
|
|
||||||
self.comma_statlog = StatLog() if intercept else None
|
|
||||||
self.stats_socket = f"{STATS_SOCKET}_sp"
|
|
||||||
|
|
||||||
def connect(self) -> None:
|
|
||||||
super().connect()
|
|
||||||
if self.comma_statlog:
|
|
||||||
self.comma_statlog.connect()
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
super().__del__()
|
|
||||||
if self.comma_statlog:
|
|
||||||
self.comma_statlog.__del__()
|
|
||||||
|
|
||||||
def _send(self, metric: str) -> None:
|
|
||||||
super()._send(metric)
|
|
||||||
if self.comma_statlog:
|
|
||||||
self.comma_statlog._send(metric)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def default_converter(obj):
|
|
||||||
if isinstance(obj, (datetime, date)):
|
|
||||||
return obj.isoformat()
|
|
||||||
if isinstance(obj, set):
|
|
||||||
return list(obj)
|
|
||||||
if isinstance(obj, Decimal):
|
|
||||||
return float(obj)
|
|
||||||
return str(obj) # fallback for unknown types
|
|
||||||
|
|
||||||
def raw(self, name: str, value: dict) -> None:
|
|
||||||
encoded_dict = base64.b64encode(json.dumps(value, default=self.default_converter).encode("utf-8")).decode("utf-8")
|
|
||||||
self._send(f"{name}:{encoded_dict}|{METRIC_TYPE.RAW}")
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> NoReturn:
|
def main() -> NoReturn:
|
||||||
dongle_id = Params().get("DongleId")
|
dongle_id = Params().get("DongleId")
|
||||||
def get_influxdb_line(measurement: str, value: float | dict[str, float], timestamp: datetime, tags: dict) -> str:
|
def get_influxdb_line(measurement: str, value: float | dict[str, float], timestamp: datetime, tags: dict) -> str:
|
||||||
@@ -231,4 +180,4 @@ def main() -> NoReturn:
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
else:
|
else:
|
||||||
statlog = StatLogSP(intercept=True)
|
statlog = StatLog()
|
||||||
|
|||||||
Reference in New Issue
Block a user