FrogPilot 0.9.7

This commit is contained in:
FrogAi
2024-06-27 10:22:08 -07:00
parent da00915ac8
commit b705b02e70
682 changed files with 181798 additions and 1348 deletions
+18 -1
View File
@@ -3,7 +3,7 @@ import json
Import('qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations')
base_libs = [common, messaging, visionipc, transformations,
'm', 'OpenCL', 'ssl', 'crypto', 'pthread'] + qt_env["LIBS"]
'm', 'OpenCL', 'ssl', 'crypto', 'pthread', 'avcodec', 'avformat', 'avutil', 'yuv', 'OmxCore'] + qt_env["LIBS"]
if arch == 'larch64':
base_libs.append('EGL')
@@ -24,6 +24,11 @@ widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/wifi.cc",
"qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc",
"qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"]
frogpilot_widgets_src = ["../frogpilot/ui/qt/widgets/drive_stats.cc", "../frogpilot/ui/qt/widgets/frogpilot_controls.cc",
"../frogpilot/ui/qt/widgets/model_reviewer.cc"]
widgets_src += frogpilot_widgets_src
qt_env['CPPDEFINES'] = []
if maps:
base_libs += ['QMapLibre']
@@ -42,6 +47,17 @@ qt_src = ["main.cc", "qt/sidebar.cc", "qt/body.cc",
"qt/onroad/onroad_home.cc", "qt/onroad/annotated_camera.cc",
"qt/onroad/buttons.cc", "qt/onroad/alerts.cc"]
frogpilot_src = ["../frogpilot/navigation/ui/maps_settings.cc", "../frogpilot/navigation/ui/navigation_functions.cc",
"../frogpilot/navigation/ui/primeless_settings.cc", "../frogpilot/screenrecorder/omx_encoder.cc",
"../frogpilot/screenrecorder/screenrecorder.cc", "../frogpilot/ui/qt/offroad/data_settings.cc",
"../frogpilot/ui/qt/offroad/device_settings.cc", "../frogpilot/ui/qt/offroad/frogpilot_settings.cc",
"../frogpilot/ui/qt/offroad/lateral_settings.cc", "../frogpilot/ui/qt/offroad/longitudinal_settings.cc",
"../frogpilot/ui/qt/offroad/model_settings.cc", "../frogpilot/ui/qt/offroad/sounds_settings.cc",
"../frogpilot/ui/qt/offroad/theme_settings.cc", "../frogpilot/ui/qt/offroad/utilities.cc",
"../frogpilot/ui/qt/offroad/vehicle_settings.cc", "../frogpilot/ui/qt/offroad/visual_settings.cc"]
qt_src += frogpilot_src
# build translation files
with open(File("translations/languages.json").abspath) as f:
languages = json.loads(f.read())
@@ -83,6 +99,7 @@ if GetOption('extras'):
qt_env.Program('tests/test_translations', [asset_obj, 'tests/test_runner.cc', 'tests/test_translations.cc'] + qt_src, LIBS=qt_libs)
qt_env.Program('tests/ui_snapshot', [asset_obj, "tests/ui_snapshot.cc"] + qt_src, LIBS=qt_libs)
qt_env['CPPPATH'] += ["../frogpilot/screenrecorder/openmax/include/"]
if GetOption('extras') and arch != "Darwin":
# setup and factory resetter
+45 -10
View File
@@ -13,6 +13,9 @@
#include "selfdrive/ui/qt/maps/map_settings.h"
#endif
#include "selfdrive/frogpilot/ui/qt/widgets/drive_stats.h"
#include "selfdrive/frogpilot/ui/qt/widgets/model_reviewer.h"
// HomeWindow: the container for the offroad and onroad UIs
HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
@@ -65,6 +68,13 @@ void HomeWindow::updateState(const UIState &s) {
body->setEnabled(true);
slayout->setCurrentWidget(body);
}
if (s.scene.started) {
showDriverView(s.scene.driver_camera_timer >= 10, true);
if (s.scene.map_open) {
showSidebar(false);
}
}
}
void HomeWindow::offroadTransition(bool offroad) {
@@ -73,24 +83,32 @@ void HomeWindow::offroadTransition(bool offroad) {
if (offroad) {
slayout->setCurrentWidget(home);
} else {
showSidebar(params.getBool("Sidebar"));
slayout->setCurrentWidget(onroad);
}
}
void HomeWindow::showDriverView(bool show) {
void HomeWindow::showDriverView(bool show, bool started) {
if (show) {
emit closeSettings();
slayout->setCurrentWidget(driver_view);
sidebar->setVisible(show == false);
} else {
slayout->setCurrentWidget(home);
if (started) {
slayout->setCurrentWidget(onroad);
sidebar->setVisible(params.getBool("Sidebar"));
} else {
slayout->setCurrentWidget(home);
sidebar->setVisible(show == false);
}
}
sidebar->setVisible(show == false);
}
void HomeWindow::mousePressEvent(QMouseEvent* e) {
// Handle sidebar collapsing
if ((onroad->isVisible() || body->isVisible()) && (!sidebar->isVisible() || e->x() > sidebar->width())) {
sidebar->setVisible(!sidebar->isVisible() && !onroad->isMapVisible());
params.putBool("Sidebar", sidebar->isVisible());
}
}
@@ -130,6 +148,9 @@ OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) {
QObject::connect(alert_notif, &QPushButton::clicked, [=] { center_layout->setCurrentIndex(2); });
header_layout->addWidget(alert_notif, 0, Qt::AlignHCenter | Qt::AlignLeft);
date = new ElidedLabel();
header_layout->addWidget(date, 0, Qt::AlignHCenter | Qt::AlignLeft);
version = new ElidedLabel();
header_layout->addWidget(version, 0, Qt::AlignHCenter | Qt::AlignRight);
@@ -145,19 +166,26 @@ OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) {
home_layout->setContentsMargins(0, 0, 0, 0);
home_layout->setSpacing(30);
// left: MapSettings/PrimeAdWidget
// left: MapSettings
QStackedWidget *left_widget = new QStackedWidget(this);
#ifdef ENABLE_MAPS
left_widget->addWidget(new MapSettings);
#else
left_widget->addWidget(new QWidget);
#endif
left_widget->addWidget(new PrimeAdWidget);
left_widget->setStyleSheet("border-radius: 10px;");
left_widget->addWidget(new DriveStats);
left_widget->setCurrentIndex(uiState()->hasPrime() ? 0 : 1);
connect(uiState(), &UIState::primeChanged, [=](bool prime) {
left_widget->setCurrentIndex(prime ? 0 : 1);
ModelReview *modelReview = new ModelReview(this);
left_widget->addWidget(modelReview);
left_widget->setStyleSheet("border-radius: 10px;");
left_widget->setCurrentIndex(1);
connect(modelReview, &ModelReview::driveRated, [=]() {
left_widget->setCurrentIndex(1);
});
connect(uiState(), &UIState::reviewModel, [=]() {
left_widget->setCurrentIndex(2);
});
home_layout->addWidget(left_widget, 1);
@@ -195,6 +223,8 @@ OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) {
timer = new QTimer(this);
timer->callOnTimeout(this, &OffroadHome::refresh);
QObject::connect(uiState(), &UIState::togglesUpdated, this, &OffroadHome::refresh);
setStyleSheet(R"(
* {
color: white;
@@ -224,7 +254,12 @@ void OffroadHome::hideEvent(QHideEvent *event) {
}
void OffroadHome::refresh() {
version->setText(getBrand() + " " + QString::fromStdString(params.get("UpdaterCurrentDescription")));
QString model = processModelName(uiState()->scene.model_name);
date->setText(QLocale(uiState()->language.mid(5)).toString(QDateTime::currentDateTime(), "dddd, MMMM d"));
version->setText(getBrand() + " v" + getVersion().left(14).trimmed() + " - " + model);
date->setVisible(util::system_time_valid());
bool updateAvailable = update_widget->refresh();
int alerts = alerts_widget->refresh();
+7 -1
View File
@@ -39,6 +39,9 @@ private:
OffroadAlert* alerts_widget;
QPushButton* alert_notif;
QPushButton* update_notif;
// FrogPilot variables
ElidedLabel* date;
};
class HomeWindow : public QWidget {
@@ -53,7 +56,7 @@ signals:
public slots:
void offroadTransition(bool offroad);
void showDriverView(bool show);
void showDriverView(bool show, bool started=false);
void showSidebar(bool show);
void showMapPanel(bool show);
@@ -69,6 +72,9 @@ private:
DriverViewWindow *driver_view;
QStackedLayout *slayout;
// FrogPilot variables
Params params;
private slots:
void updateState(const UIState &s);
};
+96 -1
View File
@@ -75,7 +75,7 @@ void MapWindow::initLayers() {
QVariantMap transition;
transition["duration"] = 400; // ms
m_map->setPaintProperty("navLayer", "line-color", QColor("#31a1ee"));
m_map->setPaintProperty("navLayer", "line-color", getNavPathColor(uiState()->scene.navigate_on_openpilot));
m_map->setPaintProperty("navLayer", "line-color-transition", transition);
m_map->setPaintProperty("navLayer", "line-width", 7.5);
m_map->setLayoutProperty("navLayer", "line-cap", "round");
@@ -110,6 +110,50 @@ void MapWindow::initLayers() {
// TODO: remove, symbol-sort-key does not seem to matter outside of each layer
m_map->setLayoutProperty("carPosLayer", "symbol-sort-key", 0);
}
// Credit goes to jakethesnake420!
if (!m_map->layerExists("buildingsLayer")) {
qDebug() << "Initializing buildingsLayer";
QVariantMap buildings;
buildings["id"] = "buildingsLayer";
buildings["source"] = "composite";
buildings["source-layer"] = "building";
buildings["type"] = "fill-extrusion";
buildings["minzoom"] = 15;
m_map->addLayer("buildingsLayer", buildings);
m_map->setFilter("buildingsLayer", QVariantList({"==", "extrude", "true"}));
QVariantList fillExtrusionHight = {
"interpolate",
QVariantList{"linear"},
QVariantList{"zoom"},
15, 0,
15.05, QVariantList{"get", "height"}
};
QVariantList fillExtrusionBase = {
"interpolate",
QVariantList{"linear"},
QVariantList{"zoom"},
15, 0,
15.05, QVariantList{"get", "min_height"}
};
QVariantList fillExtrusionOpacity = {
"interpolate",
QVariantList{"linear"},
QVariantList{"zoom"},
15, 0,
15.5, .6,
17, .6,
20, 0
};
m_map->setPaintProperty("buildingsLayer", "fill-extrusion-color", QColor("grey"));
m_map->setPaintProperty("buildingsLayer", "fill-extrusion-opacity", fillExtrusionOpacity);
m_map->setPaintProperty("buildingsLayer", "fill-extrusion-height", fillExtrusionHight);
m_map->setPaintProperty("buildingsLayer", "fill-extrusion-base", fillExtrusionBase);
m_map->setLayoutProperty("buildingsLayer", "visibility", "visible");
}
}
void MapWindow::updateState(const UIState &s) {
@@ -127,6 +171,21 @@ void MapWindow::updateState(const UIState &s) {
}
prev_time_valid = sm.valid("clocks");
if (sm.updated("modelV2")) {
// set path color on change, and show map on rising edge of navigate on openpilot
bool nav_enabled = sm["modelV2"].getModelV2().getNavEnabled() &&
(sm["controlsState"].getControlsState().getEnabled() || uiState()->scene.always_on_lateral_active);
if (nav_enabled != uiState()->scene.navigate_on_openpilot) {
if (loaded_once) {
m_map->setPaintProperty("navLayer", "line-color", getNavPathColor(nav_enabled));
}
if (nav_enabled) {
emit requestVisible(true);
}
}
uiState()->scene.navigate_on_openpilot = nav_enabled;
}
if (sm.updated("liveLocationKalman")) {
auto locationd_location = sm["liveLocationKalman"].getLiveLocationKalman();
auto locationd_pos = locationd_location.getPositionGeodetic();
@@ -234,6 +293,41 @@ void MapWindow::updateState(const UIState &s) {
route_rcv_frame = sm.rcv_frame("navRoute");
updateDestinationMarker();
}
// Credit to jakethesnake420
if (loaded_once && (sm.rcv_frame("uiPlan") != model_rcv_frame)) {
auto locationd_location = sm["liveLocationKalman"].getLiveLocationKalman();
auto model_path = model_to_collection(locationd_location.getCalibratedOrientationECEF(), locationd_location.getPositionECEF(), sm["uiPlan"].getUiPlan().getPosition());
QMapLibre::Feature model_path_feature(QMapLibre::Feature::LineStringType, model_path, {}, {});
QVariantMap modelV2Path;
modelV2Path["type"] = "geojson";
modelV2Path["data"] = QVariant::fromValue<QMapLibre::Feature>(model_path_feature);
m_map->updateSource("modelPathSource", modelV2Path);
model_rcv_frame = sm.rcv_frame("uiPlan");
}
// Map Styling - Credit goes to OPKR!
int map_style = uiState()->scene.map_style;
if (map_style != previous_map_style) {
std::array<std::string, 11> styleUrls = {
"mapbox://styles/commaai/clkqztk0f00ou01qyhsa5bzpj", // Stock openpilot
"mapbox://styles/mapbox/streets-v11", // Mapbox Streets
"mapbox://styles/mapbox/outdoors-v11", // Mapbox Outdoors
"mapbox://styles/mapbox/light-v10", // Mapbox Light
"mapbox://styles/mapbox/dark-v10", // Mapbox Dark
"mapbox://styles/mapbox/satellite-v9", // Mapbox Satellite
"mapbox://styles/mapbox/satellite-streets-v11", // Mapbox Satellite Streets
"mapbox://styles/mapbox/navigation-day-v1", // Mapbox Navigation Day
"mapbox://styles/mapbox/navigation-night-v1", // Mapbox Navigation Night
"mapbox://styles/mapbox/traffic-night-v2", // Mapbox Traffic Night
"mapbox://styles/mike854/clt0hm8mw01ok01p4blkr27jp" // mike854's (Satellite hybrid)
};
m_map->setStyleUrl(QString::fromStdString(styleUrls[map_style]));
}
previous_map_style = map_style;
}
void MapWindow::setError(const QString &err_str) {
@@ -366,6 +460,7 @@ void MapWindow::pinchTriggered(QPinchGesture *gesture) {
void MapWindow::offroadTransition(bool offroad) {
if (offroad) {
clearRoute();
uiState()->scene.navigate_on_openpilot = false;
routing_problem = false;
} else {
auto dest = coordinate_from_param("NavDestination");
+10
View File
@@ -70,10 +70,20 @@ private:
MapInstructions* map_instructions;
MapETA* map_eta;
// Blue with normal nav, green when nav is input into the model
QColor getNavPathColor(bool nav_enabled) {
return nav_enabled ? QColor("#31ee73") : QColor("#31a1ee");
}
void clearRoute();
void updateDestinationMarker();
uint64_t route_rcv_frame = 0;
// FrogPilot variables
int previous_map_style;
uint64_t model_rcv_frame = 0;
private slots:
void updateState(const UIState &s);
+4 -1
View File
@@ -8,12 +8,15 @@
#include <eigen3/Eigen/Dense>
#include <QGeoCoordinate>
#include "common/params.h"
#include "common/util.h"
#include "common/transformations/coordinates.hpp"
#include "common/transformations/orientation.hpp"
#include "cereal/messaging/messaging.h"
const QString MAPBOX_TOKEN = util::getenv("MAPBOX_TOKEN").c_str();
const QString MAPBOX_TOKEN = !util::getenv("MAPBOX_TOKEN").empty() ? util::getenv("MAPBOX_TOKEN").c_str() :
!Params().get("MapboxSecretKey").empty() ? QString::fromStdString(Params().get("MapboxSecretKey")) :
QString();
const QString MAPS_HOST = util::getenv("MAPS_HOST", MAPBOX_TOKEN.isEmpty() ? "https://maps.comma.ai" : "https://api.mapbox.com").c_str();
const QString MAPS_CACHE_PATH = "/data/mbgl-cache-navd.db";
+8
View File
@@ -41,3 +41,11 @@ void MapPanel::toggleMapSettings() {
emit mapPanelRequested();
show();
}
void MapPanel::showEvent(QShowEvent *event) {
uiState()->scene.map_open = true;
}
void MapPanel::hideEvent(QHideEvent *event) {
uiState()->scene.map_open = false;
}
+4
View File
@@ -18,4 +18,8 @@ public slots:
private:
QStackedLayout *content_stack;
// FrogPilot widgets
void hideEvent(QHideEvent *event);
void showEvent(QShowEvent *event);
};
+9 -1
View File
@@ -62,7 +62,7 @@ MapSettings::MapSettings(bool closeable, QWidget *parent) : QFrame(parent) {
title->setStyleSheet("color: #FFFFFF; font-size: 54px; font-weight: 600;");
heading->addWidget(title);
auto *subtitle = new QLabel(tr("Manage at connect.comma.ai"), this);
subtitle = new QLabel(tr("Manage at connect.comma.ai"), this);
subtitle->setStyleSheet("color: #A0A0A0; font-size: 40px; font-weight: 300;");
heading->addWidget(subtitle);
}
@@ -93,6 +93,8 @@ MapSettings::MapSettings(bool closeable, QWidget *parent) : QFrame(parent) {
setStyleSheet("MapSettings { background-color: #333333; }");
QObject::connect(NavManager::instance(), &NavManager::updated, this, &MapSettings::refresh);
wifi = new WifiManager(this);
}
void MapSettings::showEvent(QShowEvent *event) {
@@ -138,6 +140,12 @@ void MapSettings::refresh() {
for (; n < widgets.size(); ++n) widgets[n]->setVisible(false);
setUpdatesEnabled(true);
// Use IP for NOO without Prime
if (!uiState()->hasPrime()) {
QString ipAddress = QString("%1:8082").arg(wifi->getIp4Address());
subtitle->setText(tr("Manage at %1").arg(ipAddress));
}
}
void MapSettings::navigateTo(const QJsonObject &place) {
+6
View File
@@ -12,6 +12,7 @@
#include <QVBoxLayout>
#include "common/params.h"
#include "selfdrive/ui/qt/network/wifi_manager.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/widgets/controls.h"
@@ -64,6 +65,11 @@ private:
DestinationWidget *work_widget;
std::vector<DestinationWidget *> widgets;
// FrogPilot variables
QLabel *subtitle;
WifiManager *wifi;
signals:
void closeSettings();
};
+27 -5
View File
@@ -6,7 +6,6 @@
#include <QScrollBar>
#include <QStyle>
#include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/widgets/controls.h"
@@ -127,9 +126,21 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
ListWidget *list = new ListWidget(this);
// Enable tethering layout
tetheringToggle = new ToggleControl(tr("Enable Tethering"), "", "", wifi->isTetheringEnabled());
std::vector<QString> tetheringSelection{tr("Off"), tr("Always"), tr("Only Onroad"), tr("Until Reboot")};
tetheringToggle = new ButtonParamControl("TetheringEnabled", tr("Enable Tethering"),
tr("Allow tethering with your data SIM and keep it active either while driving or continuously."),
"", tetheringSelection);
if (params.getInt("TetheringEnabled") == 3) {
params.remove("TetheringEnabled");
tetheringToggle->setCheckedButton(0);
}
QButtonGroup *buttonGroup = tetheringToggle->findChild<QButtonGroup *>();
if (buttonGroup) {
QObject::connect(buttonGroup, QOverload<int>::of(&QButtonGroup::buttonClicked), [this](int id) {
toggleTethering(id);
});
}
list->addItem(tetheringToggle);
QObject::connect(tetheringToggle, &ToggleControl::toggleFlipped, this, &AdvancedNetworking::toggleTethering);
// Change tethering password
ButtonControl *editPasswordButton = new ButtonControl(tr("Tethering Password"), tr("EDIT"));
@@ -221,8 +232,8 @@ void AdvancedNetworking::refresh() {
update();
}
void AdvancedNetworking::toggleTethering(bool enabled) {
wifi->setTetheringEnabled(enabled);
void AdvancedNetworking::toggleTethering(int id) {
wifi->setTetheringEnabled(id == 1 || id == 3);
tetheringToggle->setEnabled(false);
}
@@ -290,6 +301,17 @@ WifiUI::WifiUI(QWidget *parent, WifiManager* wifi) : QWidget(parent), wifi(wifi)
color: #696969;
}
)");
// FrogPilot variables
QObject::connect(uiState(), &UIState::uiUpdate, this, &WifiUI::updateState);
}
void WifiUI::updateState(const UIState &s) {
if (!isVisible() || s.sm->frame % UI_FREQ != 0) {
return;
}
refresh();
}
void WifiUI::refresh() {
+6 -2
View File
@@ -6,6 +6,7 @@
#include "selfdrive/ui/qt/widgets/input.h"
#include "selfdrive/ui/qt/widgets/ssh_keys.h"
#include "selfdrive/ui/qt/widgets/toggle.h"
#include "selfdrive/ui/ui.h"
class WifiItem : public QWidget {
Q_OBJECT
@@ -45,6 +46,9 @@ private:
ListWidget *wifi_list_widget = nullptr;
std::vector<WifiItem*> wifi_items;
// FrogPilot widgets
void updateState(const UIState &s);
signals:
void connectToNetwork(const Network n);
@@ -59,7 +63,7 @@ public:
private:
LabelControl* ipLabel;
ToggleControl* tetheringToggle;
ButtonParamControl* tetheringToggle;
ToggleControl* roamingToggle;
ButtonControl* editApnButton;
ButtonControl* hiddenNetworkButton;
@@ -72,7 +76,7 @@ signals:
void requestWifiScreen();
public slots:
void toggleTethering(bool enabled);
void toggleTethering(int id);
void refresh();
};
+1 -1
View File
@@ -58,6 +58,7 @@ public:
void setTetheringEnabled(bool enabled);
bool isTetheringEnabled();
void changeTetheringPassword(const QString &newPassword);
QString getIp4Address();
QString getTetheringPassword();
private:
@@ -72,7 +73,6 @@ private:
QString getAdapter(const uint = NM_DEVICE_TYPE_WIFI);
uint getAdapterType(const QDBusObjectPath &path);
QString getIp4Address();
void deactivateConnectionBySsid(const QString &ssid);
void deactivateConnection(const QDBusObjectPath &path);
QVector<QDBusObjectPath> getActiveConnections();
+129 -18
View File
@@ -15,6 +15,8 @@
#include "selfdrive/ui/qt/widgets/scrollview.h"
#include "selfdrive/ui/qt/widgets/ssh_keys.h"
#include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h"
TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
// param, title, desc, icon
std::vector<std::tuple<QString, QString, QString, QString>> toggle_defs{
@@ -114,6 +116,11 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
connect(toggles["ExperimentalLongitudinalEnabled"], &ToggleControl::toggleFlipped, [=]() {
updateToggles();
});
// FrogPilot signals
connect(toggles["IsMetric"], &ToggleControl::toggleFlipped, [=](bool metric) {
updateMetric(metric);
});
}
void TogglesPanel::updateState(const UIState &s) {
@@ -137,6 +144,16 @@ void TogglesPanel::showEvent(QShowEvent *event) {
}
void TogglesPanel::updateToggles() {
UIState *s = uiState();
UIScene &scene = s->scene;
auto disengage_on_accelerator_toggle = toggles["DisengageOnAccelerator"];
disengage_on_accelerator_toggle->setVisible(!scene.always_on_lateral);
auto driver_camera_toggle = toggles["RecordFront"];
driver_camera_toggle->setVisible(!(scene.no_logging && scene.no_uploads));
auto nav_settings_left_toggle = toggles["NavSettingLeftSide"];
nav_settings_left_toggle->setVisible(!scene.full_map);
auto experimental_mode_toggle = toggles["ExperimentalMode"];
auto op_long_toggle = toggles["ExperimentalLongitudinalEnabled"];
const QString e2e_description = QString("%1<br>"
@@ -152,17 +169,16 @@ void TogglesPanel::updateToggles() {
.arg(tr("New Driving Visualization"))
.arg(tr("The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner."));
const bool is_release = params.getBool("IsReleaseBranch");
auto cp_bytes = params.get("CarParamsPersistent");
if (!cp_bytes.empty()) {
AlignedBuffer aligned_buf;
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size()));
cereal::CarParams::Reader CP = cmsg.getRoot<cereal::CarParams>();
if (!CP.getExperimentalLongitudinalAvailable() || is_release) {
if (!CP.getExperimentalLongitudinalAvailable()) {
params.remove("ExperimentalLongitudinalEnabled");
}
op_long_toggle->setVisible(CP.getExperimentalLongitudinalAvailable() && !is_release);
op_long_toggle->setVisible(CP.getExperimentalLongitudinalAvailable());
if (hasLongitudinalControl(CP)) {
// normal description and toggle
experimental_mode_toggle->setEnabled(true);
@@ -179,11 +195,7 @@ void TogglesPanel::updateToggles() {
QString long_desc = unavailable + " " + \
tr("openpilot longitudinal control may come in a future update.");
if (CP.getExperimentalLongitudinalAvailable()) {
if (is_release) {
long_desc = unavailable + " " + tr("An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches.");
} else {
long_desc = tr("Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode.");
}
long_desc = tr("Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode.");
}
experimental_mode_toggle->setDescription("<b>" + long_desc + "</b><br><br>" + e2e_description);
}
@@ -221,6 +233,8 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to reset calibration?"), tr("Reset"), this)) {
params.remove("CalibrationParams");
params.remove("LiveTorqueParameters");
params_storage.remove("CalibrationParams");
params_storage.remove("LiveTorqueParameters");
}
});
addItem(resetCalibBtn);
@@ -348,6 +362,19 @@ void DevicePanel::showEvent(QShowEvent *event) {
ListWidget::showEvent(event);
}
void SettingsWindow::hideEvent(QHideEvent *event) {
closeMapBoxInstructions();
closeMapSelection();
closePanel();
closeParentToggle();
mapboxInstructionsOpen = false;
mapSelectionOpen = false;
panelOpen = false;
parentToggleOpen = false;
subParentToggleOpen = false;
}
void SettingsWindow::showEvent(QShowEvent *event) {
setCurrentPanel(0);
}
@@ -368,23 +395,41 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
panel_widget = new QStackedWidget();
// close button
QPushButton *close_btn = new QPushButton(tr("×"));
QPushButton *close_btn = new QPushButton(tr("← Back"));
close_btn->setStyleSheet(R"(
QPushButton {
font-size: 140px;
padding-bottom: 20px;
border-radius: 100px;
font-size: 50px;
border-radius: 25px;
background-color: #292929;
font-weight: 400;
font-weight: 500;
}
QPushButton:pressed {
background-color: #3B3B3B;
background-color: #ADADAD;
}
)");
close_btn->setFixedSize(200, 200);
sidebar_layout->addSpacing(45);
sidebar_layout->addWidget(close_btn, 0, Qt::AlignCenter);
QObject::connect(close_btn, &QPushButton::clicked, this, &SettingsWindow::closeSettings);
close_btn->setFixedSize(300, 125);
sidebar_layout->addSpacing(10);
sidebar_layout->addWidget(close_btn, 0, Qt::AlignRight);
QObject::connect(close_btn, &QPushButton::clicked, [this]() {
if (mapboxInstructionsOpen) {
closeMapBoxInstructions();
mapboxInstructionsOpen = false;
} else if (mapSelectionOpen) {
closeMapSelection();
mapSelectionOpen = false;
} else if (subParentToggleOpen) {
closeSubParentToggle();
subParentToggleOpen = false;
} else if (parentToggleOpen) {
closeParentToggle();
parentToggleOpen = false;
} else if (panelOpen) {
closePanel();
panelOpen = false;
} else {
closeSettings();
}
});
// setup panels
DevicePanel *device = new DevicePanel(this);
@@ -394,11 +439,23 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
TogglesPanel *toggles = new TogglesPanel(this);
QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription);
// FrogPilot panels
QObject::connect(toggles, &TogglesPanel::updateMetric, this, &SettingsWindow::updateMetric);
FrogPilotSettingsWindow *frogpilotSettingsWindow = new FrogPilotSettingsWindow(this);
QObject::connect(frogpilotSettingsWindow, &FrogPilotSettingsWindow::closeMapBoxInstructions, [this]() {mapboxInstructionsOpen=false;});
QObject::connect(frogpilotSettingsWindow, &FrogPilotSettingsWindow::openMapBoxInstructions, [this]() {mapboxInstructionsOpen=true;});
QObject::connect(frogpilotSettingsWindow, &FrogPilotSettingsWindow::openMapSelection, [this]() {mapSelectionOpen=true;});
QObject::connect(frogpilotSettingsWindow, &FrogPilotSettingsWindow::openPanel, [this]() {panelOpen=true;});
QObject::connect(frogpilotSettingsWindow, &FrogPilotSettingsWindow::openParentToggle, [this]() {parentToggleOpen=true;});
QObject::connect(frogpilotSettingsWindow, &FrogPilotSettingsWindow::openSubParentToggle, [this]() {subParentToggleOpen=true;});
QList<QPair<QString, QWidget *>> panels = {
{tr("Device"), device},
{tr("Network"), new Networking(this)},
{tr("Toggles"), toggles},
{tr("Software"), new SoftwarePanel(this)},
{tr("FrogPilot"), frogpilotSettingsWindow},
};
nav_btns = new QButtonGroup(this);
@@ -432,6 +489,60 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
panel_widget->addWidget(panel_frame);
QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() {
if (w->widget() == frogpilotSettingsWindow) {
bool tuningLevelConfirmed = params.getBool("TuningLevelConfirmed");
if (!tuningLevelConfirmed) {
int frogpilotHours = paramsTracking.getInt("FrogPilotMinutes") / 60;
int openpilotHours = params.getInt("openpilotMinutes") / 60;
if (frogpilotHours < 1 && openpilotHours < 100) {
if (FrogPilotConfirmationDialog::toggleAlert(tr("Welcome to FrogPilot! Since you're new to FrogPilot, the 'Minimal' toggle preset has been applied, but you can change this at any time via the 'Tuning Level' button!"), tr("Sounds good!"), this, true)) {
params.putBool("TuningLevelConfirmed", true);
params.putInt("TuningLevel", 0);
}
} else if (frogpilotHours < 50 && openpilotHours < 100) {
if (FrogPilotConfirmationDialog::toggleAlert(tr("Since you're fairly new to FrogPilot, the 'Minimal' toggle preset has been applied, but you can change this at any time via the 'Tuning Level' button!"), tr("Sounds good!"), this, true)) {
params.putBool("TuningLevelConfirmed", true);
params.putInt("TuningLevel", 0);
}
} else if (frogpilotHours < 100) {
if (openpilotHours >= 100) {
if (FrogPilotConfirmationDialog::toggleAlert(tr("Since you're experienced with openpilot, the 'Standard' toggle preset has been applied, but you can change this at any time via the 'Tuning Level' button!"), tr("Sounds good!"), this, true)) {
params.putBool("TuningLevelConfirmed", true);
params.putInt("TuningLevel", 1);
}
} else {
if (FrogPilotConfirmationDialog::toggleAlert(tr("Since you're experienced with FrogPilot, the 'Standard' toggle preset has been applied, but you can change this at any time via the 'Tuning Level' button!"), tr("Sounds good!"), this, true)) {
params.putBool("TuningLevelConfirmed", true);
params.putInt("TuningLevel", 1);
}
}
} else if (frogpilotHours >= 100) {
if (FrogPilotConfirmationDialog::toggleAlert(tr("Since you're very experienced with FrogPilot, the 'Advanced' toggle preset has been applied, but you can change this at any time via the 'Tuning Level' button!"), tr("Sounds good!"), this, true)) {
params.putBool("TuningLevelConfirmed", true);
params.putInt("TuningLevel", 2);
}
}
}
}
if (mapboxInstructionsOpen) {
closeMapBoxInstructions();
mapboxInstructionsOpen = false;
}
if (mapSelectionOpen) {
closeMapSelection();
mapSelectionOpen = false;
}
if (panelOpen) {
closePanel();
panelOpen = false;
}
if (parentToggleOpen) {
closeParentToggle();
parentToggleOpen = false;
}
btn->setChecked(true);
panel_widget->setCurrentWidget(w);
});
+31
View File
@@ -25,17 +25,38 @@ public:
protected:
void showEvent(QShowEvent *event) override;
// FrogPilot widgets
void hideEvent(QHideEvent *event) override;
signals:
void closeSettings();
void reviewTrainingGuide();
void showDriverView();
void expandToggleDescription(const QString &param);
// FrogPilot signals
void closeMapBoxInstructions();
void closeMapSelection();
void closePanel();
void closeParentToggle();
void closeSubParentToggle();
void updateMetric(bool metric, bool bootRun=false);
private:
QPushButton *sidebar_alert_widget;
QWidget *sidebar_widget;
QButtonGroup *nav_btns;
QStackedWidget *panel_widget;
// FrogPilot variables
Params params;
Params paramsTracking{"/persist/tracking"};
bool mapboxInstructionsOpen;
bool mapSelectionOpen;
bool panelOpen;
bool parentToggleOpen;
bool subParentToggleOpen;
};
class DevicePanel : public ListWidget {
@@ -56,6 +77,9 @@ private slots:
private:
Params params;
ButtonControl *pair_device;
// FrogPilot variables
Params params_storage{"/persist/params"};
};
class TogglesPanel : public ListWidget {
@@ -64,6 +88,10 @@ public:
explicit TogglesPanel(SettingsWindow *parent);
void showEvent(QShowEvent *event) override;
signals:
// FrogPilot signals
void updateMetric(bool metric, bool bootRun=false);
public slots:
void expandToggleDescription(const QString &param);
@@ -98,4 +126,7 @@ private:
Params params;
ParamWatcher *fs_watch;
// FrogPilot variables
Params params_memory{"/dev/shm/params"};
};
+40 -8
View File
@@ -21,7 +21,7 @@ void SoftwarePanel::checkForUpdates() {
}
SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
onroadLbl = new QLabel(tr("Updates are only downloaded while the car is off."));
onroadLbl = new QLabel(tr("Updates are only downloaded while the car is off or in park."));
onroadLbl->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left; padding-top: 30px; padding-bottom: 30px;");
addItem(onroadLbl);
@@ -29,6 +29,12 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
versionLbl = new LabelControl(tr("Current Version"), "");
addItem(versionLbl);
// automatic updates toggle
ParamControl *automaticUpdatesToggle = new ParamControl("AutomaticUpdates", tr("Automatically Update FrogPilot"),
tr("FrogPilot will automatically update itself and it's assets when you're offroad and connected to Wi-Fi."), "");
connect(automaticUpdatesToggle, &ToggleControl::toggleFlipped, this, &updateFrogPilotToggles);
addItem(automaticUpdatesToggle);
// download update btn
downloadBtn = new ButtonControl(tr("Download"), tr("CHECK"));
connect(downloadBtn, &ButtonControl::clicked, [=]() {
@@ -38,6 +44,7 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
} else {
std::system("pkill -SIGHUP -f system.updated.updated");
}
params_memory.putBool("ManualUpdateInitiated", true);
});
addItem(downloadBtn);
@@ -54,6 +61,12 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
connect(targetBranchBtn, &ButtonControl::clicked, [=]() {
auto current = params.get("GitBranch");
QStringList branches = QString::fromStdString(params.get("UpdaterAvailableBranches")).split(",");
if (!uiState()->scene.frogs_go_moo) {
branches.removeAll("FrogPilot-Development");
branches.removeAll("FrogPilot-New");
branches.removeAll("FrogPilot-Test");
branches.removeAll("MAKE-PRS-HERE");
}
for (QString b : {current.c_str(), "devel-staging", "devel", "nightly", "master-ci", "master"}) {
auto i = branches.indexOf(b);
if (i >= 0) {
@@ -70,19 +83,30 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
checkForUpdates();
}
});
if (!params.getBool("IsTestedBranch")) {
addItem(targetBranchBtn);
}
addItem(targetBranchBtn);
// uninstall button
auto uninstallBtn = new ButtonControl(tr("Uninstall %1").arg(getBrand()), tr("UNINSTALL"));
connect(uninstallBtn, &ButtonControl::clicked, [&]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to uninstall?"), tr("Uninstall"), this)) {
if (FrogPilotConfirmationDialog::yesorno(tr("Do you want to delete deep storage FrogPilot assets? This includes your toggle settings for quick reinstalls."), this)) {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure? This is 100% unrecoverable and if you reinstall FrogPilot you'll lose all your previous settings!"), this)) {
std::system("rm -rf /persist/params");
}
}
params.putBool("DoUninstall", true);
}
});
addItem(uninstallBtn);
// error log button
auto errorLogBtn = new ButtonControl(tr("Error Log"), tr("VIEW"), tr("View the error log for openpilot crashes."));
connect(errorLogBtn, &ButtonControl::clicked, [=]() {
std::string txt = util::read_file("/data/crashes/error.txt");
ConfirmationDialog::rich(QString::fromStdString(txt), this);
});
addItem(errorLogBtn);
fs_watch = new ParamWatcher(this);
QObject::connect(fs_watch, &ParamWatcher::paramChanged, [=](const QString &param_name, const QString &param_value) {
updateLabels();
@@ -104,6 +128,9 @@ void SoftwarePanel::showEvent(QShowEvent *event) {
}
void SoftwarePanel::updateLabels() {
UIState *s = uiState();
UIScene &scene = s->scene;
// add these back in case the files got removed
fs_watch->addParam("LastUpdateTime");
fs_watch->addParam("UpdateFailedCount");
@@ -111,12 +138,15 @@ void SoftwarePanel::updateLabels() {
fs_watch->addParam("UpdateAvailable");
if (!isVisible()) {
scene.downloading_update = false;
return;
}
// updater only runs offroad
onroadLbl->setVisible(is_onroad);
downloadBtn->setVisible(!is_onroad);
// updater only runs offroad or when parked
bool parked = scene.parked || scene.frogs_go_moo;
onroadLbl->setVisible(is_onroad && !parked);
downloadBtn->setVisible(!is_onroad || parked);
// download update
QString updater_state = QString::fromStdString(params.get("UpdaterState"));
@@ -124,7 +154,9 @@ void SoftwarePanel::updateLabels() {
if (updater_state != "idle") {
downloadBtn->setEnabled(false);
downloadBtn->setValue(updater_state);
scene.downloading_update = true;
} else {
scene.downloading_update = false;
if (failed) {
downloadBtn->setText(tr("CHECK"));
downloadBtn->setValue(tr("failed to check for update"));
@@ -148,7 +180,7 @@ void SoftwarePanel::updateLabels() {
versionLbl->setText(QString::fromStdString(params.get("UpdaterCurrentDescription")));
versionLbl->setDescription(QString::fromStdString(params.get("UpdaterCurrentReleaseNotes")));
installBtn->setVisible(!is_onroad && params.getBool("UpdateAvailable"));
installBtn->setVisible((!is_onroad || parked) && params.getBool("UpdateAvailable"));
installBtn->setValue(QString::fromStdString(params.get("UpdaterNewDescription")));
installBtn->setDescription(QString::fromStdString(params.get("UpdaterNewReleaseNotes")));
+20 -4
View File
@@ -6,11 +6,17 @@
#include "selfdrive/ui/qt/util.h"
void OnroadAlerts::updateState(const UIState &s) {
Alert a = getAlert(*(s.sm), s.scene.started_frame);
Alert a = getAlert(*(s.sm), s.scene.started_frame, s.scene.force_onroad);
if (!alert.equal(a)) {
alert = a;
update();
}
// FrogPilot variables
const UIScene &scene = s.scene;
hide_alerts = scene.hide_alerts;
road_name_ui = scene.road_name_ui;
}
void OnroadAlerts::clear() {
@@ -18,7 +24,7 @@ void OnroadAlerts::clear() {
update();
}
OnroadAlerts::Alert OnroadAlerts::getAlert(const SubMaster &sm, uint64_t started_frame) {
OnroadAlerts::Alert OnroadAlerts::getAlert(const SubMaster &sm, uint64_t started_frame, bool force_onroad) {
const cereal::ControlsState::Reader &cs = sm["controlsState"].getControlsState();
const uint64_t controls_frame = sm.rcv_frame("controlsState");
@@ -28,7 +34,7 @@ OnroadAlerts::Alert OnroadAlerts::getAlert(const SubMaster &sm, uint64_t started
cs.getAlertType().cStr(), cs.getAlertSize(), cs.getAlertStatus()};
}
if (!sm.updated("controlsState") && (sm.frame - started_frame) > 5 * UI_FREQ) {
if (!sm.updated("controlsState") && (sm.frame - started_frame) > 5 * UI_FREQ && !force_onroad) {
const int CONTROLS_TIMEOUT = 5;
const int controls_missing = (nanos_since_boot() - sm.rcv_time("controlsState")) / 1e9;
@@ -56,8 +62,15 @@ OnroadAlerts::Alert OnroadAlerts::getAlert(const SubMaster &sm, uint64_t started
void OnroadAlerts::paintEvent(QPaintEvent *event) {
if (alert.size == cereal::ControlsState::AlertSize::NONE) {
alert_height = 0;
return;
}
if (hide_alerts && alert.status == cereal::ControlsState::AlertStatus::NORMAL) {
alert_height = 0;
return;
}
static std::map<cereal::ControlsState::AlertSize, const int> alert_heights = {
{cereal::ControlsState::AlertSize::SMALL, 271},
{cereal::ControlsState::AlertSize::MID, 420},
@@ -67,11 +80,14 @@ void OnroadAlerts::paintEvent(QPaintEvent *event) {
int margin = 40;
int radius = 30;
int offset = road_name_ui ? 25 : 0;
alert_height = h - margin + offset;
if (alert.size == cereal::ControlsState::AlertSize::FULL) {
margin = 0;
radius = 0;
offset = 0;
}
QRect r = QRect(0 + margin, height() - h + margin, width() - margin*2, h - margin*2);
QRect r = QRect(0 + margin, height() - h + margin - offset, width() - margin*2, h - margin*2);
QPainter p(this);
+11 -1
View File
@@ -12,6 +12,9 @@ public:
void updateState(const UIState &s);
void clear();
// FrogPilot variables
int alert_height;
protected:
struct Alert {
QString text1;
@@ -29,11 +32,18 @@ protected:
{cereal::ControlsState::AlertStatus::NORMAL, QColor(0x15, 0x15, 0x15, 0xf1)},
{cereal::ControlsState::AlertStatus::USER_PROMPT, QColor(0xDA, 0x6F, 0x25, 0xf1)},
{cereal::ControlsState::AlertStatus::CRITICAL, QColor(0xC9, 0x22, 0x31, 0xf1)},
// FrogPilot alert colors
{cereal::ControlsState::AlertStatus::FROGPILOT, QColor(0x17, 0x86, 0x44, 0xf1)},
};
void paintEvent(QPaintEvent*) override;
OnroadAlerts::Alert getAlert(const SubMaster &sm, uint64_t started_frame);
OnroadAlerts::Alert getAlert(const SubMaster &sm, uint64_t started_frame, bool force_onroad);
QColor bg;
Alert alert = {};
// FrogPilot variables
bool hide_alerts;
bool road_name_ui;
};
File diff suppressed because it is too large Load Diff
+146 -4
View File
@@ -6,17 +6,49 @@
#include "selfdrive/ui/qt/onroad/buttons.h"
#include "selfdrive/ui/qt/widgets/cameraview.h"
#include "selfdrive/frogpilot/screenrecorder/screenrecorder.h"
class PedalIcons : public QWidget {
Q_OBJECT
public:
explicit PedalIcons(QWidget *parent = 0);
void updateState(const UIScene &scene);
private:
void paintEvent(QPaintEvent *event) override;
QPixmap brake_pedal_img;
QPixmap gas_pedal_img;
bool accelerating;
bool brakeLightOn;
bool decelerating;
bool dynamicPedals;
bool standstill;
bool staticPedals;
float acceleration;
};
class AnnotatedCameraWidget : public CameraWidget {
Q_OBJECT
public:
explicit AnnotatedCameraWidget(VisionStreamType type, QWidget* parent = 0);
void updateState(const UIState &s);
void updateState(int alert_height, const UIState &s);
MapSettingsButton *map_settings_btn;
// FrogPilot variables
QRect newSpeedLimitRect;
QString accelerationUnit;
float accelerationConversion;
private:
void drawText(QPainter &p, int x, int y, const QString &text, int alpha = 255);
void drawText(QPainter &p, int x, int y, const QString &text, int alpha = 255, bool overridePen = false);
QVBoxLayout *main_layout;
ExperimentalButton *experimental_btn;
@@ -40,15 +72,125 @@ private:
int skip_frame_count = 0;
bool wide_cam_requested = false;
// FrogPilot widgets
void drawCEMStatus(QPainter &p);
void drawRoadName(QPainter &p);
void drawTurnSignals(QPainter &p);
void initializeFrogPilotWidgets();
void paintFrogPilotWidgets(QPainter &painter);
void updateFrogPilotVariables(int alert_height, const UIScene &scene);
void updateSignals();
// FrogPilot variables
Params params_memory{"/dev/shm/params"};
DistanceButton *distance_btn;
PedalIcons *pedal_icons;
ScreenRecorder *screenRecorder;
QPixmap chillModeIcon;
QPixmap curveIcon;
QPixmap curveSpeedLeftIcon;
QPixmap curveSpeedRightIcon;
QPixmap dashboardIcon;
QPixmap experimentalModeIcon;
QPixmap leadIcon;
QPixmap lightIcon;
QPixmap mapDataIcon;
QPixmap navigationIcon;
QPixmap speedIcon;
QPixmap stopSignImg;
QPixmap turnIcon;
QPixmap upcomingMapsIcon;
QPoint dmIconPosition;
QString leadDistanceUnit;
QString leadSpeedUnit;
QString signalStyle;
QTimer *animationTimer;
QVector<QPixmap> blindspotImages;
QVector<QPixmap> signalImages;
bool bigMapOpen;
bool blindSpotLeft;
bool blindSpotRight;
bool cemStatus;
bool compass;
bool experimentalMode;
bool hideCSCUI;
bool hideMapIcon;
bool hideMaxSpeed;
bool hideSpeed;
bool hideSpeedLimit;
bool leadInfo;
bool leftCurve;
bool mapOpen;
bool mtscEnabled;
bool onroadDistanceButton;
bool roadNameUI;
bool showSLCOffset;
bool slcOverridden;
bool speedLimitChanged;
bool speedLimitSources;
bool trafficModeActive;
bool turnSignalAnimation;
bool turnSignalLeft;
bool turnSignalRight;
bool useStockColors;
bool useSI;
bool useViennaSLCSign;
bool vtscControllingCurve;
bool vtscEnabled;
float dashboardSpeedLimit;
float distanceConversion;
float laneDetectionWidth;
float lead_x;
float lead_y;
float mapsSpeedLimit;
float mtscSpeed;
float navigationSpeedLimit;
float slcSpeedLimitOffset;
float speedConversion;
float speedConversionMetrics;
float unconfirmedSpeedLimit;
float upcomingSpeedLimit;
float vtscSpeed;
int alertHeight;
int animationFrameIndex;
int cameraView;
int conditionalStatus;
int desiredFollow;
int modelLength;
int signalAnimationLength;
int signalHeight;
int signalMovement;
int signalWidth;
int standstillDuration;
int totalFrames;
std::string speedLimitSource;
inline QColor blueColor(int alpha = 255) { return QColor(0, 0, 255, alpha); }
inline QColor greenColor(int alpha = 242) { return QColor(23, 134, 68, alpha); }
inline QColor orangeColor(int alpha = 255) { return QColor(255, 165, 0, alpha); }
inline QColor purpleColor(int alpha = 255) { return QColor(128, 0, 128, alpha); }
inline QColor yellowColor(int alpha = 255) { return QColor(255, 255, 0, alpha); }
protected:
void paintGL() override;
void initializeGL() override;
void showEvent(QShowEvent *event) override;
void updateFrameMat() override;
void drawLaneLines(QPainter &painter, const UIState *s);
void drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd);
void drawLaneLines(QPainter &painter, const UIState *s, float v_ego);
void drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd, float v_ego, const QColor &lead_marker_color, bool adjacent = false);
void drawHud(QPainter &p);
void drawDriverState(QPainter &painter, const UIState *s);
void paintEvent(QPaintEvent *event) override;
inline QColor redColor(int alpha = 255) { return QColor(201, 34, 49, alpha); }
inline QColor whiteColor(int alpha = 255) { return QColor(255, 255, 255, alpha); }
inline QColor blackColor(int alpha = 255) { return QColor(0, 0, 0, alpha); }
+231 -8
View File
@@ -4,53 +4,179 @@
#include "selfdrive/ui/qt/util.h"
void drawIcon(QPainter &p, const QPoint &center, const QPixmap &img, const QBrush &bg, float opacity) {
void drawIcon(QPainter &p, const QPoint &center, const QPixmap &img, const QBrush &bg, float opacity, const int angle) {
p.setRenderHint(QPainter::Antialiasing);
p.setOpacity(1.0); // bg dictates opacity of ellipse
p.setPen(Qt::NoPen);
p.setBrush(bg);
p.drawEllipse(center, btn_size / 2, btn_size / 2);
p.save();
p.translate(center);
p.rotate(angle);
p.setOpacity(opacity);
p.drawPixmap(center - QPoint(img.width() / 2, img.height() / 2), img);
p.drawPixmap(-QPoint(img.width() / 2, img.height() / 2), img);
p.setOpacity(1.0);
p.restore();
}
// ExperimentalButton
ExperimentalButton::ExperimentalButton(QWidget *parent) : experimental_mode(false), engageable(false), QPushButton(parent) {
setFixedSize(btn_size, btn_size);
setFixedSize(btn_size, btn_size + 10);
engage_img = loadPixmap("../assets/img_chffr_wheel.png", {img_size, img_size});
experimental_img = loadPixmap("../assets/img_experimental.svg", {img_size, img_size});
QObject::connect(this, &QPushButton::clicked, this, &ExperimentalButton::changeMode);
// FrogPilot variables
wheel_gif_path = "../frogpilot/assets/active_theme/steering_wheel/wheel.gif";
wheel_png_path = "../frogpilot/assets/active_theme/steering_wheel/wheel.png";
gif_label = new QLabel(this);
gif_label->setScaledContents(true);
}
ExperimentalButton::~ExperimentalButton() {
if (gif != nullptr) {
gif->stop();
delete gif;
gif = nullptr;
gif_label->hide();
}
}
void ExperimentalButton::changeMode() {
const auto cp = (*uiState()->sm)["carParams"].getCarParams();
bool can_change = hasLongitudinalControl(cp) && params.getBool("ExperimentalModeConfirmed");
if (can_change) {
params.putBool("ExperimentalMode", !experimental_mode);
if (conditional_experimental) {
int override_value = (conditional_status >= 1 && conditional_status <= 6) ? 0 : conditional_status >= 7 ? 5 : 6;
params_memory.putInt("CEStatus", override_value);
} else {
params.putBool("ExperimentalMode", !experimental_mode);
}
}
}
void ExperimentalButton::updateState(const UIState &s) {
const auto cs = (*s.sm)["controlsState"].getControlsState();
bool eng = cs.getEngageable() || cs.getEnabled();
bool eng = cs.getEngageable() || cs.getEnabled() || always_on_lateral_active;
if ((cs.getExperimentalMode() != experimental_mode) || (eng != engageable)) {
engageable = eng;
experimental_mode = cs.getExperimentalMode();
update();
}
// FrogPilot variables
const UIScene &scene = s.scene;
always_on_lateral_active = scene.always_on_lateral_active;
big_map = scene.big_map;
conditional_experimental = scene.conditional_experimental;
conditional_status = scene.conditional_status;
map_open = scene.map_open;
navigate_on_openpilot = scene.navigate_on_openpilot;
rotating_wheel = scene.rotating_wheel;
traffic_mode_active = scene.traffic_mode_active;
use_stock_wheel = scene.use_stock_wheel;
if (rotating_wheel && steering_angle_deg != scene.steering_angle_deg) {
steering_angle_deg = scene.steering_angle_deg;
update();
} else if (!rotating_wheel) {
steering_angle_deg = 0;
}
if (params_memory.getBool("UpdateWheelImage")) {
updateIcon();
params_memory.remove("UpdateWheelImage");
}
}
void ExperimentalButton::updateBackgroundColor() {
static const QMap<QString, QColor> status_color_map {
{"default", QColor(0, 0, 0, 166)},
{"always_on_lateral_active", bg_colors[STATUS_ALWAYS_ON_LATERAL_ACTIVE]},
{"conditional_overridden", bg_colors[STATUS_CONDITIONAL_OVERRIDDEN]},
{"experimental_mode_active", bg_colors[STATUS_EXPERIMENTAL_MODE_ACTIVE]},
{"navigation_active", bg_colors[STATUS_NAVIGATION_ACTIVE]},
{"traffic_mode_active", bg_colors[STATUS_TRAFFIC_MODE_ACTIVE]}
};
if (isDown() || !engageable || use_stock_wheel) {
background_color = status_color_map["default"];
return;
}
if (always_on_lateral_active) {
background_color = status_color_map["always_on_lateral_active"];
} else if (conditional_status == 1 || conditional_status == 3 || conditional_status == 5) {
background_color = status_color_map["conditional_overridden"];
} else if (experimental_mode) {
background_color = status_color_map["experimental_mode_active"];
} else if (navigate_on_openpilot) {
background_color = status_color_map["navigation_active"];
} else if (traffic_mode_active) {
background_color = status_color_map["traffic_mode_active"];
} else {
background_color = status_color_map["default"];
}
}
void ExperimentalButton::updateIcon() {
if (gif != nullptr) {
gif->stop();
delete gif;
gif = nullptr;
gif_label->hide();
}
if (QFile::exists(wheel_gif_path)) {
gif = new QMovie(wheel_gif_path);
if (!gif->isValid()) {
delete gif;
gif = nullptr;
return;
}
gif_label->setMovie(gif);
gif_label->resize(img_size, img_size);
gif_label->move((btn_size - img_size) / 2, (btn_size - img_size) / 2);
gif_label->show();
gif->start();
use_gif = true;
image_empty = false;
} else if (QFile::exists(wheel_png_path)) {
img = loadPixmap(wheel_png_path, {img_size, img_size});
image_empty = false;
use_gif = false;
} else {
image_empty = true;
use_gif = false;
}
update();
}
void ExperimentalButton::paintEvent(QPaintEvent *event) {
if ((big_map && map_open) || image_empty || use_gif) {
return;
}
QPainter p(this);
QPixmap img = experimental_mode ? experimental_img : engage_img;
drawIcon(p, QPoint(btn_size / 2, btn_size / 2), img, QColor(0, 0, 0, 166), (isDown() || !engageable) ? 0.6 : 1.0);
if (use_stock_wheel) {
img = experimental_mode ? experimental_img : engage_img;
}
updateBackgroundColor();
drawIcon(p, QPoint(btn_size / 2, btn_size / 2), img, background_color, (isDown() || !engageable) ? 0.6 : 1.0, steering_angle_deg);
}
// MapSettingsButton
MapSettingsButton::MapSettingsButton(QWidget *parent) : QPushButton(parent) {
setFixedSize(btn_size, btn_size);
setFixedSize(btn_size, btn_size + 20);
settings_img = loadPixmap("../assets/navigation/icon_directions_outlined.svg", {img_size, img_size});
// hidden by default, made visible if map is created (has prime or mapbox token)
@@ -62,3 +188,100 @@ void MapSettingsButton::paintEvent(QPaintEvent *event) {
QPainter p(this);
drawIcon(p, QPoint(btn_size / 2, btn_size / 2), settings_img, QColor(0, 0, 0, 166), isDown() ? 0.6 : 1.0);
}
// FrogPilot buttons
// DistanceButton
DistanceButton::DistanceButton(QWidget *parent) : QPushButton(parent) {
setFixedSize(btn_size * 1.5, btn_size * 1.5);
gif_label = new QLabel(this);
gif_label->setScaledContents(true);
connect(this, &QPushButton::pressed, [this] {params_memory.putBool("OnroadDistanceButtonPressed", true);});
connect(this, &QPushButton::released, [this] {params_memory.putBool("OnroadDistanceButtonPressed", false);});
}
DistanceButton::~DistanceButton() {
qDeleteAll(profile_data_gif);
profile_data_gif.clear();
profile_data_png.clear();
}
void DistanceButton::updateState(const UIScene &scene) {
bool state_changed = (traffic_mode_active != scene.traffic_mode_active) ||
(personality != static_cast<int>(scene.personality) + 1 && !traffic_mode_active);
if (!state_changed) {
return;
}
personality = static_cast<int>(scene.personality) + 1;
traffic_mode_active = scene.traffic_mode_active;
int profile_index = traffic_mode_active ? 0 : personality;
if (QMovie *gif = profile_data_gif.value(profile_index)) {
gif_label->setMovie(gif);
gif_label->resize(btn_size, btn_size);
gif_label->move(UI_BORDER_SIZE, btn_size / 2.5);
gif_label->show();
gif->start();
use_gif = true;
} else {
gif_label->hide();
profile_image = profile_data_png.value(profile_index);
use_gif = false;
}
update();
}
void DistanceButton::updateIcon() {
qDeleteAll(profile_data_gif);
profile_data_gif.clear();
profile_data_png.clear();
static const QVector<QString> file_names = {
"../frogpilot/assets/active_theme/distance_icons/traffic",
"../frogpilot/assets/active_theme/distance_icons/aggressive",
"../frogpilot/assets/active_theme/distance_icons/standard",
"../frogpilot/assets/active_theme/distance_icons/relaxed"
};
for (int i = 0; i < file_names.size(); ++i) {
const QString &file_name = file_names[i];
QString gif_file = file_name + ".gif";
QString png_file = file_name + ".png";
QString fallback_file = QString("../frogpilot/assets/stock_theme/distance_icons/%1.png").arg(QFileInfo(file_name).baseName().toLower());
if (QFile::exists(gif_file)) {
QMovie *movie = new QMovie(gif_file);
profile_data_gif.push_back(movie);
profile_data_png.push_back(QPixmap());
} else {
QPixmap pixmap = loadPixmap(QFile::exists(png_file) ? png_file : fallback_file, QSize(btn_size * 1.25, btn_size * 1.25));
profile_data_gif.push_back(nullptr);
profile_data_png.push_back(pixmap);
}
}
personality = 0;
}
void DistanceButton::paintEvent(QPaintEvent *event) {
if (use_gif) {
return;
}
QPainter p(this);
p.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
drawIcon(p, QPoint((btn_size / 2) + UI_BORDER_SIZE, btn_size - (UI_BORDER_SIZE * 1.5)), profile_image, Qt::transparent, 1.0);
}
+65 -1
View File
@@ -1,5 +1,7 @@
#pragma once
#include <QLabel>
#include <QMovie>
#include <QPushButton>
#include "selfdrive/ui/ui.h"
@@ -14,6 +16,10 @@ public:
explicit ExperimentalButton(QWidget *parent = 0);
void updateState(const UIState &s);
// FrogPilot widgets
~ExperimentalButton();
void updateIcon();
private:
void paintEvent(QPaintEvent *event) override;
void changeMode();
@@ -23,6 +29,37 @@ private:
QPixmap experimental_img;
bool experimental_mode;
bool engageable;
// FrogPilot widgets
void updateBackgroundColor();
// FrogPilot variables
Params params_memory{"/dev/shm/params"};
QColor background_color;
QLabel *gif_label;
QMovie *gif;
QPixmap img;
QString wheel_gif_path;
QString wheel_png_path;
bool always_on_lateral_active;
bool big_map;
bool conditional_experimental;
bool image_empty;
bool map_open;
bool navigate_on_openpilot;
bool rotating_wheel;
bool traffic_mode_active;
bool use_gif;
bool use_stock_wheel;
int conditional_status;
int steering_angle_deg;
};
@@ -38,4 +75,31 @@ private:
QPixmap settings_img;
};
void drawIcon(QPainter &p, const QPoint &center, const QPixmap &img, const QBrush &bg, float opacity);
void drawIcon(QPainter &p, const QPoint &center, const QPixmap &img, const QBrush &bg, float opacity, const int angle = 0);
// FrogPilot buttons
class DistanceButton : public QPushButton {
Q_OBJECT
public:
explicit DistanceButton(QWidget *parent = 0);
~DistanceButton();
void updateIcon();
void updateState(const UIScene &scene);
private:
void paintEvent(QPaintEvent *event) override;
Params params_memory{"/dev/shm/params"};
QLabel *gif_label;
QPixmap profile_image;
QVector<QPixmap> profile_data_png;
QVector<QMovie*> profile_data_gif;
bool traffic_mode_active;
bool use_gif;
int personality;
};
+274 -4
View File
@@ -1,5 +1,6 @@
#include "selfdrive/ui/qt/onroad/onroad_home.h"
#include <QApplication>
#include <QPainter>
#include <QStackedLayout>
@@ -48,6 +49,13 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) {
QObject::connect(uiState(), &UIState::uiUpdate, this, &OnroadWindow::updateState);
QObject::connect(uiState(), &UIState::offroadTransition, this, &OnroadWindow::offroadTransition);
QObject::connect(uiState(), &UIState::primeChanged, this, &OnroadWindow::primeChanged);
// FrogPilot variables
QObject::connect(&clickTimer, &QTimer::timeout, [this]() {
clickTimer.stop();
QMouseEvent event(QEvent::MouseButtonPress, timeoutPoint, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QApplication::sendEvent(this, &event);
});
}
void OnroadWindow::updateState(const UIState &s) {
@@ -55,29 +63,97 @@ void OnroadWindow::updateState(const UIState &s) {
return;
}
if (s.scene.map_on_left) {
if (s.scene.map_on_left || s.scene.full_map) {
split->setDirection(QBoxLayout::LeftToRight);
} else {
split->setDirection(QBoxLayout::RightToLeft);
}
alerts->updateState(s);
nvg->updateState(s);
nvg->updateState(alerts->alert_height, s);
bool shouldUpdate = false;
QColor bgColor = bg_colors[s.status];
if (bg != bgColor) {
// repaint border
bg = bgColor;
shouldUpdate = true;
}
// FrogPilot variables
const UIScene &scene = s.scene;
acceleration = scene.acceleration;
accelerationJerk = scene.acceleration_jerk;
accelerationJerkDifference = scene.acceleration_jerk_difference;
blindSpotLeft = scene.blind_spot_left;
blindSpotRight = scene.blind_spot_right;
fps = scene.fps;
friction = scene.friction;
latAccel = scene.lat_accel;
liveValid = scene.live_valid;
showBlindspot = scene.show_blind_spot && (blindSpotLeft || blindSpotRight);
showFPS = scene.show_fps;
showJerk = scene.jerk_metrics;
showSignal = scene.signal_metrics && (turnSignalLeft || turnSignalRight);
showSteering = scene.steering_metrics;
showTuning = scene.lateral_tuning_metrics;
speedJerk = scene.speed_jerk;
speedJerkDifference = scene.speed_jerk_difference;
steer = scene.steer;
steeringAngleDeg = scene.steering_angle_deg;
turnSignalLeft = scene.turn_signal_left;
turnSignalRight = scene.turn_signal_right;
if (showBlindspot || showFPS || showJerk || showSignal || showSteering || showTuning) {
shouldUpdate = true;
}
if (shouldUpdate) {
update();
}
}
void OnroadWindow::mousePressEvent(QMouseEvent* e) {
// FrogPilot variables
UIState *s = uiState();
UIScene &scene = s->scene;
QPoint pos = e->pos();
if (scene.speed_limit_changed && nvg->newSpeedLimitRect.contains(pos)) {
params_memory.putBool("SpeedLimitAccepted", true);
return;
}
if (scene.experimental_mode_via_tap && pos != timeoutPoint) {
if (clickTimer.isActive()) {
clickTimer.stop();
if (scene.conditional_experimental) {
int override_value = scene.conditional_status != 0 && scene.conditional_status <= 6 ? 0 : scene.conditional_status >= 7 ? 5 : 6;
params_memory.putInt("CEStatus", override_value);
} else {
params.putBoolNonBlocking("ExperimentalMode", !params.getBool("ExperimentalMode"));
}
} else {
clickTimer.start(500);
}
return;
}
#ifdef ENABLE_MAPS
if (map != nullptr) {
// Switch between map and sidebar when using navigate on openpilot
bool sidebarVisible = geometry().x() > 0;
bool show_map = !sidebarVisible;
bool show_map = scene.navigate_on_openpilot ? sidebarVisible : !sidebarVisible;
map->setVisible(show_map && !map->isVisible());
if (scene.big_map) {
map->setFixedWidth(width());
} else {
map->setFixedWidth(topWidget(this)->width() / 2 - UI_BORDER_SIZE);
}
}
#endif
// propagation event to parent(HomeWindow)
@@ -125,5 +201,199 @@ void OnroadWindow::primeChanged(bool prime) {
void OnroadWindow::paintEvent(QPaintEvent *event) {
QPainter p(this);
p.fillRect(rect(), QColor(bg.red(), bg.green(), bg.blue(), 255));
// FrogPilot variables
UIState *s = uiState();
SubMaster &sm = *(s->sm);
QRect rect = this->rect();
QColor bgColor(bg.red(), bg.green(), bg.blue(), 255);
p.fillRect(rect, bgColor);
if (showSteering) {
static float smoothedSteer = 0.0;
smoothedSteer = 0.1 * std::abs(steer) + 0.9 * smoothedSteer;
if (std::abs(smoothedSteer - steer) < 0.01) {
smoothedSteer = steer;
}
QLinearGradient gradient(rect.topLeft(), rect.bottomLeft());
gradient.setColorAt(0.0, bg_colors[STATUS_TRAFFIC_MODE_ACTIVE]);
gradient.setColorAt(0.15, bg_colors[STATUS_EXPERIMENTAL_MODE_ACTIVE]);
gradient.setColorAt(0.5, bg_colors[STATUS_CONDITIONAL_OVERRIDDEN]);
gradient.setColorAt(0.85, bg_colors[STATUS_ENGAGED]);
gradient.setColorAt(1.0, bg_colors[STATUS_ENGAGED]);
QBrush brush(gradient);
if (steeringAngleDeg != 0) {
int visibleHeight = rect.height() * smoothedSteer;
QRect rectToFill, rectToHide;
if (steeringAngleDeg < 0) {
rectToFill = QRect(rect.x(), rect.y() + rect.height() - visibleHeight, UI_BORDER_SIZE, visibleHeight);
rectToHide = QRect(rect.x(), rect.y(), UI_BORDER_SIZE, rect.height() - visibleHeight);
} else {
rectToFill = QRect(rect.x() + rect.width() - UI_BORDER_SIZE, rect.y() + rect.height() - visibleHeight, UI_BORDER_SIZE, visibleHeight);
rectToHide = QRect(rect.x() + rect.width() - UI_BORDER_SIZE, rect.y(), UI_BORDER_SIZE, rect.height() - visibleHeight);
}
p.fillRect(rectToFill, brush);
p.fillRect(rectToHide, bgColor);
}
}
if (showBlindspot || showSignal) {
static bool leftFlickerActive = false;
static bool rightFlickerActive = false;
std::function<QColor(bool, bool, bool&)> getBorderColor = [&](bool blindSpot, bool turnSignal, bool &flickerActive) -> QColor {
if (showSignal && turnSignal) {
if (blindSpot) {
if (sm.frame % (UI_FREQ / 5) == 0) {
flickerActive = !flickerActive;
}
return flickerActive ? bg_colors[STATUS_TRAFFIC_MODE_ACTIVE] : bg_colors[STATUS_CONDITIONAL_OVERRIDDEN];
} else if (sm.frame % (UI_FREQ / 2) == 0) {
flickerActive = !flickerActive;
}
return flickerActive ? bg_colors[STATUS_CONDITIONAL_OVERRIDDEN] : bg;
} else if (showBlindspot && blindSpot) {
return bg_colors[STATUS_TRAFFIC_MODE_ACTIVE];
} else {
return bg;
}
};
QColor borderColorLeft = getBorderColor(blindSpotLeft, turnSignalLeft, leftFlickerActive);
QColor borderColorRight = getBorderColor(blindSpotRight, turnSignalRight, rightFlickerActive);
p.fillRect(rect.x(), rect.y(), rect.width() / 2, rect.height(), borderColorLeft);
p.fillRect(rect.x() + rect.width() / 2, rect.y(), rect.width() / 2, rect.height(), borderColorRight);
}
QString logicsDisplayString;
if (showJerk) {
if (bg == bg_colors[STATUS_ENGAGED] || bg == bg_colors[STATUS_TRAFFIC_MODE_ACTIVE]) {
maxAcceleration = std::max(maxAcceleration, acceleration);
}
maxAccelTimer = maxAcceleration == acceleration && maxAcceleration != 0 ? UI_FREQ * 5 : maxAccelTimer - 1;
logicsDisplayString += QString("Acceleration: %1 %2 - ").arg(acceleration, 0, 'f', 2).arg(nvg->accelerationUnit);
logicsDisplayString += QString("Max: %1 %2 | ").arg(maxAcceleration, 0, 'f', 2).arg(nvg->accelerationUnit);
logicsDisplayString += QString("Acceleration Jerk: %1 | ").arg(accelerationJerk, 0, 'f', 2);
logicsDisplayString += QString("Speed Jerk: %1").arg(speedJerk, 0, 'f', 2);
}
if (showTuning) {
if (!logicsDisplayString.isEmpty()) {
logicsDisplayString += " | ";
}
logicsDisplayString += QString("Friction: %1 | ").arg(liveValid ? QString::number(friction, 'f', 2) : "Calculating...");
logicsDisplayString += QString("Lateral Acceleration: %1").arg(liveValid ? QString::number(latAccel, 'f', 2) : "Calculating...");
}
if (!logicsDisplayString.isEmpty()) {
p.save();
p.setFont(InterFont(28, QFont::DemiBold));
p.setRenderHint(QPainter::TextAntialiasing);
QFontMetrics fontMetrics(p.font());
int x = (rect.width() - fontMetrics.horizontalAdvance(logicsDisplayString)) / 2 - UI_BORDER_SIZE;
int y = rect.top() + (fontMetrics.height() / 1.5);
QStringList parts = logicsDisplayString.split("|");
for (QString part : parts) {
if (part.contains("Max:") && maxAccelTimer > 0) {
QString baseText = QString("Acceleration: %1 %2 - ").arg(acceleration, 0, 'f', 2).arg(nvg->accelerationUnit);
p.setPen(whiteColor());
p.drawText(x, y, baseText);
x += fontMetrics.horizontalAdvance(baseText);
QString maxText = QString("Max: %1 %2 | ").arg(maxAcceleration, 0, 'f', 2).arg(nvg->accelerationUnit);
p.setPen(redColor());
p.drawText(x, y, maxText);
x += fontMetrics.horizontalAdvance(maxText);
} else if (part.contains("Acceleration Jerk") && accelerationJerkDifference != 0) {
QString baseText = QString("Acceleration Jerk: %1").arg(accelerationJerk, 0, 'f', 2);
p.setPen(whiteColor());
p.drawText(x, y, baseText);
x += fontMetrics.horizontalAdvance(baseText);
QString diffText = QString(" (%1) | ").arg(accelerationJerkDifference, 0, 'f', 2);
p.setPen(redColor());
p.drawText(x, y, diffText);
x += fontMetrics.horizontalAdvance(diffText);
} else if (part.contains("Speed Jerk") && speedJerkDifference != 0) {
QString baseText = QString("Speed Jerk: %1").arg(speedJerk, 0, 'f', 2);
p.setPen(whiteColor());
p.drawText(x, y, baseText);
x += fontMetrics.horizontalAdvance(baseText);
QString diffText = QString(" (%1)").arg(speedJerkDifference, 0, 'f', 2);
if (showTuning) {
diffText += " | ";
}
p.setPen(redColor());
p.drawText(x, y, diffText);
x += fontMetrics.horizontalAdvance(diffText);
} else if (part.contains("Speed Jerk") && !showTuning) {
p.setPen(whiteColor());
p.drawText(x, y, part);
x += fontMetrics.horizontalAdvance(part);
} else if (part.contains("Lateral Acceleration")) {
p.setPen(whiteColor());
p.drawText(x, y, part);
x += fontMetrics.horizontalAdvance(part);
} else {
part += " | ";
p.setPen(whiteColor());
p.drawText(x, y, part);
x += fontMetrics.horizontalAdvance(part);
}
}
p.restore();
}
if (showFPS) {
qint64 currentMillis = QDateTime::currentMSecsSinceEpoch();
static std::queue<std::pair<qint64, float>> fpsQueue;
static float avgFPS = 0.0;
static float maxFPS = 0.0;
static float minFPS = 99.9;
minFPS = std::min(minFPS, fps);
maxFPS = std::max(maxFPS, fps);
fpsQueue.push({currentMillis, fps});
while (!fpsQueue.empty() && currentMillis - fpsQueue.front().first > 60000) {
fpsQueue.pop();
}
if (!fpsQueue.empty()) {
float totalFPS = 0.0;
for (auto tempQueue = fpsQueue; !tempQueue.empty(); tempQueue.pop()) {
totalFPS += tempQueue.front().second;
}
avgFPS = totalFPS / fpsQueue.size();
}
QString fpsDisplayString = QString("FPS: %1 | Min: %3 | Max: %4 | Avg: %5")
.arg(qRound(fps))
.arg(qRound(minFPS))
.arg(qRound(maxFPS))
.arg(qRound(avgFPS));
p.setFont(InterFont(28, QFont::DemiBold));
p.setRenderHint(QPainter::TextAntialiasing);
p.setPen(whiteColor());
int textWidth = p.fontMetrics().horizontalAdvance(fpsDisplayString);
int xPos = (rect.width() - textWidth) / 2;
int yPos = rect.bottom() - 5;
p.drawText(xPos, yPos, fpsDisplayString);
}
}
+37
View File
@@ -24,6 +24,43 @@ private:
QWidget *map = nullptr;
QHBoxLayout* split;
// FrogPilot variables
bool blindSpotLeft;
bool blindSpotRight;
bool liveValid;
bool showBlindspot;
bool showFPS;
bool showJerk;
bool showSignal;
bool showSteering;
bool showTuning;
bool turnSignalLeft;
bool turnSignalRight;
float acceleration;
float accelerationJerk;
float accelerationJerkDifference;
float fps;
float friction;
float latAccel;
float maxAcceleration;
float speedJerk;
float speedJerkDifference;
float steer;
int maxAccelTimer;
int steeringAngleDeg;
QPoint timeoutPoint = QPoint(420, 69);
QTimer clickTimer;
inline QColor redColor(int alpha = 255) { return QColor(201, 34, 49, alpha); }
inline QColor whiteColor(int alpha = 255) { return QColor(255, 255, 255, alpha); }
Params params;
Params params_memory{"/dev/shm/params"};
private slots:
void offroadTransition(bool offroad);
void primeChanged(bool prime);
+265 -24
View File
@@ -25,10 +25,6 @@ void Sidebar::drawMetric(QPainter &p, const QPair<QString, QString> &label, QCol
}
Sidebar::Sidebar(QWidget *parent) : QFrame(parent), onroad(false), flag_pressed(false), settings_pressed(false) {
home_img = loadPixmap("../assets/images/button_home.png", home_btn.size());
flag_img = loadPixmap("../assets/images/button_flag.png", home_btn.size());
settings_img = loadPixmap("../assets/images/button_settings.png", settings_btn.size(), Qt::IgnoreAspectRatio);
connect(this, &Sidebar::valueChanged, [=] { update(); });
setAttribute(Qt::WA_OpaquePaintEvent);
@@ -38,15 +34,151 @@ Sidebar::Sidebar(QWidget *parent) : QFrame(parent), onroad(false), flag_pressed(
QObject::connect(uiState(), &UIState::uiUpdate, this, &Sidebar::updateState);
pm = std::make_unique<PubMaster, const std::initializer_list<const char *>>({"userFlag"});
// FrogPilot variables
home_label = new QLabel(this);
settings_label = new QLabel(this);
flagPngPath = "../frogpilot/assets/active_theme/icons/button_flag.png";
homeGifPath = "../frogpilot/assets/active_theme/icons/button_home.gif";
homePngPath = "../frogpilot/assets/active_theme/icons/button_home.png";
settingsGifPath = "../frogpilot/assets/active_theme/icons/button_settings.gif";
settingsPngPath = "../frogpilot/assets/active_theme/icons/button_settings.png";
randomEventGifPath = "../frogpilot/assets/random_events/icons/button_home.gif";
QObject::connect(uiState(), &UIState::themeUpdated, this, &Sidebar::updateIcons);
}
void Sidebar::showEvent(QShowEvent *event) {
updateIcons();
}
void Sidebar::updateIcons() {
updateIcon(home_label, home_gif, homeGifPath, home_btn, homePngPath, isHomeGif);
updateIcon(settings_label, settings_gif, settingsGifPath, settings_btn, settingsPngPath, isSettingsGif);
}
void Sidebar::updateIcon(QLabel *&label, QMovie *&gif, const QString &gifPath, const QRect &btnRect, const QString &pngPath, bool &isGif) {
QString selectedGifPath = gifPath;
if (util::random_int(1, 100) == 100 && btnRect == home_btn && isRandomEvents) {
selectedGifPath = randomEventGifPath;
}
if (gif != nullptr) {
gif->stop();
delete gif;
gif = nullptr;
if (label) {
label->hide();
}
}
if (QFile::exists(selectedGifPath)) {
gif = new QMovie(selectedGifPath);
if (gif->isValid()) {
gif->setScaledSize(btnRect.size());
if (label) {
label->setGeometry(btnRect);
label->setMovie(gif);
label->show();
}
gif->start();
isGif = true;
} else {
delete gif;
gif = nullptr;
isGif = false;
}
} else {
if (btnRect == home_btn) {
home_img = loadPixmap(homePngPath, btnRect.size());
flag_img = loadPixmap(flagPngPath, btnRect.size());
} else {
settings_img = loadPixmap(settingsPngPath, btnRect.size(), Qt::IgnoreAspectRatio);
}
isGif = false;
}
}
void Sidebar::mousePressEvent(QMouseEvent *event) {
if (onroad && home_btn.contains(event->pos())) {
UIState *s = uiState();
UIScene &scene = s->scene;
QPoint pos = event->pos();
QRect cpuRect = {30, 496, 240, 126};
QRect memoryRect = {30, 654, 240, 126};
QRect tempRect = {30, 338, 240, 126};
static int showChip = 0;
static int showMemory = 0;
static int showTemp = 0;
if (cpuRect.contains(pos) && isSidebarMetrics) {
showChip = (showChip + 1) % 3;
isCPU = (showChip == 1);
isGPU = (showChip == 2);
scene.cpu_metrics = isCPU;
scene.gpu_metrics = isGPU;
params.putBoolNonBlocking("ShowCPU", isCPU);
params.putBoolNonBlocking("ShowGPU", isGPU);
update();
return;
}
if (memoryRect.contains(pos) && isSidebarMetrics) {
showMemory = (showMemory + 1) % 4;
isMemoryUsage = (showMemory == 1);
isStorageLeft = (showMemory == 2);
isStorageUsed = (showMemory == 3);
scene.memory_metrics = isMemoryUsage;
scene.storage_left_metrics = isStorageLeft;
scene.storage_used_metrics = isStorageUsed;
params.putBoolNonBlocking("ShowMemoryUsage", isMemoryUsage);
params.putBoolNonBlocking("ShowStorageLeft", isStorageLeft);
params.putBoolNonBlocking("ShowStorageUsed", isStorageUsed);
update();
return;
}
if (tempRect.contains(pos) && isSidebarMetrics) {
showTemp = (showTemp + 1) % 3;
isFahrenheit = showTemp == 2;
scene.fahrenheit = isFahrenheit;
scene.numerical_temp = showTemp != 0;
params.putBoolNonBlocking("Fahrenheit", showTemp == 2);
params.putBoolNonBlocking("NumericalTemp", showTemp != 0);
update();
return;
}
if (onroad && home_btn.contains(pos)) {
flag_pressed = true;
update();
} else if (settings_btn.contains(event->pos())) {
return;
}
if (settings_btn.contains(pos)) {
settings_pressed = true;
update();
return;
}
}
@@ -70,7 +202,22 @@ void Sidebar::offroadTransition(bool offroad) {
}
void Sidebar::updateState(const UIState &s) {
if (!isVisible()) return;
if (!isVisible()) {
if (home_gif != nullptr) {
home_gif->stop();
delete home_gif;
home_gif = nullptr;
home_label->hide();
}
if (settings_gif != nullptr) {
settings_gif->stop();
delete settings_gif;
settings_gif = nullptr;
settings_label->hide();
}
return;
}
auto &sm = *(s.sm);
@@ -85,27 +232,95 @@ void Sidebar::updateState(const UIState &s) {
connectStatus = ItemStatus{{tr("CONNECT"), tr("OFFLINE")}, warning_color};
} else {
connectStatus = nanos_since_boot() - last_ping < 80e9
? ItemStatus{{tr("CONNECT"), tr("ONLINE")}, good_color}
? ItemStatus{{tr("CONNECT"), tr("ONLINE")}, sidebar_color3}
: ItemStatus{{tr("CONNECT"), tr("ERROR")}, danger_color};
}
setProperty("connectStatus", QVariant::fromValue(connectStatus));
ItemStatus tempStatus = {{tr("TEMP"), tr("HIGH")}, danger_color};
ItemStatus tempStatus = {{tr("TEMP"), isNumericalTemp ? max_temp : tr("HIGH")}, danger_color};
auto ts = deviceState.getThermalStatus();
if (ts == cereal::DeviceState::ThermalStatus::GREEN) {
tempStatus = {{tr("TEMP"), tr("GOOD")}, good_color};
tempStatus = {{tr("TEMP"), isNumericalTemp ? max_temp : tr("GOOD")}, sidebar_color1};
} else if (ts == cereal::DeviceState::ThermalStatus::YELLOW) {
tempStatus = {{tr("TEMP"), tr("OK")}, warning_color};
tempStatus = {{tr("TEMP"), isNumericalTemp ? max_temp : tr("OK")}, warning_color};
}
setProperty("tempStatus", QVariant::fromValue(tempStatus));
ItemStatus pandaStatus = {{tr("VEHICLE"), tr("ONLINE")}, good_color};
ItemStatus pandaStatus = {{tr("VEHICLE"), tr("ONLINE")}, sidebar_color2};
if (s.scene.pandaType == cereal::PandaState::PandaType::UNKNOWN) {
pandaStatus = {{tr("NO"), tr("PANDA")}, danger_color};
} else if (s.scene.started && !sm["liveLocationKalman"].getLiveLocationKalman().getGpsOK()) {
pandaStatus = {{tr("GPS"), tr("SEARCH")}, warning_color};
}
setProperty("pandaStatus", QVariant::fromValue(pandaStatus));
// FrogPilot variables
const UIScene &scene = s.scene;
isCPU = scene.cpu_metrics;
isFahrenheit = scene.fahrenheit;
isGPU = scene.gpu_metrics;
isIP = scene.ip_metrics;
isMemoryUsage = scene.memory_metrics;
isNumericalTemp = scene.numerical_temp;
isRandomEvents = scene.random_events;
isSidebarMetrics = scene.sidebar_metrics;
isStorageLeft = scene.storage_left_metrics;
isStorageUsed = scene.storage_used_metrics;
bool useStockColors = scene.use_stock_colors;
sidebar_color1 = useStockColors ? good_color : scene.sidebar_color1;
sidebar_color2 = useStockColors ? good_color : scene.sidebar_color2;
sidebar_color3 = useStockColors ? good_color : scene.sidebar_color3;
const cereal::FrogPilotDeviceState::Reader &frogpilotDeviceState = sm["frogpilotDeviceState"].getFrogpilotDeviceState();
int maxTempC = deviceState.getMaxTempC();
max_temp = isFahrenheit ? QString::number(maxTempC * 9 / 5 + 32) + "°F" : QString::number(maxTempC) + "°C";
if (isCPU || isGPU) {
auto cpu_loads = deviceState.getCpuUsagePercent();
int cpu_usage = cpu_loads.size() != 0 ? std::accumulate(cpu_loads.begin(), cpu_loads.end(), 0) / cpu_loads.size() : 0;
int gpu_usage = deviceState.getGpuUsagePercent();
int usage = isGPU ? gpu_usage : cpu_usage;
QString chip_usage = QString::number(usage) + "%";
ItemStatus cpuStatus = {{isGPU ? tr("GPU") : tr("CPU"), chip_usage}, sidebar_color2};
if (usage >= 85) {
cpuStatus = {{isGPU ? tr("GPU") : tr("CPU"), chip_usage}, danger_color};
} else if (usage >= 70) {
cpuStatus = {{isGPU ? tr("GPU") : tr("CPU"), chip_usage}, warning_color};
}
setProperty("cpuStatus", QVariant::fromValue(cpuStatus));
}
if (isMemoryUsage || isStorageLeft || isStorageUsed) {
int memory_usage = deviceState.getMemoryUsagePercent();
int storage_left = frogpilotDeviceState.getFreeSpace();
int storage_used = frogpilotDeviceState.getUsedSpace();
QString memory = QString::number(memory_usage) + "%";
QString storage = QString::number(isStorageLeft ? storage_left : storage_used) + tr(" GB");
if (isMemoryUsage) {
ItemStatus memoryStatus = {{tr("MEMORY"), memory}, sidebar_color3};
if (memory_usage >= 85) {
memoryStatus = {{tr("MEMORY"), memory}, danger_color};
} else if (memory_usage >= 70) {
memoryStatus = {{tr("MEMORY"), memory}, warning_color};
}
setProperty("memoryStatus", QVariant::fromValue(memoryStatus));
} else {
ItemStatus storageStatus = {{isStorageLeft ? tr("LEFT") : tr("USED"), storage}, sidebar_color3};
if (25 > storage_left && storage_left >= 10) {
storageStatus = {{isStorageLeft ? tr("LEFT") : tr("USED"), storage}, warning_color};
} else if (10 > storage_left) {
storageStatus = {{isStorageLeft ? tr("LEFT") : tr("USED"), storage}, danger_color};
}
setProperty("storageStatus", QVariant::fromValue(storageStatus));
}
}
}
void Sidebar::paintEvent(QPaintEvent *event) {
@@ -116,28 +331,54 @@ void Sidebar::paintEvent(QPaintEvent *event) {
p.fillRect(rect(), QColor(57, 57, 57));
// buttons
p.setOpacity(settings_pressed ? 0.65 : 1.0);
p.drawPixmap(settings_btn.x(), settings_btn.y(), settings_img);
p.setOpacity(onroad && flag_pressed ? 0.65 : 1.0);
p.drawPixmap(home_btn.x(), home_btn.y(), onroad ? flag_img : home_img);
if (!isSettingsGif) {
p.setOpacity(settings_pressed ? 0.65 : 1.0);
p.drawPixmap(settings_btn.x(), settings_btn.y(), settings_img);
}
if (!isHomeGif) {
p.setOpacity(onroad && flag_pressed ? 0.65 : 1.0);
p.drawPixmap(home_btn.x(), home_btn.y(), onroad ? flag_img : home_img);
}
p.setOpacity(1.0);
// network
int x = 58;
const QColor gray(0x54, 0x54, 0x54);
for (int i = 0; i < 5; ++i) {
p.setBrush(i < net_strength ? Qt::white : gray);
p.drawEllipse(x, 196, 27, 27);
x += 37;
p.setFont(InterFont(35));
if (isIP) {
p.setPen(QColor(0xff, 0xff, 0xff));
p.save();
p.setFont(InterFont(30));
QRect ipBox = QRect(50, 196, 225, 27);
p.drawText(ipBox, Qt::AlignLeft | Qt::AlignVCenter, uiState()->wifi->getIp4Address());
p.restore();
} else {
for (int i = 0; i < 5; ++i) {
p.setBrush(i < net_strength ? Qt::white : gray);
p.drawEllipse(x, 196, 27, 27);
x += 37;
}
p.setPen(QColor(0xff, 0xff, 0xff));
}
p.setFont(InterFont(35));
p.setPen(QColor(0xff, 0xff, 0xff));
const QRect r = QRect(50, 247, 100, 50);
p.drawText(r, Qt::AlignCenter, net_type);
// metrics
drawMetric(p, temp_status.first, temp_status.second, 338);
drawMetric(p, panda_status.first, panda_status.second, 496);
drawMetric(p, connect_status.first, connect_status.second, 654);
if (isCPU || isGPU) {
drawMetric(p, cpu_status.first, cpu_status.second, 496);
} else {
drawMetric(p, panda_status.first, panda_status.second, 496);
}
if (isMemoryUsage) {
drawMetric(p, memory_status.first, memory_status.second, 654);
} else if (isStorageLeft || isStorageUsed) {
drawMetric(p, storage_status.first, storage_status.second, 654);
} else {
drawMetric(p, connect_status.first, connect_status.second, 654);
}
}
+48
View File
@@ -3,7 +3,9 @@
#include <memory>
#include <QFrame>
#include <QLabel>
#include <QMap>
#include <QMovie>
#include "selfdrive/ui/ui.h"
@@ -18,6 +20,11 @@ class Sidebar : public QFrame {
Q_PROPERTY(QString netType MEMBER net_type NOTIFY valueChanged);
Q_PROPERTY(int netStrength MEMBER net_strength NOTIFY valueChanged);
// FrogPilot properties
Q_PROPERTY(ItemStatus cpuStatus MEMBER cpu_status NOTIFY valueChanged)
Q_PROPERTY(ItemStatus memoryStatus MEMBER memory_status NOTIFY valueChanged)
Q_PROPERTY(ItemStatus storageStatus MEMBER storage_status NOTIFY valueChanged)
public:
explicit Sidebar(QWidget* parent = 0);
@@ -59,4 +66,45 @@ protected:
private:
std::unique_ptr<PubMaster> pm;
// FrogPilot widgets
void showEvent(QShowEvent *event);
void updateIcon(QLabel *&label, QMovie *&gif, const QString &gifPath, const QRect &btnRect, const QString &pngPath, bool &isGif);
void updateIcons();
// FrogPilot variables
Params params;
ItemStatus cpu_status, memory_status, storage_status;
bool isCPU;
bool isFahrenheit;
bool isGPU;
bool isHomeGif;
bool isIP;
bool isMemoryUsage;
bool isNumericalTemp;
bool isRandomEvents;
bool isSettingsGif;
bool isSidebarMetrics;
bool isStorageLeft;
bool isStorageUsed;
QColor sidebar_color1;
QColor sidebar_color2;
QColor sidebar_color3;
QLabel *home_label;
QLabel *settings_label;
QMovie *home_gif;
QMovie *settings_gif;
QString flagPngPath;
QString homeGifPath;
QString homePngPath;
QString max_temp;
QString randomEventGifPath;
QString settingsGifPath;
QString settingsPngPath;
};
+1 -1
View File
@@ -88,7 +88,7 @@ Spinner::Spinner(QWidget *parent) : QWidget(parent) {
}
QProgressBar::chunk {
border-radius: 10px;
background-color: white;
background-color: rgba(23, 134, 68, 255);
}
)");
+5 -4
View File
@@ -26,7 +26,7 @@ QString getVersion() {
}
QString getBrand() {
return QObject::tr("openpilot");
return QObject::tr("FrogPilot");
}
QString getUserAgent() {
@@ -247,9 +247,10 @@ QPixmap bootstrapPixmap(const QString &id) {
bool hasLongitudinalControl(const cereal::CarParams::Reader &car_params) {
// Using the experimental longitudinal toggle, returns whether longitudinal control
// will be active without needing a restart of openpilot
return car_params.getExperimentalLongitudinalAvailable()
? Params().getBool("ExperimentalLongitudinalEnabled")
: car_params.getOpenpilotLongitudinalControl();
Params params = Params();
return (car_params.getExperimentalLongitudinalAvailable()
? params.getBool("ExperimentalLongitudinalEnabled")
: car_params.getOpenpilotLongitudinalControl()) && !params.getBool("DisableOpenpilotLongitudinal");
}
// ParamWatcher
+27
View File
@@ -132,6 +132,10 @@ public:
toggle.update();
}
void refresh() {
toggle.togglePosition();
}
signals:
void toggleFlipped(bool state);
@@ -221,10 +225,12 @@ public:
button->setMinimumWidth(minimum_button_width);
hlayout->addWidget(button);
button_group->addButton(button, i);
button->installEventFilter(this);
}
QObject::connect(button_group, QOverload<int>::of(&QButtonGroup::buttonClicked), [=](int id) {
params.put(key, std::to_string(id));
emit buttonClicked(id);
});
}
@@ -234,6 +240,12 @@ public:
}
}
void setEnabledButtons(int id, bool enable) {
if (QAbstractButton *button = button_group->button(id)) {
button->setEnabled(enable);
}
}
void setCheckedButton(int id) {
button_group->button(id)->setChecked(true);
}
@@ -247,6 +259,21 @@ public:
refresh();
}
signals:
void buttonClicked(int id);
void disabledButtonClicked(int id);
protected:
bool eventFilter(QObject *obj, QEvent *event) override {
if (event->type() == QEvent::MouseButtonPress) {
QPushButton *button = qobject_cast<QPushButton *>(obj);
if (button && !button->isEnabled()) {
emit disabledButtonClicked(button_group->id(button));
}
}
return AbstractControl::eventFilter(obj, event);
}
private:
std::string key;
Params params;
+21 -4
View File
@@ -141,19 +141,25 @@ InputDialog::InputDialog(const QString &title, QWidget *parent, const QString &s
QObject::connect(k, &Keyboard::emitEnter, this, &InputDialog::handleEnter);
QObject::connect(k, &Keyboard::emitBackspace, this, [=]() {
line->backspace();
updateMaxLengthSublabel(line->text());
});
QObject::connect(k, &Keyboard::emitKey, this, [=](const QString &key) {
line->insert(key.left(1));
if (line->text().length() < maxLength || maxLength == DEFAULT_MAX_LENGTH) {
line->insert(key.left(1));
updateMaxLengthSublabel(line->text());
}
});
main_layout->addWidget(k, 2, Qt::AlignBottom);
}
QString InputDialog::getText(const QString &prompt, QWidget *parent, const QString &subtitle,
bool secret, int minLength, const QString &defaultText) {
bool secret, int minLength, const QString &defaultText, int maxLength) {
InputDialog d = InputDialog(prompt, parent, subtitle, secret);
d.line->setText(defaultText);
d.setMinLength(minLength);
d.setMaxLength(maxLength);
d.updateMaxLengthSublabel(defaultText);
const int ret = d.exec();
return ret ? d.text() : QString();
}
@@ -186,10 +192,21 @@ void InputDialog::setMinLength(int length) {
minLength = length;
}
// FrogPilot functions
void InputDialog::setMaxLength(int length) {
maxLength = length;
}
void InputDialog::updateMaxLengthSublabel(const QString &text) {
if (maxLength != DEFAULT_MAX_LENGTH) {
sublabel->setText(tr("Characters: %1/%2").arg(text.length()).arg(maxLength));
}
}
// ConfirmationDialog
ConfirmationDialog::ConfirmationDialog(const QString &prompt_text, const QString &confirm_text, const QString &cancel_text,
const bool rich, QWidget *parent) : DialogBase(parent) {
const bool rich, QWidget *parent, const bool is_long) : DialogBase(parent) {
QFrame *container = new QFrame(this);
container->setStyleSheet(R"(
QFrame { background-color: #1B1B1B; color: #C9C9C9; }
@@ -197,7 +214,7 @@ ConfirmationDialog::ConfirmationDialog(const QString &prompt_text, const QString
#confirm_btn:pressed { background-color: #3049F4; }
)");
QVBoxLayout *main_layout = new QVBoxLayout(container);
main_layout->setContentsMargins(32, rich ? 32 : 120, 32, 32);
main_layout->setContentsMargins(32, rich || is_long ? 32 : 120, 32, 32);
QLabel *prompt = new QLabel(prompt_text, this);
prompt->setWordWrap(true);
+12 -2
View File
@@ -9,6 +9,7 @@
#include "selfdrive/ui/qt/widgets/keyboard.h"
const int DEFAULT_MAX_LENGTH = 512;
class DialogBase : public QDialog {
Q_OBJECT
@@ -27,12 +28,15 @@ class InputDialog : public DialogBase {
public:
explicit InputDialog(const QString &title, QWidget *parent, const QString &subtitle = "", bool secret = false);
static QString getText(const QString &title, QWidget *parent, const QString &subtitle = "",
bool secret = false, int minLength = -1, const QString &defaultText = "");
bool secret = false, int minLength = -1, const QString &defaultText = "", int maxLength = DEFAULT_MAX_LENGTH);
QString text();
void setMessage(const QString &message, bool clearInputField = true);
void setMinLength(int length);
void show();
// FrogPilot widgets
void setMaxLength(int length);
private:
int minLength;
QLineEdit *line;
@@ -42,6 +46,12 @@ private:
QVBoxLayout *main_layout;
QPushButton *eye_btn;
// FrogPilot widgets
void updateMaxLengthSublabel(const QString &text);
// FrogPilot variables
int maxLength;
private slots:
void handleEnter();
@@ -55,7 +65,7 @@ class ConfirmationDialog : public DialogBase {
public:
explicit ConfirmationDialog(const QString &prompt_text, const QString &confirm_text,
const QString &cancel_text, const bool rich, QWidget* parent);
const QString &cancel_text, const bool rich, QWidget* parent, const bool is_long=false);
static bool alert(const QString &prompt_text, QWidget *parent);
static bool confirm(const QString &prompt_text, const QString &confirm_text, QWidget *parent);
static bool rich(const QString &prompt_text, QWidget *parent);
+12
View File
@@ -32,6 +32,17 @@ AbstractAlert::AbstractAlert(bool hasRebootBtn, QWidget *parent) : QFrame(parent
footer_layout->addWidget(dismiss_btn, 0, Qt::AlignBottom | Qt::AlignLeft);
QObject::connect(dismiss_btn, &QPushButton::clicked, this, &AbstractAlert::dismiss);
disable_check_btn = new QPushButton(tr("Disable Internet Check"));
disable_check_btn->setVisible(false);
disable_check_btn->setFixedSize(625, 125);
footer_layout->addWidget(disable_check_btn, 1, Qt::AlignBottom | Qt::AlignCenter);
QObject::connect(disable_check_btn, &QPushButton::clicked, [=]() {
params.putBool("DeviceManagement", true);
params.putBool("OfflineMode", true);
});
QObject::connect(disable_check_btn, &QPushButton::clicked, this, &AbstractAlert::dismiss);
disable_check_btn->setStyleSheet(R"(color: white; background-color: #4F4F4F;)");
snooze_btn = new QPushButton(tr("Snooze Update"));
snooze_btn->setVisible(false);
snooze_btn->setFixedSize(550, 125);
@@ -107,6 +118,7 @@ int OffroadAlert::refresh() {
label->setVisible(!text.isEmpty());
alertCount += !text.isEmpty();
}
disable_check_btn->setVisible(!alerts["Offroad_ConnectivityNeeded"]->text().isEmpty());
snooze_btn->setVisible(!alerts["Offroad_ConnectivityNeeded"]->text().isEmpty());
return alertCount;
}
+1
View File
@@ -15,6 +15,7 @@ class AbstractAlert : public QFrame {
protected:
AbstractAlert(bool hasRebootBtn, QWidget *parent = nullptr);
QPushButton *disable_check_btn;
QPushButton *snooze_btn;
QVBoxLayout *scrollable_layout;
Params params;
+2
View File
@@ -18,7 +18,9 @@ SshControl::SshControl() :
}
} else {
params.remove("GithubUsername");
params_storage.remove("GithubUsername");
params.remove("GithubSshKeys");
params_storage.remove("GithubSshKeys");
refresh();
}
});
+3
View File
@@ -29,4 +29,7 @@ private:
void refresh();
void getUserKeys(const QString &username);
// FrogPilot variables
Params params_storage{"/persist/params"};
};
+1 -1
View File
@@ -75,7 +75,7 @@ void Toggle::setEnabled(bool value) {
enabled = value;
if (value) {
circleColor.setRgb(0xfafafa);
green.setRgb(0x33ab4c);
green.setRgb(0x178644);
} else {
circleColor.setRgb(0x888888);
green.setRgb(0x227722);
+30 -1
View File
@@ -81,6 +81,35 @@ WiFiPromptWidget::WiFiPromptWidget(QWidget *parent) : QFrame(parent) {
}
stack->addWidget(uploading);
// not uploading data
QWidget *notUploading = new QWidget;
QVBoxLayout *not_uploading_layout = new QVBoxLayout(notUploading);
not_uploading_layout->setContentsMargins(64, 56, 64, 56);
not_uploading_layout->setSpacing(36);
{
QHBoxLayout *title_layout = new QHBoxLayout;
{
QLabel *title = new QLabel(tr("Uploading disabled"));
title->setStyleSheet("font-size: 64px; font-weight: 600;");
title->setWordWrap(true);
title->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
title_layout->addWidget(title);
title_layout->addStretch();
QLabel *icon = new QLabel;
QPixmap pixmap("../frogpilot/assets/other_images/icon_wifi_uploading_disabled.svg");
icon->setPixmap(pixmap.scaledToWidth(120, Qt::SmoothTransformation));
title_layout->addWidget(icon);
}
not_uploading_layout->addLayout(title_layout);
QLabel *desc = new QLabel(tr("Toggle off the 'Turn Off Data Uploads' toggle to re-enable uploads."));
desc->setStyleSheet("font-size: 48px; font-weight: 400;");
desc->setWordWrap(true);
not_uploading_layout->addWidget(desc);
}
stack->addWidget(notUploading);
setStyleSheet(R"(
WiFiPromptWidget {
background-color: #333333;
@@ -99,5 +128,5 @@ void WiFiPromptWidget::updateState(const UIState &s) {
auto network_type = sm["deviceState"].getDeviceState().getNetworkType();
auto uploading = network_type == cereal::DeviceState::NetworkType::WIFI ||
network_type == cereal::DeviceState::NetworkType::ETHERNET;
stack->setCurrentIndex(uploading ? 1 : 0);
stack->setCurrentIndex(s.scene.no_uploads ? 2 : uploading ? 1 : 0);
}
+4
View File
@@ -18,6 +18,10 @@ signals:
public slots:
void updateState(const UIState &s);
private:
// FrogPilot variables
Params params;
protected:
QStackedLayout *stack;
};
+8 -3
View File
@@ -74,7 +74,12 @@ void MainWindow::closeSettings() {
main_layout->setCurrentWidget(homeWindow);
if (uiState()->scene.started) {
homeWindow->showSidebar(false);
// Map is always shown when using navigate on openpilot
if (uiState()->scene.navigate_on_openpilot) {
homeWindow->showMapPanel(true);
} else {
homeWindow->showSidebar(params.getBool("Sidebar"));
}
}
}
@@ -87,8 +92,8 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
case QEvent::MouseButtonPress:
case QEvent::MouseMove: {
// ignore events when device is awakened by resetInteractiveTimeout
ignore = !device()->isAwake();
device()->resetInteractiveTimeout();
ignore = !device()->isAwake() || uiState()->scene.driver_camera_timer >= 10;
device()->resetInteractiveTimeout(uiState()->scene.screen_timeout, uiState()->scene.screen_timeout_onroad);
break;
}
default:
+3
View File
@@ -22,4 +22,7 @@ private:
HomeWindow *homeWindow;
SettingsWindow *settingsWindow;
OnboardingWindow *onboardingWindow;
// FrogPilot variables
Params params;
};
+104 -7
View File
@@ -3,6 +3,7 @@ import numpy as np
import time
import wave
from pathlib import Path
from cereal import car, messaging
from openpilot.common.basedir import BASEDIR
@@ -13,6 +14,8 @@ from openpilot.common.swaglog import cloudlog
from openpilot.system import micd
from openpilot.selfdrive.frogpilot.frogpilot_variables import ACTIVE_THEME_PATH, RANDOM_EVENTS_PATH, get_frogpilot_toggles, params_memory
SAMPLE_RATE = 48000
SAMPLE_BUFFER = 4096 # (approx 100ms)
MAX_VOLUME = 1.0
@@ -38,6 +41,23 @@ sound_list: dict[int, tuple[str, int | None, float]] = {
AudibleAlert.warningSoft: ("warning_soft.wav", None, MAX_VOLUME),
AudibleAlert.warningImmediate: ("warning_immediate.wav", None, MAX_VOLUME),
# FrogPilot sounds
AudibleAlert.angry: ("angry.wav", 1, MAX_VOLUME),
AudibleAlert.continued: ("continued.wav", 1, MAX_VOLUME),
AudibleAlert.dejaVu: ("dejaVu.wav", 1, MAX_VOLUME),
AudibleAlert.doc: ("doc.wav", 1, MAX_VOLUME),
AudibleAlert.fart: ("fart.wav", 1, MAX_VOLUME),
AudibleAlert.firefox: ("firefox.wav", 1, MAX_VOLUME),
AudibleAlert.goat: ("goat.wav", None, MAX_VOLUME),
AudibleAlert.hal9000: ("hal9000.wav", 1, MAX_VOLUME),
AudibleAlert.mail: ("mail.wav", 1, MAX_VOLUME),
AudibleAlert.nessie: ("nessie.wav", 1, MAX_VOLUME),
AudibleAlert.noice: ("noice.wav", 1, MAX_VOLUME),
AudibleAlert.promptRepeat: ("prompt_repeat.wav", None, MAX_VOLUME),
AudibleAlert.startup: ("startup.wav", 1, MAX_VOLUME),
AudibleAlert.thisIsFine: ("this_is_fine.wav", 1, MAX_VOLUME),
AudibleAlert.uwu: ("uwu.wav", 1, MAX_VOLUME),
}
def check_controls_timeout_alert(sm):
@@ -52,8 +72,6 @@ def check_controls_timeout_alert(sm):
class Soundd:
def __init__(self):
self.load_sounds()
self.current_alert = AudibleAlert.none
self.current_volume = MIN_VOLUME
self.current_sound_frame = 0
@@ -62,6 +80,33 @@ class Soundd:
self.spl_filter_weighted = FirstOrderFilter(0, 2.5, FILTER_DT, initialized=False)
# FrogPilot variables
self.frogpilot_toggles = get_frogpilot_toggles()
self.auto_volume = 0
self.previous_sound_pack = None
self.random_events_directory = Path(RANDOM_EVENTS_PATH) / "sounds"
self.random_events_map = {
AudibleAlert.angry: MAX_VOLUME,
AudibleAlert.continued: MAX_VOLUME,
AudibleAlert.dejaVu: MAX_VOLUME,
AudibleAlert.doc: MAX_VOLUME,
AudibleAlert.fart: MAX_VOLUME,
AudibleAlert.firefox: MAX_VOLUME,
AudibleAlert.goat: MAX_VOLUME,
AudibleAlert.hal9000: MAX_VOLUME,
AudibleAlert.mail: MAX_VOLUME,
AudibleAlert.nessie: MAX_VOLUME,
AudibleAlert.noice: MAX_VOLUME,
AudibleAlert.thisIsFine: MAX_VOLUME,
AudibleAlert.uwu: MAX_VOLUME,
}
self.update_frogpilot_sounds()
def load_sounds(self):
self.loaded_sounds: dict[int, np.ndarray] = {}
@@ -69,7 +114,17 @@ class Soundd:
for sound in sound_list:
filename, play_count, volume = sound_list[sound]
wavefile = wave.open(BASEDIR + "/selfdrive/assets/sounds/" + filename, 'r')
if sound in self.random_events_map:
wavefile = wave.open(str(self.random_events_directory / filename), 'r')
else:
try:
wavefile = wave.open(str(self.sound_directory / filename), 'r')
except FileNotFoundError:
if filename == "prompt_repeat.wav":
filename = "prompt.wav"
if filename == "startup.wav":
filename = "engage.wav"
wavefile = wave.open(BASEDIR + "/selfdrive/assets/sounds/" + filename, 'r')
assert wavefile.getnchannels() == 1
assert wavefile.getsampwidth() == 2
@@ -136,18 +191,31 @@ class Soundd:
# sounddevice must be imported after forking processes
import sounddevice as sd
sm = messaging.SubMaster(['controlsState', 'microphone'])
sm = messaging.SubMaster(['controlsState', 'microphone', 'frogpilotPlan'])
with self.get_stream(sd) as stream:
rk = Ratekeeper(20)
cloudlog.info(f"soundd stream started: {stream.samplerate=} {stream.channels=} {stream.dtype=} {stream.device=}, {stream.blocksize=}")
while True:
while not params_memory.get_bool("TestingSound"):
sm.update(0)
if sm.updated['microphone'] and self.current_alert == AudibleAlert.none: # only update volume filter when not playing alert
self.spl_filter_weighted.update(sm["microphone"].soundPressureWeightedDb)
self.current_volume = self.calculate_volume(float(self.spl_filter_weighted.x))
if self.frogpilot_toggles.alert_volume_control:
self.spl_filter_weighted.update(sm["microphone"].soundPressureWeightedDb)
self.auto_volume = self.calculate_volume(float(self.spl_filter_weighted.x))
self.current_volume = 0.0
else:
self.spl_filter_weighted.update(sm["microphone"].soundPressureWeightedDb)
self.current_volume = self.calculate_volume(float(self.spl_filter_weighted.x))
elif self.frogpilot_toggles.alert_volume_control and self.current_alert in self.volume_map:
self.current_volume = self.volume_map[self.current_alert] / 100.0
if self.current_volume == 1.01:
self.current_volume = self.auto_volume
elif self.current_alert in self.random_events_map:
self.current_volume = self.random_events_map[self.current_alert]
self.get_audible_alert(sm)
@@ -155,6 +223,35 @@ class Soundd:
assert stream.active
# Update FrogPilot parameters
if sm['frogpilotPlan'].togglesUpdated:
self.frogpilot_toggles = get_frogpilot_toggles()
self.update_frogpilot_sounds()
def update_frogpilot_sounds(self):
self.volume_map = {
AudibleAlert.engage: self.frogpilot_toggles.engage_volume,
AudibleAlert.disengage: self.frogpilot_toggles.disengage_volume,
AudibleAlert.refuse: self.frogpilot_toggles.refuse_volume,
AudibleAlert.prompt: self.frogpilot_toggles.prompt_volume,
AudibleAlert.promptRepeat: self.frogpilot_toggles.prompt_volume,
AudibleAlert.promptDistracted: self.frogpilot_toggles.promptDistracted_volume,
AudibleAlert.warningSoft: self.frogpilot_toggles.warningSoft_volume,
AudibleAlert.warningImmediate: self.frogpilot_toggles.warningImmediate_volume,
AudibleAlert.goat: self.frogpilot_toggles.prompt_volume,
}
if self.frogpilot_toggles.sound_pack != "stock":
self.sound_directory = Path(ACTIVE_THEME_PATH) / "sounds"
else:
self.sound_directory = Path(BASEDIR) / "selfdrive" / "assets" / "sounds"
if self.frogpilot_toggles.sound_pack != self.previous_sound_pack:
self.load_sounds()
self.previous_sound_pack = self.frogpilot_toggles.sound_pack
def main():
s = Soundd()
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+325 -18
View File
@@ -45,11 +45,23 @@ int get_path_length_idx(const cereal::XYZTData::Reader &line, const float path_h
}
void update_leads(UIState *s, const cereal::RadarState::Reader &radar_state, const cereal::XYZTData::Reader &line) {
for (int i = 0; i < 2; ++i) {
auto lead_data = (i == 0) ? radar_state.getLeadOne() : radar_state.getLeadTwo();
SubMaster &sm = *(s->sm);
float path_offset_z = sm["liveCalibration"].getLiveCalibration().getHeight()[0];
cereal::RadarState::LeadData::Reader (cereal::RadarState::Reader::*get_lead_data[6])() const = {
&cereal::RadarState::Reader::getLeadOne,
&cereal::RadarState::Reader::getLeadTwo,
&cereal::RadarState::Reader::getLeadLeft,
&cereal::RadarState::Reader::getLeadRight,
&cereal::RadarState::Reader::getLeadLeftFar,
&cereal::RadarState::Reader::getLeadRightFar
};
for (int i = 0; i < 6; ++i) {
auto lead_data = (radar_state.*get_lead_data[i])();
if (lead_data.getStatus()) {
float z = line.getZ()[get_path_length_idx(line, lead_data.getDRel())];
calib_frame_to_full_frame(s, lead_data.getDRel(), -lead_data.getYRel(), z + 1.22, &s->scene.lead_vertices[i]);
calib_frame_to_full_frame(s, lead_data.getDRel(), -lead_data.getYRel(), z + path_offset_z, &s->scene.lead_vertices[i]);
}
}
}
@@ -79,12 +91,17 @@ void update_line_data(const UIState *s, const cereal::XYZTData::Reader &line,
void update_model(UIState *s,
const cereal::ModelDataV2::Reader &model,
const cereal::UiPlan::Reader &plan) {
SubMaster &sm = *(s->sm);
UIScene &scene = s->scene;
scene.left_curve = model.getOrientationRate().getZ()[33 - 1] < 0;
float path_offset_z = sm["liveCalibration"].getLiveCalibration().getHeight()[0];
auto plan_position = plan.getPosition();
scene.model_length = model.getPosition().getX()[33 - 1];
if (plan_position.getX().size() < model.getPosition().getX().size()) {
plan_position = model.getPosition();
}
float max_distance = std::clamp(*(plan_position.getX().end() - 1),
float max_distance = scene.unlimited_road_ui_length ? *(plan_position.getX().end() - 1) :
std::clamp(*(plan_position.getX().end() - 1),
MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE);
// update lane lines
@@ -93,7 +110,7 @@ void update_model(UIState *s,
int max_idx = get_path_length_idx(lane_lines[0], max_distance);
for (int i = 0; i < std::size(scene.lane_line_vertices); i++) {
scene.lane_line_probs[i] = lane_line_probs[i];
update_line_data(s, lane_lines[i], 0.025 * scene.lane_line_probs[i], 0, &scene.lane_line_vertices[i], max_idx);
update_line_data(s, lane_lines[i], (scene.model_ui ? scene.lane_line_width : 0.025) * scene.lane_line_probs[i], 0, &scene.lane_line_vertices[i], max_idx);
}
// update road edges
@@ -101,17 +118,46 @@ void update_model(UIState *s,
const auto road_edge_stds = model.getRoadEdgeStds();
for (int i = 0; i < std::size(scene.road_edge_vertices); i++) {
scene.road_edge_stds[i] = road_edge_stds[i];
update_line_data(s, road_edges[i], 0.025, 0, &scene.road_edge_vertices[i], max_idx);
update_line_data(s, road_edges[i], scene.model_ui ? scene.road_edge_width : 0.025, 0, &scene.road_edge_vertices[i], max_idx);
}
// Update adjacent paths
for (int i = 4; i <= 5; i++) {
update_line_data(s, lane_lines[i], (i == 4 ? scene.lane_width_left : scene.lane_width_right) / 2.0f, 0, &scene.track_adjacent_vertices[i], max_idx, false);
}
// update path
auto lead_one = (*s->sm)["radarState"].getRadarState().getLeadOne();
if (lead_one.getStatus()) {
const float lead_d = lead_one.getDRel() * 2.;
max_distance = std::clamp((float)(lead_d - fmin(lead_d * 0.35, 10.)), 0.0f, max_distance);
float path_width;
if (scene.dynamic_path_width) {
float multiplier = scene.enabled ? 1.0f : scene.always_on_lateral_active ? 0.75f : 0.50f;
path_width = scene.path_width * multiplier;
} else {
path_width = scene.path_width;
}
if (scene.radarless_model) {
auto lead_count = model.getLeadsV3().size();
if (lead_count > 0) {
auto lead_one = model.getLeadsV3()[0];
if (lead_one.getProb() > scene.lead_detection_probability) {
const float lead_d = lead_one.getX()[0] * 2.;
max_distance = std::clamp((float)(lead_d - fmin(lead_d * 0.35, 10.)), 0.0f, max_distance);
}
}
} else {
auto lead_one = (*s->sm)["radarState"].getRadarState().getLeadOne();
if (lead_one.getModelProb() > scene.lead_detection_probability) {
const float lead_d = lead_one.getDRel() * 2.;
max_distance = std::clamp((float)(lead_d - fmin(lead_d * 0.35, 10.)), 0.0f, max_distance);
}
}
max_idx = get_path_length_idx(plan_position, max_distance);
update_line_data(s, plan_position, 0.9, 1.22, &scene.track_vertices, max_idx, false);
update_line_data(s, plan_position, scene.model_ui ? path_width * (1 - (scene.path_edge_width / 100.0f)) : 0.9, path_offset_z, &scene.track_vertices, max_idx, false);
// Update path edges
update_line_data(s, plan_position, scene.model_ui ? path_width : 0, path_offset_z, &scene.track_edge_vertices, max_idx, false);
}
void update_dmonitoring(UIState *s, const cereal::DriverStateV2::Reader &driverstate, float dm_fade_state, bool is_rhd) {
@@ -154,6 +200,7 @@ static void update_sockets(UIState *s) {
}
static void update_state(UIState *s) {
Params params = Params();
SubMaster &sm = *(s->sm);
UIScene &scene = s->scene;
@@ -197,9 +244,94 @@ static void update_state(UIState *s) {
} else if ((s->sm->frame - s->sm->rcv_frame("pandaStates")) > 5*UI_FREQ) {
scene.pandaType = cereal::PandaState::PandaType::UNKNOWN;
}
if (sm.updated("carControl")) {
auto carControl = sm["carControl"].getCarControl();
scene.steer = carControl.getActuators().getSteer();
}
if (sm.updated("carParams")) {
scene.longitudinal_control = sm["carParams"].getCarParams().getOpenpilotLongitudinalControl();
}
if (sm.updated("carState")) {
auto carState = sm["carState"].getCarState();
scene.acceleration = carState.getAEgo();
scene.blind_spot_left = carState.getLeftBlindspot();
scene.blind_spot_right = carState.getRightBlindspot();
scene.parked = carState.getGearShifter() == cereal::CarState::GearShifter::PARK;
scene.reverse = carState.getGearShifter() == cereal::CarState::GearShifter::REVERSE;
scene.standstill = carState.getStandstill() && !scene.reverse;
scene.steering_angle_deg = -carState.getSteeringAngleDeg();
scene.turn_signal_left = carState.getLeftBlinker();
scene.turn_signal_right = carState.getRightBlinker();
}
if (sm.updated("controlsState")) {
auto controlsState = sm["controlsState"].getControlsState();
scene.enabled = controlsState.getEnabled();
scene.experimental_mode = scene.enabled && controlsState.getExperimentalMode();
}
if (sm.updated("deviceState")) {
auto deviceState = sm["deviceState"].getDeviceState();
scene.online = deviceState.getNetworkType() != cereal::DeviceState::NetworkType::NONE;
}
if (sm.updated("frogpilotCarControl")) {
auto frogpilotCarControl = sm["frogpilotCarControl"].getFrogpilotCarControl();
scene.always_on_lateral_active = !scene.enabled && frogpilotCarControl.getAlwaysOnLateralActive();
}
if (sm.updated("frogpilotCarState")) {
auto frogpilotCarState = sm["frogpilotCarState"].getFrogpilotCarState();
scene.brake_lights_on = frogpilotCarState.getBrakeLights();
scene.dashboard_speed_limit = frogpilotCarState.getDashboardSpeedLimit();
scene.traffic_mode_active = frogpilotCarState.getTrafficModeActive();
}
if (sm.updated("frogpilotNavigation")) {
auto frogpilotNavigation = sm["frogpilotNavigation"].getFrogpilotNavigation();
scene.navigation_speed_limit = frogpilotNavigation.getNavigationSpeedLimit();
}
if (sm.updated("frogpilotPlan")) {
auto frogpilotPlan = sm["frogpilotPlan"].getFrogpilotPlan();
scene.acceleration_jerk = frogpilotPlan.getAccelerationJerk();
scene.acceleration_jerk_difference = frogpilotPlan.getAccelerationJerkStock() - scene.acceleration_jerk;
scene.desired_follow = frogpilotPlan.getDesiredFollowDistance();
scene.lane_width_left = frogpilotPlan.getLaneWidthLeft();
scene.lane_width_right = frogpilotPlan.getLaneWidthRight();
scene.mtsc_speed = frogpilotPlan.getMtscSpeed();
scene.red_light = frogpilotPlan.getRedLight();
scene.speed_jerk = frogpilotPlan.getSpeedJerk();
scene.speed_jerk_difference = frogpilotPlan.getSpeedJerkStock() - scene.speed_jerk;
scene.speed_limit = frogpilotPlan.getSlcSpeedLimit();
scene.speed_limit_changed = scene.speed_limit_controller && frogpilotPlan.getSpeedLimitChanged();
scene.speed_limit_map = frogpilotPlan.getSlcMapSpeedLimit();
scene.speed_limit_offset = frogpilotPlan.getSlcSpeedLimitOffset();
scene.speed_limit_overridden = frogpilotPlan.getSlcOverridden();
scene.speed_limit_overridden_speed = frogpilotPlan.getSlcOverriddenSpeed();
scene.speed_limit_source = frogpilotPlan.getSlcSpeedLimitSource().cStr();
scene.unconfirmed_speed_limit = frogpilotPlan.getUnconfirmedSlcSpeedLimit();
scene.upcoming_speed_limit = frogpilotPlan.getUpcomingSLCSpeedLimit();
scene.vtsc_controlling_curve = frogpilotPlan.getVtscControllingCurve();
scene.vtsc_speed = frogpilotPlan.getVtscSpeed();
if (frogpilotPlan.getTogglesUpdated() && sm.frame % UI_FREQ == 0) {
scene.frogpilot_toggles = QJsonDocument::fromJson(s->params_memory.get("FrogPilotToggles", true).c_str()).object();
ui_update_params(s);
ui_update_theme(s);
}
}
if (sm.updated("liveLocationKalman")) {
auto liveLocationKalman = sm["liveLocationKalman"].getLiveLocationKalman();
auto orientation = liveLocationKalman.getCalibratedOrientationNED();
if (orientation.getValid()) {
scene.bearing_deg = RAD2DEG(orientation.getValue()[2]);
}
}
if (sm.updated("liveTorqueParameters")) {
auto liveTorqueParameters = sm["liveTorqueParameters"].getLiveTorqueParameters();
scene.friction = liveTorqueParameters.getFrictionCoefficientFiltered();
scene.lat_accel = liveTorqueParameters.getLatAccelFactorFiltered();
scene.live_valid = liveTorqueParameters.getLiveValid();
}
if (sm.updated("navInstruction")) {
auto navInstruction = sm["navInstruction"].getNavInstruction();
scene.upcoming_maneuver_distance = navInstruction.getManeuverDistance();
}
if (sm.updated("wideRoadCameraState")) {
auto cam_state = sm["wideRoadCameraState"].getWideRoadCameraState();
float scale = (cam_state.getSensor() == cereal::FrameData::ImageSensor::AR0231) ? 6.0f : 1.0f;
@@ -220,28 +352,162 @@ void ui_update_params(UIState *s) {
auto params = Params();
s->scene.is_metric = params.getBool("IsMetric");
s->scene.map_on_left = params.getBool("NavSettingLeftSide");
ui_update_frogpilot_params(s);
}
void ui_update_frogpilot_params(UIState *s) {
UIScene &scene = s->scene;
scene.acceleration_path = scene.frogpilot_toggles.value("acceleration_path").toBool();
scene.adjacent_path = scene.frogpilot_toggles.value("adjacent_paths").toBool();
scene.adjacent_path_metrics = scene.frogpilot_toggles.value("adjacent_path_metrics").toBool();
scene.always_on_lateral = scene.frogpilot_toggles.value("always_on_lateral").toBool();
scene.big_map = scene.frogpilot_toggles.value("big_map").toBool();
scene.blind_spot_path = scene.frogpilot_toggles.value("blind_spot_path").toBool();
scene.camera_view = scene.frogpilot_toggles.value("camera_view").toDouble();
scene.cem_status = scene.frogpilot_toggles.value("cem_status").toBool();
scene.compass = scene.frogpilot_toggles.value("compass").toBool();
scene.conditional_experimental = scene.frogpilot_toggles.value("conditional_experimental_mode").toBool();
scene.cpu_metrics = scene.frogpilot_toggles.value("cpu_metrics").toBool();
scene.driver_camera_in_reverse = scene.frogpilot_toggles.value("driver_camera_in_reverse").toBool();
scene.dynamic_path_width = scene.frogpilot_toggles.value("dynamic_path_width").toBool();
scene.dynamic_pedals_on_ui = scene.frogpilot_toggles.value("dynamic_pedals_on_ui").toBool();
scene.experimental_mode_via_tap = scene.frogpilot_toggles.value("experimental_mode_via_tap").toBool();
scene.fahrenheit = scene.frogpilot_toggles.value("fahrenheit").toBool();
scene.frogs_go_moo = scene.frogpilot_toggles.value("frogs_go_moo").toBool();
scene.full_map = scene.frogpilot_toggles.value("full_map").toBool();
scene.gpu_metrics = scene.frogpilot_toggles.value("gpu_metrics").toBool();
scene.hide_alerts = scene.frogpilot_toggles.value("hide_alerts").toBool();
scene.hide_csc_ui = scene.frogpilot_toggles.value("hide_csc_ui").toBool();
scene.hide_lead_marker = scene.frogpilot_toggles.value("hide_lead_marker").toBool();
scene.hide_map_icon = scene.frogpilot_toggles.value("hide_map_icon").toBool();
scene.hide_max_speed = scene.frogpilot_toggles.value("hide_max_speed").toBool();
scene.hide_speed = scene.frogpilot_toggles.value("hide_speed").toBool();
scene.hide_speed_limit = scene.frogpilot_toggles.value("hide_speed_limit").toBool();
scene.ip_metrics = scene.frogpilot_toggles.value("ip_metrics").toBool();
scene.jerk_metrics = scene.frogpilot_toggles.value("jerk_metrics").toBool();
scene.lateral_tuning_metrics = scene.frogpilot_toggles.value("lateral_tuning_metrics").toBool();
scene.lane_detection_width = scene.frogpilot_toggles.value("lane_detection_width").toDouble();
scene.lane_line_width = scene.frogpilot_toggles.value("lane_line_width").toDouble();
scene.lead_detection_probability = scene.frogpilot_toggles.value("lead_detection_probability").toDouble();
scene.lead_metrics = scene.frogpilot_toggles.value("lead_metrics").toBool();
scene.map_style = scene.frogpilot_toggles.value("map_style").toDouble();
scene.memory_metrics = scene.frogpilot_toggles.value("memory_metrics").toBool();
scene.minimum_lane_change_speed = scene.frogpilot_toggles.value("minimum_lane_change_speed").toDouble();
scene.model = scene.frogpilot_toggles.value("model").toString();
scene.model_name = scene.frogpilot_toggles.value("model_name").toString();
scene.model_randomizer = scene.frogpilot_toggles.value("model_randomizer").toBool();
scene.model_ui = scene.frogpilot_toggles.value("model_ui").toBool();
scene.mtsc_enabled = scene.frogpilot_toggles.value("map_turn_speed_controller").toBool();
scene.no_logging = scene.frogpilot_toggles.value("no_logging").toBool();
scene.no_uploads = scene.frogpilot_toggles.value("no_uploads").toBool();
scene.numerical_temp = scene.frogpilot_toggles.value("numerical_temp").toBool();
scene.onroad_distance_button = scene.frogpilot_toggles.value("onroad_distance_button").toBool();
scene.path_edge_width = scene.frogpilot_toggles.value("path_edge_width").toDouble();
scene.path_width = scene.frogpilot_toggles.value("path_width").toDouble();
scene.pedals_on_ui = scene.frogpilot_toggles.value("pedals_on_ui").toBool();
scene.radarless_model = scene.frogpilot_toggles.value("radarless_model").toBool();
scene.random_events = scene.frogpilot_toggles.value("random_events").toBool();
scene.rainbow_path = scene.frogpilot_toggles.value("rainbow_path").toBool();
scene.road_edge_width = scene.frogpilot_toggles.value("road_edge_width").toDouble();
scene.road_name_ui = scene.frogpilot_toggles.value("road_name_ui").toBool();
scene.rotating_wheel = scene.frogpilot_toggles.value("rotating_wheel").toBool();
if (scene.screen_brightness == -1) {
scene.screen_brightness = scene.frogpilot_toggles.value("screen_brightness").toDouble();
}
if (scene.screen_brightness_onroad == -1) {
scene.screen_brightness_onroad = scene.frogpilot_toggles.value("screen_brightness_onroad").toDouble();
}
scene.screen_recorder = scene.frogpilot_toggles.value("screen_recorder").toBool();
scene.screen_timeout = scene.frogpilot_toggles.value("screen_timeout").toDouble();
scene.screen_timeout_onroad = scene.frogpilot_toggles.value("screen_timeout_onroad").toDouble();
scene.show_blind_spot = scene.frogpilot_toggles.value("blind_spot_metrics").toBool();
scene.show_fps = scene.frogpilot_toggles.value("show_fps").toBool();
scene.show_speed_limit_offset = scene.frogpilot_toggles.value("show_speed_limit_offset").toBool();
scene.show_speed_limits = scene.frogpilot_toggles.value("show_speed_limits").toBool();
scene.show_stopping_point = scene.frogpilot_toggles.value("show_stopping_point").toBool();
scene.show_stopping_point_metrics = scene.frogpilot_toggles.value("show_stopping_point_metrics").toBool();
scene.sidebar_metrics = scene.frogpilot_toggles.value("sidebar_metrics").toBool();
scene.signal_metrics = scene.frogpilot_toggles.value("signal_metrics").toBool();
scene.speed_limit_controller = scene.frogpilot_toggles.value("speed_limit_controller").toBool();
scene.speed_limit_sources = scene.frogpilot_toggles.value("speed_limit_sources").toBool();
scene.speed_limit_vienna = scene.frogpilot_toggles.value("speed_limit_vienna").toBool();
scene.standby_mode = scene.frogpilot_toggles.value("standby_mode").toBool();
scene.static_pedals_on_ui = scene.frogpilot_toggles.value("static_pedals_on_ui").toBool();
scene.steering_metrics = scene.frogpilot_toggles.value("steering_metrics").toBool();
scene.stopped_timer = scene.frogpilot_toggles.value("stopped_timer").toBool();
scene.storage_left_metrics = scene.frogpilot_toggles.value("storage_left_metrics").toBool();
scene.storage_used_metrics = scene.frogpilot_toggles.value("storage_used_metrics").toBool();
scene.tethering_config = scene.frogpilot_toggles.value("tethering_config").toDouble();
scene.unlimited_road_ui_length = scene.frogpilot_toggles.value("unlimited_road_ui_length").toBool();
scene.use_si_metrics = scene.frogpilot_toggles.value("use_si_metrics").toBool();
scene.use_wheel_speed = scene.frogpilot_toggles.value("use_wheel_speed").toBool();
scene.vtsc_enabled = scene.frogpilot_toggles.value("vision_turn_controller").toBool();
if (scene.tethering_config == 1) {
s->wifi->setTetheringEnabled(true);
}
emit s->togglesUpdated();
}
void ui_update_theme(UIState *s) {
UIScene &scene = s->scene;
scene.use_stock_colors = scene.frogpilot_toggles.value("color_scheme").toString() == "stock";
scene.use_stock_wheel = scene.frogpilot_toggles.value("wheel_image").toString() == "stock";
if (!scene.use_stock_colors) {
scene.use_stock_colors |= !loadThemeColors("", true).isValid();
scene.lane_lines_color = loadThemeColors("LaneLines");
scene.lead_marker_color = loadThemeColors("LeadMarker");
scene.path_color = loadThemeColors("Path");
scene.path_edges_color = loadThemeColors("PathEdge");
scene.sidebar_color1 = loadThemeColors("Sidebar1");
scene.sidebar_color2 = loadThemeColors("Sidebar2");
scene.sidebar_color3 = loadThemeColors("Sidebar3");
}
emit s->themeUpdated();
}
void UIState::updateStatus() {
if (scene.started && sm->updated("controlsState")) {
auto controls_state = (*sm)["controlsState"].getControlsState();
auto state = controls_state.getState();
auto previous_status = status;
if (state == cereal::ControlsState::OpenpilotState::PRE_ENABLED || state == cereal::ControlsState::OpenpilotState::OVERRIDING) {
status = STATUS_OVERRIDE;
} else if (scene.always_on_lateral_active) {
status = STATUS_ALWAYS_ON_LATERAL_ACTIVE;
} else if (scene.traffic_mode_active && scene.enabled) {
status = STATUS_TRAFFIC_MODE_ACTIVE;
} else {
status = controls_state.getEnabled() ? STATUS_ENGAGED : STATUS_DISENGAGED;
status = scene.enabled ? STATUS_ENGAGED : STATUS_DISENGAGED;
}
scene.wake_up_screen = controls_state.getAlertStatus() != cereal::ControlsState::AlertStatus::NORMAL || status != previous_status;
}
scene.started |= scene.force_onroad;
scene.started &= !params_memory.getBool("ForceOffroad");
// Handle onroad/offroad transition
if (scene.started != started_prev || sm->frame == 1) {
if (scene.started) {
status = STATUS_DISENGAGED;
scene.started_frame = sm->frame;
} else if (scene.started_timer > 15*60*UI_FREQ && scene.model_randomizer) {
emit reviewModel();
}
started_prev = scene.started;
scene.world_objects_visible = false;
emit offroadTransition(!scene.started);
if (scene.tethering_config == 2) {
wifi->setTetheringEnabled(scene.started);
}
}
}
@@ -250,6 +516,8 @@ UIState::UIState(QObject *parent) : QObject(parent) {
"modelV2", "controlsState", "liveCalibration", "radarState", "deviceState",
"pandaStates", "carParams", "driverMonitoringState", "carState", "liveLocationKalman", "driverStateV2",
"wideRoadCameraState", "managerState", "navInstruction", "navRoute", "uiPlan", "clocks",
"carControl", "liveTorqueParameters", "frogpilotCarControl", "frogpilotCarState", "frogpilotDeviceState",
"frogpilotNavigation", "frogpilotPlan",
});
Params params;
@@ -263,6 +531,13 @@ UIState::UIState(QObject *parent) : QObject(parent) {
timer = new QTimer(this);
QObject::connect(timer, &QTimer::timeout, this, &UIState::update);
timer->start(1000 / UI_FREQ);
// FrogPilot variables
wifi = new WifiManager(this);
scene.frogpilot_toggles = QJsonDocument::fromJson(QString::fromStdString(params_memory.get("FrogPilotToggles", true)).toUtf8()).object();
ui_update_params(this);
ui_update_theme(this);
}
void UIState::update() {
@@ -274,6 +549,16 @@ void UIState::update() {
watchdog_kick(nanos_since_boot());
}
emit uiUpdate(*this);
// FrogPilot variables
scene.conditional_status = scene.conditional_experimental && scene.enabled ? params_memory.getInt("CEStatus") : 0;
scene.driver_camera_timer = scene.driver_camera_in_reverse && scene.reverse ? scene.driver_camera_timer + 1 : 0;
scene.force_onroad = params_memory.getBool("ForceOnroad");
scene.started_timer = scene.started || started_prev ? scene.started_timer + 1 : 0;
if (scene.downloading_update || scene.frogpilot_panel_active) {
device()->resetInteractiveTimeout(scene.screen_timeout, scene.screen_timeout_onroad);
}
}
void UIState::setPrimeType(PrimeType type) {
@@ -312,16 +597,18 @@ void Device::setAwake(bool on) {
}
}
void Device::resetInteractiveTimeout(int timeout) {
void Device::resetInteractiveTimeout(int timeout, int timeout_onroad) {
if (timeout == -1) {
timeout = (ignition_on ? 10 : 30);
} else {
timeout = (ignition_on ? timeout_onroad : timeout);
}
interactive_timeout = timeout * UI_FREQ;
}
void Device::updateBrightness(const UIState &s) {
float clipped_brightness = offroad_brightness;
if (s.scene.started && s.scene.light_sensor > 0) {
if (s.scene.started && s.scene.light_sensor >= 0) {
clipped_brightness = s.scene.light_sensor;
// CIE 1931 - https://www.photonstophotos.net/GeneralTopics/Exposure/Psychometric_Lightness_and_Gamma.htm
@@ -338,6 +625,12 @@ void Device::updateBrightness(const UIState &s) {
int brightness = brightness_filter.update(clipped_brightness);
if (!awake) {
brightness = 0;
} else if (s.scene.started && s.scene.standby_mode && !s.scene.wake_up_screen && interactive_timeout == 0) {
brightness = 0;
} else if (s.scene.started && s.scene.screen_brightness_onroad != 101) {
brightness = interactive_timeout > 0 ? fmax(5, s.scene.screen_brightness_onroad) : s.scene.screen_brightness_onroad;
} else if (s.scene.screen_brightness != 101) {
brightness = s.scene.screen_brightness;
}
if (brightness != last_brightness) {
@@ -349,16 +642,30 @@ void Device::updateBrightness(const UIState &s) {
}
void Device::updateWakefulness(const UIState &s) {
bool ignition_just_turned_off = !s.scene.ignition && ignition_on;
bool ignition_state_changed = s.scene.ignition != ignition_on;
ignition_on = s.scene.ignition;
if (ignition_just_turned_off) {
resetInteractiveTimeout();
if (ignition_on && s.scene.standby_mode) {
if (s.scene.wake_up_screen) {
resetInteractiveTimeout(s.scene.screen_timeout, s.scene.screen_timeout_onroad);
}
}
if (ignition_state_changed) {
if (ignition_on && s.scene.screen_brightness_onroad == 0 && !s.scene.standby_mode) {
resetInteractiveTimeout(0, 0);
} else {
resetInteractiveTimeout(s.scene.screen_timeout, s.scene.screen_timeout_onroad);
}
} else if (interactive_timeout > 0 && --interactive_timeout == 0) {
emit interactiveTimeout();
}
setAwake(s.scene.ignition || interactive_timeout > 0);
if (s.scene.screen_brightness_onroad != 0) {
setAwake(s.scene.ignition || interactive_timeout > 0);
} else {
setAwake(interactive_timeout > 0);
}
}
UIState *uiState() {
+197 -2
View File
@@ -1,5 +1,6 @@
#pragma once
#include <iostream>
#include <memory>
#include <string>
@@ -14,8 +15,12 @@
#include "common/mat.h"
#include "common/params.h"
#include "common/timing.h"
#include "selfdrive/ui/qt/network/wifi_manager.h"
#include "selfdrive/ui/qt/util.h"
#include "system/hardware/hw.h"
#include "selfdrive/frogpilot/ui/qt/widgets/frogpilot_controls.h"
const int UI_BORDER_SIZE = 30;
const int UI_HEADER_HEIGHT = 420;
@@ -50,6 +55,13 @@ typedef enum UIStatus {
STATUS_DISENGAGED,
STATUS_OVERRIDE,
STATUS_ENGAGED,
// FrogPilot statuses
STATUS_ALWAYS_ON_LATERAL_ACTIVE,
STATUS_CONDITIONAL_OVERRIDDEN,
STATUS_EXPERIMENTAL_MODE_ACTIVE,
STATUS_NAVIGATION_ACTIVE,
STATUS_TRAFFIC_MODE_ACTIVE,
} UIStatus;
enum PrimeType {
@@ -67,6 +79,13 @@ const QColor bg_colors [] = {
[STATUS_DISENGAGED] = QColor(0x17, 0x33, 0x49, 0xc8),
[STATUS_OVERRIDE] = QColor(0x91, 0x9b, 0x95, 0xf1),
[STATUS_ENGAGED] = QColor(0x17, 0x86, 0x44, 0xf1),
// FrogPilot colors
[STATUS_ALWAYS_ON_LATERAL_ACTIVE] = QColor(0x0a, 0xba, 0xb5, 0xf1),
[STATUS_CONDITIONAL_OVERRIDDEN] = QColor(0xff, 0xff, 0x00, 0xf1),
[STATUS_EXPERIMENTAL_MODE_ACTIVE] = QColor(0xda, 0x6f, 0x25, 0xf1),
[STATUS_NAVIGATION_ACTIVE] = QColor(0x31, 0xa1, 0xee, 0xf1),
[STATUS_TRAFFIC_MODE_ACTIVE] = QColor(0xc9, 0x22, 0x31, 0xf1),
};
@@ -86,7 +105,7 @@ typedef struct UIScene {
QPolygonF road_edge_vertices[2];
// lead
QPointF lead_vertices[2];
QPointF lead_vertices[6];
// DMoji state
float driver_pose_vals[3];
@@ -95,12 +114,173 @@ typedef struct UIScene {
float driver_pose_coss[3];
vec3 face_kpts_draw[std::size(default_face_kpts_3d)];
bool navigate_on_openpilot = false;
cereal::LongitudinalPersonality personality;
float light_sensor = -1;
bool started, ignition, is_metric, map_on_left, longitudinal_control;
bool world_objects_visible = false;
uint64_t started_frame;
// FrogPilot variables
bool acceleration_path;
bool adjacent_path;
bool adjacent_path_metrics;
bool always_on_lateral;
bool always_on_lateral_active;
bool big_map;
bool blind_spot_left;
bool blind_spot_path;
bool blind_spot_right;
bool brake_lights_on;
bool cem_status;
bool compass;
bool conditional_experimental;
bool cpu_metrics;
bool downloading_update;
bool driver_camera_in_reverse;
bool dynamic_path_width;
bool dynamic_pedals_on_ui;
bool enabled;
bool experimental_mode;
bool experimental_mode_via_tap;
bool fahrenheit;
bool force_onroad;
bool frogpilot_panel_active;
bool frogs_go_moo;
bool full_map;
bool gpu_metrics;
bool hide_alerts;
bool hide_csc_ui;
bool hide_lead_marker;
bool hide_map_icon;
bool hide_max_speed;
bool hide_speed;
bool hide_speed_limit;
bool ip_metrics;
bool jerk_metrics;
bool lateral_tuning_metrics;
bool lead_metrics;
bool left_curve;
bool live_valid;
bool map_open;
bool memory_metrics;
bool model_randomizer;
bool model_ui;
bool mtsc_enabled;
bool no_logging;
bool no_uploads;
bool numerical_temp;
bool online;
bool onroad_distance_button;
bool parked;
bool pedals_on_ui;
bool radarless_model;
bool rainbow_path;
bool random_events;
bool red_light;
bool reverse;
bool road_name_ui;
bool rotating_wheel;
bool screen_recorder;
bool show_blind_spot;
bool show_fps;
bool show_speed_limit_offset;
bool show_speed_limits;
bool show_stopping_point;
bool show_stopping_point_metrics;
bool sidebar_metrics;
bool signal_metrics;
bool speed_limit_changed;
bool speed_limit_controller;
bool speed_limit_overridden;
bool speed_limit_sources;
bool speed_limit_vienna;
bool standby_mode;
bool standstill;
bool static_pedals_on_ui;
bool steering_metrics;
bool stopped_timer;
bool storage_left_metrics;
bool storage_used_metrics;
bool tethering_enabled;
bool traffic_mode;
bool traffic_mode_active;
bool turn_signal_left;
bool turn_signal_right;
bool unlimited_road_ui_length;
bool use_si_metrics;
bool use_stock_colors;
bool use_stock_wheel;
bool use_wheel_speed;
bool vtsc_controlling_curve;
bool vtsc_enabled;
bool wake_up_screen;
double fps;
float acceleration;
float acceleration_jerk;
float acceleration_jerk_difference;
float dashboard_speed_limit;
float friction;
float lane_detection_width;
float lane_line_width;
float lane_width_left;
float lane_width_right;
float lat_accel;
float lead_detection_probability;
float mtsc_speed;
float navigation_speed_limit;
float path_edge_width;
float path_width;
float road_edge_width;
float speed_jerk;
float speed_jerk_difference;
float speed_limit;
float speed_limit_map;
float speed_limit_offset;
float speed_limit_overridden_speed;
float steer;
float unconfirmed_speed_limit;
float upcoming_maneuver_distance;
float upcoming_speed_limit;
float vtsc_speed;
int bearing_deg;
int camera_view;
int conditional_status;
int desired_follow;
int driver_camera_timer;
int map_style;
int minimum_lane_change_speed;
int model_length;
int screen_brightness = -1;
int screen_brightness_onroad = -1;
int screen_timeout;
int screen_timeout_onroad;
int started_timer;
int steering_angle_deg;
int tethering_config;
std::string speed_limit_source;
QColor lane_lines_color;
QColor lead_marker_color;
QColor path_color;
QColor path_edges_color;
QColor sidebar_color1;
QColor sidebar_color2;
QColor sidebar_color3;
QJsonObject frogpilot_toggles;
QPolygonF track_adjacent_vertices[6];
QPolygonF track_edge_vertices;
QString model;
QString model_name;
} UIScene;
class UIState : public QObject {
@@ -128,12 +308,23 @@ public:
QTransform car_space_transform;
// FrogPilot variables
Params params_memory{"/dev/shm/params"};
WifiManager *wifi = nullptr;
signals:
void uiUpdate(const UIState &s);
void offroadTransition(bool offroad);
void primeChanged(bool prime);
void primeTypeChanged(PrimeType prime_type);
// FrogPilot signals
void driveRated();
void reviewModel();
void themeUpdated();
void togglesUpdated();
private slots:
void update();
@@ -175,7 +366,7 @@ signals:
void interactiveTimeout();
public slots:
void resetInteractiveTimeout(int timeout = -1);
void resetInteractiveTimeout(int timeout = -1, int timeout_onroad = -1);
void update(const UIState &s);
};
@@ -190,3 +381,7 @@ void update_dmonitoring(UIState *s, const cereal::DriverStateV2::Reader &drivers
void update_leads(UIState *s, const cereal::RadarState::Reader &radar_state, const cereal::XYZTData::Reader &line);
void update_line_data(const UIState *s, const cereal::XYZTData::Reader &line,
float y_off, float z_off, QPolygonF *pvd, int max_idx, bool allow_invert);
// FrogPilot functions
void ui_update_frogpilot_params(UIState *s);
void ui_update_theme(UIState *s);
+2 -1
View File
@@ -6,6 +6,7 @@ import os
from openpilot.common.basedir import BASEDIR
UI_DIR = os.path.join(BASEDIR, "selfdrive", "ui")
FROG_UI_DIR = os.path.join(BASEDIR, "selfdrive", "frogpilot", "ui")
TRANSLATIONS_DIR = os.path.join(UI_DIR, "translations")
LANGUAGES_FILE = os.path.join(TRANSLATIONS_DIR, "languages.json")
TRANSLATIONS_INCLUDE_FILE = os.path.join(TRANSLATIONS_DIR, "alerts_generated.h")
@@ -33,7 +34,7 @@ def update_translations(vanish: bool = False, translation_files: None | list[str
for file in translation_files:
tr_file = os.path.join(translations_dir, f"{file}.ts")
args = f"lupdate -locations none -recursive {UI_DIR} -ts {tr_file} -I {BASEDIR}"
args = f"lupdate -locations none -recursive {UI_DIR} {FROG_UI_DIR} -ts {tr_file} -I {BASEDIR}"
if vanish:
args += " -no-obsolete"
if file in PLURAL_ONLY: