mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-02 06:03:43 +08:00
FrogPilot 0.9.7
This commit is contained in:
+18
-1
@@ -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
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -18,4 +18,8 @@ public slots:
|
||||
|
||||
private:
|
||||
QStackedLayout *content_stack;
|
||||
|
||||
// FrogPilot widgets
|
||||
void hideEvent(QHideEvent *event);
|
||||
void showEvent(QShowEvent *event);
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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,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();
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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 ¶m);
|
||||
|
||||
// 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 ¶m);
|
||||
|
||||
@@ -98,4 +126,7 @@ private:
|
||||
|
||||
Params params;
|
||||
ParamWatcher *fs_watch;
|
||||
|
||||
// FrogPilot variables
|
||||
Params params_memory{"/dev/shm/params"};
|
||||
};
|
||||
|
||||
@@ -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 ¶m_name, const QString ¶m_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")));
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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); }
|
||||
|
||||
@@ -4,53 +4,179 @@
|
||||
|
||||
#include "selfdrive/ui/qt/util.h"
|
||||
|
||||
void drawIcon(QPainter &p, const QPoint ¢er, const QPixmap &img, const QBrush &bg, float opacity) {
|
||||
void drawIcon(QPainter &p, const QPoint ¢er, 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);
|
||||
}
|
||||
|
||||
@@ -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 ¢er, const QPixmap &img, const QBrush &bg, float opacity);
|
||||
void drawIcon(QPainter &p, const QPoint ¢er, 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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
)");
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -18,7 +18,9 @@ SshControl::SshControl() :
|
||||
}
|
||||
} else {
|
||||
params.remove("GithubUsername");
|
||||
params_storage.remove("GithubUsername");
|
||||
params.remove("GithubSshKeys");
|
||||
params_storage.remove("GithubSshKeys");
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -29,4 +29,7 @@ private:
|
||||
|
||||
void refresh();
|
||||
void getUserKeys(const QString &username);
|
||||
|
||||
// FrogPilot variables
|
||||
Params params_storage{"/persist/params"};
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,10 @@ signals:
|
||||
public slots:
|
||||
void updateState(const UIState &s);
|
||||
|
||||
private:
|
||||
// FrogPilot variables
|
||||
Params params;
|
||||
|
||||
protected:
|
||||
QStackedLayout *stack;
|
||||
};
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -22,4 +22,7 @@ private:
|
||||
HomeWindow *homeWindow;
|
||||
SettingsWindow *settingsWindow;
|
||||
OnboardingWindow *onboardingWindow;
|
||||
|
||||
// FrogPilot variables
|
||||
Params params;
|
||||
};
|
||||
|
||||
+104
-7
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user