This commit is contained in:
Your Name
2024-04-27 13:43:16 -05:00
parent 21363ce751
commit ea1aad5ed1
128 changed files with 3533 additions and 1918 deletions

6
selfdrive/ui/.gitignore vendored Executable file → Normal file
View File

@@ -3,11 +3,13 @@ moc_*
translations/main_test_en.* translations/main_test_en.*
_text
_spinner
ui ui
mui
watch3 watch3
installer/installers/* installer/installers/*
qt/text
qt/spinner
qt/setup/setup qt/setup/setup
qt/setup/reset qt/setup/reset
qt/setup/wifi qt/setup/wifi

53
selfdrive/ui/SConscript Executable file → Normal file
View File

@@ -11,29 +11,26 @@ if arch == 'larch64':
maps = arch in ['larch64', 'aarch64', 'x86_64'] maps = arch in ['larch64', 'aarch64', 'x86_64']
if maps and arch != 'larch64':
rpath = [Dir(f"#third_party/mapbox-gl-native-qt/{arch}").srcnode().abspath]
qt_env["RPATH"] += rpath
if arch == "Darwin": if arch == "Darwin":
del base_libs[base_libs.index('OpenCL')] del base_libs[base_libs.index('OpenCL')]
qt_env['FRAMEWORKS'] += ['OpenCL'] qt_env['FRAMEWORKS'] += ['OpenCL']
# FIXME: remove this once we're on 5.15 (24.04)
qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"]
qt_util = qt_env.Library("qt_util", ["#selfdrive/ui/qt/api.cc", "#selfdrive/ui/qt/util.cc"], LIBS=base_libs) qt_util = qt_env.Library("qt_util", ["#selfdrive/ui/qt/api.cc", "#selfdrive/ui/qt/util.cc"], LIBS=base_libs)
widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/drive_stats.cc", "qt/widgets/wifi.cc", widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/drive_stats.cc", "qt/widgets/wifi.cc",
"qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc", "qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc",
"qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc", "qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc",
"qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.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", "qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc",
"../frogpilot/ui/frogpilot_functions.cc", "../frogpilot/navigation/ui/navigation_settings.cc", "../frogpilot/ui/qt/widgets/frogpilot_controls.cc", "../frogpilot/navigation/ui/navigation_settings.cc",
"../frogpilot/ui/control_settings.cc", "../frogpilot/ui/vehicle_settings.cc", "../frogpilot/ui/qt/offroad/control_settings.cc", "../frogpilot/ui/qt/offroad/vehicle_settings.cc",
"../frogpilot/ui/visual_settings.cc", "../frogpilot/ui/qt/offroad/visual_settings.cc"]
"../oscarpilot/settings/settings.cc", "../oscarpilot/settings/basic.cc",
]
qt_env['CPPDEFINES'] = [] qt_env['CPPDEFINES'] = []
if maps: if maps:
base_libs += ['qmapboxgl'] base_libs += ['QMapLibre']
widgets_src += ["qt/maps/map_helpers.cc", "qt/maps/map_settings.cc", "qt/maps/map.cc", "qt/maps/map_panel.cc", widgets_src += ["qt/maps/map_helpers.cc", "qt/maps/map_settings.cc", "qt/maps/map.cc", "qt/maps/map_panel.cc",
"qt/maps/map_eta.cc", "qt/maps/map_instructions.cc"] "qt/maps/map_eta.cc", "qt/maps/map_instructions.cc"]
qt_env['CPPDEFINES'] += ["ENABLE_MAPS"] qt_env['CPPDEFINES'] += ["ENABLE_MAPS"]
@@ -48,21 +45,6 @@ qt_src = ["main.cc", "qt/sidebar.cc", "qt/onroad.cc", "qt/body.cc",
"qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc", "qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc",
"../frogpilot/screenrecorder/omx_encoder.cc", "../frogpilot/screenrecorder/screenrecorder.cc"] "../frogpilot/screenrecorder/omx_encoder.cc", "../frogpilot/screenrecorder/screenrecorder.cc"]
def is_running_on_wsl2():
try:
with open('/proc/version', 'r') as f:
return 'WSL2' in f.read()
except FileNotFoundError:
return False
if is_running_on_wsl2():
qt_env.Append(CXXFLAGS=['-DWSL2'])
base_libs.remove('OmxCore')
qt_libs.remove('OmxCore')
qt_src.remove("../frogpilot/screenrecorder/screenrecorder.cc")
qt_src.remove("../frogpilot/screenrecorder/omx_encoder.cc")
print("Building for WSL2. Removing Screen Recorder")
# build translation files # build translation files
with open(File("translations/languages.json").abspath) as f: with open(File("translations/languages.json").abspath) as f:
languages = json.loads(f.read()) languages = json.loads(f.read())
@@ -94,8 +76,8 @@ asset_obj = qt_env.Object("assets", assets)
qt_env.SharedLibrary("qt/python_helpers", ["qt/qt_window.cc"], LIBS=qt_libs) qt_env.SharedLibrary("qt/python_helpers", ["qt/qt_window.cc"], LIBS=qt_libs)
# spinner and text window # spinner and text window
qt_env.Program("qt/text", ["qt/text.cc"], LIBS=qt_libs) qt_env.Program("_text", ["qt/text.cc"], LIBS=qt_libs)
qt_env.Program("qt/spinner", ["qt/spinner.cc"], LIBS=qt_libs) qt_env.Program("_spinner", ["qt/spinner.cc"], LIBS=qt_libs)
# build main UI # build main UI
qt_env.Program("ui", qt_src + [asset_obj], LIBS=qt_libs) qt_env.Program("ui", qt_src + [asset_obj], LIBS=qt_libs)
@@ -115,28 +97,25 @@ if GetOption('extras') and arch != "Darwin":
# build updater UI # build updater UI
qt_env.Program("qt/setup/updater", ["qt/setup/updater.cc", asset_obj], LIBS=qt_libs) qt_env.Program("qt/setup/updater", ["qt/setup/updater.cc", asset_obj], LIBS=qt_libs)
# build mui
qt_env.Program("mui", ["mui.cc"], LIBS=qt_libs)
# build installers # build installers
senv = qt_env.Clone() senv = qt_env.Clone()
senv['LINKFLAGS'].append('-Wl,-strip-debug') senv['LINKFLAGS'].append('-Wl,-strip-debug')
release = "release3" release = "release3"
dashcam = "dashcam3"
installers = [ installers = [
("openpilot", release), ("openpilot", release),
("openpilot_test", f"{release}-staging"), ("openpilot_test", f"{release}-staging"),
("openpilot_nightly", "nightly"), ("openpilot_nightly", "nightly"),
("openpilot_internal", "master"), ("openpilot_internal", "master"),
("dashcam", dashcam),
("dashcam_test", f"{dashcam}-staging"),
] ]
cont = {} cont = senv.Command(f"installer/continue_openpilot.o", f"installer/continue_openpilot.sh",
for brand in ("openpilot", "dashcam"): "ld -r -b binary -o $TARGET $SOURCE")
cont[brand] = senv.Command(f"installer/continue_{brand}.o", f"installer/continue_{brand}.sh",
"ld -r -b binary -o $TARGET $SOURCE")
for name, branch in installers: for name, branch in installers:
brand = "dashcam" if "dashcam" in branch else "openpilot" d = {'BRANCH': f"'\"{branch}\"'"}
d = {'BRANCH': f"'\"{branch}\"'", 'BRAND': f"'\"{brand}\"'"}
if "internal" in name: if "internal" in name:
d['INTERNAL'] = "1" d['INTERNAL'] = "1"
@@ -145,7 +124,7 @@ if GetOption('extras') and arch != "Darwin":
r.raise_for_status() r.raise_for_status()
d['SSH_KEYS'] = f'\\"{r.text.strip()}\\"' d['SSH_KEYS'] = f'\\"{r.text.strip()}\\"'
obj = senv.Object(f"installer/installers/installer_{name}.o", ["installer/installer.cc"], CPPDEFINES=d) obj = senv.Object(f"installer/installers/installer_{name}.o", ["installer/installer.cc"], CPPDEFINES=d)
f = senv.Program(f"installer/installers/installer_{name}", [obj, cont[brand]], LIBS=qt_libs) f = senv.Program(f"installer/installers/installer_{name}", [obj, cont], LIBS=qt_libs)
# keep installers small # keep installers small
assert f[0].get_size() < 350*1e3 assert f[0].get_size() < 350*1e3

0
selfdrive/ui/__init__.py Normal file
View File

View File

@@ -0,0 +1,4 @@
#!/usr/bin/bash
cd /data/openpilot
exec ./launch_openpilot.sh

4
selfdrive/ui/installer/installer.cc Executable file → Normal file
View File

@@ -33,8 +33,8 @@ const QString CACHE_PATH = "/data/openpilot.cache";
#define INSTALL_PATH "/data/openpilot" #define INSTALL_PATH "/data/openpilot"
#define TMP_INSTALL_PATH "/data/tmppilot" #define TMP_INSTALL_PATH "/data/tmppilot"
extern const uint8_t str_continue[] asm("_binary_selfdrive_ui_installer_continue_" BRAND "_sh_start"); extern const uint8_t str_continue[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_start");
extern const uint8_t str_continue_end[] asm("_binary_selfdrive_ui_installer_continue_" BRAND "_sh_end"); extern const uint8_t str_continue_end[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_end");
bool time_valid() { bool time_valid() {
time_t rawtime; time_t rawtime;

0
selfdrive/ui/installer/installer.h Executable file → Normal file
View File

0
selfdrive/ui/main.cc Executable file → Normal file
View File

50
selfdrive/ui/mui.cc Normal file
View File

@@ -0,0 +1,50 @@
#include <QApplication>
#include <QtWidgets>
#include <QTimer>
#include "cereal/messaging/messaging.h"
#include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/qt_window.h"
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QWidget w;
setMainWindow(&w);
w.setStyleSheet("background-color: black;");
// our beautiful UI
QVBoxLayout *layout = new QVBoxLayout(&w);
QLabel *label = new QLabel("");
layout->addWidget(label, 0, Qt::AlignCenter);
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [=]() {
static SubMaster sm({"deviceState", "controlsState"});
bool onroad_prev = sm.allAliveAndValid({"deviceState"}) &&
sm["deviceState"].getDeviceState().getStarted();
sm.update(0);
bool onroad = sm.allAliveAndValid({"deviceState"}) &&
sm["deviceState"].getDeviceState().getStarted();
if (onroad) {
label->setText("");
auto cs = sm["controlsState"].getControlsState();
UIStatus status = cs.getEnabled() ? STATUS_ENGAGED : STATUS_DISENGAGED;
label->setStyleSheet(QString("color: %1; font-size: 250px;").arg(bg_colors[status].name()));
} else {
label->setText("offroad");
label->setStyleSheet("color: grey; font-size: 40px;");
}
if ((onroad != onroad_prev) || sm.frame < 2) {
Hardware::set_brightness(50);
Hardware::set_display_power(onroad);
}
});
timer.start(50);
return a.exec();
}

0
selfdrive/ui/qt/api.cc Executable file → Normal file
View File

0
selfdrive/ui/qt/api.h Executable file → Normal file
View File

163
selfdrive/ui/qt/body.cc Executable file → Normal file
View File

@@ -3,54 +3,159 @@
#include <cmath> #include <cmath>
#include <algorithm> #include <algorithm>
#include <QPainter> #include <QPainter>
#include <QStackedLayout> #include <QStackedLayout>
#include <QApplication>
#include <QGridLayout>
#include <QString>
#include <QTransform>
#include <QPixmap>
#include "common/params.h" #include "common/params.h"
#include "common/timing.h" #include "common/timing.h"
#include "system/hardware/hw.h" RecordButton::RecordButton(QWidget *parent) : QPushButton(parent) {
#include "selfdrive/ui/qt/qt_window.h" setCheckable(true);
#include "selfdrive/ui/qt/util.h" setChecked(false);
setFixedSize(148, 148);
BodyWindow::BodyWindow(QWidget *parent) : QWidget(parent) { QObject::connect(this, &QPushButton::toggled, [=]() {
QGridLayout *layout = new QGridLayout(this); setEnabled(false);
layout->setSpacing(0); });
layout->setMargin(200); }
setAttribute(Qt::WA_OpaquePaintEvent); void RecordButton::paintEvent(QPaintEvent *event) {
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
setStyleSheet(R"( QPoint center(width() / 2, height() / 2);
BodyWindow {
background-color: blue; QColor bg(isChecked() ? "#FFFFFF" : "#737373");
} QColor accent(isChecked() ? "#FF0000" : "#FFFFFF");
)"); if (!isEnabled()) {
bg = QColor("#404040");
accent = QColor("#FFFFFF");
}
if (isDown()) {
accent.setAlphaF(0.7);
}
p.setPen(Qt::NoPen);
p.setBrush(bg);
p.drawEllipse(center, 74, 74);
p.setPen(QPen(accent, 6));
p.setBrush(Qt::NoBrush);
p.drawEllipse(center, 42, 42);
p.setPen(Qt::NoPen);
p.setBrush(accent);
p.drawEllipse(center, 22, 22);
}
BodyWindow::BodyWindow(QWidget *parent) : fuel_filter(1.0, 5., 1. / UI_FREQ), QWidget(parent) {
QStackedLayout *layout = new QStackedLayout(this);
layout->setStackingMode(QStackedLayout::StackAll);
QWidget *w = new QWidget;
QVBoxLayout *vlayout = new QVBoxLayout(w);
vlayout->setMargin(45);
layout->addWidget(w);
// face
face = new QLabel();
face->setAlignment(Qt::AlignCenter);
layout->addWidget(face);
awake = new QMovie("../assets/body/awake.gif", {}, this);
awake->setCacheMode(QMovie::CacheAll);
sleep = new QMovie("../assets/body/sleep.gif", {}, this);
sleep->setCacheMode(QMovie::CacheAll);
// record button
btn = new RecordButton(this);
vlayout->addWidget(btn, 0, Qt::AlignBottom | Qt::AlignRight);
QObject::connect(btn, &QPushButton::clicked, [=](bool checked) {
btn->setEnabled(false);
Params().putBool("DisableLogging", !checked);
last_button = nanos_since_boot();
});
w->raise();
QObject::connect(uiState(), &UIState::uiUpdate, this, &BodyWindow::updateState); QObject::connect(uiState(), &UIState::uiUpdate, this, &BodyWindow::updateState);
} }
void BodyWindow::paintEvent(QPaintEvent *event) { void BodyWindow::paintEvent(QPaintEvent *event) {
QPainter painter(this); QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
QPixmap comma_img = loadPixmap("../assets/oscarpilot_ready.png"); p.fillRect(rect(), QColor(0, 0, 0));
// Calculate the top-left position to center the image in the window. // battery outline + detail
int x = (this->width() - comma_img.width()) / 2; p.translate(width() - 136, 16);
int y = (this->height() - comma_img.height()) / 2; const QColor gray = QColor("#737373");
p.setBrush(Qt::NoBrush);
p.setPen(QPen(gray, 4, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
p.drawRoundedRect(2, 2, 78, 36, 8, 8);
// Draw the pixmap at the calculated position. p.setPen(Qt::NoPen);
painter.drawPixmap(x, y, comma_img); p.setBrush(gray);
} p.drawRoundedRect(84, 12, 6, 16, 4, 4);
p.drawRect(84, 12, 3, 16);
// battery level
double fuel = std::clamp(fuel_filter.x(), 0.2f, 1.0f);
const int m = 5; // manual margin since we can't do an inner border
p.setPen(Qt::NoPen);
p.setBrush(fuel > 0.25 ? QColor("#32D74B") : QColor("#FF453A"));
p.drawRoundedRect(2 + m, 2 + m, (78 - 2*m)*fuel, 36 - 2*m, 4, 4);
void BodyWindow::updateState(const UIState &s) { // charging status
if (charging) {
p.setPen(Qt::NoPen);
p.setBrush(Qt::white);
const QPolygonF charger({
QPointF(12.31, 0),
QPointF(12.31, 16.92),
QPointF(18.46, 16.92),
QPointF(6.15, 40),
QPointF(6.15, 23.08),
QPointF(0, 23.08),
});
p.drawPolygon(charger.translated(98, 0));
}
} }
void BodyWindow::offroadTransition(bool offroad) { void BodyWindow::offroadTransition(bool offroad) {
btn->setChecked(true);
btn->setEnabled(true);
fuel_filter.reset(1.0);
}
void BodyWindow::updateState(const UIState &s) {
if (!isVisible()) {
return;
}
const SubMaster &sm = *(s.sm);
auto cs = sm["carState"].getCarState();
charging = cs.getCharging();
fuel_filter.update(cs.getFuelGauge());
// TODO: use carState.standstill when that's fixed
const bool standstill = std::abs(cs.getVEgo()) < 0.01;
QMovie *m = standstill ? sleep : awake;
if (m != face->movie()) {
face->setMovie(m);
face->movie()->start();
}
// update record button state
if (sm.updated("managerState") && (sm.rcv_time("managerState") - last_button)*1e-9 > 0.5) {
for (auto proc : sm["managerState"].getManagerState().getProcesses()) {
if (proc.getName() == "loggerd") {
btn->setEnabled(true);
btn->setChecked(proc.getRunning());
}
}
}
update();
} }

View File

@@ -1,60 +0,0 @@
#include "selfdrive/ui/qt/body.h"
#include <cmath>
#include <algorithm>
#include <QPainter>
#include <QStackedLayout>
#include <QApplication>
#include <QGridLayout>
#include <QString>
#include <QTransform>
#include <QPixmap>
#include "common/params.h"
#include "common/timing.h"
#include "system/hardware/hw.h"
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/util.h"
void LogoWidget::paintEvent(QPaintEvent *event) {
QPainter painter(this);
}
BodyWindow::BodyWindow(QWidget *parent) : QWidget(parent) {
QGridLayout *layout = new QGridLayout(this);
layout->setSpacing(0);
layout->setMargin(200);
setAttribute(Qt::WA_OpaquePaintEvent);
setStyleSheet(R"(
BodyWindow {
background-color: blue;
}
)");
QObject::connect(uiState(), &UIState::uiUpdate, this, &BodyWindow::updateState);
}
void BodyWindow::paintEvent(QPaintEvent *event) {
QPainter painter(this);
QPixmap comma_img = loadPixmap("../assets/oscarpilot_ready.png");
// Calculate the top-left position to center the image in the window.
int x = (this->width() - comma_img.width()) / 2;
int y = (this->height() - comma_img.height()) / 2;
// Draw the pixmap at the calculated position.
painter.drawPixmap(x, y, comma_img);
}
void BodyWindow::updateState(const UIState &s) {
}
void BodyWindow::offroadTransition(bool offroad) {
}

30
selfdrive/ui/qt/body.h Executable file → Normal file
View File

@@ -3,21 +3,35 @@
#include <QMovie> #include <QMovie>
#include <QLabel> #include <QLabel>
#include <QPushButton> #include <QPushButton>
#include <QPixmap>
#include <QProgressBar>
#include <QSocketNotifier>
#include <QVariantAnimation>
#include <QWidget>
#include "common/util.h" #include "common/util.h"
#include "selfdrive/ui/ui.h" #include "selfdrive/ui/ui.h"
class BodyWindow : public QWidget { class RecordButton : public QPushButton {
Q_OBJECT Q_OBJECT
public: public:
BodyWindow(QWidget* parent = 0); RecordButton(QWidget* parent = 0);
private: private:
void paintEvent(QPaintEvent*) override; void paintEvent(QPaintEvent*) override;
};
class BodyWindow : public QWidget {
Q_OBJECT
public:
BodyWindow(QWidget* parent = 0);
private:
bool charging = false;
uint64_t last_button = 0;
FirstOrderFilter fuel_filter;
QLabel *face;
QMovie *awake, *sleep;
RecordButton *btn;
void paintEvent(QPaintEvent*) override;
private slots: private slots:
void updateState(const UIState &s); void updateState(const UIState &s);
void offroadTransition(bool onroad); void offroadTransition(bool onroad);

View File

@@ -1,38 +0,0 @@
#pragma once
#include <QMovie>
#include <QLabel>
#include <QPushButton>
#include "common/util.h"
#include "selfdrive/ui/ui.h"
class RecordButton : public QPushButton {
Q_OBJECT
public:
RecordButton(QWidget* parent = 0);
private:
void paintEvent(QPaintEvent*) override;
};
class BodyWindow : public QWidget {
Q_OBJECT
public:
BodyWindow(QWidget* parent = 0);
private:
bool charging = false;
uint64_t last_button = 0;
FirstOrderFilter fuel_filter;
QLabel *face;
QMovie *awake, *sleep;
RecordButton *btn;
void paintEvent(QPaintEvent*) override;
private slots:
void updateState(const UIState &s);
void offroadTransition(bool onroad);
};

View File

@@ -1,161 +0,0 @@
#include "selfdrive/ui/qt/body.h"
#include <cmath>
#include <algorithm>
#include <QPainter>
#include <QStackedLayout>
#include "common/params.h"
#include "common/timing.h"
RecordButton::RecordButton(QWidget *parent) : QPushButton(parent) {
setCheckable(true);
setChecked(false);
setFixedSize(148, 148);
QObject::connect(this, &QPushButton::toggled, [=]() {
setEnabled(false);
});
}
void RecordButton::paintEvent(QPaintEvent *event) {
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
QPoint center(width() / 2, height() / 2);
QColor bg(isChecked() ? "#FFFFFF" : "#737373");
QColor accent(isChecked() ? "#FF0000" : "#FFFFFF");
if (!isEnabled()) {
bg = QColor("#404040");
accent = QColor("#FFFFFF");
}
if (isDown()) {
accent.setAlphaF(0.7);
}
p.setPen(Qt::NoPen);
p.setBrush(bg);
p.drawEllipse(center, 74, 74);
p.setPen(QPen(accent, 6));
p.setBrush(Qt::NoBrush);
p.drawEllipse(center, 42, 42);
p.setPen(Qt::NoPen);
p.setBrush(accent);
p.drawEllipse(center, 22, 22);
}
BodyWindow::BodyWindow(QWidget *parent) : fuel_filter(1.0, 5., 1. / UI_FREQ), QWidget(parent) {
QStackedLayout *layout = new QStackedLayout(this);
layout->setStackingMode(QStackedLayout::StackAll);
QWidget *w = new QWidget;
QVBoxLayout *vlayout = new QVBoxLayout(w);
vlayout->setMargin(45);
layout->addWidget(w);
// face
face = new QLabel();
face->setAlignment(Qt::AlignCenter);
layout->addWidget(face);
awake = new QMovie("../assets/body/awake.gif", {}, this);
awake->setCacheMode(QMovie::CacheAll);
sleep = new QMovie("../assets/body/sleep.gif", {}, this);
sleep->setCacheMode(QMovie::CacheAll);
// record button
btn = new RecordButton(this);
vlayout->addWidget(btn, 0, Qt::AlignBottom | Qt::AlignRight);
QObject::connect(btn, &QPushButton::clicked, [=](bool checked) {
btn->setEnabled(false);
Params().putBool("DisableLogging", !checked);
last_button = nanos_since_boot();
});
w->raise();
QObject::connect(uiState(), &UIState::uiUpdate, this, &BodyWindow::updateState);
}
void BodyWindow::paintEvent(QPaintEvent *event) {
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
p.fillRect(rect(), QColor(0, 0, 0));
// battery outline + detail
p.translate(width() - 136, 16);
const QColor gray = QColor("#737373");
p.setBrush(Qt::NoBrush);
p.setPen(QPen(gray, 4, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
p.drawRoundedRect(2, 2, 78, 36, 8, 8);
p.setPen(Qt::NoPen);
p.setBrush(gray);
p.drawRoundedRect(84, 12, 6, 16, 4, 4);
p.drawRect(84, 12, 3, 16);
// battery level
double fuel = std::clamp(fuel_filter.x(), 0.2f, 1.0f);
const int m = 5; // manual margin since we can't do an inner border
p.setPen(Qt::NoPen);
p.setBrush(fuel > 0.25 ? QColor("#32D74B") : QColor("#FF453A"));
p.drawRoundedRect(2 + m, 2 + m, (78 - 2*m)*fuel, 36 - 2*m, 4, 4);
// charging status
if (charging) {
p.setPen(Qt::NoPen);
p.setBrush(Qt::white);
const QPolygonF charger({
QPointF(12.31, 0),
QPointF(12.31, 16.92),
QPointF(18.46, 16.92),
QPointF(6.15, 40),
QPointF(6.15, 23.08),
QPointF(0, 23.08),
});
p.drawPolygon(charger.translated(98, 0));
}
}
void BodyWindow::offroadTransition(bool offroad) {
btn->setChecked(true);
btn->setEnabled(true);
fuel_filter.reset(1.0);
}
void BodyWindow::updateState(const UIState &s) {
if (!isVisible()) {
return;
}
const SubMaster &sm = *(s.sm);
auto cs = sm["carState"].getCarState();
charging = cs.getCharging();
fuel_filter.update(cs.getFuelGauge());
// TODO: use carState.standstill when that's fixed
const bool standstill = std::abs(cs.getVEgo()) < 0.01;
QMovie *m = standstill ? sleep : awake;
if (m != face->movie()) {
face->setMovie(m);
face->movie()->start();
}
// update record button state
if (sm.updated("managerState") && (sm.rcv_time("managerState") - last_button)*1e-9 > 0.5) {
for (auto proc : sm["managerState"].getManagerState().getProcesses()) {
if (proc.getName() == "loggerd") {
btn->setEnabled(true);
btn->setChecked(proc.getRunning());
}
}
}
update();
}

View File

@@ -1,52 +0,0 @@
#include "selfdrive/ui/qt/body.h"
#include <cmath>
#include <algorithm>
#include <QPainter>
#include <QStackedLayout>
#include <QApplication>
#include <QGridLayout>
#include <QString>
#include <QTransform>
#include <QPixmap>
#include <QWebEngineView> // Include the QWebEngineView header
#include "common/params.h"
#include "common/timing.h"
#include "system/hardware/hw.h"
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/util.h"
void LogoWidget::paintEvent(QPaintEvent *event) {
QPainter painter(this);
}
BodyWindow::BodyWindow(QWidget *parent) : QWidget(parent) {
// Create a QWebEngineView
QWebEngineView *view = new QWebEngineView(this);
view->setUrl(QUrl("http://www.fark.com/")); // Set the URL to fark.com
// Filler
QGridLayout *layout = new QGridLayout(this);
layout->setSpacing(0);
layout->setMargin(0); // Set margin to 0 to fill the entire window
layout->addWidget(view, 0, 0); // Add the view to the layout
setAttribute(Qt::WA_OpaquePaintEvent);
setStyleSheet(R"(
BodyWindow {
background-color: blue;
}
)");
QObject::connect(uiState(), &UIState::uiUpdate, this, &BodyWindow::updateState);
}
void BodyWindow::updateState(const UIState &s) {
}
void BodyWindow::offroadTransition(bool offroad) {
}

53
selfdrive/ui/qt/home.cc Executable file → Normal file
View File

@@ -45,7 +45,7 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
}); });
slayout->addWidget(driver_view); slayout->addWidget(driver_view);
setAttribute(Qt::WA_NoSystemBackground); setAttribute(Qt::WA_NoSystemBackground);
// QObject::connect(uiState(), &UIState::uiUpdate, this, &HomeWindow::updateState); QObject::connect(uiState(), &UIState::uiUpdate, this, &HomeWindow::updateState);
QObject::connect(uiState(), &UIState::offroadTransition, this, &HomeWindow::offroadTransition); QObject::connect(uiState(), &UIState::offroadTransition, this, &HomeWindow::offroadTransition);
QObject::connect(uiState(), &UIState::offroadTransition, sidebar, &Sidebar::offroadTransition); QObject::connect(uiState(), &UIState::offroadTransition, sidebar, &Sidebar::offroadTransition);
} }
@@ -66,37 +66,45 @@ void HomeWindow::updateState(const UIState &s) {
body->setEnabled(true); body->setEnabled(true);
slayout->setCurrentWidget(body); slayout->setCurrentWidget(body);
} }
if (s.scene.started) {
showDriverView(s.scene.driver_camera_timer >= 10, true);
}
} }
void HomeWindow::offroadTransition(bool offroad) { void HomeWindow::offroadTransition(bool offroad) {
body->setEnabled(false); body->setEnabled(false);
sidebar->setVisible(false); sidebar->setVisible(offroad);
if (offroad) { if (offroad) {
slayout->setCurrentWidget(body); slayout->setCurrentWidget(home);
} else { } else {
slayout->setCurrentWidget(onroad); slayout->setCurrentWidget(onroad);
uiState()->scene.map_open = onroad->isMapVisible();
} }
} }
void HomeWindow::showDriverView(bool show) { void HomeWindow::showDriverView(bool show, bool started) {
if (show) { if (show) {
emit closeSettings(); emit closeSettings();
slayout->setCurrentWidget(driver_view); slayout->setCurrentWidget(driver_view);
sidebar->setVisible(show == false);
} else { } else {
slayout->setCurrentWidget(body); if (started) {
slayout->setCurrentWidget(onroad);
sidebar->setVisible(params.getBool("Sidebar"));
} else {
slayout->setCurrentWidget(home);
sidebar->setVisible(show == false);
}
} }
sidebar->setVisible(false);
} }
void HomeWindow::mousePressEvent(QMouseEvent* e) { void HomeWindow::mousePressEvent(QMouseEvent* e) {
if (body->isVisible()) {
showSidebar(true);
slayout->setCurrentWidget(home);
} else {
// Handle sidebar collapsing // Handle sidebar collapsing
if ((onroad->isVisible() || body->isVisible()) && (!sidebar->isVisible() || e->x() > sidebar->width())) { if ((onroad->isVisible() || body->isVisible()) && (!sidebar->isVisible() || e->x() > sidebar->width())) {
sidebar->setVisible(!sidebar->isVisible() && !onroad->isMapVisible()); sidebar->setVisible(!sidebar->isVisible() && !onroad->isMapVisible());
} uiState()->scene.map_open = onroad->isMapVisible();
params.putBool("Sidebar", sidebar->isVisible());
} }
} }
@@ -165,9 +173,9 @@ OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) {
left_widget->addWidget(new DriveStats); left_widget->addWidget(new DriveStats);
left_widget->setStyleSheet("border-radius: 10px;"); left_widget->setStyleSheet("border-radius: 10px;");
left_widget->setCurrentIndex(params.getBool("DriveStats") ? 2 : uiState()->hasPrime() ? 0 : 1); left_widget->setCurrentIndex(2);
connect(uiState(), &UIState::primeChanged, [=](bool prime) { connect(uiState(), &UIState::primeChanged, [=](bool prime) {
left_widget->setCurrentIndex(prime ? 0 : 1); left_widget->setCurrentIndex(2);
}); });
home_layout->addWidget(left_widget, 1); home_layout->addWidget(left_widget, 1);
@@ -222,17 +230,6 @@ OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) {
font-size: 55px; font-size: 55px;
} }
)"); )");
// Set the model name
std::map<int, QString> MODEL_NAME {
{0, "New Delhi"},
{1, "Blue Diamond V1"},
{2, "Blue Diamond V2"},
{3, "Farmville"},
{4, "New Lemon Pie"},
};
modelName = MODEL_NAME[params.getInt("Model")];
} }
void OffroadHome::showEvent(QShowEvent *event) { void OffroadHome::showEvent(QShowEvent *event) {
@@ -245,8 +242,10 @@ void OffroadHome::hideEvent(QHideEvent *event) {
} }
void OffroadHome::refresh() { void OffroadHome::refresh() {
QString model = QString::fromStdString(params.get("ModelName"));
date->setText(QLocale(uiState()->language.mid(5)).toString(QDateTime::currentDateTime(), "dddd, MMMM d")); date->setText(QLocale(uiState()->language.mid(5)).toString(QDateTime::currentDateTime(), "dddd, MMMM d"));
version->setText(getBrand() + " v" + getVersion().left(14).trimmed() + " - " + modelName); version->setText(getBrand() + " v" + getVersion().left(14).trimmed() + " - " + model);
bool updateAvailable = update_widget->refresh(); bool updateAvailable = update_widget->refresh();
int alerts = alerts_widget->refresh(); int alerts = alerts_widget->refresh();

8
selfdrive/ui/qt/home.h Executable file → Normal file
View File

@@ -33,7 +33,6 @@ private:
Params params; Params params;
QTimer* timer; QTimer* timer;
ElidedLabel* date;
ElidedLabel* version; ElidedLabel* version;
QStackedLayout* center_layout; QStackedLayout* center_layout;
UpdateAlert *update_widget; UpdateAlert *update_widget;
@@ -42,7 +41,7 @@ private:
QPushButton* update_notif; QPushButton* update_notif;
// FrogPilot variables // FrogPilot variables
QString modelName; ElidedLabel* date;
}; };
class HomeWindow : public QWidget { class HomeWindow : public QWidget {
@@ -57,7 +56,7 @@ signals:
public slots: public slots:
void offroadTransition(bool offroad); void offroadTransition(bool offroad);
void showDriverView(bool show); void showDriverView(bool show, bool started=false);
void showSidebar(bool show); void showSidebar(bool show);
void showMapPanel(bool show); void showMapPanel(bool show);
@@ -73,6 +72,9 @@ private:
DriverViewWindow *driver_view; DriverViewWindow *driver_view;
QStackedLayout *slayout; QStackedLayout *slayout;
// FrogPilot variables
Params params;
private slots: private slots:
void updateState(const UIState &s); void updateState(const UIState &s);
}; };

83
selfdrive/ui/qt/maps/map.cc Executable file → Normal file
View File

@@ -18,7 +18,7 @@ const float MAX_PITCH = 50;
const float MIN_PITCH = 0; const float MIN_PITCH = 0;
const float MAP_SCALE = 2; const float MAP_SCALE = 2;
MapWindow::MapWindow(const QMapboxGLSettings &settings) : m_settings(settings), velocity_filter(0, 10, 0.05, false) { MapWindow::MapWindow(const QMapLibre::Settings &settings) : m_settings(settings), velocity_filter(0, 10, 0.05, false) {
QObject::connect(uiState(), &UIState::uiUpdate, this, &MapWindow::updateState); QObject::connect(uiState(), &UIState::uiUpdate, this, &MapWindow::updateState);
map_overlay = new QWidget (this); map_overlay = new QWidget (this);
@@ -57,10 +57,10 @@ void MapWindow::initLayers() {
if (!m_map->layerExists("modelPathLayer")) { if (!m_map->layerExists("modelPathLayer")) {
qDebug() << "Initializing modelPathLayer"; qDebug() << "Initializing modelPathLayer";
QVariantMap modelPath; QVariantMap modelPath;
modelPath["id"] = "modelPathLayer"; //modelPath["id"] = "modelPathLayer";
modelPath["type"] = "line"; modelPath["type"] = "line";
modelPath["source"] = "modelPathSource"; modelPath["source"] = "modelPathSource";
m_map->addLayer(modelPath); m_map->addLayer("modelPathLayer", modelPath);
m_map->setPaintProperty("modelPathLayer", "line-color", QColor("red")); m_map->setPaintProperty("modelPathLayer", "line-color", QColor("red"));
m_map->setPaintProperty("modelPathLayer", "line-width", 5.0); m_map->setPaintProperty("modelPathLayer", "line-width", 5.0);
m_map->setLayoutProperty("modelPathLayer", "line-cap", "round"); m_map->setLayoutProperty("modelPathLayer", "line-cap", "round");
@@ -68,10 +68,9 @@ void MapWindow::initLayers() {
if (!m_map->layerExists("navLayer")) { if (!m_map->layerExists("navLayer")) {
qDebug() << "Initializing navLayer"; qDebug() << "Initializing navLayer";
QVariantMap nav; QVariantMap nav;
nav["id"] = "navLayer";
nav["type"] = "line"; nav["type"] = "line";
nav["source"] = "navSource"; nav["source"] = "navSource";
m_map->addLayer(nav, "road-intersection"); m_map->addLayer("navLayer", nav, "road-intersection");
QVariantMap transition; QVariantMap transition;
transition["duration"] = 400; // ms transition["duration"] = 400; // ms
@@ -84,10 +83,9 @@ void MapWindow::initLayers() {
qDebug() << "Initializing pinLayer"; qDebug() << "Initializing pinLayer";
m_map->addImage("default_marker", QImage("../assets/navigation/default_marker.svg")); m_map->addImage("default_marker", QImage("../assets/navigation/default_marker.svg"));
QVariantMap pin; QVariantMap pin;
pin["id"] = "pinLayer";
pin["type"] = "symbol"; pin["type"] = "symbol";
pin["source"] = "pinSource"; pin["source"] = "pinSource";
m_map->addLayer(pin); m_map->addLayer("pinLayer", pin);
m_map->setLayoutProperty("pinLayer", "icon-pitch-alignment", "viewport"); m_map->setLayoutProperty("pinLayer", "icon-pitch-alignment", "viewport");
m_map->setLayoutProperty("pinLayer", "icon-image", "default_marker"); m_map->setLayoutProperty("pinLayer", "icon-image", "default_marker");
m_map->setLayoutProperty("pinLayer", "icon-ignore-placement", true); m_map->setLayoutProperty("pinLayer", "icon-ignore-placement", true);
@@ -100,10 +98,9 @@ void MapWindow::initLayers() {
m_map->addImage("label-arrow", QImage("../assets/images/triangle.svg")); m_map->addImage("label-arrow", QImage("../assets/images/triangle.svg"));
QVariantMap carPos; QVariantMap carPos;
carPos["id"] = "carPosLayer";
carPos["type"] = "symbol"; carPos["type"] = "symbol";
carPos["source"] = "carPosSource"; carPos["source"] = "carPosSource";
m_map->addLayer(carPos); m_map->addLayer("carPosLayer", carPos);
m_map->setLayoutProperty("carPosLayer", "icon-pitch-alignment", "map"); m_map->setLayoutProperty("carPosLayer", "icon-pitch-alignment", "map");
m_map->setLayoutProperty("carPosLayer", "icon-image", "label-arrow"); m_map->setLayoutProperty("carPosLayer", "icon-image", "label-arrow");
m_map->setLayoutProperty("carPosLayer", "icon-size", 0.5); m_map->setLayoutProperty("carPosLayer", "icon-size", 0.5);
@@ -112,7 +109,6 @@ void MapWindow::initLayers() {
// TODO: remove, symbol-sort-key does not seem to matter outside of each layer // TODO: remove, symbol-sort-key does not seem to matter outside of each layer
m_map->setLayoutProperty("carPosLayer", "symbol-sort-key", 0); m_map->setLayoutProperty("carPosLayer", "symbol-sort-key", 0);
} }
if (!m_map->layerExists("buildingsLayer")) { if (!m_map->layerExists("buildingsLayer")) {
qDebug() << "Initializing buildingsLayer"; qDebug() << "Initializing buildingsLayer";
QVariantMap buildings; QVariantMap buildings;
@@ -121,7 +117,7 @@ void MapWindow::initLayers() {
buildings["source-layer"] = "building"; buildings["source-layer"] = "building";
buildings["type"] = "fill-extrusion"; buildings["type"] = "fill-extrusion";
buildings["minzoom"] = 15; buildings["minzoom"] = 15;
m_map->addLayer(buildings); m_map->addLayer("buildingsLayer", buildings);
m_map->setFilter("buildingsLayer", QVariantList({"==", "extrude", "true"})); m_map->setFilter("buildingsLayer", QVariantList({"==", "extrude", "true"}));
QVariantList fillExtrusionHight = { QVariantList fillExtrusionHight = {
@@ -141,7 +137,7 @@ void MapWindow::initLayers() {
}; };
QVariantList fillExtrusionOpacity = { QVariantList fillExtrusionOpacity = {
"interpolate", "interpolate",
QVariantList{"linear"}, QVariantList{"linear"},
QVariantList{"zoom"}, QVariantList{"zoom"},
15, 0, 15, 0,
@@ -168,8 +164,7 @@ void MapWindow::updateState(const UIState &s) {
if (sm.updated("modelV2")) { if (sm.updated("modelV2")) {
// set path color on change, and show map on rising edge of navigate on openpilot // set path color on change, and show map on rising edge of navigate on openpilot
bool nav_enabled = sm["modelV2"].getModelV2().getNavEnabled() && bool nav_enabled = sm["modelV2"].getModelV2().getNavEnabled() &&
(sm["controlsState"].getControlsState().getEnabled() || sm["frogpilotCarControl"].getFrogpilotCarControl().getAlwaysOnLateral()) && (sm["controlsState"].getControlsState().getEnabled() || sm["frogpilotCarControl"].getFrogpilotCarControl().getAlwaysOnLateral());
(!params.get("NavDestination").empty() || params.getInt("PrimeType") != 0);
if (nav_enabled != uiState()->scene.navigate_on_openpilot) { if (nav_enabled != uiState()->scene.navigate_on_openpilot) {
if (loaded_once) { if (loaded_once) {
m_map->setPaintProperty("navLayer", "line-color", getNavPathColor(nav_enabled)); m_map->setPaintProperty("navLayer", "line-color", getNavPathColor(nav_enabled));
@@ -194,12 +189,24 @@ void MapWindow::updateState(const UIState &s) {
locationd_valid = (locationd_pos.getValid() && locationd_orientation.getValid() && locationd_velocity.getValid() && pos_accurate_enough); locationd_valid = (locationd_pos.getValid() && locationd_orientation.getValid() && locationd_velocity.getValid() && pos_accurate_enough);
if (locationd_valid) { if (locationd_valid) {
last_position = QMapbox::Coordinate(locationd_pos.getValue()[0], locationd_pos.getValue()[1]); last_position = QMapLibre::Coordinate(locationd_pos.getValue()[0], locationd_pos.getValue()[1]);
last_bearing = RAD2DEG(locationd_orientation.getValue()[2]); last_bearing = RAD2DEG(locationd_orientation.getValue()[2]);
velocity_filter.update(std::max(10.0, locationd_velocity.getValue()[0])); velocity_filter.update(std::max(10.0, locationd_velocity.getValue()[0]));
} }
} }
// 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");
}
if (sm.updated("navRoute") && sm["navRoute"].getNavRoute().getCoordinates().size()) { if (sm.updated("navRoute") && sm["navRoute"].getNavRoute().getCoordinates().size()) {
auto nav_dest = coordinate_from_param("NavDestination"); auto nav_dest = coordinate_from_param("NavDestination");
bool allow_open = std::exchange(last_valid_nav_dest, nav_dest) != nav_dest && bool allow_open = std::exchange(last_valid_nav_dest, nav_dest) != nav_dest &&
@@ -231,10 +238,10 @@ void MapWindow::updateState(const UIState &s) {
if (locationd_valid) { if (locationd_valid) {
// Update current location marker // Update current location marker
auto point = coordinate_to_collection(*last_position); auto point = coordinate_to_collection(*last_position);
QMapbox::Feature feature1(QMapbox::Feature::PointType, point, {}, {}); QMapLibre::Feature feature1(QMapLibre::Feature::PointType, point, {}, {});
QVariantMap carPosSource; QVariantMap carPosSource;
carPosSource["type"] = "geojson"; carPosSource["type"] = "geojson";
carPosSource["data"] = QVariant::fromValue<QMapbox::Feature>(feature1); carPosSource["data"] = QVariant::fromValue<QMapLibre::Feature>(feature1);
m_map->updateSource("carPosSource", carPosSource); m_map->updateSource("carPosSource", carPosSource);
// Map bearing isn't updated when interacting, keep location marker up to date // Map bearing isn't updated when interacting, keep location marker up to date
@@ -275,16 +282,40 @@ void MapWindow::updateState(const UIState &s) {
qWarning() << "Updating navLayer with new route"; qWarning() << "Updating navLayer with new route";
auto route = sm["navRoute"].getNavRoute(); auto route = sm["navRoute"].getNavRoute();
auto route_points = capnp_coordinate_list_to_collection(route.getCoordinates()); auto route_points = capnp_coordinate_list_to_collection(route.getCoordinates());
QMapbox::Feature feature(QMapbox::Feature::LineStringType, route_points, {}, {}); QMapLibre::Feature feature(QMapLibre::Feature::LineStringType, route_points, {}, {});
QVariantMap navSource; QVariantMap navSource;
navSource["type"] = "geojson"; navSource["type"] = "geojson";
navSource["data"] = QVariant::fromValue<QMapbox::Feature>(feature); navSource["data"] = QVariant::fromValue<QMapLibre::Feature>(feature);
m_map->updateSource("navSource", navSource); m_map->updateSource("navSource", navSource);
m_map->setLayoutProperty("navLayer", "visibility", "visible"); m_map->setLayoutProperty("navLayer", "visibility", "visible");
route_rcv_frame = sm.rcv_frame("navRoute"); route_rcv_frame = sm.rcv_frame("navRoute");
updateDestinationMarker(); updateDestinationMarker();
} }
// Map Styling - Credit goes to OPKR!
int map_style = uiState()->scene.map_style;
if (map_style != previous_map_style) {
std::unordered_map<int, std::string> styleUrls = {
{0, "mapbox://styles/commaai/clkqztk0f00ou01qyhsa5bzpj"}, // Stock openpilot
{1, "mapbox://styles/mapbox/streets-v11"}, // Mapbox Streets
{2, "mapbox://styles/mapbox/outdoors-v11"}, // Mapbox Outdoors
{3, "mapbox://styles/mapbox/light-v10"}, // Mapbox Light
{4, "mapbox://styles/mapbox/dark-v10"}, // Mapbox Dark
{5, "mapbox://styles/mapbox/satellite-v9"}, // Mapbox Satellite
{6, "mapbox://styles/mapbox/satellite-streets-v11"}, // Mapbox Satellite Streets
{7, "mapbox://styles/mapbox/navigation-day-v1"}, // Mapbox Navigation Day
{8, "mapbox://styles/mapbox/navigation-night-v1"}, // Mapbox Navigation Night
{9, "mapbox://styles/mapbox/traffic-night-v2"}, // Mapbox Traffic Night
{10, "mapbox://styles/mike854/clt0hm8mw01ok01p4blkr27jp"}, // mike854's (Satellite hybrid)
};
std::unordered_map<int, std::string>::iterator it = styleUrls.find(map_style);
m_map->setStyleUrl(QString::fromStdString(it->second));
}
previous_map_style = map_style;
} }
void MapWindow::setError(const QString &err_str) { void MapWindow::setError(const QString &err_str) {
@@ -301,24 +332,24 @@ void MapWindow::resizeGL(int w, int h) {
} }
void MapWindow::initializeGL() { void MapWindow::initializeGL() {
m_map.reset(new QMapboxGL(this, m_settings, size(), 1)); m_map.reset(new QMapLibre::Map(this, m_settings, size(), 1));
if (last_position) { if (last_position) {
m_map->setCoordinateZoom(*last_position, MAX_ZOOM); m_map->setCoordinateZoom(*last_position, MAX_ZOOM);
} else { } else {
m_map->setCoordinateZoom(QMapbox::Coordinate(64.31990695292795, -149.79038934046247), MIN_ZOOM); m_map->setCoordinateZoom(QMapLibre::Coordinate(64.31990695292795, -149.79038934046247), MIN_ZOOM);
} }
m_map->setMargins({0, 350, 0, 50}); m_map->setMargins({0, 350, 0, 50});
m_map->setPitch(MIN_PITCH); m_map->setPitch(MIN_PITCH);
m_map->setStyleUrl("mapbox://styles/commaai/clkqztk0f00ou01qyhsa5bzpj"); m_map->setStyleUrl("mapbox://styles/commaai/clkqztk0f00ou01qyhsa5bzpj");
QObject::connect(m_map.data(), &QMapboxGL::mapChanged, [=](QMapboxGL::MapChange change) { QObject::connect(m_map.data(), &QMapLibre::Map::mapChanged, [=](QMapLibre::Map::MapChange change) {
// set global animation duration to 0 ms so visibility changes are instant // set global animation duration to 0 ms so visibility changes are instant
if (change == QMapboxGL::MapChange::MapChangeDidFinishLoadingStyle) { if (change == QMapLibre::Map::MapChange::MapChangeDidFinishLoadingStyle) {
m_map->setTransitionOptions(0, 0); m_map->setTransitionOptions(0, 0);
} }
if (change == QMapboxGL::MapChange::MapChangeDidFinishLoadingMap) { if (change == QMapLibre::Map::MapChange::MapChangeDidFinishLoadingMap) {
loaded_once = true; loaded_once = true;
} }
}); });
@@ -426,10 +457,10 @@ void MapWindow::updateDestinationMarker() {
auto nav_dest = coordinate_from_param("NavDestination"); auto nav_dest = coordinate_from_param("NavDestination");
if (nav_dest.has_value()) { if (nav_dest.has_value()) {
auto point = coordinate_to_collection(*nav_dest); auto point = coordinate_to_collection(*nav_dest);
QMapbox::Feature feature(QMapbox::Feature::PointType, point, {}, {}); QMapLibre::Feature feature(QMapLibre::Feature::PointType, point, {}, {});
QVariantMap pinSource; QVariantMap pinSource;
pinSource["type"] = "geojson"; pinSource["type"] = "geojson";
pinSource["data"] = QVariant::fromValue<QMapbox::Feature>(feature); pinSource["data"] = QVariant::fromValue<QMapLibre::Feature>(feature);
m_map->updateSource("pinSource", pinSource); m_map->updateSource("pinSource", pinSource);
m_map->setPaintProperty("pinLayer", "visibility", "visible"); m_map->setPaintProperty("pinLayer", "visibility", "visible");
} else { } else {

17
selfdrive/ui/qt/maps/map.h Executable file → Normal file
View File

@@ -6,7 +6,8 @@
#include <QGestureEvent> #include <QGestureEvent>
#include <QLabel> #include <QLabel>
#include <QMap> #include <QMap>
#include <QMapboxGL> #include <QMapLibre/Map>
#include <QMapLibre/Settings>
#include <QMouseEvent> #include <QMouseEvent>
#include <QOpenGLWidget> #include <QOpenGLWidget>
#include <QPixmap> #include <QPixmap>
@@ -27,7 +28,7 @@ class MapWindow : public QOpenGLWidget {
Q_OBJECT Q_OBJECT
public: public:
MapWindow(const QMapboxGLSettings &); MapWindow(const QMapLibre::Settings &);
~MapWindow(); ~MapWindow();
private: private:
@@ -35,8 +36,8 @@ private:
void paintGL() final; void paintGL() final;
void resizeGL(int w, int h) override; void resizeGL(int w, int h) override;
QMapboxGLSettings m_settings; QMapLibre::Settings m_settings;
QScopedPointer<QMapboxGL> m_map; QScopedPointer<QMapLibre::Map> m_map;
void initLayers(); void initLayers();
@@ -56,8 +57,8 @@ private:
int interaction_counter = 0; int interaction_counter = 0;
// Position // Position
std::optional<QMapbox::Coordinate> last_valid_nav_dest; std::optional<QMapLibre::Coordinate> last_valid_nav_dest;
std::optional<QMapbox::Coordinate> last_position; std::optional<QMapLibre::Coordinate> last_position;
std::optional<float> last_bearing; std::optional<float> last_bearing;
FirstOrderFilter velocity_filter; FirstOrderFilter velocity_filter;
bool locationd_valid = false; bool locationd_valid = false;
@@ -80,6 +81,10 @@ private:
// FrogPilot variables // FrogPilot variables
Params params; Params params;
int previous_map_style;
uint64_t model_rcv_frame = 0;
private slots: private slots:
void updateState(const UIState &s); void updateState(const UIState &s);

0
selfdrive/ui/qt/maps/map_eta.cc Executable file → Normal file
View File

0
selfdrive/ui/qt/maps/map_eta.h Executable file → Normal file
View File

37
selfdrive/ui/qt/maps/map_helpers.cc Executable file → Normal file
View File

@@ -16,24 +16,25 @@ QString get_mapbox_token() {
return MAPBOX_TOKEN.isEmpty() ? CommaApi::create_jwt({}, 4 * 7 * 24 * 3600) : MAPBOX_TOKEN; return MAPBOX_TOKEN.isEmpty() ? CommaApi::create_jwt({}, 4 * 7 * 24 * 3600) : MAPBOX_TOKEN;
} }
QMapboxGLSettings get_mapbox_settings() { QMapLibre::Settings get_mapbox_settings() {
QMapboxGLSettings settings; QMapLibre::Settings settings;
settings.setProviderTemplate(QMapLibre::Settings::ProviderTemplate::MapboxProvider);
if (!Hardware::PC()) { if (!Hardware::PC()) {
settings.setCacheDatabasePath(MAPS_CACHE_PATH); settings.setCacheDatabasePath(MAPS_CACHE_PATH);
settings.setCacheDatabaseMaximumSize(100 * 1024 * 1024); settings.setCacheDatabaseMaximumSize(100 * 1024 * 1024);
} }
settings.setApiBaseUrl(MAPS_HOST); settings.setApiBaseUrl(MAPS_HOST);
settings.setAccessToken(get_mapbox_token()); settings.setApiKey(get_mapbox_token());
return settings; return settings;
} }
QGeoCoordinate to_QGeoCoordinate(const QMapbox::Coordinate &in) { QGeoCoordinate to_QGeoCoordinate(const QMapLibre::Coordinate &in) {
return QGeoCoordinate(in.first, in.second); return QGeoCoordinate(in.first, in.second);
} }
QMapbox::CoordinatesCollections model_to_collection( QMapLibre::CoordinatesCollections model_to_collection(
const cereal::LiveLocationKalman::Measurement::Reader &calibratedOrientationECEF, const cereal::LiveLocationKalman::Measurement::Reader &calibratedOrientationECEF,
const cereal::LiveLocationKalman::Measurement::Reader &positionECEF, const cereal::LiveLocationKalman::Measurement::Reader &positionECEF,
const cereal::XYZTData::Reader &line){ const cereal::XYZTData::Reader &line){
@@ -42,7 +43,7 @@ QMapbox::CoordinatesCollections model_to_collection(
Eigen::Vector3d orient(calibratedOrientationECEF.getValue()[0], calibratedOrientationECEF.getValue()[1], calibratedOrientationECEF.getValue()[2]); Eigen::Vector3d orient(calibratedOrientationECEF.getValue()[0], calibratedOrientationECEF.getValue()[1], calibratedOrientationECEF.getValue()[2]);
Eigen::Matrix3d ecef_from_local = euler2rot(orient); Eigen::Matrix3d ecef_from_local = euler2rot(orient);
QMapbox::Coordinates coordinates; QMapLibre::Coordinates coordinates;
auto x = line.getX(); auto x = line.getX();
auto y = line.getY(); auto y = line.getY();
auto z = line.getZ(); auto z = line.getZ();
@@ -52,28 +53,28 @@ QMapbox::CoordinatesCollections model_to_collection(
coordinates.push_back({point_geodetic.lat, point_geodetic.lon}); coordinates.push_back({point_geodetic.lat, point_geodetic.lon});
} }
return {QMapbox::CoordinatesCollection{coordinates}}; return {QMapLibre::CoordinatesCollection{coordinates}};
} }
QMapbox::CoordinatesCollections coordinate_to_collection(const QMapbox::Coordinate &c) { QMapLibre::CoordinatesCollections coordinate_to_collection(const QMapLibre::Coordinate &c) {
QMapbox::Coordinates coordinates{c}; QMapLibre::Coordinates coordinates{c};
return {QMapbox::CoordinatesCollection{coordinates}}; return {QMapLibre::CoordinatesCollection{coordinates}};
} }
QMapbox::CoordinatesCollections capnp_coordinate_list_to_collection(const capnp::List<cereal::NavRoute::Coordinate>::Reader& coordinate_list) { QMapLibre::CoordinatesCollections capnp_coordinate_list_to_collection(const capnp::List<cereal::NavRoute::Coordinate>::Reader& coordinate_list) {
QMapbox::Coordinates coordinates; QMapLibre::Coordinates coordinates;
for (auto const &c : coordinate_list) { for (auto const &c : coordinate_list) {
coordinates.push_back({c.getLatitude(), c.getLongitude()}); coordinates.push_back({c.getLatitude(), c.getLongitude()});
} }
return {QMapbox::CoordinatesCollection{coordinates}}; return {QMapLibre::CoordinatesCollection{coordinates}};
} }
QMapbox::CoordinatesCollections coordinate_list_to_collection(const QList<QGeoCoordinate> &coordinate_list) { QMapLibre::CoordinatesCollections coordinate_list_to_collection(const QList<QGeoCoordinate> &coordinate_list) {
QMapbox::Coordinates coordinates; QMapLibre::Coordinates coordinates;
for (auto &c : coordinate_list) { for (auto &c : coordinate_list) {
coordinates.push_back({c.latitude(), c.longitude()}); coordinates.push_back({c.latitude(), c.longitude()});
} }
return {QMapbox::CoordinatesCollection{coordinates}}; return {QMapLibre::CoordinatesCollection{coordinates}};
} }
QList<QGeoCoordinate> polyline_to_coordinate_list(const QString &polylineString) { QList<QGeoCoordinate> polyline_to_coordinate_list(const QString &polylineString) {
@@ -118,7 +119,7 @@ QList<QGeoCoordinate> polyline_to_coordinate_list(const QString &polylineString)
return path; return path;
} }
std::optional<QMapbox::Coordinate> coordinate_from_param(const std::string &param) { std::optional<QMapLibre::Coordinate> coordinate_from_param(const std::string &param) {
QString json_str = QString::fromStdString(Params().get(param)); QString json_str = QString::fromStdString(Params().get(param));
if (json_str.isEmpty()) return {}; if (json_str.isEmpty()) return {};
@@ -127,7 +128,7 @@ std::optional<QMapbox::Coordinate> coordinate_from_param(const std::string &para
QJsonObject json = doc.object(); QJsonObject json = doc.object();
if (json["latitude"].isDouble() && json["longitude"].isDouble()) { if (json["latitude"].isDouble() && json["longitude"].isDouble()) {
QMapbox::Coordinate coord(json["latitude"].toDouble(), json["longitude"].toDouble()); QMapLibre::Coordinate coord(json["latitude"].toDouble(), json["longitude"].toDouble());
return coord; return coord;
} else { } else {
return {}; return {};

17
selfdrive/ui/qt/maps/map_helpers.h Executable file → Normal file
View File

@@ -3,8 +3,9 @@
#include <optional> #include <optional>
#include <string> #include <string>
#include <utility> #include <utility>
#include <QMapLibre/Map>
#include <QMapLibre/Settings>
#include <eigen3/Eigen/Dense> #include <eigen3/Eigen/Dense>
#include <QMapboxGL>
#include <QGeoCoordinate> #include <QGeoCoordinate>
#include "common/params.h" #include "common/params.h"
@@ -18,15 +19,15 @@ const QString MAPS_HOST = util::getenv("MAPS_HOST", MAPBOX_TOKEN.isEmpty() ? "ht
const QString MAPS_CACHE_PATH = "/data/mbgl-cache-navd.db"; const QString MAPS_CACHE_PATH = "/data/mbgl-cache-navd.db";
QString get_mapbox_token(); QString get_mapbox_token();
QMapboxGLSettings get_mapbox_settings(); QMapLibre::Settings get_mapbox_settings();
QGeoCoordinate to_QGeoCoordinate(const QMapbox::Coordinate &in); QGeoCoordinate to_QGeoCoordinate(const QMapLibre::Coordinate &in);
QMapbox::CoordinatesCollections model_to_collection( QMapLibre::CoordinatesCollections model_to_collection(
const cereal::LiveLocationKalman::Measurement::Reader &calibratedOrientationECEF, const cereal::LiveLocationKalman::Measurement::Reader &calibratedOrientationECEF,
const cereal::LiveLocationKalman::Measurement::Reader &positionECEF, const cereal::LiveLocationKalman::Measurement::Reader &positionECEF,
const cereal::XYZTData::Reader &line); const cereal::XYZTData::Reader &line);
QMapbox::CoordinatesCollections coordinate_to_collection(const QMapbox::Coordinate &c); QMapLibre::CoordinatesCollections coordinate_to_collection(const QMapLibre::Coordinate &c);
QMapbox::CoordinatesCollections capnp_coordinate_list_to_collection(const capnp::List<cereal::NavRoute::Coordinate>::Reader &coordinate_list); QMapLibre::CoordinatesCollections capnp_coordinate_list_to_collection(const capnp::List<cereal::NavRoute::Coordinate>::Reader &coordinate_list);
QMapbox::CoordinatesCollections coordinate_list_to_collection(const QList<QGeoCoordinate> &coordinate_list); QMapLibre::CoordinatesCollections coordinate_list_to_collection(const QList<QGeoCoordinate> &coordinate_list);
QList<QGeoCoordinate> polyline_to_coordinate_list(const QString &polylineString); QList<QGeoCoordinate> polyline_to_coordinate_list(const QString &polylineString);
std::optional<QMapbox::Coordinate> coordinate_from_param(const std::string &param); std::optional<QMapLibre::Coordinate> coordinate_from_param(const std::string &param);
std::pair<QString, QString> map_format_distance(float d, bool is_metric); std::pair<QString, QString> map_format_distance(float d, bool is_metric);

0
selfdrive/ui/qt/maps/map_instructions.cc Executable file → Normal file
View File

0
selfdrive/ui/qt/maps/map_instructions.h Executable file → Normal file
View File

7
selfdrive/ui/qt/maps/map_panel.cc Executable file → Normal file
View File

@@ -8,7 +8,7 @@
#include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/ui.h" #include "selfdrive/ui/ui.h"
MapPanel::MapPanel(const QMapboxGLSettings &mapboxSettings, QWidget *parent) : QFrame(parent) { MapPanel::MapPanel(const QMapLibre::Settings &mapboxSettings, QWidget *parent) : QFrame(parent) {
content_stack = new QStackedLayout(this); content_stack = new QStackedLayout(this);
content_stack->setContentsMargins(0, 0, 0, 0); content_stack->setContentsMargins(0, 0, 0, 0);
@@ -41,8 +41,3 @@ void MapPanel::toggleMapSettings() {
emit mapPanelRequested(); emit mapPanelRequested();
show(); show();
} }
void MapPanel::setVisible(bool visible) {
QFrame::setVisible(visible);
uiState()->scene.map_open = visible;
}

5
selfdrive/ui/qt/maps/map_panel.h Executable file → Normal file
View File

@@ -1,15 +1,14 @@
#pragma once #pragma once
#include <QFrame> #include <QFrame>
#include <QMapboxGL> #include <QMapLibre/Settings>
#include <QStackedLayout> #include <QStackedLayout>
class MapPanel : public QFrame { class MapPanel : public QFrame {
Q_OBJECT Q_OBJECT
public: public:
explicit MapPanel(const QMapboxGLSettings &settings, QWidget *parent = nullptr); explicit MapPanel(const QMapLibre::Settings &settings, QWidget *parent = nullptr);
void setVisible(bool visible);
signals: signals:
void mapPanelRequested(); void mapPanelRequested();

16
selfdrive/ui/qt/maps/map_settings.cc Executable file → Normal file
View File

@@ -62,13 +62,7 @@ MapSettings::MapSettings(bool closeable, QWidget *parent) : QFrame(parent) {
title->setStyleSheet("color: #FFFFFF; font-size: 54px; font-weight: 600;"); title->setStyleSheet("color: #FFFFFF; font-size: 54px; font-weight: 600;");
heading->addWidget(title); heading->addWidget(title);
// NOO without Prime IP extraction subtitle = new QLabel(tr("Manage at connect.comma.ai"), this);
if (notPrime) {
ipAddress = QString("%1:8082").arg(wifi->getIp4Address());
subtitle = new QLabel(tr("Manage at %1").arg(ipAddress), this);
} else {
subtitle = new QLabel(tr("Manage at connect.comma.ai"), this);
}
subtitle->setStyleSheet("color: #A0A0A0; font-size: 40px; font-weight: 300;"); subtitle->setStyleSheet("color: #A0A0A0; font-size: 40px; font-weight: 300;");
heading->addWidget(subtitle); heading->addWidget(subtitle);
} }
@@ -99,6 +93,8 @@ MapSettings::MapSettings(bool closeable, QWidget *parent) : QFrame(parent) {
setStyleSheet("MapSettings { background-color: #333333; }"); setStyleSheet("MapSettings { background-color: #333333; }");
QObject::connect(NavManager::instance(), &NavManager::updated, this, &MapSettings::refresh); QObject::connect(NavManager::instance(), &NavManager::updated, this, &MapSettings::refresh);
wifi = new WifiManager(this);
} }
void MapSettings::showEvent(QShowEvent *event) { void MapSettings::showEvent(QShowEvent *event) {
@@ -145,9 +141,9 @@ void MapSettings::refresh() {
setUpdatesEnabled(true); setUpdatesEnabled(true);
// NOO without Prime IP update // Use IP for NOO without Prime
if (notPrime) { if (!uiState()->hasPrime()) {
ipAddress = QString("%1:8082").arg(wifi->getIp4Address()); QString ipAddress = QString("%1:8082").arg(wifi->getIp4Address());
subtitle->setText(tr("Manage at %1").arg(ipAddress)); subtitle->setText(tr("Manage at %1").arg(ipAddress));
} }
} }

5
selfdrive/ui/qt/maps/map_settings.h Executable file → Normal file
View File

@@ -66,10 +66,9 @@ private:
std::vector<DestinationWidget *> widgets; std::vector<DestinationWidget *> widgets;
// FrogPilot variables // FrogPilot variables
bool notPrime = Params().getInt("PrimeType") == 0;
QLabel *subtitle; QLabel *subtitle;
QString ipAddress;
WifiManager *wifi = new WifiManager(this); WifiManager *wifi;
signals: signals:
void closeSettings(); void closeSettings();

12
selfdrive/ui/qt/network/networking.cc Executable file → Normal file
View File

@@ -82,11 +82,11 @@ void Networking::connectToNetwork(const Network n) {
if (wifi->isKnownConnection(n.ssid)) { if (wifi->isKnownConnection(n.ssid)) {
wifi->activateWifiConnection(n.ssid); wifi->activateWifiConnection(n.ssid);
} else if (n.security_type == SecurityType::OPEN) { } else if (n.security_type == SecurityType::OPEN) {
wifi->connect(n); wifi->connect(n, false);
} else if (n.security_type == SecurityType::WPA) { } else if (n.security_type == SecurityType::WPA) {
QString pass = InputDialog::getText(tr("Enter password"), this, tr("for \"%1\"").arg(QString::fromUtf8(n.ssid)), true, 8); QString pass = InputDialog::getText(tr("Enter password"), this, tr("for \"%1\"").arg(QString::fromUtf8(n.ssid)), true, 8);
if (!pass.isEmpty()) { if (!pass.isEmpty()) {
wifi->connect(n, pass); wifi->connect(n, false, pass);
} }
} }
} }
@@ -96,7 +96,7 @@ void Networking::wrongPassword(const QString &ssid) {
const Network &n = wifi->seenNetworks.value(ssid); const Network &n = wifi->seenNetworks.value(ssid);
QString pass = InputDialog::getText(tr("Wrong password"), this, tr("for \"%1\"").arg(QString::fromUtf8(n.ssid)), true, 8); QString pass = InputDialog::getText(tr("Wrong password"), this, tr("for \"%1\"").arg(QString::fromUtf8(n.ssid)), true, 8);
if (!pass.isEmpty()) { if (!pass.isEmpty()) {
wifi->connect(n, pass); wifi->connect(n, false, pass);
} }
} }
} }
@@ -131,7 +131,7 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
list->addItem(tetheringToggle); list->addItem(tetheringToggle);
QObject::connect(tetheringToggle, &ToggleControl::toggleFlipped, this, &AdvancedNetworking::toggleTethering); QObject::connect(tetheringToggle, &ToggleControl::toggleFlipped, this, &AdvancedNetworking::toggleTethering);
if (params.getBool("TetheringEnabled")) { if (params.getBool("TetheringEnabled")) {
tetheringToggle->setVisualOn(); tetheringToggle->refresh();
uiState()->scene.tethering_enabled = true; uiState()->scene.tethering_enabled = true;
} }
@@ -196,9 +196,9 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
hidden_network.ssid = ssid.toUtf8(); hidden_network.ssid = ssid.toUtf8();
if (!pass.isEmpty()) { if (!pass.isEmpty()) {
hidden_network.security_type = SecurityType::WPA; hidden_network.security_type = SecurityType::WPA;
wifi->connect(hidden_network, pass); wifi->connect(hidden_network, true, pass);
} else { } else {
wifi->connect(hidden_network); wifi->connect(hidden_network, true);
} }
emit requestWifiScreen(); emit requestWifiScreen();
} }

0
selfdrive/ui/qt/network/networking.h Executable file → Normal file
View File

0
selfdrive/ui/qt/network/networkmanager.h Executable file → Normal file
View File

3
selfdrive/ui/qt/network/wifi_manager.cc Executable file → Normal file
View File

@@ -166,7 +166,7 @@ SecurityType WifiManager::getSecurityType(const QVariantMap &properties) {
} }
} }
void WifiManager::connect(const Network &n, const QString &password, const QString &username) { void WifiManager::connect(const Network &n, const bool is_hidden, const QString &password, const QString &username) {
setCurrentConnecting(n.ssid); setCurrentConnecting(n.ssid);
forgetConnection(n.ssid); // Clear all connections that may already exist to the network we are connecting forgetConnection(n.ssid); // Clear all connections that may already exist to the network we are connecting
Connection connection; Connection connection;
@@ -176,6 +176,7 @@ void WifiManager::connect(const Network &n, const QString &password, const QStri
connection["connection"]["autoconnect-retries"] = 0; connection["connection"]["autoconnect-retries"] = 0;
connection["802-11-wireless"]["ssid"] = n.ssid; connection["802-11-wireless"]["ssid"] = n.ssid;
connection["802-11-wireless"]["hidden"] = is_hidden;
connection["802-11-wireless"]["mode"] = "infrastructure"; connection["802-11-wireless"]["mode"] = "infrastructure";
if (n.security_type == SecurityType::WPA) { if (n.security_type == SecurityType::WPA) {

2
selfdrive/ui/qt/network/wifi_manager.h Executable file → Normal file
View File

@@ -52,7 +52,7 @@ public:
std::optional<QDBusPendingCall> activateWifiConnection(const QString &ssid); std::optional<QDBusPendingCall> activateWifiConnection(const QString &ssid);
NetworkType currentNetworkType(); NetworkType currentNetworkType();
void updateGsmSettings(bool roaming, QString apn, bool metered); void updateGsmSettings(bool roaming, QString apn, bool metered);
void connect(const Network &ssid, const QString &password = {}, const QString &username = {}); void connect(const Network &ssid, const bool is_hidden = false, const QString &password = {}, const QString &username = {});
// Tethering functions // Tethering functions
void setTetheringEnabled(bool enabled); void setTetheringEnabled(bool enabled);

0
selfdrive/ui/qt/offroad/driverview.cc Executable file → Normal file
View File

0
selfdrive/ui/qt/offroad/driverview.h Executable file → Normal file
View File

0
selfdrive/ui/qt/offroad/experimental_mode.cc Executable file → Normal file
View File

0
selfdrive/ui/qt/offroad/experimental_mode.h Executable file → Normal file
View File

14
selfdrive/ui/qt/offroad/onboarding.cc Executable file → Normal file
View File

@@ -183,8 +183,8 @@ void DeclinePage::showEvent(QShowEvent *event) {
void OnboardingWindow::updateActiveScreen() { void OnboardingWindow::updateActiveScreen() {
if (!accepted_terms) { if (!accepted_terms) {
setCurrentIndex(0); setCurrentIndex(0);
// } else if (!training_done && !params.getBool("Passive")) { } else if (!training_done) {
// setCurrentIndex(1); setCurrentIndex(1);
} else { } else {
emit onboardingDone(); emit onboardingDone();
} }
@@ -199,7 +199,7 @@ OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) {
TermsPage* terms = new TermsPage(this); TermsPage* terms = new TermsPage(this);
addWidget(terms); addWidget(terms);
connect(terms, &TermsPage::acceptedTerms, [=]() { connect(terms, &TermsPage::acceptedTerms, [=]() {
Params().put("HasAcceptedTerms", current_terms_version); params.put("HasAcceptedTerms", current_terms_version);
accepted_terms = true; accepted_terms = true;
updateActiveScreen(); updateActiveScreen();
}); });
@@ -209,7 +209,7 @@ OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) {
addWidget(tr); addWidget(tr);
connect(tr, &TrainingGuide::completedTraining, [=]() { connect(tr, &TrainingGuide::completedTraining, [=]() {
training_done = true; training_done = true;
Params().put("CompletedTrainingVersion", current_training_version); params.put("CompletedTrainingVersion", current_training_version);
updateActiveScreen(); updateActiveScreen();
}); });
@@ -230,11 +230,5 @@ OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) {
background-color: #4F4F4F; background-color: #4F4F4F;
} }
)"); )");
// # Oscar sez
Params().put("HasAcceptedTerms", current_terms_version);
Params().put("CompletedTrainingVersion", current_training_version);
accepted_terms = true;
emit onboardingDone();
updateActiveScreen(); updateActiveScreen();
} }

0
selfdrive/ui/qt/offroad/onboarding.h Executable file → Normal file
View File

385
selfdrive/ui/qt/offroad/settings.cc Executable file → Normal file
View File

@@ -2,6 +2,8 @@
#include <cassert> #include <cassert>
#include <cmath> #include <cmath>
#include <iomanip>
#include <iostream>
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <vector> #include <vector>
@@ -25,9 +27,9 @@
#include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/frogpilot/navigation/ui/navigation_settings.h" #include "selfdrive/frogpilot/navigation/ui/navigation_settings.h"
#include "selfdrive/frogpilot/ui/control_settings.h" #include "selfdrive/frogpilot/ui/qt/offroad/control_settings.h"
#include "selfdrive/frogpilot/ui/vehicle_settings.h" #include "selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h"
#include "selfdrive/frogpilot/ui/visual_settings.h" #include "selfdrive/frogpilot/ui/qt/offroad/visual_settings.h"
TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
// param, title, desc, icon // param, title, desc, icon
@@ -97,9 +99,14 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
std::vector<QString> longi_button_texts{tr("Aggressive"), tr("Standard"), tr("Relaxed")}; std::vector<QString> longi_button_texts{tr("Aggressive"), tr("Standard"), tr("Relaxed")};
long_personality_setting = new ButtonParamControl("LongitudinalPersonality", tr("Driving Personality"), long_personality_setting = new ButtonParamControl("LongitudinalPersonality", tr("Driving Personality"),
tr("Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. " tr("Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. "
"In relaxed mode openpilot will stay further away from lead cars."), "In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with "
"your steering wheel distance button."),
"../assets/offroad/icon_speed_limit.png", "../assets/offroad/icon_speed_limit.png",
longi_button_texts); longi_button_texts);
// set up uiState update for personality setting
QObject::connect(uiState(), &UIState::uiUpdate, this, &TogglesPanel::updateState);
for (auto &[param, title, desc, icon] : toggle_defs) { for (auto &[param, title, desc, icon] : toggle_defs) {
auto toggle = new ParamControl(param, title, desc, icon, this); auto toggle = new ParamControl(param, title, desc, icon, this);
@@ -110,7 +117,7 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
toggles[param.toStdString()] = toggle; toggles[param.toStdString()] = toggle;
// insert longitudinal personality after NDOG toggle // insert longitudinal personality after NDOG toggle
if (param == "DisengageOnAccelerator" && !params.getInt("AdjustablePersonalities")) { if (param == "DisengageOnAccelerator") {
addItem(long_personality_setting); addItem(long_personality_setting);
} }
} }
@@ -129,6 +136,18 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
}); });
} }
void TogglesPanel::updateState(const UIState &s) {
const SubMaster &sm = *(s.sm);
if (sm.updated("controlsState")) {
auto personality = sm["controlsState"].getControlsState().getPersonality();
if (personality != s.scene.personality && s.scene.started && isVisible()) {
long_personality_setting->setCheckedButton(static_cast<int>(personality));
}
uiState()->scene.personality = personality;
}
}
void TogglesPanel::expandToggleDescription(const QString &param) { void TogglesPanel::expandToggleDescription(const QString &param) {
toggles[param.toStdString()]->showDescription(); toggles[param.toStdString()]->showDescription();
} }
@@ -138,6 +157,13 @@ void TogglesPanel::showEvent(QShowEvent *event) {
} }
void TogglesPanel::updateToggles() { void TogglesPanel::updateToggles() {
auto disengage_on_accelerator_toggle = toggles["DisengageOnAccelerator"];
disengage_on_accelerator_toggle->setVisible(!params.getBool("AlwaysOnLateral"));
auto driver_camera_toggle = toggles["RecordFront"];
driver_camera_toggle->setVisible(!(params.getBool("DeviceManagement") && params.getBool("NoLogging") && params.getBool("NoUploads")));
auto nav_settings_left_toggle = toggles["NavSettingLeftSide"];
nav_settings_left_toggle->setVisible(!params.getBool("FullMap"));
auto experimental_mode_toggle = toggles["ExperimentalMode"]; auto experimental_mode_toggle = toggles["ExperimentalMode"];
auto op_long_toggle = toggles["ExperimentalLongitudinalEnabled"]; auto op_long_toggle = toggles["ExperimentalLongitudinalEnabled"];
const QString e2e_description = QString("%1<br>" const QString e2e_description = QString("%1<br>"
@@ -173,7 +199,12 @@ void TogglesPanel::updateToggles() {
op_long_toggle->setVisible(CP.getExperimentalLongitudinalAvailable() && !is_release); op_long_toggle->setVisible(CP.getExperimentalLongitudinalAvailable() && !is_release);
if (hasLongitudinalControl(CP)) { if (hasLongitudinalControl(CP)) {
// normal description and toggle // normal description and toggle
experimental_mode_toggle->setEnabled(!params.getBool("ConditionalExperimental")); bool conditional_experimental = params.getBool("ConditionalExperimental");
if (conditional_experimental) {
params.putBool("ExperimentalMode", true);
experimental_mode_toggle->refresh();
}
experimental_mode_toggle->setEnabled(!conditional_experimental);
experimental_mode_toggle->setDescription(e2e_description); experimental_mode_toggle->setDescription(e2e_description);
long_personality_setting->setEnabled(true); long_personality_setting->setEnabled(true);
} else { } else {
@@ -225,15 +256,13 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
}); });
addItem(resetCalibBtn); addItem(resetCalibBtn);
if (!params.getBool("Passive")) { auto retrainingBtn = new ButtonControl(tr("Review Training Guide"), tr("REVIEW"), tr("Review the rules, features, and limitations of openpilot"));
auto retrainingBtn = new ButtonControl(tr("Review Training Guide"), tr("REVIEW"), tr("Review the rules, features, and limitations of openpilot")); connect(retrainingBtn, &ButtonControl::clicked, [=]() {
connect(retrainingBtn, &ButtonControl::clicked, [=]() { if (ConfirmationDialog::confirm(tr("Are you sure you want to review the training guide?"), tr("Review"), this)) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to review the training guide?"), tr("Review"), this)) { emit reviewTrainingGuide();
emit reviewTrainingGuide(); }
} });
}); addItem(retrainingBtn);
addItem(retrainingBtn);
}
if (Hardware::TICI()) { if (Hardware::TICI()) {
auto regulatoryBtn = new ButtonControl(tr("Regulatory"), tr("VIEW"), ""); auto regulatoryBtn = new ButtonControl(tr("Regulatory"), tr("VIEW"), "");
@@ -258,44 +287,251 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
addItem(translateBtn); addItem(translateBtn);
// Delete driving footage button // Delete driving footage button
auto deleteFootageBtn = new ButtonControl(tr("Delete Driving Data"), tr("DELETE"), tr("This button provides a swift and secure way to permanently delete all " auto deleteDrivingDataBtn = new ButtonControl(tr("Delete Driving Data"), tr("DELETE"), tr("This button provides a swift and secure way to permanently delete all "
"stored driving footage and data from your device. Ideal for maintaining privacy or freeing up space.") "stored driving footage and data from your device. Ideal for maintaining privacy or freeing up space.")
); );
connect(deleteFootageBtn, &ButtonControl::clicked, [this]() { connect(deleteDrivingDataBtn, &ButtonControl::clicked, [=]() {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to permanently delete all of your driving footage and data?"), tr("Delete"), this)) return; if (!ConfirmationDialog::confirm(tr("Are you sure you want to permanently delete all of your driving footage and data?"), tr("Delete"), this)) return;
std::thread([&] { std::thread([&] {
deleteDrivingDataBtn->setValue(tr("Deleting footage..."));
std::system("rm -rf /data/media/0/realdata"); std::system("rm -rf /data/media/0/realdata");
deleteDrivingDataBtn->setValue(tr("Deleted!"));
std::this_thread::sleep_for(std::chrono::seconds(3));
deleteDrivingDataBtn->setValue("");
}).detach(); }).detach();
}); });
addItem(deleteFootageBtn); addItem(deleteDrivingDataBtn);
// Panda flashing button // Panda flashing button
auto flashPandaBtn = new ButtonControl(tr("Flash Panda"), tr("FLASH"), "Use this button to troubleshoot and update the Panda device's firmware."); auto flashPandaBtn = new ButtonControl(tr("Flash Panda"), tr("FLASH"), tr("Use this button to troubleshoot and update the Panda device's firmware."));
connect(flashPandaBtn, &ButtonControl::clicked, [this]() { connect(flashPandaBtn, &ButtonControl::clicked, [=]() {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to flash the Panda?"), tr("Flash"), this)) return; if (ConfirmationDialog::confirm(tr("Are you sure you want to flash the Panda?"), tr("Flash"), this)) {
QProcess process; std::thread([=]() {
// Get Panda type flashPandaBtn->setValue(tr("Flashing..."));
SubMaster &sm = *(uiState()->sm);
auto pandaStates = sm["pandaStates"].getPandaStates(); QProcess process;
// Choose recovery script based on Panda type
if (pandaStates.size() != 0) { process.setWorkingDirectory("/data/openpilot/panda/board");
auto pandaType = pandaStates[0].getPandaType(); process.start("/bin/sh", QStringList{"-c", "./recover.py"});
bool isRedPanda = (pandaType == cereal::PandaState::PandaType::RED_PANDA || process.waitForFinished();
pandaType == cereal::PandaState::PandaType::RED_PANDA_V2); process.start("/bin/sh", QStringList{"-c", "./flash.py"});
QString recoveryScript = isRedPanda ? "./recover.sh" : "./recover.py"; process.waitForFinished();
// Run recovery script and flash Panda
process.setWorkingDirectory("/data/openpilot/panda/board"); process.setWorkingDirectory("/data/openpilot/panda/tests");
process.start("/bin/sh", QStringList{"-c", recoveryScript}); process.start("/bin/sh", QStringList{"-c", "python reflash_internal_panda.py"});
process.waitForFinished(); process.waitForFinished();
Hardware::soft_reboot();
}).detach();
} }
// Run the killall script as a redundancy
process.setWorkingDirectory("/data/openpilot/panda");
process.start("/bin/sh", QStringList{"-c", "pkill -f boardd; PYTHONPATH=.. python -c \"from panda import Panda; Panda().flash()\""});
process.waitForFinished();
Hardware::reboot();
}); });
addItem(flashPandaBtn); addItem(flashPandaBtn);
// Reset toggle button
auto resetTogglesBtn = new ButtonControl(tr("Reset Toggle Settings"), tr("RESET"), tr("Reset your toggle settings back to default."));
connect(resetTogglesBtn, &ButtonControl::clicked, [=]() {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to completely reset your toggle settings? This is irreversible!"), tr("Reset"), this)) return;
std::thread([&] {
resetTogglesBtn->setValue(tr("Resetting toggles..."));
std::system("find /data/params -type f ! -name 'FrogPilotDrives' ! -name 'FrogPilotMinutes' ! -name 'FrogPilotKilometers' -exec rm {} +");
std::system("find /persist/params -type f ! -name 'FrogPilotDrives' ! -name 'FrogPilotMinutes' ! -name 'FrogPilotKilometers' -exec rm {} +");
Hardware::soft_reboot();
}).detach();
});
addItem(resetTogglesBtn);
// Backup FrogPilot
std::vector<QString> frogpilotBackupOptions{tr("Backup"), tr("Delete"), tr("Restore")};
FrogPilotButtonsControl *frogpilotBackup = new FrogPilotButtonsControl(tr("FrogPilot Backups"), tr("Backup, delete, or restore your FrogPilot backups."), "", frogpilotBackupOptions);
connect(frogpilotBackup, &FrogPilotButtonsControl::buttonClicked, [=](int id) {
QDir backupDir("/data/backups");
if (id == 0) {
QString nameSelection = InputDialog::getText(tr("Name your backup"), this, "", false, 1);
if (!nameSelection.isEmpty()) {
std::thread([=]() {
frogpilotBackup->setValue(tr("Backing up..."));
std::string fullBackupPath = backupDir.absolutePath().toStdString() + "/" + nameSelection.toStdString();
std::ostringstream commandStream;
commandStream << "mkdir -p " << std::quoted(fullBackupPath)
<< " && rsync -av /data/openpilot/ " << std::quoted(fullBackupPath + "/");
std::string command = commandStream.str();
int result = std::system(command.c_str());
if (result == 0) {
std::cout << "Backup successful to " << fullBackupPath << std::endl;
frogpilotBackup->setValue(tr("Success!"));
std::this_thread::sleep_for(std::chrono::seconds(3));
frogpilotBackup->setValue("");
} else {
std::cerr << "Backup failed with error code: " << result << std::endl;
frogpilotBackup->setValue(tr("Failed..."));
std::this_thread::sleep_for(std::chrono::seconds(3));
frogpilotBackup->setValue("");
}
}).detach();
}
} else if (id == 1) {
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QString selection = MultiOptionDialog::getSelection(tr("Select a backup to delete"), backupNames, "", this);
if (!selection.isEmpty()) {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to delete this backup?"), tr("Delete"), this)) return;
std::thread([=]() {
frogpilotBackup->setValue(tr("Deleting..."));
QDir dirToDelete(backupDir.absoluteFilePath(selection));
if (dirToDelete.removeRecursively()) {
frogpilotBackup->setValue(tr("Deleted!"));
} else {
frogpilotBackup->setValue(tr("Failed..."));
}
std::this_thread::sleep_for(std::chrono::seconds(3));
frogpilotBackup->setValue("");
}).detach();
}
} else {
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QString selection = MultiOptionDialog::getSelection(tr("Select a restore point"), backupNames, "", this);
if (!selection.isEmpty()) {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to restore this version of FrogPilot?"), tr("Restore"), this)) return;
std::thread([=]() {
frogpilotBackup->setValue(tr("Restoring..."));
std::string sourcePath = backupDir.absolutePath().toStdString() + "/" + selection.toStdString();
std::string targetPath = "/data/safe_staging/finalized";
std::string consistentFilePath = targetPath + "/.overlay_consistent";
std::ostringstream commandStream;
commandStream << "rsync -av --delete --exclude='.overlay_consistent' " << std::quoted(sourcePath + "/") << " " << std::quoted(targetPath + "/");
std::string command = commandStream.str();
int result = std::system(command.c_str());
if (result == 0) {
std::cout << "Restore successful from " << sourcePath << " to " << targetPath << std::endl;
std::ofstream consistentFile(consistentFilePath);
if (consistentFile) {
consistentFile.close();
std::cout << ".overlay_consistent file created successfully." << std::endl;
} else {
std::cerr << "Failed to create .overlay_consistent file." << std::endl;
frogpilotBackup->setValue(tr("Failed..."));
std::this_thread::sleep_for(std::chrono::seconds(3));
frogpilotBackup->setValue("");
}
Hardware::soft_reboot();
} else {
std::cerr << "Restore failed with error code: " << result << std::endl;
}
}).detach();
}
}
});
addItem(frogpilotBackup);
// Backup toggles
std::vector<QString> toggleBackupOptions{tr("Backup"), tr("Delete"), tr("Restore")};
FrogPilotButtonsControl *toggleBackup = new FrogPilotButtonsControl(tr("Toggle Backups"), tr("Backup, delete, or restore your toggle backups."), "", toggleBackupOptions);
connect(toggleBackup, &FrogPilotButtonsControl::buttonClicked, [=](int id) {
QDir backupDir("/data/toggle_backups");
if (id == 0) {
QString nameSelection = InputDialog::getText(tr("Name your backup"), this, "", false, 1);
if (!nameSelection.isEmpty()) {
std::thread([=]() {
toggleBackup->setValue(tr("Backing up..."));
std::string fullBackupPath = backupDir.absolutePath().toStdString() + "/" + nameSelection.toStdString() + "/";
std::ostringstream commandStream;
commandStream << "mkdir -p " << std::quoted(fullBackupPath)
<< " && rsync -av /data/params/d/ " << std::quoted(fullBackupPath);
std::string command = commandStream.str();
int result = std::system(command.c_str());
if (result == 0) {
std::cout << "Backup successful to " << fullBackupPath << std::endl;
toggleBackup->setValue(tr("Success!"));
std::this_thread::sleep_for(std::chrono::seconds(3));
toggleBackup->setValue("");
} else {
std::cerr << "Backup failed with error code: " << result << std::endl;
toggleBackup->setValue(tr("Failed..."));
std::this_thread::sleep_for(std::chrono::seconds(3));
toggleBackup->setValue("");
}
}).detach();
}
} else if (id == 1) {
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QString selection = MultiOptionDialog::getSelection(tr("Select a backup to delete"), backupNames, "", this);
if (!selection.isEmpty()) {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to delete this backup?"), tr("Delete"), this)) return;
std::thread([=]() {
toggleBackup->setValue(tr("Deleting..."));
QDir dirToDelete(backupDir.absoluteFilePath(selection));
if (dirToDelete.removeRecursively()) {
toggleBackup->setValue(tr("Deleted!"));
} else {
toggleBackup->setValue(tr("Failed..."));
}
std::this_thread::sleep_for(std::chrono::seconds(3));
toggleBackup->setValue("");
}).detach();
}
} else {
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QString selection = MultiOptionDialog::getSelection(tr("Select a restore point"), backupNames, "", this);
if (!selection.isEmpty()) {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to restore this toggle backup?"), tr("Restore"), this)) return;
std::thread([=]() {
toggleBackup->setValue(tr("Restoring..."));
std::string sourcePath = backupDir.absolutePath().toStdString() + "/" + selection.toStdString() + "/";
std::string targetPath = "/data/params/d/";
std::ostringstream commandStream;
commandStream << "rsync -av --delete " << std::quoted(sourcePath) << " " << std::quoted(targetPath);
std::string command = commandStream.str();
int result = std::system(command.c_str());
if (result == 0) {
std::cout << "Restore successful from " << sourcePath << " to " << targetPath << std::endl;
toggleBackup->setValue(tr("Success!"));
paramsMemory.putBool("FrogPilotTogglesUpdated", true);
std::this_thread::sleep_for(std::chrono::seconds(3));
toggleBackup->setValue("");
paramsMemory.putBool("FrogPilotTogglesUpdated", false);
} else {
std::cerr << "Restore failed with error code: " << result << std::endl;
toggleBackup->setValue(tr("Failed..."));
std::this_thread::sleep_for(std::chrono::seconds(3));
toggleBackup->setValue("");
}
}).detach();
}
}
});
addItem(toggleBackup);
auto lockDoorsButton = new ButtonControl(tr("Lock Doors"), tr("LOCK"), tr("Use this button to lock the doors on Toyota/Lexus vehicles."));
connect(lockDoorsButton, &ButtonControl::clicked, [this]() {
QProcess *process = new QProcess(this);
QString scriptPath = "/data/openpilot/frogpilot/controls/lib/lock_doors.py";
QStringList arguments{"--lock"};
process->start("python3", QStringList() << scriptPath << arguments);
});
addItem(lockDoorsButton);
QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
for (auto btn : findChildren<ButtonControl *>()) { for (auto btn : findChildren<ButtonControl *>()) {
btn->setEnabled(offroad); btn->setEnabled(offroad);
@@ -311,6 +547,11 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
power_layout->addWidget(reboot_btn); power_layout->addWidget(reboot_btn);
QObject::connect(reboot_btn, &QPushButton::clicked, this, &DevicePanel::reboot); QObject::connect(reboot_btn, &QPushButton::clicked, this, &DevicePanel::reboot);
QPushButton *softreboot_btn = new QPushButton(tr("Soft Reboot"));
softreboot_btn->setObjectName("softreboot_btn");
power_layout->addWidget(softreboot_btn);
QObject::connect(softreboot_btn, &QPushButton::clicked, this, &DevicePanel::softreboot);
QPushButton *poweroff_btn = new QPushButton(tr("Power Off")); QPushButton *poweroff_btn = new QPushButton(tr("Power Off"));
poweroff_btn->setObjectName("poweroff_btn"); poweroff_btn->setObjectName("poweroff_btn");
power_layout->addWidget(poweroff_btn); power_layout->addWidget(poweroff_btn);
@@ -321,6 +562,8 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
} }
setStyleSheet(R"( setStyleSheet(R"(
#softreboot_btn { height: 120px; border-radius: 15px; background-color: #e2e22c; }
#softreboot_btn:pressed { background-color: #ffe224; }
#reboot_btn { height: 120px; border-radius: 15px; background-color: #393939; } #reboot_btn { height: 120px; border-radius: 15px; background-color: #393939; }
#reboot_btn:pressed { background-color: #4a4a4a; } #reboot_btn:pressed { background-color: #4a4a4a; }
#poweroff_btn { height: 120px; border-radius: 15px; background-color: #E22C2C; } #poweroff_btn { height: 120px; border-radius: 15px; background-color: #E22C2C; }
@@ -366,6 +609,18 @@ void DevicePanel::reboot() {
} }
} }
void DevicePanel::softreboot() {
if (!uiState()->engaged()) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to soft reboot?"), tr("Soft Reboot"), this)) {
if (!uiState()->engaged()) {
params.putBool("DoSoftReboot", true);
}
}
} else {
ConfirmationDialog::alert(tr("Disengage to Soft Reboot"), this);
}
}
void DevicePanel::poweroff() { void DevicePanel::poweroff() {
if (!uiState()->engaged()) { if (!uiState()->engaged()) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to power off?"), tr("Power Off"), this)) { if (ConfirmationDialog::confirm(tr("Are you sure you want to power off?"), tr("Power Off"), this)) {
@@ -379,6 +634,15 @@ void DevicePanel::poweroff() {
} }
} }
void SettingsWindow::hideEvent(QHideEvent *event) {
closeParentToggle();
parentToggleOpen = false;
subParentToggleOpen = false;
previousScrollPosition = 0;
}
void SettingsWindow::showEvent(QShowEvent *event) { void SettingsWindow::showEvent(QShowEvent *event) {
setCurrentPanel(0); setCurrentPanel(0);
} }
@@ -396,33 +660,34 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
// setup two main layouts // setup two main layouts
sidebar_widget = new QWidget; sidebar_widget = new QWidget;
QVBoxLayout *sidebar_layout = new QVBoxLayout(sidebar_widget); QVBoxLayout *sidebar_layout = new QVBoxLayout(sidebar_widget);
sidebar_layout->setMargin(0);
panel_widget = new QStackedWidget(); panel_widget = new QStackedWidget();
// close button // close button
QPushButton *close_btn = new QPushButton(tr("← Back")); QPushButton *close_btn = new QPushButton(tr("← Back"));
close_btn->setStyleSheet(R"( close_btn->setStyleSheet(R"(
QPushButton { QPushButton {
font-size: 50px; color: white;
padding-bottom: 0px;
border 1px grey solid;
border-radius: 25px; border-radius: 25px;
background-color: #292929; background: #292929;
font-size: 50px;
font-weight: 500; font-weight: 500;
} }
QPushButton:pressed { QPushButton:pressed {
background-color: #3B3B3B; color: #ADADAD;
} }
)"); )");
close_btn->setFixedSize(300, 125); close_btn->setFixedSize(300, 125);
sidebar_layout->addSpacing(10); sidebar_layout->addSpacing(10);
sidebar_layout->addWidget(close_btn, 0, Qt::AlignRight); sidebar_layout->addWidget(close_btn, 0, Qt::AlignRight);
QObject::connect(close_btn, &QPushButton::clicked, [this]() { QObject::connect(close_btn, &QPushButton::clicked, [this]() {
if (frogPilotTogglesOpen) { if (subParentToggleOpen) {
frogPilotTogglesOpen = false; closeSubParentToggle();
this->closeParentToggle(); subParentToggleOpen = false;
} else if (parentToggleOpen) {
closeParentToggle();
parentToggleOpen = false;
} else { } else {
this->closeSettings(); closeSettings();
} }
}); });
@@ -435,20 +700,19 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription); QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription);
QObject::connect(toggles, &TogglesPanel::updateMetric, this, &SettingsWindow::updateMetric); QObject::connect(toggles, &TogglesPanel::updateMetric, this, &SettingsWindow::updateMetric);
// FrogPilotControlsPanel *frogpilotControls = new FrogPilotControlsPanel(this); FrogPilotControlsPanel *frogpilotControls = new FrogPilotControlsPanel(this);
// QObject::connect(frogpilotControls, &FrogPilotControlsPanel::closeParentToggle, this, [this]() {frogPilotTogglesOpen = false;}); QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openSubParentToggle, this, [this]() {subParentToggleOpen = true;});
// QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openParentToggle, this, [this]() {frogPilotTogglesOpen = true;}); QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openParentToggle, this, [this]() {parentToggleOpen = true;});
FrogPilotVisualsPanel *frogpilotVisuals = new FrogPilotVisualsPanel(this); FrogPilotVisualsPanel *frogpilotVisuals = new FrogPilotVisualsPanel(this);
QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::closeParentToggle, this, [this]() {frogPilotTogglesOpen = false;}); QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::openParentToggle, this, [this]() {parentToggleOpen = true;});
QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::openParentToggle, this, [this]() {frogPilotTogglesOpen = true;});
QList<QPair<QString, QWidget *>> panels = { QList<QPair<QString, QWidget *>> panels = {
{tr("Device"), device}, {tr("Device"), device},
{tr("Network"), new Networking(this)}, {tr("Network"), new Networking(this)},
{tr("Toggles"), toggles}, {tr("Toggles"), toggles},
{tr("Software"), new SoftwarePanel(this)}, {tr("Software"), new SoftwarePanel(this)},
// {tr("Controls"), frogpilotControls}, {tr("Controls"), frogpilotControls},
{tr("Navigation"), new FrogPilotNavigationPanel(this)}, {tr("Navigation"), new FrogPilotNavigationPanel(this)},
{tr("Vehicles"), new FrogPilotVehiclesPanel(this)}, {tr("Vehicles"), new FrogPilotVehiclesPanel(this)},
{tr("Visuals"), frogpilotVisuals}, {tr("Visuals"), frogpilotVisuals},
@@ -484,21 +748,24 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
ScrollView *panel_frame = new ScrollView(panel, this); ScrollView *panel_frame = new ScrollView(panel, this);
panel_widget->addWidget(panel_frame); panel_widget->addWidget(panel_frame);
if (name == tr("Controls") || name == tr("Visuals")) { if (name == tr("Controls")) {
QScrollBar *scrollbar = panel_frame->verticalScrollBar(); QScrollBar *scrollbar = panel_frame->verticalScrollBar();
QObject::connect(scrollbar, &QScrollBar::valueChanged, this, [this](int value) { QObject::connect(scrollbar, &QScrollBar::valueChanged, this, [this](int value) {
if (!frogPilotTogglesOpen) { if (!parentToggleOpen) {
previousScrollPosition = value; previousScrollPosition = value;
} }
}); });
QObject::connect(scrollbar, &QScrollBar::rangeChanged, this, [this, panel_frame]() { QObject::connect(scrollbar, &QScrollBar::rangeChanged, this, [this, panel_frame]() {
panel_frame->restorePosition(previousScrollPosition); if (!parentToggleOpen) {
panel_frame->restorePosition(previousScrollPosition);
}
}); });
} }
QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() { QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() {
closeParentToggle();
previousScrollPosition = 0; previousScrollPosition = 0;
btn->setChecked(true); btn->setChecked(true);
panel_widget->setCurrentWidget(w); panel_widget->setCurrentWidget(w);

27
selfdrive/ui/qt/offroad/settings.h Executable file → Normal file
View File

@@ -11,6 +11,7 @@
#include <QWidget> #include <QWidget>
#include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/widgets/controls.h" #include "selfdrive/ui/qt/widgets/controls.h"
@@ -25,6 +26,9 @@ public:
protected: protected:
void showEvent(QShowEvent *event) override; void showEvent(QShowEvent *event) override;
// FrogPilot widgets
void hideEvent(QHideEvent *event) override;
signals: signals:
void closeSettings(); void closeSettings();
void reviewTrainingGuide(); void reviewTrainingGuide();
@@ -33,7 +37,9 @@ signals:
// FrogPilot signals // FrogPilot signals
void closeParentToggle(); void closeParentToggle();
void closeSubParentToggle();
void updateMetric(); void updateMetric();
private: private:
QPushButton *sidebar_alert_widget; QPushButton *sidebar_alert_widget;
QWidget *sidebar_widget; QWidget *sidebar_widget;
@@ -41,7 +47,9 @@ private:
QStackedWidget *panel_widget; QStackedWidget *panel_widget;
// FrogPilot variables // FrogPilot variables
bool frogPilotTogglesOpen; bool parentToggleOpen;
bool subParentToggleOpen;
int previousScrollPosition; int previousScrollPosition;
}; };
@@ -56,10 +64,14 @@ signals:
private slots: private slots:
void poweroff(); void poweroff();
void reboot(); void reboot();
void softreboot();
void updateCalibDescription(); void updateCalibDescription();
private: private:
Params params; Params params;
// FrogPilot variables
Params paramsMemory{"/dev/shm/params"};
}; };
class TogglesPanel : public ListWidget { class TogglesPanel : public ListWidget {
@@ -75,6 +87,9 @@ signals:
public slots: public slots:
void expandToggleDescription(const QString &param); void expandToggleDescription(const QString &param);
private slots:
void updateState(const UIState &s);
private: private:
Params params; Params params;
std::map<std::string, ParamControl*> toggles; std::map<std::string, ParamControl*> toggles;
@@ -97,7 +112,6 @@ private:
QLabel *onroadLbl; QLabel *onroadLbl;
LabelControl *versionLbl; LabelControl *versionLbl;
ButtonControl *errorLogBtn;
ButtonControl *installBtn; ButtonControl *installBtn;
ButtonControl *downloadBtn; ButtonControl *downloadBtn;
ButtonControl *targetBranchBtn; ButtonControl *targetBranchBtn;
@@ -106,11 +120,6 @@ private:
ParamWatcher *fs_watch; ParamWatcher *fs_watch;
// FrogPilot variables // FrogPilot variables
void automaticUpdate(); Params paramsMemory{"/dev/shm/params"};
UIScene &scene;
ButtonControl *updateTime;
int deviceShutdown;
int schedule;
int time;
}; };

152
selfdrive/ui/qt/offroad/software_settings.cc Executable file → Normal file
View File

@@ -2,8 +2,6 @@
#include <cassert> #include <cassert>
#include <cmath> #include <cmath>
#include <iomanip>
#include <sstream>
#include <string> #include <string>
#include <QDebug> #include <QDebug>
@@ -18,13 +16,14 @@
#include "selfdrive/ui/qt/widgets/input.h" #include "selfdrive/ui/qt/widgets/input.h"
#include "system/hardware/hw.h" #include "system/hardware/hw.h"
#include "selfdrive/frogpilot/ui/qt/widgets/frogpilot_controls.h"
void SoftwarePanel::checkForUpdates() { void SoftwarePanel::checkForUpdates() {
std::system("pkill -SIGUSR1 -f selfdrive.updated"); std::system("pkill -SIGUSR1 -f selfdrive.updated.updated");
} }
SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) { SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent), scene(uiState()->scene) {
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;"); onroadLbl->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left; padding-top: 30px; padding-bottom: 30px;");
addItem(onroadLbl); addItem(onroadLbl);
@@ -32,40 +31,17 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
versionLbl = new LabelControl(tr("Current Version"), ""); versionLbl = new LabelControl(tr("Current Version"), "");
addItem(versionLbl); addItem(versionLbl);
// update scheduler // automatic updates toggle
std::vector<QString> scheduleOptions{tr("Manually"), tr("Daily"), tr("Weekly")}; ParamControl *automaticUpdatesToggle = new ParamControl("AutomaticUpdates", tr("Automatically Update FrogPilot"),
ButtonParamControl *preferredSchedule = new ButtonParamControl("UpdateSchedule", tr("Update Scheduler"), tr("FrogPilot will automatically update itself and it's assets when you're offroad and connected to Wi-Fi."), "");
tr("Choose the update frequency for FrogPilot's automatic updates.\n\n" connect(automaticUpdatesToggle, &ToggleControl::toggleFlipped, [this]() {
"This feature will handle the download, installation, and device reboot for a seamless 'Set and Forget' experience.\n\n" std::thread([this]() {
"Weekly updates start at midnight every Sunday."), paramsMemory.putBool("FrogPilotTogglesUpdated", true);
"", std::this_thread::sleep_for(std::chrono::seconds(1));
scheduleOptions); paramsMemory.putBool("FrogPilotTogglesUpdated", false);
schedule = params.getInt("UpdateSchedule"); }).detach();
addItem(preferredSchedule);
updateTime = new ButtonControl(tr("Update Time"), tr("SELECT"));
QStringList hours;
for (int h = 0; h < 24; h++) {
int displayHour = (h % 12 == 0) ? 12 : h % 12;
QString meridiem = (h < 12) ? "AM" : "PM";
hours << QString("%1:00 %2").arg(displayHour).arg(meridiem)
<< QString("%1:30 %2").arg(displayHour).arg(meridiem);
}
QObject::connect(updateTime, &ButtonControl::clicked, [=]() {
int currentHourIndex = params.getInt("UpdateTime");
QString currentHourLabel = hours[currentHourIndex];
QString selection = MultiOptionDialog::getSelection(tr("Select a time to automatically update"), hours, currentHourLabel, this);
if (!selection.isEmpty()) {
int selectedHourIndex = hours.indexOf(selection);
params.putInt("UpdateTime", selectedHourIndex);
updateTime->setValue(selection);
}
}); });
time = params.getInt("UpdateTime"); addItem(automaticUpdatesToggle);
deviceShutdown = params.getInt("DeviceShutdown") * 3600;
updateTime->setValue(hours[time]);
addItem(updateTime);
// download update btn // download update btn
downloadBtn = new ButtonControl(tr("Download"), tr("CHECK")); downloadBtn = new ButtonControl(tr("Download"), tr("CHECK"));
@@ -74,8 +50,9 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
if (downloadBtn->text() == tr("CHECK")) { if (downloadBtn->text() == tr("CHECK")) {
checkForUpdates(); checkForUpdates();
} else { } else {
std::system("pkill -SIGHUP -f selfdrive.updated"); std::system("pkill -SIGHUP -f selfdrive.updated.updated");
} }
paramsMemory.putBool("ManualUpdateInitiated", true);
}); });
addItem(downloadBtn); addItem(downloadBtn);
@@ -92,6 +69,11 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
connect(targetBranchBtn, &ButtonControl::clicked, [=]() { connect(targetBranchBtn, &ButtonControl::clicked, [=]() {
auto current = params.get("GitBranch"); auto current = params.get("GitBranch");
QStringList branches = QString::fromStdString(params.get("UpdaterAvailableBranches")).split(","); QStringList branches = QString::fromStdString(params.get("UpdaterAvailableBranches")).split(",");
if (!Params("/persist/params").getBool("FrogsGoMoo")) {
branches.removeAll("FrogPilot-Development");
branches.removeAll("FrogPilot-New");
branches.removeAll("MAKE-PRS-HERE");
}
for (QString b : {current.c_str(), "devel-staging", "devel", "nightly", "master-ci", "master"}) { for (QString b : {current.c_str(), "devel-staging", "devel", "nightly", "master-ci", "master"}) {
auto i = branches.indexOf(b); auto i = branches.indexOf(b);
if (i >= 0) { if (i >= 0) {
@@ -122,7 +104,7 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
addItem(uninstallBtn); addItem(uninstallBtn);
// error log button // error log button
errorLogBtn = new ButtonControl(tr("Error Log"), tr("VIEW"), "View the error log for debugging purposes when openpilot crashes."); auto errorLogBtn = new ButtonControl(tr("Error Log"), tr("VIEW"), tr("View the error log for openpilot crashes."));
connect(errorLogBtn, &ButtonControl::clicked, [=]() { connect(errorLogBtn, &ButtonControl::clicked, [=]() {
std::string txt = util::read_file("/data/community/crashes/error.txt"); std::string txt = util::read_file("/data/community/crashes/error.txt");
ConfirmationDialog::rich(QString::fromStdString(txt), this); ConfirmationDialog::rich(QString::fromStdString(txt), this);
@@ -131,8 +113,6 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
fs_watch = new ParamWatcher(this); fs_watch = new ParamWatcher(this);
QObject::connect(fs_watch, &ParamWatcher::paramChanged, [=](const QString &param_name, const QString &param_value) { QObject::connect(fs_watch, &ParamWatcher::paramChanged, [=](const QString &param_name, const QString &param_value) {
schedule = params.getInt("UpdateSchedule");
time = params.getInt("UpdateTime");
updateLabels(); updateLabels();
}); });
@@ -141,8 +121,6 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
updateLabels(); updateLabels();
}); });
QObject::connect(uiState(), &UIState::uiUpdate, this, &SoftwarePanel::automaticUpdate);
updateLabels(); updateLabels();
} }
@@ -160,16 +138,15 @@ void SoftwarePanel::updateLabels() {
fs_watch->addParam("UpdaterState"); fs_watch->addParam("UpdaterState");
fs_watch->addParam("UpdateAvailable"); fs_watch->addParam("UpdateAvailable");
fs_watch->addParam("UpdateSchedule");
fs_watch->addParam("UpdateTime");
if (!isVisible()) { if (!isVisible()) {
return; return;
} }
// updater only runs offroad // updater only runs offroad or when parked
onroadLbl->setVisible(is_onroad); bool parked = scene.parked;
downloadBtn->setVisible(!is_onroad);
onroadLbl->setVisible(is_onroad && !parked);
downloadBtn->setVisible(!is_onroad || parked);
// download update // download update
QString updater_state = QString::fromStdString(params.get("UpdaterState")); QString updater_state = QString::fromStdString(params.get("UpdaterState"));
@@ -201,82 +178,9 @@ void SoftwarePanel::updateLabels() {
versionLbl->setText(QString::fromStdString(params.get("UpdaterCurrentDescription"))); versionLbl->setText(QString::fromStdString(params.get("UpdaterCurrentDescription")));
versionLbl->setDescription(QString::fromStdString(params.get("UpdaterCurrentReleaseNotes"))); 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->setValue(QString::fromStdString(params.get("UpdaterNewDescription")));
installBtn->setDescription(QString::fromStdString(params.get("UpdaterNewReleaseNotes"))); installBtn->setDescription(QString::fromStdString(params.get("UpdaterNewReleaseNotes")));
updateTime->setVisible(params.getInt("UpdateSchedule"));
update(); update();
} }
void SoftwarePanel::automaticUpdate() {
static int timer = 0;
static std::chrono::system_clock::time_point lastOffroadTime;
if (!is_onroad) {
timer = (timer == 0) ? 0 : std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - lastOffroadTime).count();
lastOffroadTime = std::chrono::system_clock::now();
} else {
timer = 0;
}
bool isWifiConnected = (*uiState()->sm)["deviceState"].getDeviceState().getNetworkType() == cereal::DeviceState::NetworkType::WIFI;
if (schedule == 0 || is_onroad || !isWifiConnected || isVisible()) return;
static bool isDownloadCompleted = false;
if (isDownloadCompleted && params.getBool("UpdateAvailable")) {
params.putBool(timer > deviceShutdown ? "DoShutdown" : "DoReboot", true);
return;
}
int updateHour = time / 2;
int updateMinute = (time % 2) * 30;
if (updateHour >= 1 && updateHour <= 11 && time >= 24) {
updateHour += 12;
} else if (updateHour == 12 && time < 24) {
updateHour = 0;
}
std::time_t currentTimeT = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::tm now = *std::localtime(&currentTimeT);
static bool updateCheckedToday = false;
static int lastCheckedDay = now.tm_yday;
if (lastCheckedDay != now.tm_yday) {
updateCheckedToday = false;
lastCheckedDay = now.tm_yday;
}
if (now.tm_hour != updateHour || now.tm_min < updateMinute) return;
std::string lastUpdateStr = params.get("Updated");
std::tm lastUpdate = {};
std::istringstream iss(lastUpdateStr);
if (iss >> std::get_time(&lastUpdate, "%Y-%m-%d %H:%M:%S")) {
lastUpdate.tm_year -= 1900;
lastUpdate.tm_mon -= 1;
}
std::time_t lastUpdateTimeT = std::mktime(&lastUpdate);
if (lastUpdate.tm_yday == now.tm_yday) {
return;
}
if (!isDownloadCompleted) {
std::chrono::hours durationSinceLastUpdate = std::chrono::duration_cast<std::chrono::hours>(std::chrono::system_clock::now() - std::chrono::system_clock::from_time_t(lastUpdateTimeT));
int daysSinceLastUpdate = durationSinceLastUpdate.count() / 24;
if ((schedule == 1 && daysSinceLastUpdate >= 1) || (schedule == 2 && (now.tm_yday / 7) != (std::localtime(&lastUpdateTimeT)->tm_yday / 7))) {
if (downloadBtn->text() == tr("CHECK") && !updateCheckedToday) {
checkForUpdates();
updateCheckedToday = true;
} else {
std::system("pkill -SIGHUP -f selfdrive.updated");
isDownloadCompleted = true;
}
}
}
}

0
selfdrive/ui/qt/offroad/text_view.qml Executable file → Normal file
View File

1511
selfdrive/ui/qt/onroad.cc Executable file → Normal file

File diff suppressed because it is too large Load Diff

132
selfdrive/ui/qt/onroad.h Executable file → Normal file
View File

@@ -2,7 +2,8 @@
#include <memory> #include <memory>
#include <QElapsedTimer> #include <QMovie>
#include <QLabel>
#include <QPushButton> #include <QPushButton>
#include <QStackedLayout> #include <QStackedLayout>
#include <QWidget> #include <QWidget>
@@ -16,7 +17,6 @@
const int btn_size = 192; const int btn_size = 192;
const int img_size = (btn_size / 4) * 3; const int img_size = (btn_size / 4) * 3;
// FrogPilot global variables
static double fps; static double fps;
// ***** onroad widgets ***** // ***** onroad widgets *****
@@ -42,13 +42,14 @@ class Compass : public QWidget {
public: public:
explicit Compass(QWidget *parent = nullptr); explicit Compass(QWidget *parent = nullptr);
void initializeStaticElements(); void updateState();
void updateState(int bearing_deg);
protected: protected:
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
private: private:
UIScene &scene;
int bearingDeg; int bearingDeg;
int circleOffset; int circleOffset;
int compassSize; int compassSize;
@@ -56,10 +57,37 @@ private:
int innerCompass; int innerCompass;
int x; int x;
int y; int y;
QPixmap compassInnerImg; QPixmap compassInnerImg;
QPixmap staticElements; QPixmap staticElements;
}; };
class DistanceButton : public QPushButton {
public:
explicit DistanceButton(QWidget *parent = nullptr);
void buttonPressed();
void buttonReleased();
void updateState();
protected:
void paintEvent(QPaintEvent *event) override;
private:
Params paramsMemory{"/dev/shm/params"};
UIScene &scene;
bool trafficModeActive;
int personality;
QElapsedTimer transitionTimer;
QVector<std::pair<QPixmap, QString>> profile_data;
QVector<std::pair<QPixmap, QString>> profile_data_kaofui;
};
class ExperimentalButton : public QPushButton { class ExperimentalButton : public QPushButton {
Q_OBJECT Q_OBJECT
@@ -78,14 +106,24 @@ private:
bool engageable; bool engageable;
// FrogPilot variables // FrogPilot variables
Params paramsMemory{"/dev/shm/params"};
UIScene &scene; UIScene &scene;
std::map<int, QPixmap> wheelImages; QMap<int, QPixmap> wheelImages;
QMap<int, QMovie*> wheelImagesGif;
QMovie engage_gif;
QLabel *gifLabel;
bool docRandomEventTriggered;
bool firefoxRandomEventTriggered; bool firefoxRandomEventTriggered;
bool rotatingWheel; bool rotatingWheel;
bool treeFiddyRandomEventTriggered;
bool weebRandomEventTriggered;
int steeringAngleDeg; int steeringAngleDeg;
int wheelIcon; int wheelIcon;
int wheelIconGif;
int y_offset; int y_offset;
}; };
@@ -102,28 +140,25 @@ private:
QPixmap settings_img; QPixmap settings_img;
}; };
// FrogPilot buttons class PedalIcons : public QWidget {
class PersonalityButton : public QPushButton { Q_OBJECT
public:
explicit PersonalityButton(QWidget *parent = 0);
void checkUpdate(); public:
void handleClick(); explicit PedalIcons(QWidget *parent = 0);
void updateState(); void updateState();
private: private:
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
Params params; QPixmap brake_pedal_img;
Params paramsMemory{"/dev/shm/params"}; QPixmap gas_pedal_img;
UIScene &scene; UIScene &scene;
int personalityProfile = 0; bool accelerating;
bool decelerating;
QElapsedTimer transitionTimer; float acceleration;
QVector<std::pair<QPixmap, QString>> profile_data;
}; };
// container window for the NVG UI // container window for the NVG UI
@@ -163,79 +198,86 @@ private:
bool wide_cam_requested = false; bool wide_cam_requested = false;
// FrogPilot widgets // FrogPilot widgets
void initializeFrogPilotWidgets();
void paintFrogPilotWidgets(QPainter &p);
void updateFrogPilotWidgets();
void drawLeadInfo(QPainter &p); void drawLeadInfo(QPainter &p);
void drawSLCConfirmation(QPainter &p);
void drawStatusBar(QPainter &p); void drawStatusBar(QPainter &p);
void drawTurnSignals(QPainter &p); void drawTurnSignals(QPainter &p);
void initializeFrogPilotWidgets();
void updateFrogPilotWidgets(QPainter &p);
// FrogPilot variables // FrogPilot variables
Params paramsMemory{"/dev/shm/params"}; Params paramsMemory{"/dev/shm/params"};
UIScene &scene; UIScene &scene;
Compass *compass_img; Compass *compass_img;
PersonalityButton *personality_btn; DistanceButton *distance_btn;
PedalIcons *pedal_icons;
ScreenRecorder *recorder_btn; ScreenRecorder *recorder_btn;
QHBoxLayout *bottom_layout; QHBoxLayout *bottom_layout;
bool accelerationPath; bool alwaysOnLateralActive;
bool adjacentPath; bool bigMapOpen;
bool alwaysOnLateral;
bool blindSpotLeft; bool blindSpotLeft;
bool blindSpotRight; bool blindSpotRight;
bool compass; bool compass;
bool conditionalExperimental;
bool experimentalMode; bool experimentalMode;
bool hideSpeed;
bool leadInfo; bool leadInfo;
bool mapOpen; bool mapOpen;
bool muteDM; bool onroadDistanceButton;
bool onroadAdjustableProfiles;
bool reverseCruise;
bool roadNameUI; bool roadNameUI;
bool showDriverCamera; bool showAlwaysOnLateralStatusBar;
bool showConditionalExperimentalStatusBar;
bool showSLCOffset; bool showSLCOffset;
bool slcOverridden; bool slcOverridden;
bool speedLimitController;
bool trafficModeActive;
bool turnSignalLeft; bool turnSignalLeft;
bool turnSignalRight; bool turnSignalRight;
bool useSI;
bool useViennaSLCSign; bool useViennaSLCSign;
double maxAcceleration; bool vtscControllingCurve;
float cruiseAdjustment; float cruiseAdjustment;
float distanceConversion;
float laneDetectionWidth;
float laneWidthLeft; float laneWidthLeft;
float laneWidthRight; float laneWidthRight;
float slcOverriddenSpeed;
float slcSpeedLimit;
float slcSpeedLimitOffset; float slcSpeedLimitOffset;
int bearingDeg; float speedConversion;
int alertSize;
int cameraView; int cameraView;
int conditionalSpeed;
int conditionalSpeedLead;
int conditionalStatus; int conditionalStatus;
int currentHolidayTheme;
int customColors; int customColors;
int customSignals; int customSignals;
int desiredFollow;
int obstacleDistance; int obstacleDistance;
int obstacleDistanceStock; int obstacleDistanceStock;
int stoppedEquivalence;
int totalFrames = 8; int totalFrames = 8;
QTimer *animationTimer;
QString leadDistanceUnit;
QString leadSpeedUnit;
size_t animationFrameIndex; size_t animationFrameIndex;
inline QColor greenColor(int alpha = 242) { return QColor(23, 134, 68, alpha); } std::unordered_map<int, std::tuple<QString, QColor, std::map<double, QBrush>>> themeConfiguration;
std::unordered_map<int, std::tuple<QString, QColor, std::map<double, QBrush>>> holidayThemeConfiguration;
std::unordered_map<int, std::pair<QString, std::pair<QColor, std::map<double, QBrush>>>> themeConfiguration;
std::vector<QPixmap> signalImgVector; std::vector<QPixmap> signalImgVector;
QTimer *animationTimer;
inline QColor blueColor(int alpha = 255) { return QColor(0, 150, 255, alpha); }
inline QColor greenColor(int alpha = 242) { return QColor(23, 134, 68, alpha); }
protected: protected:
void paintGL() override; void paintGL() override;
void initializeGL() override; void initializeGL() override;
void showEvent(QShowEvent *event) override; void showEvent(QShowEvent *event) override;
void updateFrameMat() override; void updateFrameMat() override;
void drawLaneLines(QPainter &painter, const UIState *s); void drawLaneLines(QPainter &painter, const UIState *s);
void drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd); void drawLead(QPainter &painter, const cereal::ModelDataV2::LeadDataV3::Reader &lead_data, const QPointF &vd, const float v_ego);
void drawHud(QPainter &p); void drawHud(QPainter &p);
void drawDriverState(QPainter &painter, const UIState *s); void drawDriverState(QPainter &painter, const UIState *s);
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
@@ -270,6 +312,8 @@ private:
// FrogPilot variables // FrogPilot variables
UIScene &scene; UIScene &scene;
Params params;
Params paramsMemory{"/dev/shm/params"};
QPoint timeoutPoint = QPoint(420, 69); QPoint timeoutPoint = QPoint(420, 69);
QTimer clickTimer; QTimer clickTimer;

View File

@@ -0,0 +1,20 @@
import os
from cffi import FFI
import sip
from openpilot.common.ffi_wrapper import suffix
from openpilot.common.basedir import BASEDIR
def get_ffi():
lib = os.path.join(BASEDIR, "selfdrive", "ui", "qt", "libpython_helpers" + suffix())
ffi = FFI()
ffi.cdef("void set_main_window(void *w);")
return ffi, ffi.dlopen(lib)
def set_main_window(widget):
ffi, lib = get_ffi()
lib.set_main_window(ffi.cast('void*', sip.unwrapinstance(widget)))

0
selfdrive/ui/qt/qt_window.cc Executable file → Normal file
View File

0
selfdrive/ui/qt/qt_window.h Executable file → Normal file
View File

0
selfdrive/ui/qt/request_repeater.cc Executable file → Normal file
View File

0
selfdrive/ui/qt/request_repeater.h Executable file → Normal file
View File

9
selfdrive/ui/qt/setup/reset.cc Executable file → Normal file
View File

@@ -57,7 +57,7 @@ Reset::Reset(ResetMode mode, QWidget *parent) : QWidget(parent) {
main_layout->addSpacing(60); main_layout->addSpacing(60);
body = new QLabel(tr("Press confirm to erase all content and settings. Press cancel to resume boot.")); body = new QLabel(tr("System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot."));
body->setWordWrap(true); body->setWordWrap(true);
body->setStyleSheet("font-size: 80px; font-weight: light;"); body->setStyleSheet("font-size: 80px; font-weight: light;");
main_layout->addWidget(body, 1, Qt::AlignTop | Qt::AlignLeft); main_layout->addWidget(body, 1, Qt::AlignTop | Qt::AlignLeft);
@@ -97,11 +97,6 @@ Reset::Reset(ResetMode mode, QWidget *parent) : QWidget(parent) {
body->setText(tr("Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device.")); body->setText(tr("Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device."));
} }
// automatically start if we're just finishing up an ABL reset
if (mode == ResetMode::FORMAT) {
startReset();
}
setStyleSheet(R"( setStyleSheet(R"(
* { * {
font-family: Inter; font-family: Inter;
@@ -129,8 +124,6 @@ int main(int argc, char *argv[]) {
if (argc > 1) { if (argc > 1) {
if (strcmp(argv[1], "--recover") == 0) { if (strcmp(argv[1], "--recover") == 0) {
mode = ResetMode::RECOVER; mode = ResetMode::RECOVER;
} else if (strcmp(argv[1], "--format") == 0) {
mode = ResetMode::FORMAT;
} }
} }

1
selfdrive/ui/qt/setup/reset.h Executable file → Normal file
View File

@@ -5,7 +5,6 @@
enum ResetMode { enum ResetMode {
USER_RESET, // user initiated a factory reset from openpilot USER_RESET, // user initiated a factory reset from openpilot
RECOVER, // userdata is corrupt for some reason, give a chance to recover RECOVER, // userdata is corrupt for some reason, give a chance to recover
FORMAT, // finish up an ABL factory reset
}; };
class Reset : public QWidget { class Reset : public QWidget {

122
selfdrive/ui/qt/setup/setup.cc Executable file → Normal file
View File

@@ -20,7 +20,7 @@
#include "selfdrive/ui/qt/widgets/input.h" #include "selfdrive/ui/qt/widgets/input.h"
const std::string USER_AGENT = "AGNOSSetup-"; const std::string USER_AGENT = "AGNOSSetup-";
const QString DASHCAM_URL = "https://dashcam.comma.ai"; const QString OPENPILOT_URL = "https://openpilot.comma.ai";
bool is_elf(char *fname) { bool is_elf(char *fname) {
FILE *fp = fopen(fname, "rb"); FILE *fp = fopen(fname, "rb");
@@ -201,20 +201,7 @@ QWidget * Setup::network_setup() {
QPushButton *cont = new QPushButton(); QPushButton *cont = new QPushButton();
cont->setObjectName("navBtn"); cont->setObjectName("navBtn");
cont->setProperty("primary", true); cont->setProperty("primary", true);
QObject::connect(cont, &QPushButton::clicked, [=]() { QObject::connect(cont, &QPushButton::clicked, this, &Setup::nextPage);
auto w = currentWidget();
QTimer::singleShot(0, [=]() {
setCurrentWidget(downloading_widget);
});
QString url = InputDialog::getText(tr("Enter URL"), this, tr("for Custom Software"));
if (!url.isEmpty()) {
QTimer::singleShot(1000, this, [=]() {
download(url);
});
} else {
setCurrentWidget(w);
}
});
blayout->addWidget(cont); blayout->addWidget(cont);
// setup timer for testing internet connection // setup timer for testing internet connection
@@ -229,11 +216,11 @@ QWidget * Setup::network_setup() {
} }
repaint(); repaint();
}); });
request->sendRequest(DASHCAM_URL); request->sendRequest(OPENPILOT_URL);
QTimer *timer = new QTimer(this); QTimer *timer = new QTimer(this);
QObject::connect(timer, &QTimer::timeout, [=]() { QObject::connect(timer, &QTimer::timeout, [=]() {
if (!request->active() && cont->isVisible()) { if (!request->active() && cont->isVisible()) {
request->sendRequest(DASHCAM_URL); request->sendRequest(OPENPILOT_URL);
} }
}); });
timer->start(1000); timer->start(1000);
@@ -241,6 +228,106 @@ QWidget * Setup::network_setup() {
return widget; return widget;
} }
QWidget * radio_button(QString title, QButtonGroup *group) {
QPushButton *btn = new QPushButton(title);
btn->setCheckable(true);
group->addButton(btn);
btn->setStyleSheet(R"(
QPushButton {
height: 230;
padding-left: 100px;
padding-right: 100px;
text-align: left;
font-size: 80px;
font-weight: 400;
border-radius: 10px;
background-color: #4F4F4F;
}
QPushButton:checked {
background-color: #465BEA;
}
)");
// checkmark icon
QPixmap pix(":/img_circled_check.svg");
btn->setIcon(pix);
btn->setIconSize(QSize(0, 0));
btn->setLayoutDirection(Qt::RightToLeft);
QObject::connect(btn, &QPushButton::toggled, [=](bool checked) {
btn->setIconSize(checked ? QSize(104, 104) : QSize(0, 0));
});
return btn;
}
QWidget * Setup::software_selection() {
QWidget *widget = new QWidget();
QVBoxLayout *main_layout = new QVBoxLayout(widget);
main_layout->setContentsMargins(55, 50, 55, 50);
main_layout->setSpacing(0);
// title
QLabel *title = new QLabel(tr("Choose Software to Install"));
title->setStyleSheet("font-size: 90px; font-weight: 500;");
main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop);
main_layout->addSpacing(50);
// openpilot + custom radio buttons
QButtonGroup *group = new QButtonGroup(widget);
group->setExclusive(true);
QWidget *openpilot = radio_button(tr("openpilot"), group);
main_layout->addWidget(openpilot);
main_layout->addSpacing(30);
QWidget *custom = radio_button(tr("Custom Software"), group);
main_layout->addWidget(custom);
main_layout->addStretch();
// back + continue buttons
QHBoxLayout *blayout = new QHBoxLayout;
main_layout->addLayout(blayout);
blayout->setSpacing(50);
QPushButton *back = new QPushButton(tr("Back"));
back->setObjectName("navBtn");
QObject::connect(back, &QPushButton::clicked, this, &Setup::prevPage);
blayout->addWidget(back);
QPushButton *cont = new QPushButton(tr("Continue"));
cont->setObjectName("navBtn");
cont->setEnabled(false);
cont->setProperty("primary", true);
blayout->addWidget(cont);
QObject::connect(cont, &QPushButton::clicked, [=]() {
auto w = currentWidget();
QTimer::singleShot(0, [=]() {
setCurrentWidget(downloading_widget);
});
QString url = OPENPILOT_URL;
if (group->checkedButton() != openpilot) {
url = InputDialog::getText(tr("Enter URL"), this, tr("for Custom Software"));
}
if (!url.isEmpty()) {
QTimer::singleShot(1000, this, [=]() {
download(url);
});
} else {
setCurrentWidget(w);
}
});
connect(group, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), [=](QAbstractButton *btn) {
btn->setChecked(true);
cont->setEnabled(true);
});
return widget;
}
QWidget * Setup::downloading() { QWidget * Setup::downloading() {
QWidget *widget = new QWidget(); QWidget *widget = new QWidget();
QVBoxLayout *main_layout = new QVBoxLayout(widget); QVBoxLayout *main_layout = new QVBoxLayout(widget);
@@ -326,6 +413,7 @@ Setup::Setup(QWidget *parent) : QStackedWidget(parent) {
addWidget(getting_started()); addWidget(getting_started());
addWidget(network_setup()); addWidget(network_setup());
addWidget(software_selection());
downloading_widget = downloading(); downloading_widget = downloading();
addWidget(downloading_widget); addWidget(downloading_widget);

1
selfdrive/ui/qt/setup/setup.h Executable file → Normal file
View File

@@ -17,6 +17,7 @@ private:
QWidget *low_voltage(); QWidget *low_voltage();
QWidget *getting_started(); QWidget *getting_started();
QWidget *network_setup(); QWidget *network_setup();
QWidget *software_selection();
QWidget *downloading(); QWidget *downloading();
QWidget *download_failed(QLabel *url, QLabel *body); QWidget *download_failed(QLabel *url, QLabel *body);

31
selfdrive/ui/qt/setup/updater.cc Executable file → Normal file
View File

@@ -54,12 +54,7 @@ Updater::Updater(const QString &updater_path, const QString &manifest_path, QWid
background-color: #3049F4; background-color: #3049F4;
} }
)"); )");
QObject::connect(install, &QPushButton::clicked, this, &Updater::installUpdate);
QObject::connect(connect, &QPushButton::clicked, [=]() {
countdownTimer->stop();
setCurrentWidget(wifi);
});
hlayout->addWidget(install); hlayout->addWidget(install);
} }
@@ -119,20 +114,6 @@ Updater::Updater(const QString &updater_path, const QString &manifest_path, QWid
addWidget(wifi); addWidget(wifi);
addWidget(progress); addWidget(progress);
// Initialize the countdown timer and value
countdownValue = 5; // 5 seconds countdown
countdownTimer = new QTimer(this);
countdownTimer->setInterval(1000); // 1 second interval
// Connect the timer's timeout signal to update the countdown and button text
QObject::connect(countdownTimer, &QTimer::timeout, this, &Updater::updateCountdown);
// Start the countdown
countdownTimer->start();
// Set initial button text
install->setText(tr("Install (5)"));
setStyleSheet(R"( setStyleSheet(R"(
* { * {
color: white; color: white;
@@ -163,16 +144,6 @@ Updater::Updater(const QString &updater_path, const QString &manifest_path, QWid
)"); )");
} }
void Updater::updateCountdown() {
countdownValue--;
if (countdownValue > 0) {
install->setText(tr("Install (%1)").arg(countdownValue));
} else {
countdownTimer->stop();
installUpdate(); // Assuming this is the method that starts the update
}
}
void Updater::installUpdate() { void Updater::installUpdate() {
setCurrentWidget(progress); setCurrentWidget(progress);
QObject::connect(&proc, &QProcess::readyReadStandardOutput, this, &Updater::readProgress); QObject::connect(&proc, &QProcess::readyReadStandardOutput, this, &Updater::readProgress);

3
selfdrive/ui/qt/setup/updater.h Executable file → Normal file
View File

@@ -26,7 +26,4 @@ private:
QProgressBar *bar; QProgressBar *bar;
QPushButton *reboot; QPushButton *reboot;
QWidget *prompt, *wifi, *progress; QWidget *prompt, *wifi, *progress;
QTimer *countdownTimer;
int countdownValue;
}; };

125
selfdrive/ui/qt/sidebar.cc Executable file → Normal file
View File

@@ -43,16 +43,40 @@ Sidebar::Sidebar(QWidget *parent) : QFrame(parent), onroad(false), flag_pressed(
isCPU = params.getBool("ShowCPU"); isCPU = params.getBool("ShowCPU");
isGPU = params.getBool("ShowGPU"); isGPU = params.getBool("ShowGPU");
isIP = params.getBool("ShowIP");
isMemoryUsage = params.getBool("ShowMemoryUsage"); isMemoryUsage = params.getBool("ShowMemoryUsage");
isStorageLeft = params.getBool("ShowStorageLeft"); isStorageLeft = params.getBool("ShowStorageLeft");
isStorageUsed = params.getBool("ShowStorageUsed"); isStorageUsed = params.getBool("ShowStorageUsed");
isNumericalTemp = params.getBool("NumericalTemp"); holidayThemeConfiguration = {
isFahrenheit = params.getBool("Fahrenheit"); {0, {"stock", {QColor(255, 255, 255)}}},
{1, {"april_fools", {QColor(255, 165, 0)}}},
{2, {"christmas", {QColor(0, 72, 255)}}},
{3, {"cinco_de_mayo", {QColor(0, 104, 71)}}},
{4, {"easter", {QColor(200, 150, 200)}}},
{5, {"fourth_of_july", {QColor(0, 72, 255)}}},
{6, {"halloween", {QColor(255, 0, 0)}}},
{7, {"new_years_day", {QColor(23, 134, 68)}}},
{8, {"st_patricks_day", {QColor(0, 128, 0)}}},
{9, {"thanksgiving", {QColor(255, 0, 0)}}},
{10, {"valentines_day", {QColor(23, 134, 68)}}},
{11, {"world_frog_day", {QColor(23, 134, 68)}}},
};
for (auto &[key, themeData] : holidayThemeConfiguration) {
QString &themeName = themeData.first;
QString base = themeName == "stock" ? "../assets/images" : QString("../frogpilot/assets/holiday_themes/%1/images").arg(themeName);
std::vector<QString> paths = {base + "/button_home.png", base + "/button_flag.png", base + "/button_settings.png"};
holiday_home_imgs[key] = loadPixmap(paths[0], home_btn.size());
holiday_flag_imgs[key] = loadPixmap(paths[1], home_btn.size());
holiday_settings_imgs[key] = loadPixmap(paths[2], settings_btn.size(), Qt::IgnoreAspectRatio);
}
themeConfiguration = { themeConfiguration = {
{0, {"stock", {QColor(255, 255, 255)}}}, {0, {"stock", {QColor(255, 255, 255)}}},
{1, {"frog_theme", {QColor(0, 72, 255)}}}, {1, {"frog_theme", {QColor(23, 134, 68)}}},
{2, {"tesla_theme", {QColor(0, 72, 255)}}}, {2, {"tesla_theme", {QColor(0, 72, 255)}}},
{3, {"stalin_theme", {QColor(255, 0, 0)}}} {3, {"stalin_theme", {QColor(255, 0, 0)}}}
}; };
@@ -70,7 +94,6 @@ Sidebar::Sidebar(QWidget *parent) : QFrame(parent), onroad(false), flag_pressed(
home_img = home_imgs[scene.custom_icons]; home_img = home_imgs[scene.custom_icons];
flag_img = flag_imgs[scene.custom_icons]; flag_img = flag_imgs[scene.custom_icons];
settings_img = settings_imgs[scene.custom_icons]; settings_img = settings_imgs[scene.custom_icons];
currentColors = themeConfiguration[scene.custom_colors].second; currentColors = themeConfiguration[scene.custom_colors].second;
} }
@@ -78,35 +101,52 @@ void Sidebar::mousePressEvent(QMouseEvent *event) {
// Declare the click boxes // Declare the click boxes
QRect cpuRect = {30, 496, 240, 126}; QRect cpuRect = {30, 496, 240, 126};
QRect memoryRect = {30, 654, 240, 126}; QRect memoryRect = {30, 654, 240, 126};
QRect networkRect = {30, 196, 240, 126};
QRect tempRect = {30, 338, 240, 126}; QRect tempRect = {30, 338, 240, 126};
static int showChip = 0; static int showChip = 0;
static int showMemory = 0; static int showMemory = 0;
static int showNetwork = 0;
static int showTemp = 0; static int showTemp = 0;
// Swap between the respective metrics upon tap // Swap between the respective metrics upon tap
if (cpuRect.contains(event->pos())) { if (cpuRect.contains(event->pos())) {
showChip = (showChip + 1) % 3; showChip = (showChip + 1) % 3;
isCPU = (showChip == 1); isCPU = (showChip == 1);
isGPU = (showChip == 2); isGPU = (showChip == 2);
params.putBoolNonBlocking("ShowCPU", isCPU); params.putBoolNonBlocking("ShowCPU", isCPU);
params.putBoolNonBlocking("ShowGPU", isGPU); params.putBoolNonBlocking("ShowGPU", isGPU);
update(); update();
} else if (memoryRect.contains(event->pos())) { } else if (memoryRect.contains(event->pos())) {
showMemory = (showMemory + 1) % 4; showMemory = (showMemory + 1) % 4;
isMemoryUsage = (showMemory == 1); isMemoryUsage = (showMemory == 1);
isStorageLeft = (showMemory == 2); isStorageLeft = (showMemory == 2);
isStorageUsed = (showMemory == 3); isStorageUsed = (showMemory == 3);
params.putBoolNonBlocking("ShowMemoryUsage", isMemoryUsage); params.putBoolNonBlocking("ShowMemoryUsage", isMemoryUsage);
params.putBoolNonBlocking("ShowStorageLeft", isStorageLeft); params.putBoolNonBlocking("ShowStorageLeft", isStorageLeft);
params.putBoolNonBlocking("ShowStorageUsed", isStorageUsed); params.putBoolNonBlocking("ShowStorageUsed", isStorageUsed);
update();
} else if (networkRect.contains(event->pos())) {
showNetwork = (showNetwork + 1) % 2;
isIP = (showNetwork == 1);
params.putBoolNonBlocking("ShowIP", isIP);
update(); update();
} else if (tempRect.contains(event->pos())) { } else if (tempRect.contains(event->pos())) {
showTemp = (showTemp + 1) % 3; showTemp = (showTemp + 1) % 3;
isNumericalTemp = (showTemp != 0);
isFahrenheit = (showTemp == 2); scene.fahrenheit = showTemp == 2;
params.putBoolNonBlocking("Fahrenheit", isFahrenheit); scene.numerical_temp = showTemp != 0;
params.putBoolNonBlocking("NumericalTemp", isNumericalTemp);
params.putBoolNonBlocking("Fahrenheit", showTemp == 2);
params.putBoolNonBlocking("NumericalTemp", showTemp != 0);
update(); update();
} else if (onroad && home_btn.contains(event->pos())) { } else if (onroad && home_btn.contains(event->pos())) {
flag_pressed = true; flag_pressed = true;
@@ -147,17 +187,24 @@ void Sidebar::updateState(const UIState &s) {
setProperty("netStrength", strength > 0 ? strength + 1 : 0); setProperty("netStrength", strength > 0 ? strength + 1 : 0);
// FrogPilot properties // FrogPilot properties
home_img = home_imgs[scene.custom_icons]; if (scene.current_holiday_theme != 0) {
flag_img = flag_imgs[scene.custom_icons]; home_img = holiday_home_imgs[scene.current_holiday_theme];
settings_img = settings_imgs[scene.custom_icons]; flag_img = holiday_flag_imgs[scene.current_holiday_theme];
settings_img = holiday_settings_imgs[scene.current_holiday_theme];
currentColors = themeConfiguration[scene.custom_colors].second; currentColors = holidayThemeConfiguration[scene.current_holiday_theme].second;
} else {
home_img = home_imgs[scene.custom_icons];
flag_img = flag_imgs[scene.custom_icons];
settings_img = settings_imgs[scene.custom_icons];
currentColors = themeConfiguration[scene.custom_colors].second;
}
auto frogpilotDeviceState = sm["frogpilotDeviceState"].getFrogpilotDeviceState(); auto frogpilotDeviceState = sm["frogpilotDeviceState"].getFrogpilotDeviceState();
bool isNumericalTemp = scene.numerical_temp;
int maxTempC = deviceState.getMaxTempC(); int maxTempC = deviceState.getMaxTempC();
QString max_temp = isFahrenheit ? QString::number(maxTempC * 9 / 5 + 32) + "°F" : QString::number(maxTempC) + "°C"; QString max_temp = scene.fahrenheit ? QString::number(maxTempC * 9 / 5 + 32) + "°F" : QString::number(maxTempC) + "°C";
QColor theme_color = currentColors[0];
// FrogPilot metrics // FrogPilot metrics
if (isCPU || isGPU) { if (isCPU || isGPU) {
@@ -171,11 +218,11 @@ void Sidebar::updateState(const UIState &s) {
QString metric = isGPU ? gpu : cpu; QString metric = isGPU ? gpu : cpu;
int usage = isGPU ? gpu_usage : cpu_usage; int usage = isGPU ? gpu_usage : cpu_usage;
ItemStatus cpuStatus = {{tr(isGPU ? "GPU" : "CPU"), metric}, theme_color}; ItemStatus cpuStatus = {{isGPU ? tr("GPU") : tr("CPU"), metric}, currentColors[0]};
if (usage >= 85) { if (usage >= 85) {
cpuStatus = {{tr(isGPU ? "GPU" : "CPU"), metric}, danger_color}; cpuStatus = {{isGPU ? tr("GPU") : tr("CPU"), metric}, danger_color};
} else if (usage >= 70) { } else if (usage >= 70) {
cpuStatus = {{tr(isGPU ? "GPU" : "CPU"), metric}, warning_color}; cpuStatus = {{isGPU ? tr("GPU") : tr("CPU"), metric}, warning_color};
} }
setProperty("cpuStatus", QVariant::fromValue(cpuStatus)); setProperty("cpuStatus", QVariant::fromValue(cpuStatus));
} }
@@ -186,10 +233,10 @@ void Sidebar::updateState(const UIState &s) {
int storage_used = frogpilotDeviceState.getUsedSpace(); int storage_used = frogpilotDeviceState.getUsedSpace();
QString memory = QString::number(memory_usage) + "%"; QString memory = QString::number(memory_usage) + "%";
QString storage = QString::number(isStorageLeft ? storage_left : storage_used) + " GB"; QString storage = QString::number(isStorageLeft ? storage_left : storage_used) + tr(" GB");
if (isMemoryUsage) { if (isMemoryUsage) {
ItemStatus memoryStatus = {{tr("MEMORY"), memory}, theme_color}; ItemStatus memoryStatus = {{tr("MEMORY"), memory}, currentColors[0]};
if (memory_usage >= 85) { if (memory_usage >= 85) {
memoryStatus = {{tr("MEMORY"), memory}, danger_color}; memoryStatus = {{tr("MEMORY"), memory}, danger_color};
} else if (memory_usage >= 70) { } else if (memory_usage >= 70) {
@@ -197,11 +244,11 @@ void Sidebar::updateState(const UIState &s) {
} }
setProperty("memoryStatus", QVariant::fromValue(memoryStatus)); setProperty("memoryStatus", QVariant::fromValue(memoryStatus));
} else { } else {
ItemStatus storageStatus = {{tr(isStorageLeft ? "LEFT" : "USED"), storage}, theme_color}; ItemStatus storageStatus = {{isStorageLeft ? tr("LEFT") : tr("USED"), storage}, currentColors[0]};
if (10 <= storage_left && storage_left < 25) { if (25 > storage_left && storage_left >= 10) {
storageStatus = {{tr(isStorageLeft ? "LEFT" : "USED"), storage}, warning_color}; storageStatus = {{isStorageLeft ? tr("LEFT") : tr("USED"), storage}, warning_color};
} else if (storage_left < 10) { } else if (10 > storage_left) {
storageStatus = {{tr(isStorageLeft ? "LEFT" : "USED"), storage}, danger_color}; storageStatus = {{isStorageLeft ? tr("LEFT") : tr("USED"), storage}, danger_color};
} }
setProperty("storageStatus", QVariant::fromValue(storageStatus)); setProperty("storageStatus", QVariant::fromValue(storageStatus));
} }
@@ -213,7 +260,7 @@ void Sidebar::updateState(const UIState &s) {
connectStatus = ItemStatus{{tr("CONNECT"), tr("OFFLINE")}, warning_color}; connectStatus = ItemStatus{{tr("CONNECT"), tr("OFFLINE")}, warning_color};
} else { } else {
connectStatus = nanos_since_boot() - last_ping < 80e9 connectStatus = nanos_since_boot() - last_ping < 80e9
? ItemStatus{{tr("CONNECT"), tr("ONLINE")}, theme_color} ? ItemStatus{{tr("CONNECT"), tr("ONLINE")}, currentColors[0]}
: ItemStatus{{tr("CONNECT"), tr("ERROR")}, danger_color}; : ItemStatus{{tr("CONNECT"), tr("ERROR")}, danger_color};
} }
setProperty("connectStatus", QVariant::fromValue(connectStatus)); setProperty("connectStatus", QVariant::fromValue(connectStatus));
@@ -221,13 +268,13 @@ void Sidebar::updateState(const UIState &s) {
ItemStatus tempStatus = {{tr("TEMP"), isNumericalTemp ? max_temp : tr("HIGH")}, danger_color}; ItemStatus tempStatus = {{tr("TEMP"), isNumericalTemp ? max_temp : tr("HIGH")}, danger_color};
auto ts = deviceState.getThermalStatus(); auto ts = deviceState.getThermalStatus();
if (ts == cereal::DeviceState::ThermalStatus::GREEN) { if (ts == cereal::DeviceState::ThermalStatus::GREEN) {
tempStatus = {{tr("TEMP"), isNumericalTemp ? max_temp : tr("GOOD")}, theme_color}; tempStatus = {{tr("TEMP"), isNumericalTemp ? max_temp : tr("GOOD")}, currentColors[0]};
} else if (ts == cereal::DeviceState::ThermalStatus::YELLOW) { } else if (ts == cereal::DeviceState::ThermalStatus::YELLOW) {
tempStatus = {{tr("TEMP"), isNumericalTemp ? max_temp : tr("OK")}, warning_color}; tempStatus = {{tr("TEMP"), isNumericalTemp ? max_temp : tr("OK")}, warning_color};
} }
setProperty("tempStatus", QVariant::fromValue(tempStatus)); setProperty("tempStatus", QVariant::fromValue(tempStatus));
ItemStatus pandaStatus = {{tr("VEHICLE"), tr("ONLINE")}, theme_color}; ItemStatus pandaStatus = {{tr("VEHICLE"), tr("ONLINE")}, currentColors[0]};
if (s.scene.pandaType == cereal::PandaState::PandaType::UNKNOWN) { if (s.scene.pandaType == cereal::PandaState::PandaType::UNKNOWN) {
pandaStatus = {{tr("NO"), tr("PANDA")}, danger_color}; pandaStatus = {{tr("NO"), tr("PANDA")}, danger_color};
} else if (s.scene.started && !sm["liveLocationKalman"].getLiveLocationKalman().getGpsOK()) { } else if (s.scene.started && !sm["liveLocationKalman"].getLiveLocationKalman().getGpsOK()) {
@@ -253,14 +300,24 @@ void Sidebar::paintEvent(QPaintEvent *event) {
// network // network
int x = 58; int x = 58;
const QColor gray(0x54, 0x54, 0x54); const QColor gray(0x54, 0x54, 0x54);
for (int i = 0; i < 5; ++i) { p.setFont(InterFont(35));
p.setBrush(i < net_strength ? Qt::white : gray);
p.drawEllipse(x, 196, 27, 27); if (isIP) {
x += 37; 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); const QRect r = QRect(50, 247, 100, 50);
p.drawText(r, Qt::AlignCenter, net_type); p.drawText(r, Qt::AlignCenter, net_type);

21
selfdrive/ui/qt/sidebar.h Executable file → Normal file
View File

@@ -71,17 +71,22 @@ private:
ItemStatus cpu_status, memory_status, storage_status; ItemStatus cpu_status, memory_status, storage_status;
bool isCPU;
bool isGPU;
bool isIP;
bool isMemoryUsage;
bool isStorageLeft;
bool isStorageUsed;
std::unordered_map<int, std::pair<QString, std::vector<QColor>>> themeConfiguration; std::unordered_map<int, std::pair<QString, std::vector<QColor>>> themeConfiguration;
std::unordered_map<int, QPixmap> flag_imgs; std::unordered_map<int, QPixmap> flag_imgs;
std::unordered_map<int, QPixmap> home_imgs; std::unordered_map<int, QPixmap> home_imgs;
std::unordered_map<int, QPixmap> settings_imgs; std::unordered_map<int, QPixmap> settings_imgs;
std::vector<QColor> currentColors;
bool isCPU; std::unordered_map<int, std::pair<QString, std::vector<QColor>>> holidayThemeConfiguration;
bool isFahrenheit; std::unordered_map<int, QPixmap> holiday_flag_imgs;
bool isGPU; std::unordered_map<int, QPixmap> holiday_home_imgs;
bool isMemoryUsage; std::unordered_map<int, QPixmap> holiday_settings_imgs;
bool isNumericalTemp;
bool isStorageLeft; std::vector<QColor> currentColors;
bool isStorageUsed;
}; };

4
selfdrive/ui/qt/spinner.cc Executable file → Normal file
View File

@@ -11,7 +11,7 @@
#include <QString> #include <QString>
#include <QTransform> #include <QTransform>
#include "system/hardware/hw.h" #include "system/hardware/hw.h"
#include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/util.h"
@@ -88,7 +88,7 @@ Spinner::Spinner(QWidget *parent) : QWidget(parent) {
} }
QProgressBar::chunk { QProgressBar::chunk {
border-radius: 10px; border-radius: 10px;
background-color: rgba(179, 0, 0, 255); background-color: rgba(23, 134, 68, 255);
} }
)"); )");

0
selfdrive/ui/qt/spinner.h Executable file → Normal file
View File

48
selfdrive/ui/qt/text.cc Executable file → Normal file
View File

@@ -4,32 +4,11 @@
#include <QScrollBar> #include <QScrollBar>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QWidget> #include <QWidget>
#include <QProcess>
#include "system/hardware/hw.h" #include "system/hardware/hw.h"
#include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/widgets/scrollview.h" #include "selfdrive/ui/qt/widgets/scrollview.h"
#include "selfdrive/ui/qt/network/wifi_manager.h"
// wifi connection screen
// wifi = new QWidget;
// {
// QVBoxLayout *layout = new QVBoxLayout(wifi);
// layout->setContentsMargins(100, 100, 100, 100);
// Networking *networking = new Networking(this, false);
// networking->setStyleSheet("Networking { background-color: #292929; border-radius: 13px; }");
// layout->addWidget(networking, 1);
// QPushButton *back = new QPushButton(tr("Back"));
// back->setObjectName("navBtn");
// back->setStyleSheet("padding-left: 60px; padding-right: 60px;");
// QObject::connect(back, &QPushButton::clicked, [=]() {
// setCurrentWidget(prompt);
// });
// layout->addWidget(back, 0, Qt::AlignLeft);
// }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
initApp(argc, argv); initApp(argc, argv);
@@ -52,36 +31,11 @@ int main(int argc, char *argv[]) {
scroll->verticalScrollBar()->setValue(scroll->verticalScrollBar()->maximum()); scroll->verticalScrollBar()->setValue(scroll->verticalScrollBar()->maximum());
}); });
QPushButton *btnupdate = new QPushButton();
#ifdef __aarch64__
btnupdate->setText(QObject::tr("Update"));
QObject::connect(btnupdate, &QPushButton::clicked, [=]() {
QProcess process;
label->setText("Attempting to connect to wifi");
// TODO: make this work, then copy the compiled binary into git
// wifi = new WifiManager(null);
// connect(wifi, &WifiManager::refreshSignal, [=]() {
// label->setText("Performing update");
// process.setWorkingDirectory("/data/openpilot/");
// process.start("/bin/bash", QStringList{"-c", "update.sh"});
// process.waitForFinished();
// label->setText("Rebooting");
// Hardware::reboot();
// });
// wifi->start();
});
#else
btnupdate->setText(QObject::tr("Exit"));
QObject::connect(btnupdate, &QPushButton::clicked, &a, &QApplication::quit);
#endif
main_layout->addWidget(btnupdate, 0, 0, Qt::AlignLeft | Qt::AlignBottom);
QPushButton *btn = new QPushButton(); QPushButton *btn = new QPushButton();
#ifdef __aarch64__ #ifdef __aarch64__
btn->setText(QObject::tr("Reboot")); btn->setText(QObject::tr("Reboot"));
QObject::connect(btn, &QPushButton::clicked, [=]() { QObject::connect(btn, &QPushButton::clicked, [=]() {
Hardware::reboot(); // bbot this is the dreaded crash reboot button Hardware::reboot();
}); });
#else #else
btn->setText(QObject::tr("Exit")); btn->setText(QObject::tr("Exit"));

8
selfdrive/ui/qt/util.cc Executable file → Normal file
View File

@@ -5,6 +5,7 @@
#include <vector> #include <vector>
#include <QApplication> #include <QApplication>
#include <QDir>
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QHash> #include <QHash>
@@ -25,7 +26,7 @@ QString getVersion() {
} }
QString getBrand() { QString getBrand() {
return Params().getBool("Passive") ? QObject::tr("dashcam") : QObject::tr("OscarPilot"); return QObject::tr("FrogPilot");
} }
QString getUserAgent() { QString getUserAgent() {
@@ -114,6 +115,11 @@ void initApp(int argc, char *argv[], bool disable_hidpi) {
qputenv("QT_DBL_CLICK_DIST", QByteArray::number(150)); qputenv("QT_DBL_CLICK_DIST", QByteArray::number(150));
// ensure the current dir matches the exectuable's directory
QApplication tmp(argc, argv);
QString appDir = QCoreApplication::applicationDirPath();
QDir::setCurrent(appDir);
setQtSurfaceFormat(); setQtSurfaceFormat();
} }

0
selfdrive/ui/qt/util.h Executable file → Normal file
View File

3
selfdrive/ui/qt/widgets/cameraview.cc Executable file → Normal file
View File

@@ -41,6 +41,8 @@ const char frame_fragment_shader[] =
"out vec4 colorOut;\n" "out vec4 colorOut;\n"
"void main() {\n" "void main() {\n"
" colorOut = texture(uTexture, vTexCoord);\n" " colorOut = texture(uTexture, vTexCoord);\n"
// gamma to improve worst case visibility when dark
" colorOut.rgb = pow(colorOut.rgb, vec3(1.0/1.28));\n"
"}\n"; "}\n";
#else #else
#ifdef __APPLE__ #ifdef __APPLE__
@@ -217,7 +219,6 @@ void CameraWidget::updateFrameMat() {
if (active_stream_type == VISION_STREAM_DRIVER) { if (active_stream_type == VISION_STREAM_DRIVER) {
if (stream_width > 0 && stream_height > 0) { if (stream_width > 0 && stream_height > 0) {
frame_mat = get_driver_view_transform(w, h, stream_width, stream_height); frame_mat = get_driver_view_transform(w, h, stream_width, stream_height);
frame_mat.v[0] *= -1.0;
} }
} else { } else {
// Project point at "infinity" to compute x and y offsets // Project point at "infinity" to compute x and y offsets

0
selfdrive/ui/qt/widgets/cameraview.h Executable file → Normal file
View File

0
selfdrive/ui/qt/widgets/controls.cc Executable file → Normal file
View File

29
selfdrive/ui/qt/widgets/controls.h Executable file → Normal file
View File

@@ -127,15 +127,15 @@ public:
QObject::connect(&toggle, &Toggle::stateChanged, this, &ToggleControl::toggleFlipped); QObject::connect(&toggle, &Toggle::stateChanged, this, &ToggleControl::toggleFlipped);
} }
void setVisualOn() {
toggle.togglePosition();
}
void setEnabled(bool enabled) { void setEnabled(bool enabled) {
toggle.setEnabled(enabled); toggle.setEnabled(enabled);
toggle.update(); toggle.update();
} }
void refresh() {
toggle.togglePosition();
}
signals: signals:
void toggleFlipped(bool state); void toggleFlipped(bool state);
@@ -206,7 +206,7 @@ public:
background-color: #4a4a4a; background-color: #4a4a4a;
} }
QPushButton:checked:enabled { QPushButton:checked:enabled {
background-color: #0048FF; background-color: #33Ab4C;
} }
QPushButton:disabled { QPushButton:disabled {
color: #33E4E4E4; color: #33E4E4E4;
@@ -227,10 +227,8 @@ public:
button_group->addButton(button, i); button_group->addButton(button, i);
} }
QObject::connect(button_group, QOverload<int, bool>::of(&QButtonGroup::buttonToggled), [=](int id, bool checked) { QObject::connect(button_group, QOverload<int>::of(&QButtonGroup::buttonClicked), [=](int id) {
if (checked) { params.put(key, std::to_string(id));
params.put(key, std::to_string(id));
}
}); });
} }
@@ -240,6 +238,19 @@ public:
} }
} }
void setCheckedButton(int id) {
button_group->button(id)->setChecked(true);
}
void refresh() {
int value = atoi(params.get(key).c_str());
button_group->button(value)->setChecked(true);
}
void showEvent(QShowEvent *event) override {
refresh();
}
private: private:
std::string key; std::string key;
Params params; Params params;

40
selfdrive/ui/qt/widgets/drive_stats.cc Executable file → Normal file
View File

@@ -5,7 +5,6 @@
#include <QJsonObject> #include <QJsonObject>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "common/params.h"
#include "selfdrive/ui/qt/request_repeater.h" #include "selfdrive/ui/qt/request_repeater.h"
#include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/util.h"
@@ -16,19 +15,19 @@ static QLabel* newLabel(const QString& text, const QString &type) {
} }
DriveStats::DriveStats(QWidget* parent) : QFrame(parent) { DriveStats::DriveStats(QWidget* parent) : QFrame(parent) {
metric_ = Params().getBool("IsMetric"); metric_ = params.getBool("IsMetric");
QVBoxLayout* main_layout = new QVBoxLayout(this); QVBoxLayout* main_layout = new QVBoxLayout(this);
main_layout->setContentsMargins(50, 50, 50, 60); main_layout->setContentsMargins(50, 25, 50, 20);
auto add_stats_layouts = [=](const QString &title, StatsLabels& labels) { auto add_stats_layouts = [=](const QString &title, StatsLabels& labels, bool FrogPilot=false) {
QGridLayout* grid_layout = new QGridLayout; QGridLayout* grid_layout = new QGridLayout;
grid_layout->setVerticalSpacing(10); grid_layout->setVerticalSpacing(10);
grid_layout->setContentsMargins(0, 10, 0, 10); grid_layout->setContentsMargins(0, 10, 0, 10);
int row = 0; int row = 0;
grid_layout->addWidget(newLabel(title, "title"), row++, 0, 1, 3); grid_layout->addWidget(newLabel(title, FrogPilot ? "frogpilot_title" : "title"), row++, 0, 1, 3);
grid_layout->addItem(new QSpacerItem(0, 50), row++, 0, 1, 1); grid_layout->addItem(new QSpacerItem(0, 10), row++, 0, 1, 1);
grid_layout->addWidget(labels.routes = newLabel("0", "number"), row, 0, Qt::AlignLeft); grid_layout->addWidget(labels.routes = newLabel("0", "number"), row, 0, Qt::AlignLeft);
grid_layout->addWidget(labels.distance = newLabel("0", "number"), row, 1, Qt::AlignLeft); grid_layout->addWidget(labels.distance = newLabel("0", "number"), row, 1, Qt::AlignLeft);
@@ -39,11 +38,12 @@ DriveStats::DriveStats(QWidget* parent) : QFrame(parent) {
grid_layout->addWidget(newLabel(tr("Hours"), "unit"), row + 1, 2, Qt::AlignLeft); grid_layout->addWidget(newLabel(tr("Hours"), "unit"), row + 1, 2, Qt::AlignLeft);
main_layout->addLayout(grid_layout); main_layout->addLayout(grid_layout);
main_layout->addStretch(1);
}; };
add_stats_layouts(tr("ALL TIME"), all_); add_stats_layouts(tr("ALL TIME"), all_);
main_layout->addStretch();
add_stats_layouts(tr("PAST WEEK"), week_); add_stats_layouts(tr("PAST WEEK"), week_);
add_stats_layouts(tr("FROGPILOT"), frogPilot_, true);
if (auto dongleId = getDongleId()) { if (auto dongleId = getDongleId()) {
QString url = CommaApi::BASE_URL + "/v1.1/devices/" + *dongleId + "/stats"; QString url = CommaApi::BASE_URL + "/v1.1/devices/" + *dongleId + "/stats";
@@ -57,13 +57,25 @@ DriveStats::DriveStats(QWidget* parent) : QFrame(parent) {
border-radius: 10px; border-radius: 10px;
} }
QLabel[type="title"] { font-size: 51px; font-weight: 500; } QLabel[type="title"] { font-size: 50px; font-weight: 500; }
QLabel[type="number"] { font-size: 78px; font-weight: 500; } QLabel[type="frogpilot_title"] { font-size: 50px; font-weight: 500; color: #178643; }
QLabel[type="unit"] { font-size: 51px; font-weight: 300; color: #A0A0A0; } QLabel[type="number"] { font-size: 65px; font-weight: 400; }
QLabel[type="unit"] { font-size: 50px; font-weight: 300; color: #A0A0A0; }
)"); )");
} }
void DriveStats::updateStats() { void DriveStats::updateStats() {
QJsonObject json = stats_.object();
auto updateFrogPilot = [this](const QJsonObject& obj, StatsLabels& labels) {
labels.routes->setText(QString::number(paramsStorage.getInt("FrogPilotDrives")));
labels.distance->setText(QString::number(int(paramsStorage.getFloat("FrogPilotKilometers") * (metric_ ? 1 : KM_TO_MILE))));
labels.distance_unit->setText(getDistanceUnit());
labels.hours->setText(QString::number(int(paramsStorage.getFloat("FrogPilotMinutes") / 60)));
};
updateFrogPilot(json["frogpilot"].toObject(), frogPilot_);
auto update = [=](const QJsonObject& obj, StatsLabels& labels) { auto update = [=](const QJsonObject& obj, StatsLabels& labels) {
labels.routes->setText(QString::number((int)obj["routes"].toDouble())); labels.routes->setText(QString::number((int)obj["routes"].toDouble()));
labels.distance->setText(QString::number(int(obj["distance"].toDouble() * (metric_ ? MILE_TO_KM : 1)))); labels.distance->setText(QString::number(int(obj["distance"].toDouble() * (metric_ ? MILE_TO_KM : 1))));
@@ -71,7 +83,6 @@ void DriveStats::updateStats() {
labels.hours->setText(QString::number((int)(obj["minutes"].toDouble() / 60))); labels.hours->setText(QString::number((int)(obj["minutes"].toDouble() / 60)));
}; };
QJsonObject json = stats_.object();
update(json["all"].toObject(), all_); update(json["all"].toObject(), all_);
update(json["week"].toObject(), week_); update(json["week"].toObject(), week_);
} }
@@ -89,9 +100,6 @@ void DriveStats::parseResponse(const QString& response, bool success) {
} }
void DriveStats::showEvent(QShowEvent* event) { void DriveStats::showEvent(QShowEvent* event) {
bool metric = Params().getBool("IsMetric"); metric_ = params.getBool("IsMetric");
if (metric_ != metric) { updateStats();
metric_ = metric;
updateStats();
}
} }

6
selfdrive/ui/qt/widgets/drive_stats.h Executable file → Normal file
View File

@@ -3,6 +3,8 @@
#include <QJsonDocument> #include <QJsonDocument>
#include <QLabel> #include <QLabel>
#include "common/params.h"
class DriveStats : public QFrame { class DriveStats : public QFrame {
Q_OBJECT Q_OBJECT
@@ -15,10 +17,12 @@ private:
inline QString getDistanceUnit() const { return metric_ ? tr("KM") : tr("Miles"); } inline QString getDistanceUnit() const { return metric_ ? tr("KM") : tr("Miles"); }
bool metric_; bool metric_;
Params params;
Params paramsStorage{"/persist/params"};
QJsonDocument stats_; QJsonDocument stats_;
struct StatsLabels { struct StatsLabels {
QLabel *routes, *distance, *distance_unit, *hours; QLabel *routes, *distance, *distance_unit, *hours;
} all_, week_; } all_, week_, frogPilot_;
private slots: private slots:
void parseResponse(const QString &response, bool success); void parseResponse(const QString &response, bool success);

0
selfdrive/ui/qt/widgets/input.cc Executable file → Normal file
View File

0
selfdrive/ui/qt/widgets/input.h Executable file → Normal file
View File

0
selfdrive/ui/qt/widgets/keyboard.cc Executable file → Normal file
View File

0
selfdrive/ui/qt/widgets/keyboard.h Executable file → Normal file
View File

10
selfdrive/ui/qt/widgets/offroad_alerts.cc Executable file → Normal file
View File

@@ -37,13 +37,9 @@ AbstractAlert::AbstractAlert(bool hasRebootBtn, QWidget *parent) : QFrame(parent
disable_check_btn->setFixedSize(625, 125); disable_check_btn->setFixedSize(625, 125);
footer_layout->addWidget(disable_check_btn, 1, Qt::AlignBottom | Qt::AlignCenter); footer_layout->addWidget(disable_check_btn, 1, Qt::AlignBottom | Qt::AlignCenter);
QObject::connect(disable_check_btn, &QPushButton::clicked, [=]() { QObject::connect(disable_check_btn, &QPushButton::clicked, [=]() {
if (!params.getBool("FireTheBabysitter")) { params.putBool("SnoozeUpdate", true);
params.putBool("FireTheBabysitter", true); params.putBool("DeviceManagement", true);
} params.putBool("OfflineMode", true);
if (!params.getBool("OfflineMode")) {
params.putBool("OfflineMode", true);
}
Hardware::reboot();
}); });
QObject::connect(disable_check_btn, &QPushButton::clicked, this, &AbstractAlert::dismiss); QObject::connect(disable_check_btn, &QPushButton::clicked, this, &AbstractAlert::dismiss);
disable_check_btn->setStyleSheet(R"(color: white; background-color: #4F4F4F;)"); disable_check_btn->setStyleSheet(R"(color: white; background-color: #4F4F4F;)");

0
selfdrive/ui/qt/widgets/offroad_alerts.h Executable file → Normal file
View File

0
selfdrive/ui/qt/widgets/prime.cc Executable file → Normal file
View File

0
selfdrive/ui/qt/widgets/prime.h Executable file → Normal file
View File

0
selfdrive/ui/qt/widgets/scrollview.cc Executable file → Normal file
View File

1
selfdrive/ui/qt/widgets/scrollview.h Executable file → Normal file
View File

@@ -10,6 +10,7 @@ public:
// FrogPilot functions // FrogPilot functions
void restorePosition(int previousScrollPosition); void restorePosition(int previousScrollPosition);
protected: protected:
void hideEvent(QHideEvent *e) override; void hideEvent(QHideEvent *e) override;
}; };

0
selfdrive/ui/qt/widgets/ssh_keys.cc Executable file → Normal file
View File

0
selfdrive/ui/qt/widgets/ssh_keys.h Executable file → Normal file
View File

2
selfdrive/ui/qt/widgets/toggle.cc Executable file → Normal file
View File

@@ -75,7 +75,7 @@ void Toggle::setEnabled(bool value) {
enabled = value; enabled = value;
if (value) { if (value) {
circleColor.setRgb(0xfafafa); circleColor.setRgb(0xfafafa);
green.setRgb(0x0048FF); green.setRgb(0x33ab4c);
} else { } else {
circleColor.setRgb(0x888888); circleColor.setRgb(0x888888);
green.setRgb(0x227722); green.setRgb(0x227722);

0
selfdrive/ui/qt/widgets/toggle.h Executable file → Normal file
View File

31
selfdrive/ui/qt/widgets/wifi.cc Executable file → Normal file
View File

@@ -81,6 +81,34 @@ WiFiPromptWidget::WiFiPromptWidget(QWidget *parent) : QFrame(parent) {
} }
stack->addWidget(uploading); stack->addWidget(uploading);
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 'Disable Uploading' toggle to enable uploads."));
desc->setStyleSheet("font-size: 48px; font-weight: 400;");
desc->setWordWrap(true);
not_uploading_layout->addWidget(desc);
}
stack->addWidget(notUploading);
setStyleSheet(R"( setStyleSheet(R"(
WiFiPromptWidget { WiFiPromptWidget {
background-color: #333333; background-color: #333333;
@@ -99,5 +127,6 @@ void WiFiPromptWidget::updateState(const UIState &s) {
auto network_type = sm["deviceState"].getDeviceState().getNetworkType(); auto network_type = sm["deviceState"].getDeviceState().getNetworkType();
auto uploading = network_type == cereal::DeviceState::NetworkType::WIFI || auto uploading = network_type == cereal::DeviceState::NetworkType::WIFI ||
network_type == cereal::DeviceState::NetworkType::ETHERNET; network_type == cereal::DeviceState::NetworkType::ETHERNET;
stack->setCurrentIndex(uploading ? 1 : 0); bool uploading_disabled = params.getBool("DeviceManagement") && params.getBool("NoUploads");
stack->setCurrentIndex(uploading_disabled ? 2 : uploading ? 1 : 0);
} }

4
selfdrive/ui/qt/widgets/wifi.h Executable file → Normal file
View File

@@ -18,6 +18,10 @@ signals:
public slots: public slots:
void updateState(const UIState &s); void updateState(const UIState &s);
private:
// FrogPilot variables
Params params;
protected: protected:
QStackedLayout *stack; QStackedLayout *stack;
}; };

37
selfdrive/ui/qt/window.cc Executable file → Normal file
View File

@@ -13,14 +13,14 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
QObject::connect(homeWindow, &HomeWindow::openSettings, this, &MainWindow::openSettings); QObject::connect(homeWindow, &HomeWindow::openSettings, this, &MainWindow::openSettings);
QObject::connect(homeWindow, &HomeWindow::closeSettings, this, &MainWindow::closeSettings); QObject::connect(homeWindow, &HomeWindow::closeSettings, this, &MainWindow::closeSettings);
oscarSettingsWindow = new OscarSettingsWindow(this); settingsWindow = new SettingsWindow(this);
main_layout->addWidget(oscarSettingsWindow); main_layout->addWidget(settingsWindow);
QObject::connect(oscarSettingsWindow, &OscarSettingsWindow::closeSettings, this, &MainWindow::closeSettings); QObject::connect(settingsWindow, &SettingsWindow::closeSettings, this, &MainWindow::closeSettings);
// QObject::connect(oscarSettingsWindow, &OscarSettingsWindow::reviewTrainingGuide, [=]() { QObject::connect(settingsWindow, &SettingsWindow::reviewTrainingGuide, [=]() {
// onboardingWindow->showTrainingGuide(); onboardingWindow->showTrainingGuide();
// main_layout->setCurrentWidget(onboardingWindow); main_layout->setCurrentWidget(onboardingWindow);
// }); });
QObject::connect(oscarSettingsWindow, &OscarSettingsWindow::showDriverView, [=] { QObject::connect(settingsWindow, &SettingsWindow::showDriverView, [=] {
homeWindow->showDriverView(true); homeWindow->showDriverView(true);
}); });
@@ -38,11 +38,11 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
closeSettings(); closeSettings();
} }
}); });
// QObject::connect(device(), &Device::interactiveTimeout, [=]() { QObject::connect(device(), &Device::interactiveTimeout, [=]() {
// if (main_layout->currentWidget() == oscarSettingsWindow) { if (main_layout->currentWidget() == settingsWindow) {
// closeSettings(); closeSettings();
// } }
// }); });
// load fonts // load fonts
QFontDatabase::addApplicationFont("../assets/fonts/Inter-Black.ttf"); QFontDatabase::addApplicationFont("../assets/fonts/Inter-Black.ttf");
@@ -66,8 +66,8 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
} }
void MainWindow::openSettings(int index, const QString &param) { void MainWindow::openSettings(int index, const QString &param) {
main_layout->setCurrentWidget(oscarSettingsWindow); main_layout->setCurrentWidget(settingsWindow);
oscarSettingsWindow->setCurrentPanel(index, param); settingsWindow->setCurrentPanel(index, param);
} }
void MainWindow::closeSettings() { void MainWindow::closeSettings() {
@@ -93,12 +93,7 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
case QEvent::MouseMove: { case QEvent::MouseMove: {
// ignore events when device is awakened by resetInteractiveTimeout // ignore events when device is awakened by resetInteractiveTimeout
ignore = !device()->isAwake(); ignore = !device()->isAwake();
// if (main_layout->currentWidget() == oscarSettingsWindow) { device()->resetInteractiveTimeout(uiState()->scene.screen_timeout, uiState()->scene.screen_timeout_onroad);
// Not working...
// device()->resetInteractiveTimeout(60 * 5); // 5 minute timeout if looking at settings window
// } else {
device()->resetInteractiveTimeout(); // Default 30 seconds otherwise
// }
break; break;
} }
default: default:

3
selfdrive/ui/qt/window.h Executable file → Normal file
View File

@@ -6,7 +6,6 @@
#include "selfdrive/ui/qt/home.h" #include "selfdrive/ui/qt/home.h"
#include "selfdrive/ui/qt/offroad/onboarding.h" #include "selfdrive/ui/qt/offroad/onboarding.h"
#include "selfdrive/ui/qt/offroad/settings.h" #include "selfdrive/ui/qt/offroad/settings.h"
#include "selfdrive/oscarpilot/settings/settings.h"
class MainWindow : public QWidget { class MainWindow : public QWidget {
Q_OBJECT Q_OBJECT
@@ -21,7 +20,7 @@ private:
QStackedLayout *main_layout; QStackedLayout *main_layout;
HomeWindow *homeWindow; HomeWindow *homeWindow;
OscarSettingsWindow *oscarSettingsWindow; SettingsWindow *settingsWindow;
OnboardingWindow *onboardingWindow; OnboardingWindow *onboardingWindow;
// FrogPilot variables // FrogPilot variables

114
selfdrive/ui/soundd.py Executable file → Normal file
View File

@@ -1,10 +1,9 @@
import math import math
import numpy as np import numpy as np
import os
import time import time
import threading
import wave import wave
from typing import Dict, Optional, Tuple
from cereal import car, messaging from cereal import car, messaging
from openpilot.common.basedir import BASEDIR from openpilot.common.basedir import BASEDIR
@@ -29,7 +28,7 @@ DB_SCALE = 30 # AMBIENT_DB + DB_SCALE is where MAX_VOLUME is applied
AudibleAlert = car.CarControl.HUDControl.AudibleAlert AudibleAlert = car.CarControl.HUDControl.AudibleAlert
sound_list: Dict[int, Tuple[str, Optional[int], float]] = { sound_list: dict[int, tuple[str, int | None, float]] = {
# AudibleAlert, file name, play count (none for infinite) # AudibleAlert, file name, play count (none for infinite)
AudibleAlert.engage: ("engage.wav", 1, MAX_VOLUME), AudibleAlert.engage: ("engage.wav", 1, MAX_VOLUME),
AudibleAlert.disengage: ("disengage.wav", 1, MAX_VOLUME), AudibleAlert.disengage: ("disengage.wav", 1, MAX_VOLUME),
@@ -42,11 +41,21 @@ sound_list: Dict[int, Tuple[str, Optional[int], float]] = {
AudibleAlert.warningSoft: ("warning_soft.wav", None, MAX_VOLUME), AudibleAlert.warningSoft: ("warning_soft.wav", None, MAX_VOLUME),
AudibleAlert.warningImmediate: ("warning_immediate.wav", None, MAX_VOLUME), AudibleAlert.warningImmediate: ("warning_immediate.wav", None, MAX_VOLUME),
AudibleAlert.firefox: ("firefox.wav", None, MAX_VOLUME), # Random Events
AudibleAlert.angry: ("angry.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.nessie: ("nessie.wav", 1, MAX_VOLUME),
AudibleAlert.noice: ("noice.wav", 1, MAX_VOLUME),
AudibleAlert.uwu: ("uwu.wav", 1, MAX_VOLUME),
# Other
AudibleAlert.goat: ("goat.wav", None, MAX_VOLUME),
} }
def check_controls_timeout_alert(sm): def check_controls_timeout_alert(sm):
controls_missing = time.monotonic() - sm.rcv_time['controlsState'] controls_missing = time.monotonic() - sm.recv_time['controlsState']
if controls_missing > CONTROLS_TIMEOUT: if controls_missing > CONTROLS_TIMEOUT:
if sm['controlsState'].enabled and (controls_missing - CONTROLS_TIMEOUT) < 10: if sm['controlsState'].enabled and (controls_missing - CONTROLS_TIMEOUT) < 10:
@@ -61,9 +70,20 @@ class Soundd:
self.params = Params() self.params = Params()
self.params_memory = Params("/dev/shm/params") self.params_memory = Params("/dev/shm/params")
self.update_frogpilot_params() self.previous_sound_directory = None
self.random_events_directory = BASEDIR + "/selfdrive/frogpilot/assets/random_events/sounds/"
self.load_sounds() self.random_events_map = {
AudibleAlert.angry: MAX_VOLUME,
AudibleAlert.doc: MAX_VOLUME,
AudibleAlert.fart: MAX_VOLUME,
AudibleAlert.firefox: MAX_VOLUME,
AudibleAlert.nessie: MAX_VOLUME,
AudibleAlert.noice: MAX_VOLUME,
AudibleAlert.uwu: MAX_VOLUME,
}
self.update_frogpilot_params()
self.current_alert = AudibleAlert.none self.current_alert = AudibleAlert.none
self.current_volume = MIN_VOLUME self.current_volume = MIN_VOLUME
@@ -74,13 +94,22 @@ class Soundd:
self.spl_filter_weighted = FirstOrderFilter(0, 2.5, FILTER_DT, initialized=False) self.spl_filter_weighted = FirstOrderFilter(0, 2.5, FILTER_DT, initialized=False)
def load_sounds(self): def load_sounds(self):
self.loaded_sounds: Dict[int, np.ndarray] = {} self.loaded_sounds: dict[int, np.ndarray] = {}
# Load all sounds # Load all sounds
for sound in sound_list: for sound in sound_list:
if sound == AudibleAlert.goat and not self.goat_scream:
continue
filename, play_count, volume = sound_list[sound] filename, play_count, volume = sound_list[sound]
wavefile = wave.open(self.sound_directory + filename, 'r') if sound in self.random_events_map:
wavefile = wave.open(self.random_events_directory + filename, 'r')
else:
try:
wavefile = wave.open(self.sound_directory + filename, 'r')
except FileNotFoundError:
wavefile = wave.open(BASEDIR + "/selfdrive/assets/sounds/" + filename, 'r')
assert wavefile.getnchannels() == 1 assert wavefile.getnchannels() == 1
assert wavefile.getsampwidth() == 2 assert wavefile.getsampwidth() == 2
@@ -156,9 +185,15 @@ class Soundd:
while True: while True:
sm.update(0) sm.update(0)
if sm.updated['microphone'] and self.current_alert == AudibleAlert.none: # only update volume filter when not playing alert if sm.updated['microphone'] and self.current_alert == AudibleAlert.none and not self.alert_volume_control: # only update volume filter when not playing alert
self.spl_filter_weighted.update(sm["microphone"].soundPressureWeightedDb) self.spl_filter_weighted.update(sm["microphone"].soundPressureWeightedDb)
self.current_volume = self.calculate_volume(float(self.spl_filter_weighted.x)) if not self.silent_mode else 0 self.current_volume = self.calculate_volume(float(self.spl_filter_weighted.x))
elif self.alert_volume_control and self.current_alert in self.volume_map:
self.current_volume = self.volume_map[self.current_alert] / 100.0
elif self.current_alert in self.random_events_map:
self.current_volume = self.random_events_map[self.current_alert]
self.get_audible_alert(sm) self.get_audible_alert(sm)
@@ -166,28 +201,67 @@ class Soundd:
assert stream.active assert stream.active
# Update FrogPilot parameters # Update FrogPilot parameters
if self.params_memory.get_bool("FrogPilotTogglesUpdated"): if self.params_memory.get_bool("FrogPilotTogglesUpdated"):
updateFrogPilotParams = threading.Thread(target=self.update_frogpilot_params) self.update_frogpilot_params()
updateFrogPilotParams.start()
def update_frogpilot_params(self): def update_frogpilot_params(self):
self.silent_mode = self.params.get_bool("SilentMode") self.alert_volume_control = self.params.get_bool("AlertVolumeControl")
self.volume_map = {
AudibleAlert.engage: self.params.get_int("EngageVolume"),
AudibleAlert.disengage: self.params.get_int("DisengageVolume"),
AudibleAlert.refuse: self.params.get_int("RefuseVolume"),
AudibleAlert.prompt: self.params.get_int("PromptVolume"),
AudibleAlert.promptRepeat: self.params.get_int("PromptVolume"),
AudibleAlert.promptDistracted: self.params.get_int("PromptDistractedVolume"),
AudibleAlert.warningSoft: self.params.get_int("WarningSoftVolume"),
AudibleAlert.warningImmediate: self.params.get_int("WarningImmediateVolume"),
AudibleAlert.goat: self.params.get_int("PromptVolume"),
}
custom_theme = self.params.get_bool("CustomTheme") custom_theme = self.params.get_bool("CustomTheme")
custom_sounds = self.params.get_int("CustomSounds") if custom_theme else 0 custom_sounds = self.params.get_int("CustomSounds") if custom_theme else 0
self.goat_scream = custom_sounds == 1 and self.params.get_bool("GoatScream")
theme_configuration = { theme_configuration = {
0: "stock",
1: "frog_theme", 1: "frog_theme",
2: "tesla_theme", 2: "tesla_theme",
3: "stalin_theme" 3: "stalin_theme"
} }
theme_name = theme_configuration.get(custom_sounds, "stock") holiday_themes = custom_theme and self.params.get_bool("HolidayThemes")
self.sound_directory = (f"{BASEDIR}/selfdrive/frogpilot/assets/custom_themes/{theme_name}/sounds/" if custom_sounds else f"{BASEDIR}/selfdrive/assets/sounds/") current_holiday_theme = self.params_memory.get_int("CurrentHolidayTheme") if holiday_themes else 0
self.load_sounds() holiday_theme_configuration = {
1: "april_fools",
2: "christmas",
3: "cinco_de_mayo",
4: "easter",
5: "fourth_of_july",
6: "halloween",
7: "new_years_day",
8: "st_patricks_day",
9: "thanksgiving",
10: "valentines_day",
11: "world_frog_day",
}
if current_holiday_theme != 0:
theme_name = holiday_theme_configuration.get(current_holiday_theme)
self.sound_directory = BASEDIR + ("/selfdrive/frogpilot/assets/holiday_themes/" + theme_name + "/sounds/")
self.goat_scream = False
else:
theme_name = theme_configuration.get(custom_sounds)
self.sound_directory = BASEDIR + ("/selfdrive/frogpilot/assets/custom_themes/" + theme_name + "/sounds/" if custom_sounds != 0 else "/selfdrive/assets/sounds/")
if self.sound_directory != self.previous_sound_directory:
self.load_sounds()
self.previous_sound_directory = self.sound_directory
def main(): def main():
s = Soundd() s = Soundd()

View File

@@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
if [ -f /TICI ] && [ ! -f qt/spinner ]; then if [ -f /TICI ] && [ ! -f _spinner ]; then
cp qt/spinner_larch64 qt/spinner cp qt/spinner_larch64 _spinner
fi fi
exec ./qt/spinner "$1" exec ./_spinner "$1"

6
selfdrive/ui/tests/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
test
playsound
test_sound
test_translations
ui_snapshot
test_ui/report

View File

View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python3
import time
import cereal.messaging as messaging
if __name__ == "__main__":
while True:
pm = messaging.PubMaster(['carParams', 'carState'])
batt = 1.
while True:
msg = messaging.new_message('carParams')
msg.carParams.carName = "COMMA BODY"
msg.carParams.notCar = True
pm.send('carParams', msg)
for b in range(100, 0, -1):
msg = messaging.new_message('carState')
msg.carState.charging = True
msg.carState.fuelGauge = b / 100.
pm.send('carState', msg)
time.sleep(0.1)
time.sleep(1)

View File

@@ -0,0 +1,18 @@
#!/bin/bash
set -e
UI_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"/..
TEST_TEXT="(WRAPPED_SOURCE_TEXT)"
TEST_TS_FILE=$UI_DIR/translations/main_test_en.ts
TEST_QM_FILE=$UI_DIR/translations/main_test_en.qm
# translation strings
UNFINISHED="<translation type=\"unfinished\"><\/translation>"
TRANSLATED="<translation>$TEST_TEXT<\/translation>"
mkdir -p $UI_DIR/translations
rm -f $TEST_TS_FILE $TEST_QM_FILE
lupdate -recursive "$UI_DIR" -ts $TEST_TS_FILE
sed -i "s/$UNFINISHED/$TRANSLATED/" $TEST_TS_FILE
lrelease $TEST_TS_FILE

View File

@@ -0,0 +1,36 @@
#!/usr/bin/env python3
import os
import sys
import time
import json
from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params
from openpilot.selfdrive.controls.lib.alertmanager import set_offroad_alert
if __name__ == "__main__":
params = Params()
with open(os.path.join(BASEDIR, "selfdrive/controls/lib/alerts_offroad.json")) as f:
offroad_alerts = json.load(f)
t = 10 if len(sys.argv) < 2 else int(sys.argv[1])
while True:
print("setting alert update")
params.put_bool("UpdateAvailable", True)
r = open(os.path.join(BASEDIR, "RELEASES.md")).read()
r = r[:r.find('\n\n')] # Slice latest release notes
params.put("UpdaterNewReleaseNotes", r + "\n")
time.sleep(t)
params.put_bool("UpdateAvailable", False)
# cycle through normal alerts
for a in offroad_alerts:
print("setting alert:", a)
set_offroad_alert(a, True)
time.sleep(t)
set_offroad_alert(a, False)
print("no alert")
time.sleep(t)

View File

@@ -0,0 +1,30 @@
#include <QApplication>
#include <QSoundEffect>
#include <QTimer>
#include <QDebug>
int main(int argc, char **argv) {
QApplication a(argc, argv);
QTimer::singleShot(0, [=]{
QSoundEffect s;
const char *vol = getenv("VOLUME");
s.setVolume(vol ? atof(vol) : 1.0);
for (int i = 1; i < argc; i++) {
QString fn = argv[i];
qDebug() << "playing" << fn;
QEventLoop loop;
s.setSource(QUrl::fromLocalFile(fn));
QEventLoop::connect(&s, &QSoundEffect::loadedChanged, &loop, &QEventLoop::quit);
loop.exec();
s.play();
QEventLoop::connect(&s, &QSoundEffect::playingChanged, &loop, &QEventLoop::quit);
loop.exec();
}
QCoreApplication::exit();
});
return a.exec();
}

View File

@@ -0,0 +1,25 @@
#define CATCH_CONFIG_RUNNER
#include "catch2/catch.hpp"
#include <QApplication>
#include <QDebug>
#include <QDir>
#include <QTranslator>
int main(int argc, char **argv) {
// unit tests for Qt
QApplication app(argc, argv);
QString language_file = "main_test_en";
qDebug() << "Loading language:" << language_file;
QTranslator translator;
QString translationsPath = QDir::cleanPath(qApp->applicationDirPath() + "/../translations");
if (!translator.load(language_file, translationsPath)) {
qDebug() << "Failed to load translation file!";
}
app.installTranslator(&translator);
const int res = Catch::Session().run(argc, argv);
return (res < 0xff ? res : 0xff);
}

View File

@@ -0,0 +1,41 @@
#!/usr/bin/env python3
import unittest
from cereal import car
from cereal import messaging
from cereal.messaging import SubMaster, PubMaster
from openpilot.selfdrive.ui.soundd import CONTROLS_TIMEOUT, check_controls_timeout_alert
import time
AudibleAlert = car.CarControl.HUDControl.AudibleAlert
class TestSoundd(unittest.TestCase):
def test_check_controls_timeout_alert(self):
sm = SubMaster(['controlsState'])
pm = PubMaster(['controlsState'])
for _ in range(100):
cs = messaging.new_message('controlsState')
cs.controlsState.enabled = True
pm.send("controlsState", cs)
time.sleep(0.01)
sm.update(0)
self.assertFalse(check_controls_timeout_alert(sm))
for _ in range(CONTROLS_TIMEOUT * 110):
sm.update(0)
time.sleep(0.01)
self.assertTrue(check_controls_timeout_alert(sm))
# TODO: add test with micd for checking that soundd actually outputs sounds
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,48 @@
#include "catch2/catch.hpp"
#include "common/params.h"
#include "selfdrive/ui/qt/window.h"
const QString TEST_TEXT = "(WRAPPED_SOURCE_TEXT)"; // what each string should be translated to
QRegExp RE_NUM("\\d*");
QStringList getParentWidgets(QWidget* widget){
QStringList parentWidgets;
while (widget->parentWidget() != Q_NULLPTR) {
widget = widget->parentWidget();
parentWidgets.append(widget->metaObject()->className());
}
return parentWidgets;
}
template <typename T>
void checkWidgetTrWrap(MainWindow &w) {
for (auto widget : w.findChildren<T>()) {
const QString text = widget->text();
bool isNumber = RE_NUM.exactMatch(text);
bool wrapped = text.contains(TEST_TEXT);
QString parentWidgets = getParentWidgets(widget).join("->");
if (!text.isEmpty() && !isNumber && !wrapped) {
FAIL(("\"" + text + "\" must be wrapped. Parent widgets: " + parentWidgets).toStdString());
}
// warn if source string wrapped, but UI adds text
// TODO: add way to ignore this
if (wrapped && text != TEST_TEXT) {
WARN(("\"" + text + "\" is dynamic and needs a custom retranslate function. Parent widgets: " + parentWidgets).toStdString());
}
}
}
// Tests all strings in the UI are wrapped with tr()
TEST_CASE("UI: test all strings wrapped") {
Params().remove("LanguageSetting");
Params().remove("HardwareSerial");
Params().remove("DongleId");
qputenv("TICI", "1");
MainWindow w;
checkWidgetTrWrap<QPushButton*>(w);
checkWidgetTrWrap<QLabel*>(w);
}

Some files were not shown because too many files have changed in this diff Show More