From e7a3ea1b32b36fe220c2e78faecb874c9eeb99e8 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sun, 14 Sep 2025 12:18:59 +0200 Subject: [PATCH] (TICI) sync `master` and `master-tici` (#1245) * SL: bugfix parameter handling in sunnylink restore and remote setting (#1234) * refactor: improve parameter handling in sunnylink for robustness - Updated `get_param_as_byte` to return `None` for nonexistent parameters. - Enhanced param compression and encoding in `sunnylinkd`. * refactor: centralize parameter restoration with new helper function - Added `save_param_from_base64_encoded_string` to handle param decoding and saving. - Updated backup manager and sunnylinkd to use the new method. - Improved code readability and reduced duplication in parameter handling logic. * don't bother * clean (cherry picked from commit b7f8dd11a559f9cf7a427990e6d158d96a10a707) * UI: Developer UI (#1233) (cherry picked from commit 1bb4ca2547448f2189f67cb54db150e78bc4d487) * Revert & Reapply "UI: Developer UI" temporarily due to QT version mismatch (#1237) * Revert "UI: Developer UI (#1233)" This reverts commit 1bb4ca2547448f2189f67cb54db150e78bc4d487. * Reapply "UI: Developer UI (#1233)" This reverts commit b0a77049dacbdd59bc21d154820ba8cadf4ad201. * QColorConstants is not on device's QT version. Thanks @kumar for the fix (cherry picked from commit 810a2d9448df89ef84ecc1c794b63fad55350d69) * Revert "UI: Developer UI" (#1238) * Revert "Revert & Reapply "UI: Developer UI" temporarily due to QT version mismatch (#1237)" This reverts commit 810a2d9448df89ef84ecc1c794b63fad55350d69. * Revert "UI: Developer UI (#1233)" This reverts commit 1bb4ca2547448f2189f67cb54db150e78bc4d487. (cherry picked from commit 1be13fdc55a24a72bc33703c7343c9421fdd0842) * Reapply "UI: Developer UI" (#1238) (#1239) This reverts commit 1be13fdc55a24a72bc33703c7343c9421fdd0842. Co-authored-by: Jason Wen (cherry picked from commit 4f44d6e6436041a4f09a4d5c530405fc019c2492) --------- Co-authored-by: Nayan Co-authored-by: Jason Wen --- common/params_keys.h | 1 + selfdrive/ui/qt/onroad/alerts.cc | 9 + selfdrive/ui/qt/onroad/annotated_camera.h | 1 + selfdrive/ui/qt/onroad/driver_monitoring.cc | 5 + selfdrive/ui/sunnypilot/SConscript | 1 + .../qt/offroad/settings/visuals_panel.cc | 12 + .../qt/offroad/settings/visuals_panel.h | 1 + .../sunnypilot/qt/onroad/annotated_camera.cc | 5 + .../sunnypilot/qt/onroad/annotated_camera.h | 3 + .../qt/onroad/developer_ui/developer_ui.cc | 227 ++++++++++++++++++ .../qt/onroad/developer_ui/developer_ui.h | 31 +++ .../qt/onroad/developer_ui/ui_elements.h | 19 ++ selfdrive/ui/sunnypilot/qt/onroad/hud.cc | 190 +++++++++++++++ selfdrive/ui/sunnypilot/qt/onroad/hud.h | 39 ++- selfdrive/ui/sunnypilot/ui.cc | 16 +- selfdrive/ui/sunnypilot/ui.h | 4 + selfdrive/ui/sunnypilot/ui_scene.h | 12 + selfdrive/ui/ui.h | 5 + sunnypilot/sunnylink/athena/sunnylinkd.py | 26 +- sunnypilot/sunnylink/backups/manager.py | 29 +-- sunnypilot/sunnylink/utils.py | 38 ++- 21 files changed, 631 insertions(+), 43 deletions(-) create mode 100644 selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.cc create mode 100644 selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.h create mode 100644 selfdrive/ui/sunnypilot/qt/onroad/developer_ui/ui_elements.h create mode 100644 selfdrive/ui/sunnypilot/ui_scene.h diff --git a/common/params_keys.h b/common/params_keys.h index afb6b348eb..fc7842720b 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -146,6 +146,7 @@ inline static std::unordered_map keys = { {"CustomAccLongPressIncrement", {PERSISTENT | BACKUP, INT, "5"}}, {"CustomAccShortPressIncrement", {PERSISTENT | BACKUP, INT, "1"}}, {"DeviceBootMode", {PERSISTENT | BACKUP, INT, "0"}}, + {"DevUIInfo", {PERSISTENT | BACKUP, INT, "0"}}, {"EnableCopyparty", {PERSISTENT | BACKUP, BOOL}}, {"EnableGithubRunner", {PERSISTENT | BACKUP, BOOL}}, {"GithubRunnerSufficientVoltage", {CLEAR_ON_MANAGER_START , BOOL}}, diff --git a/selfdrive/ui/qt/onroad/alerts.cc b/selfdrive/ui/qt/onroad/alerts.cc index d6829c6b08..2e8f3612eb 100644 --- a/selfdrive/ui/qt/onroad/alerts.cc +++ b/selfdrive/ui/qt/onroad/alerts.cc @@ -4,6 +4,9 @@ #include #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); @@ -73,6 +76,12 @@ 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 diff --git a/selfdrive/ui/qt/onroad/annotated_camera.h b/selfdrive/ui/qt/onroad/annotated_camera.h index e3ca837907..5d9d21ab6b 100644 --- a/selfdrive/ui/qt/onroad/annotated_camera.h +++ b/selfdrive/ui/qt/onroad/annotated_camera.h @@ -12,6 +12,7 @@ #include "selfdrive/ui/sunnypilot/qt/onroad/model.h" #define ExperimentalButton ExperimentalButtonSP #define ModelRenderer ModelRendererSP +#define HudRenderer HudRendererSP #else #include "selfdrive/ui/qt/onroad/buttons.h" #include "selfdrive/ui/qt/onroad/hud.h" diff --git a/selfdrive/ui/qt/onroad/driver_monitoring.cc b/selfdrive/ui/qt/onroad/driver_monitoring.cc index 49f2c950b4..e67c483047 100644 --- a/selfdrive/ui/qt/onroad/driver_monitoring.cc +++ b/selfdrive/ui/qt/onroad/driver_monitoring.cc @@ -73,6 +73,11 @@ void DriverMonitorRenderer::draw(QPainter &painter, const QRect &surface_rect) { float y = surface_rect.height() - offset; float opacity = is_active ? 0.65f : 0.2f; +#ifdef SUNNYPILOT + const int dev_ui_info = uiStateSP()->scene.dev_ui_info; + y -= dev_ui_info > 1 ? 50 : 0; +#endif + drawIcon(painter, QPoint(x, y), dm_img, QColor(0, 0, 0, 70), opacity); QPointF keypoints[std::size(DEFAULT_FACE_KPTS_3D)]; diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript index 2f3c8ddd8d..807bf02478 100644 --- a/selfdrive/ui/sunnypilot/SConscript +++ b/selfdrive/ui/sunnypilot/SConscript @@ -39,6 +39,7 @@ qt_src = [ "sunnypilot/qt/offroad/settings/visuals_panel.cc", "sunnypilot/qt/onroad/annotated_camera.cc", "sunnypilot/qt/onroad/buttons.cc", + "sunnypilot/qt/onroad/developer_ui/developer_ui.cc", "sunnypilot/qt/onroad/hud.cc", "sunnypilot/qt/onroad/model.cc", "sunnypilot/qt/onroad/onroad_home.cc", diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc index dd2f05416d..c3aaf12d22 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc @@ -72,6 +72,15 @@ VisualsPanel::VisualsPanel(QWidget *parent) : QWidget(parent) { list->addItem(chevron_info_settings); param_watcher->addParam("ChevronInfo"); + // Visuals: Developer UI Info (Dev UI) + std::vector dev_ui_settings_texts{tr("Off"), tr("Right"), tr("Right &&\nBottom")}; + dev_ui_settings = new ButtonParamControlSP( + "DevUIInfo", tr("Developer UI"), tr("Display real-time parameters and metrics from various sources."), + "", + dev_ui_settings_texts, + 380); + list->addItem(dev_ui_settings); + sunnypilotScroller = new ScrollViewSP(list, this); vlayout->addWidget(sunnypilotScroller); @@ -90,4 +99,7 @@ void VisualsPanel::paramsRefresh() { if (chevron_info_settings) { chevron_info_settings->refresh(); } + if (dev_ui_settings) { + dev_ui_settings->refresh(); + } } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.h index f342662c22..30ff31c301 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.h @@ -28,4 +28,5 @@ protected: std::map toggles; ParamWatcher * param_watcher; ButtonParamControlSP *chevron_info_settings; + ButtonParamControlSP *dev_ui_settings; }; diff --git a/selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.cc b/selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.cc index 3721a3d198..1d5567161a 100644 --- a/selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.cc +++ b/selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.cc @@ -14,3 +14,8 @@ AnnotatedCameraWidgetSP::AnnotatedCameraWidgetSP(VisionStreamType type, QWidget void AnnotatedCameraWidgetSP::updateState(const UIState &s) { AnnotatedCameraWidget::updateState(s); } + +void AnnotatedCameraWidgetSP::showEvent(QShowEvent *event) { + AnnotatedCameraWidget::showEvent(event); + ui_update_params_sp(uiState()); +} diff --git a/selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.h b/selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.h index 46ce7d4be3..8c0a385657 100644 --- a/selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.h +++ b/selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.h @@ -15,4 +15,7 @@ class AnnotatedCameraWidgetSP : public AnnotatedCameraWidget { public: explicit AnnotatedCameraWidgetSP(VisionStreamType type, QWidget *parent = nullptr); void updateState(const UIState &s) override; + +protected: + void showEvent(QShowEvent *event) override; }; diff --git a/selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.cc b/selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.cc new file mode 100644 index 0000000000..292ba6f7bb --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.cc @@ -0,0 +1,227 @@ +/** + * 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 + +#include "common/util.h" +#include "selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.h" + + +// Add Relative Distance to Primary Lead Car +// Unit: Meters +UiElement DeveloperUi::getDRel(bool lead_status, float lead_d_rel) { + QString value = lead_status ? QString::number(lead_d_rel, 'f', 0) : "-"; + QColor color = QColor(255, 255, 255, 255); + + if (lead_status) { + // Orange if close, Red if very close + if (lead_d_rel < 5) { + color = QColor(255, 0, 0, 255); + } else if (lead_d_rel < 15) { + color = QColor(255, 188, 0, 255); + } + } + + return UiElement(value, "REL DIST", "m", color); +} + +// Add Relative Velocity vs Primary Lead Car +// Unit: kph if metric, else mph +UiElement DeveloperUi::getVRel(bool lead_status, float lead_v_rel, bool is_metric, const QString &speed_unit) { + QString value = lead_status ? QString::number(lead_v_rel * (is_metric ? MS_TO_KPH : MS_TO_MPH), 'f', 0) : "-"; + QColor color = QColor(255, 255, 255, 255); + + if (lead_status) { + // Red if approaching faster than 10mph + // Orange if approaching (negative) + if (lead_v_rel < -4.4704) { + color = QColor(255, 0, 0, 255); + } else if (lead_v_rel < 0) { + color = QColor(255, 188, 0, 255); + } + } + + return UiElement(value, "REL SPEED", speed_unit, color); +} + +// Add Real Steering Angle +// Unit: Degrees +UiElement DeveloperUi::getSteeringAngleDeg(float angle_steers, bool lat_active, bool steer_override) { + QString value = QString("%1%2%3").arg(QString::number(angle_steers, 'f', 1)).arg("°").arg(""); + QColor color = lat_active ? (steer_override ? QColor(0x91, 0x9b, 0x95, 0xff) : QColor(0, 255, 0, 255)) : QColor(255, 255, 255, 255); + + // Red if large steering angle + // Orange if moderate steering angle + if (std::fabs(angle_steers) > 180) { + color = QColor(255, 0, 0, 255); + } else if (std::fabs(angle_steers) > 90) { + color = QColor(255, 188, 0, 255); + } + + return UiElement(value, "REAL STEER", "", color); +} + +// Add Actual Lateral Acceleration (roll compensated) when using Torque +// Unit: m/s² +UiElement DeveloperUi::getActualLateralAccel(float curvature, float v_ego, float roll, bool lat_active, bool steer_override) { + double actualLateralAccel = (curvature * pow(v_ego, 2)) - (roll * 9.81); + + QString value = QString::number(actualLateralAccel, 'f', 2); + QColor color = lat_active ? (steer_override ? QColor(0x91, 0x9b, 0x95, 0xff) : QColor(0, 255, 0, 255)) : QColor(255, 255, 255, 255); + + return UiElement(value, "ACTUAL L.A.", "m/s²", color); +} + +// Add Desired Steering Angle when using PID +// Unit: Degrees +UiElement DeveloperUi::getSteeringAngleDesiredDeg(bool lat_active, float steer_angle_desired, float angle_steers) { + QString value = lat_active ? QString("%1%2%3").arg(QString::number(steer_angle_desired, 'f', 1)).arg("°").arg("") : "-"; + QColor color = QColor(255, 255, 255, 255); + + if (lat_active) { + // Red if large steering angle + // Orange if moderate steering angle + if (std::fabs(angle_steers) > 180) { + color = QColor(255, 0, 0, 255); + } else if (std::fabs(angle_steers) > 90) { + color = QColor(255, 188, 0, 255); + } else { + color = QColor(0, 255, 0, 255); + } + } + + return UiElement(value, "DESIRED STEER", "", color); +} + +// Add Device Memory (RAM) Usage +// Unit: Percent +UiElement DeveloperUi::getMemoryUsagePercent(int memory_usage_percent) { + QString value = QString("%1%2").arg(QString::number(memory_usage_percent, 'd', 0)).arg("%"); + QColor color = (memory_usage_percent > 85) ? QColor(255, 188, 0, 255) : QColor(255, 255, 255, 255); + + return UiElement(value, "RAM", "", color); +} + +// Add Vehicle Current Acceleration +// Unit: m/s² +UiElement DeveloperUi::getAEgo(float a_ego) { + QString value = QString::number(a_ego, 'f', 1); + QColor color = QColor(255, 255, 255, 255); + + return UiElement(value, "ACC.", "m/s²", color); +} + +// Add Relative Velocity to Primary Lead Car +// Unit: kph if metric, else mph +UiElement DeveloperUi::getVEgoLead(bool lead_status, float lead_v_rel, float v_ego, bool is_metric, const QString &speed_unit) { + QString value = lead_status ? QString::number((lead_v_rel + v_ego) * (is_metric ? MS_TO_KPH : MS_TO_MPH), 'f', 0) : "-"; + QColor color = QColor(255, 255, 255, 255); + + if (lead_status) { + // Red if approaching faster than 10mph + // Orange if approaching (negative) + if (lead_v_rel < -4.4704) { + color = QColor(255, 0, 0, 255); + } else if (lead_v_rel < 0) { + color = QColor(255, 188, 0, 255); + } + } + + return UiElement(value, "L.S.", speed_unit, color); +} + +// Add Friction Coefficient Raw from torqued +// Unit: None +UiElement DeveloperUi::getFrictionCoefficientFiltered(float friction_coefficient_filtered, bool live_valid) { + QString value = QString::number(friction_coefficient_filtered, 'f', 3); + QColor color = live_valid ? QColor(0, 255, 0, 255) : QColor(255, 255, 255, 255); + + return UiElement(value, "FRIC.", "", color); +} + +// Add Lateral Acceleration Factor Raw from torqued +// Unit: m/s² +UiElement DeveloperUi::getLatAccelFactorFiltered(float lat_accel_factor_filtered, bool live_valid) { + QString value = QString::number(lat_accel_factor_filtered, 'f', 3); + QColor color = live_valid ? QColor(0, 255, 0, 255) : QColor(255, 255, 255, 255); + + return UiElement(value, "L.A.", "m/s²", color); +} + +// Add Steering Torque from Car EPS +// Unit: Newton Meters +UiElement DeveloperUi::getSteeringTorqueEps(float steering_torque_eps) { + QString value = QString::number(std::fabs(steering_torque_eps), 'f', 1); + QColor color = QColor(255, 255, 255, 255); + + return UiElement(value, "E.T.", "N·dm", color); +} + +// Add Bearing Degree and Direction from Car (Compass) +// Unit: Meters +UiElement DeveloperUi::getBearingDeg(float bearing_accuracy_deg, float bearing_deg) { + QString value = (bearing_accuracy_deg != 180.00) ? QString("%1%2%3").arg(QString::number(bearing_deg, 'd', 0)).arg("°").arg("") : "-"; + QColor color = QColor(255, 255, 255, 255); + QString dir_value; + + if (bearing_accuracy_deg != 180.00) { + if (((bearing_deg >= 337.5) && (bearing_deg <= 360)) || ((bearing_deg >= 0) && (bearing_deg <= 22.5))) { + dir_value = "N"; + } else if ((bearing_deg > 22.5) && (bearing_deg < 67.5)) { + dir_value = "NE"; + } else if ((bearing_deg >= 67.5) && (bearing_deg <= 112.5)) { + dir_value = "E"; + } else if ((bearing_deg > 112.5) && (bearing_deg < 157.5)) { + dir_value = "SE"; + } else if ((bearing_deg >= 157.5) && (bearing_deg <= 202.5)) { + dir_value = "S"; + } else if ((bearing_deg > 202.5) && (bearing_deg < 247.5)) { + dir_value = "SW"; + } else if ((bearing_deg >= 247.5) && (bearing_deg <= 292.5)) { + dir_value = "W"; + } else if ((bearing_deg > 292.5) && (bearing_deg < 337.5)) { + dir_value = "NW"; + } + } else { + dir_value = "OFF"; + } + + return UiElement(QString("%1 | %2").arg(dir_value).arg(value), "B.D.", "", color); +} + +// Add Altitude of Current Location +// Unit: Meters +UiElement DeveloperUi::getAltitude(float gps_accuracy, float altitude) { + QString value = (gps_accuracy != 0.00) ? QString::number(altitude, 'f', 1) : "-"; + QColor color = QColor(255, 255, 255, 255); + + return UiElement(value, "ALT.", "m", color); +} + +// Add Actuators Output +// Unit: Degree (angle) or m/s² (torque) +UiElement DeveloperUi::getActuatorsOutputLateral(cereal::CarParams::SteerControlType steerControlType, + cereal::CarControl::Actuators::Reader &actuators, + float desiredCurvature, float v_ego, float roll, bool lat_active, bool steer_override) { + QString label; + QString value; + QString unit; + + if (steerControlType == cereal::CarParams::SteerControlType::ANGLE) { + label = "DESIRED STEER"; + value = QString("%1%2%3").arg(QString::number(actuators.getSteeringAngleDeg(), 'f', 1)).arg("°").arg(""); + } else { + label = "DESIRED L.A."; + double desiredLateralAccel = (desiredCurvature * pow(v_ego, 2)) - (roll * 9.81); + value = QString::number(desiredLateralAccel, 'f', 2); + unit = "m/s²"; + } + + value = lat_active ? value : "-"; + QColor color = lat_active ? (steer_override ? QColor(0x91, 0x9b, 0x95, 0xff) : QColor(0, 255, 0, 255)) : QColor(255, 255, 255, 255); + + return UiElement(value, label, unit, color); +} diff --git a/selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.h b/selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.h new file mode 100644 index 0000000000..0c5c472209 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.h @@ -0,0 +1,31 @@ +/** + * 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/util.h" +#include "selfdrive/ui/sunnypilot/qt/onroad/developer_ui/ui_elements.h" + +class DeveloperUi { + +public: + static UiElement getDRel(bool lead_status, float lead_d_rel); + static UiElement getVRel(bool lead_status, float lead_v_rel, bool is_metric, const QString &speed_unit); + static UiElement getSteeringAngleDeg(float angle_steers, bool lat_active, bool steer_override); + static UiElement getActualLateralAccel(float curvature, float v_ego, float roll, bool lat_active, bool steer_override); + static UiElement getSteeringAngleDesiredDeg(bool lat_active, float steer_angle_desired, float angle_steers); + static UiElement getMemoryUsagePercent(int memory_usage_percent); + static UiElement getAEgo(float a_ego); + static UiElement getVEgoLead(bool lead_status, float lead_v_rel, float v_ego, bool is_metric, const QString &speed_unit); + static UiElement getFrictionCoefficientFiltered(float friction_coefficient_filtered, bool live_valid); + static UiElement getLatAccelFactorFiltered(float lat_accel_factor_filtered, bool live_valid); + static UiElement getSteeringTorqueEps(float steering_torque_eps); + static UiElement getBearingDeg(float bearing_accuracy_deg, float bearing_deg); + static UiElement getAltitude(float gps_accuracy, float altitude); + static UiElement getActuatorsOutputLateral(cereal::CarParams::SteerControlType steerControlType, + cereal::CarControl::Actuators::Reader &actuators, + float desiredCurvature, float v_ego, float roll, bool lat_active, bool steer_override); +}; diff --git a/selfdrive/ui/sunnypilot/qt/onroad/developer_ui/ui_elements.h b/selfdrive/ui/sunnypilot/qt/onroad/developer_ui/ui_elements.h new file mode 100644 index 0000000000..3711e5ac05 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/onroad/developer_ui/ui_elements.h @@ -0,0 +1,19 @@ +/** + * 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 + +struct UiElement { + QString value{}; + QString label{}; + QString units{}; + QColor color{}; + + explicit UiElement(const QString &value = "", const QString &label = "", const QString &units = "", const QColor &color = QColor(255, 255, 255, 255)) + : value(value), label(label), units(units), color(color) {} +}; diff --git a/selfdrive/ui/sunnypilot/qt/onroad/hud.cc b/selfdrive/ui/sunnypilot/qt/onroad/hud.cc index 233ca59f98..9ead933d04 100644 --- a/selfdrive/ui/sunnypilot/qt/onroad/hud.cc +++ b/selfdrive/ui/sunnypilot/qt/onroad/hud.cc @@ -7,12 +7,202 @@ #include "selfdrive/ui/sunnypilot/qt/onroad/hud.h" +#include "selfdrive/ui/qt/util.h" + + HudRendererSP::HudRendererSP() {} void HudRendererSP::updateState(const UIState &s) { HudRenderer::updateState(s); + + const SubMaster &sm = *(s.sm); + const bool cs_alive = sm.alive("controlsState"); + const auto cs = sm["controlsState"].getControlsState(); + const auto car_state = sm["carState"].getCarState(); + const auto car_control = sm["carControl"].getCarControl(); + const auto radar_state = sm["radarState"].getRadarState(); + const auto is_gps_location_external = sm.rcv_frame("gpsLocationExternal") > 1; + const auto gpsLocation = is_gps_location_external ? sm["gpsLocationExternal"].getGpsLocationExternal() : sm["gpsLocation"].getGpsLocation(); + const auto ltp = sm["liveTorqueParameters"].getLiveTorqueParameters(); + const auto car_params = sm["carParams"].getCarParams(); + + static int reverse_delay = 0; + bool reverse_allowed = false; + if (int(car_state.getGearShifter()) != 4) { + reverse_delay = 0; + reverse_allowed = false; + } else { + reverse_delay += 50; + if (reverse_delay >= 1000) { + reverse_allowed = true; + } + } + + reversing = reverse_allowed; + is_metric = s.scene.is_metric; + + // Handle older routes where vEgoCluster is not set + v_ego_cluster_seen = v_ego_cluster_seen || car_state.getVEgoCluster() != 0.0; + float v_ego = v_ego_cluster_seen ? car_state.getVEgoCluster() : car_state.getVEgo(); + speed = cs_alive ? std::max(0.0, v_ego) : 0.0; + speed *= is_metric ? MS_TO_KPH : MS_TO_MPH; + + latActive = car_control.getLatActive(); + steerOverride = car_state.getSteeringPressed(); + + devUiInfo = s.scene.dev_ui_info; + + speedUnit = is_metric ? tr("km/h") : tr("mph"); + lead_d_rel = radar_state.getLeadOne().getDRel(); + lead_v_rel = radar_state.getLeadOne().getVRel(); + lead_status = radar_state.getLeadOne().getStatus(); + steerControlType = car_params.getSteerControlType(); + actuators = car_control.getActuators(); + torqueLateral = steerControlType == cereal::CarParams::SteerControlType::TORQUE; + angleSteers = car_state.getSteeringAngleDeg(); + desiredCurvature = cs.getDesiredCurvature(); + curvature = cs.getCurvature(); + roll = sm["liveParameters"].getLiveParameters().getRoll(); + memoryUsagePercent = sm["deviceState"].getDeviceState().getMemoryUsagePercent(); + gpsAccuracy = is_gps_location_external ? gpsLocation.getHorizontalAccuracy() : 1.0; // External reports accuracy, internal does not. + altitude = gpsLocation.getAltitude(); + vEgo = car_state.getVEgo(); + aEgo = car_state.getAEgo(); + steeringTorqueEps = car_state.getSteeringTorqueEps(); + bearingAccuracyDeg = gpsLocation.getBearingAccuracyDeg(); + bearingDeg = gpsLocation.getBearingDeg(); + torquedUseParams = ltp.getUseParams(); + latAccelFactorFiltered = ltp.getLatAccelFactorFiltered(); + frictionCoefficientFiltered = ltp.getFrictionCoefficientFiltered(); + liveValid = ltp.getLiveValid(); } void HudRendererSP::draw(QPainter &p, const QRect &surface_rect) { HudRenderer::draw(p, surface_rect); + if (!reversing) { + // Bottom Dev UI + if (devUiInfo == 2) { + QRect rect_bottom(surface_rect.left(), surface_rect.bottom() - 60, surface_rect.width(), 61); + p.setPen(Qt::NoPen); + p.setBrush(QColor(0, 0, 0, 100)); + p.drawRect(rect_bottom); + drawBottomDevUI(p, rect_bottom.left(), rect_bottom.center().y()); + } + + // Right Dev UI + if (devUiInfo != 0) { + QRect rect_right(surface_rect.right() - (UI_BORDER_SIZE * 2), UI_BORDER_SIZE * 1.5, 184, 170); + drawRightDevUI(p, surface_rect.right() - 184 - UI_BORDER_SIZE * 2, UI_BORDER_SIZE * 2 + rect_right.height()); + } + } +} + +void HudRendererSP::drawText(QPainter &p, int x, int y, const QString &text, QColor color) { + QRect real_rect = p.fontMetrics().boundingRect(text); + real_rect.moveCenter({x, y - real_rect.height() / 2}); + p.setPen(color); + p.drawText(real_rect.x(), real_rect.bottom(), text); +} + +int HudRendererSP::drawRightDevUIElement(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color) { + + p.setFont(InterFont(28, QFont::Bold)); + x += 92; + y += 80; + drawText(p, x, y, label); + + p.setFont(InterFont(30 * 2, QFont::Bold)); + y += 65; + drawText(p, x, y, value, color); + + p.setFont(InterFont(28, QFont::Bold)); + + if (units.length() > 0) { + p.save(); + x += 120; + y -= 25; + p.translate(x, y); + p.rotate(-90); + drawText(p, 0, 0, units); + p.restore(); + } + + return 130; +} + +void HudRendererSP::drawRightDevUI(QPainter &p, int x, int y) { + int rh = 5; + int ry = y; + + UiElement dRelElement = DeveloperUi::getDRel(lead_status, lead_d_rel); + rh += drawRightDevUIElement(p, x, ry, dRelElement.value, dRelElement.label, dRelElement.units, dRelElement.color); + ry = y + rh; + + UiElement vRelElement = DeveloperUi::getVRel(lead_status, lead_v_rel, is_metric, speedUnit); + rh += drawRightDevUIElement(p, x, ry, vRelElement.value, vRelElement.label, vRelElement.units, vRelElement.color); + ry = y + rh; + + UiElement steeringAngleDegElement = DeveloperUi::getSteeringAngleDeg(angleSteers, latActive, steerOverride); + rh += drawRightDevUIElement(p, x, ry, steeringAngleDegElement.value, steeringAngleDegElement.label, steeringAngleDegElement.units, steeringAngleDegElement.color); + ry = y + rh; + + UiElement actuatorsOutputLateralElement = DeveloperUi::getActuatorsOutputLateral(steerControlType, actuators, desiredCurvature, vEgo, roll, latActive, steerOverride); + rh += drawRightDevUIElement(p, x, ry, actuatorsOutputLateralElement.value, actuatorsOutputLateralElement.label, actuatorsOutputLateralElement.units, actuatorsOutputLateralElement.color); + ry = y + rh; + + UiElement actualLateralAccelElement = DeveloperUi::getActualLateralAccel(curvature, vEgo, roll, latActive, steerOverride); + rh += drawRightDevUIElement(p, x, ry, actualLateralAccelElement.value, actualLateralAccelElement.label, actualLateralAccelElement.units, actualLateralAccelElement.color); +} + +int HudRendererSP::drawBottomDevUIElement(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color) { + p.setFont(InterFont(38, QFont::Bold)); + QFontMetrics fm(p.font()); + QRect init_rect = fm.boundingRect(label + " "); + QRect real_rect = fm.boundingRect(init_rect, 0, label + " "); + real_rect.moveCenter({x, y}); + + QRect init_rect2 = fm.boundingRect(value); + QRect real_rect2 = fm.boundingRect(init_rect2, 0, value); + real_rect2.moveTop(real_rect.top()); + real_rect2.moveLeft(real_rect.right() + 10); + + QRect init_rect3 = fm.boundingRect(units); + QRect real_rect3 = fm.boundingRect(init_rect3, 0, units); + real_rect3.moveTop(real_rect.top()); + real_rect3.moveLeft(real_rect2.right() + 10); + + p.setPen(Qt::white); + p.drawText(real_rect, Qt::AlignLeft | Qt::AlignVCenter, label); + + p.setPen(color); + p.drawText(real_rect2, Qt::AlignRight | Qt::AlignVCenter, value); + p.drawText(real_rect3, Qt::AlignLeft | Qt::AlignVCenter, units); + return 430; +} + +void HudRendererSP::drawBottomDevUI(QPainter &p, int x, int y) { + int rw = 90; + + UiElement aEgoElement = DeveloperUi::getAEgo(aEgo); + rw += drawBottomDevUIElement(p, rw, y, aEgoElement.value, aEgoElement.label, aEgoElement.units, aEgoElement.color); + + UiElement vEgoLeadElement = DeveloperUi::getVEgoLead(lead_status, lead_v_rel, vEgo, is_metric, speedUnit); + rw += drawBottomDevUIElement(p, rw, y, vEgoLeadElement.value, vEgoLeadElement.label, vEgoLeadElement.units, vEgoLeadElement.color); + + if (torqueLateral && torquedUseParams) { + UiElement frictionCoefficientFilteredElement = DeveloperUi::getFrictionCoefficientFiltered(frictionCoefficientFiltered, liveValid); + rw += drawBottomDevUIElement(p, rw, y, frictionCoefficientFilteredElement.value, frictionCoefficientFilteredElement.label, frictionCoefficientFilteredElement.units, frictionCoefficientFilteredElement.color); + + UiElement latAccelFactorFilteredElement = DeveloperUi::getLatAccelFactorFiltered(latAccelFactorFiltered, liveValid); + rw += drawBottomDevUIElement(p, rw, y, latAccelFactorFilteredElement.value, latAccelFactorFilteredElement.label, latAccelFactorFilteredElement.units, latAccelFactorFilteredElement.color); + } else { + UiElement steeringTorqueEpsElement = DeveloperUi::getSteeringTorqueEps(steeringTorqueEps); + rw += drawBottomDevUIElement(p, rw, y, steeringTorqueEpsElement.value, steeringTorqueEpsElement.label, steeringTorqueEpsElement.units, steeringTorqueEpsElement.color); + + UiElement bearingDegElement = DeveloperUi::getBearingDeg(bearingAccuracyDeg, bearingDeg); + rw += drawBottomDevUIElement(p, rw, y, bearingDegElement.value, bearingDegElement.label, bearingDegElement.units, bearingDegElement.color); + } + + UiElement altitudeElement = DeveloperUi::getAltitude(gpsAccuracy, altitude); + rw += drawBottomDevUIElement(p, rw, y, altitudeElement.value, altitudeElement.label, altitudeElement.units, altitudeElement.color); } diff --git a/selfdrive/ui/sunnypilot/qt/onroad/hud.h b/selfdrive/ui/sunnypilot/qt/onroad/hud.h index 1e98cd3a52..968789bc15 100644 --- a/selfdrive/ui/sunnypilot/qt/onroad/hud.h +++ b/selfdrive/ui/sunnypilot/qt/onroad/hud.h @@ -7,9 +7,8 @@ #pragma once -#include - #include "selfdrive/ui/qt/onroad/hud.h" +#include "selfdrive/ui/sunnypilot/qt/onroad/developer_ui/developer_ui.h" class HudRendererSP : public HudRenderer { Q_OBJECT @@ -18,4 +17,40 @@ public: HudRendererSP(); void updateState(const UIState &s) override; void draw(QPainter &p, const QRect &surface_rect) override; + +private: + Params params; + void drawText(QPainter &p, int x, int y, const QString &text, QColor color = Qt::white); + void drawRightDevUI(QPainter &p, int x, int y); + int drawRightDevUIElement(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color); + int drawBottomDevUIElement(QPainter &p, int x, int y, const QString &value, const QString &label, const QString &units, QColor &color); + void drawBottomDevUI(QPainter &p, int x, int y); + + bool lead_status; + float lead_d_rel; + float lead_v_rel; + bool torqueLateral; + float angleSteers; + float desiredCurvature; + float curvature; + float roll; + int memoryUsagePercent; + int devUiInfo; + float gpsAccuracy; + float altitude; + float vEgo; + float aEgo; + float steeringTorqueEps; + float bearingAccuracyDeg; + float bearingDeg; + bool torquedUseParams; + float latAccelFactorFiltered; + float frictionCoefficientFiltered; + bool liveValid; + QString speedUnit; + bool latActive; + bool steerOverride; + bool reversing; + cereal::CarParams::SteerControlType steerControlType; + cereal::CarControl::Actuators::Reader actuators; }; diff --git a/selfdrive/ui/sunnypilot/ui.cc b/selfdrive/ui/sunnypilot/ui.cc index b2701356cc..1277195df1 100644 --- a/selfdrive/ui/sunnypilot/ui.cc +++ b/selfdrive/ui/sunnypilot/ui.cc @@ -18,13 +18,22 @@ UIStateSP::UIStateSP(QObject *parent) : UIState(parent) { "modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", "pandaStates", "carParams", "driverMonitoringState", "carState", "driverStateV2", "wideRoadCameraState", "managerState", "selfdriveState", "longitudinalPlan", - "modelManagerSP", "selfdriveStateSP", "longitudinalPlanSP", "backupManagerSP" + "modelManagerSP", "selfdriveStateSP", "longitudinalPlanSP", "backupManagerSP", + "carControl", "gpsLocationExternal", "gpsLocation", "liveTorqueParameters", + "carStateSP", "liveParameters" }); // update timer timer = new QTimer(this); QObject::connect(timer, &QTimer::timeout, this, &UIStateSP::update); timer->start(1000 / UI_FREQ); + + // Param watcher for UIScene param updates + param_watcher = new ParamWatcher(this); + connect(param_watcher, &ParamWatcher::paramChanged, [=](const QString ¶m_name, const QString ¶m_value) { + ui_update_params_sp(this); + }); + param_watcher->addParam("DevUIInfo"); } // This method overrides completely the update method from the parent class intentionally. @@ -39,6 +48,11 @@ void UIStateSP::update() { emit uiUpdate(*this); } +void ui_update_params_sp(UIStateSP *s) { + auto params = Params(); + s->scene.dev_ui_info = std::atoi(params.get("DevUIInfo").c_str()); +} + DeviceSP::DeviceSP(QObject *parent) : Device(parent) { QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &DeviceSP::update); QObject::connect(this, &Device::displayPowerChanged, this, &DeviceSP::handleDisplayPowerChanged); diff --git a/selfdrive/ui/sunnypilot/ui.h b/selfdrive/ui/sunnypilot/ui.h index cf8de1c4bb..393f997cbd 100644 --- a/selfdrive/ui/sunnypilot/ui.h +++ b/selfdrive/ui/sunnypilot/ui.h @@ -13,6 +13,7 @@ #include "selfdrive/ui/sunnypilot/qt/network/sunnylink/models/role_model.h" #include "selfdrive/ui/sunnypilot/qt/network/sunnylink/models/sponsor_role_model.h" #include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/util.h" class UIStateSP : public UIState { Q_OBJECT @@ -73,6 +74,7 @@ private slots: private: std::vector sunnylinkRoles = {}; std::vector sunnylinkUsers = {}; + ParamWatcher *param_watcher; }; UIStateSP *uiStateSP(); @@ -92,3 +94,5 @@ private: DeviceSP *deviceSP(); inline DeviceSP *device() { return deviceSP(); } + +void ui_update_params_sp(UIStateSP *s); diff --git a/selfdrive/ui/sunnypilot/ui_scene.h b/selfdrive/ui/sunnypilot/ui_scene.h new file mode 100644 index 0000000000..93e0cd6c91 --- /dev/null +++ b/selfdrive/ui/sunnypilot/ui_scene.h @@ -0,0 +1,12 @@ +/** + * 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 + +typedef struct UISceneSP : UIScene { + int dev_ui_info = 0; +} UISceneSP; diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index e78b573b66..5b3872b3d4 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -66,6 +66,11 @@ typedef struct UIScene { uint64_t started_frame; } UIScene; +#ifdef SUNNYPILOT +#include "sunnypilot/ui_scene.h" +#define UIScene UISceneSP +#endif + class UIState : public QObject { Q_OBJECT diff --git a/sunnypilot/sunnylink/athena/sunnylinkd.py b/sunnypilot/sunnylink/athena/sunnylinkd.py index 363fa1defc..25a77c367b 100755 --- a/sunnypilot/sunnylink/athena/sunnylinkd.py +++ b/sunnypilot/sunnylink/athena/sunnylinkd.py @@ -23,7 +23,7 @@ from websocket import (ABNF, WebSocket, WebSocketException, WebSocketTimeoutExce import cereal.messaging as messaging from sunnypilot.sunnylink.api import SunnylinkApi -from sunnypilot.sunnylink.utils import sunnylink_need_register, sunnylink_ready, get_param_as_byte +from sunnypilot.sunnylink.utils import sunnylink_need_register, sunnylink_ready, get_param_as_byte, save_param_from_base64_encoded_string SUNNYLINK_ATHENA_HOST = os.getenv('SUNNYLINK_ATHENA_HOST', 'wss://ws.stg.api.sunnypilot.ai') HANDLER_THREADS = int(os.getenv('HANDLER_THREADS', "4")) @@ -184,14 +184,18 @@ def getParams(params_keys: list[str], compression: bool = False) -> str | dict[s try: param_keys_validated = [key for key in params_keys if key in getParamsAllKeys()] - params_dict: dict[str, list[dict[str, str | bool | int ]]] = {"params": [ - { + params_dict: dict[str, list[dict[str, str | bool | int]]] = {"params": []} + for key in param_keys_validated: + value = get_param_as_byte(key) + if value is None: + continue + + params_dict["params"].append({ "key": key, - "value": base64.b64encode(gzip.compress(get_param_as_byte(key)) if compression else get_param_as_byte(key)).decode('utf-8'), + "value": base64.b64encode(gzip.compress(value) if compression else value).decode('utf-8'), "type": int(params.get_type(key).value), "is_compressed": compression - } for key in param_keys_validated - ]} + }) response = {str(param.get('key')): str(param.get('value')) for param in params_dict.get("params", [])} response |= {"params": json.dumps(params_dict.get("params", []))} # Upcoming for settings v1 @@ -204,15 +208,9 @@ def getParams(params_keys: list[str], compression: bool = False) -> str | dict[s @dispatcher.add_method def saveParams(params_to_update: dict[str, str], compression: bool = False) -> None: - params = Params() - params_dict = {key: base64.b64decode(value) for key, value in params_to_update.items()} - - if compression: - params_dict = {key: gzip.decompress(value) for key, value in params_dict.items()} - - for key, value in params_dict.items(): + for key, value in params_to_update.items(): try: - params.put(key, value) + save_param_from_base64_encoded_string(key, value, compression) except Exception as e: cloudlog.error(f"sunnylinkd.saveParams.exception {e}") diff --git a/sunnypilot/sunnylink/backups/manager.py b/sunnypilot/sunnylink/backups/manager.py index 315300c73c..e52b547afe 100644 --- a/sunnypilot/sunnylink/backups/manager.py +++ b/sunnypilot/sunnylink/backups/manager.py @@ -12,7 +12,7 @@ from enum import Enum from typing import Any from openpilot.common.git import get_branch -from openpilot.common.params import Params, ParamKeyType, ParamKeyFlag +from openpilot.common.params import Params, ParamKeyFlag from openpilot.common.realtime import Ratekeeper from openpilot.common.swaglog import cloudlog from openpilot.system.version import get_version @@ -20,7 +20,7 @@ from openpilot.system.version import get_version from cereal import messaging, custom from sunnypilot.sunnylink.api import SunnylinkApi from sunnypilot.sunnylink.backups.utils import decrypt_compressed_data, encrypt_compress_data, SnakeCaseEncoder -from sunnypilot.sunnylink.utils import get_param_as_byte +from sunnypilot.sunnylink.utils import get_param_as_byte, save_param_from_base64_encoded_string class OperationType(Enum): @@ -173,8 +173,7 @@ class BackupManagerSP: self._update_progress(75.0, OperationType.RESTORE) # Apply configuration - all_values_encoded = self._get_metadata_value(backup_metadata, "all_values_encoded", "false") - self._apply_config(config_data, str(all_values_encoded).lower() == "true") + self._apply_config(config_data) self.restore_status = custom.BackupManagerSP.Status.completed self._update_progress(100.0, OperationType.RESTORE) @@ -187,7 +186,7 @@ class BackupManagerSP: self._report_status() return False - def _apply_config(self, config_data: dict[str, str], all_values_encoded: bool = False) -> None: + def _apply_config(self, config_data: dict[str, str]) -> None: """Applies configuration data from a backup, but only for parameters marked as backupable.""" backupable_params = [k.decode('utf-8') for k in self.params.all_keys(ParamKeyFlag.BACKUP)] backupable_set_lower = {p.lower() for p in backupable_params} @@ -199,26 +198,8 @@ class BackupManagerSP: if param.lower() in backupable_set_lower: # Find real param name (with correct casing) real_param = next(p for p in backupable_params if p.lower() == param.lower()) - param_type = self.params.get_type(real_param) try: - value = base64.b64decode(encoded_value) if all_values_encoded else encoded_value - - if param_type != ParamKeyType.BYTES: - value = value.decode('utf-8') # type: ignore - - if param_type == ParamKeyType.STRING: - value = value - elif param_type == ParamKeyType.BOOL: - value = value.lower() in ('true', '1', 'yes') # type: ignore - elif param_type == ParamKeyType.INT: - value = int(value) # type: ignore - elif param_type == ParamKeyType.FLOAT: - value = float(value) # type: ignore - elif param_type == ParamKeyType.TIME: - value = str(value) - elif param_type == ParamKeyType.JSON: - value = json.loads(value) - self.params.put(real_param, value) + save_param_from_base64_encoded_string(real_param, encoded_value) restored_count += 1 except Exception as e: cloudlog.error(f"Failed to restore param {param}: {str(e)}") diff --git a/sunnypilot/sunnylink/utils.py b/sunnypilot/sunnylink/utils.py index 569afd26b6..1310b91f0e 100644 --- a/sunnypilot/sunnylink/utils.py +++ b/sunnypilot/sunnylink/utils.py @@ -1,3 +1,5 @@ +import base64 +import gzip import json from sunnypilot.sunnylink.api import SunnylinkApi, UNREGISTERED_SUNNYLINK_DONGLE_ID from openpilot.common.params import Params, ParamKeyType @@ -58,13 +60,45 @@ def get_api_token(): print(f"API Token: {token}") -def get_param_as_byte(param_name: str) -> bytes: +def get_param_as_byte(param_name: str) -> bytes | None: + """Get a parameter as bytes. Returns None if the parameter does not exist.""" params = Params() param = params.get(param_name) - param_type = params.get_type(param_name) + if param is None: + return None + param_type = params.get_type(param_name) if param_type == ParamKeyType.BYTES: return bytes(param) elif param_type == ParamKeyType.JSON: return json.dumps(param).encode('utf-8') return str(param).encode('utf-8') + + +def save_param_from_base64_encoded_string(param_name: str, base64_encoded_data: str, is_compressed=False) -> None: + """Save a parameter from bytes. Overwrites the parameter if it already exists.""" + params = Params() + # Find real param name (with correct casing) + param_type = params.get_type(param_name) + value = base64.b64decode(base64_encoded_data) + + if is_compressed: + value = gzip.decompress(value) + + # We convert to string anything that isn't bytes first. We later transform further. + if param_type != ParamKeyType.BYTES: + value = value.decode('utf-8') # type: ignore + + if param_type == ParamKeyType.STRING: + value = value + elif param_type == ParamKeyType.BOOL: + value = value.lower() in ('true', '1', 'yes') # type: ignore + elif param_type == ParamKeyType.INT: + value = int(value) # type: ignore + elif param_type == ParamKeyType.FLOAT: + value = float(value) # type: ignore + elif param_type == ParamKeyType.TIME: + value = str(value) # type: ignore + elif param_type == ParamKeyType.JSON: + value = json.loads(value) + params.put(param_name, value)