wip
This commit is contained in:
6
selfdrive/ui/.gitignore
vendored
Executable file → Normal file
6
selfdrive/ui/.gitignore
vendored
Executable file → Normal file
@@ -3,11 +3,13 @@ moc_*
|
||||
|
||||
translations/main_test_en.*
|
||||
|
||||
_text
|
||||
_spinner
|
||||
|
||||
ui
|
||||
mui
|
||||
watch3
|
||||
installer/installers/*
|
||||
qt/text
|
||||
qt/spinner
|
||||
qt/setup/setup
|
||||
qt/setup/reset
|
||||
qt/setup/wifi
|
||||
|
||||
51
selfdrive/ui/SConscript
Executable file → Normal file
51
selfdrive/ui/SConscript
Executable file → Normal file
@@ -11,29 +11,26 @@ if arch == 'larch64':
|
||||
|
||||
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":
|
||||
del base_libs[base_libs.index('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)
|
||||
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/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/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/control_settings.cc", "../frogpilot/ui/vehicle_settings.cc",
|
||||
"../frogpilot/ui/visual_settings.cc",
|
||||
"../oscarpilot/settings/settings.cc", "../oscarpilot/settings/basic.cc",
|
||||
]
|
||||
"../frogpilot/ui/qt/widgets/frogpilot_controls.cc", "../frogpilot/navigation/ui/navigation_settings.cc",
|
||||
"../frogpilot/ui/qt/offroad/control_settings.cc", "../frogpilot/ui/qt/offroad/vehicle_settings.cc",
|
||||
"../frogpilot/ui/qt/offroad/visual_settings.cc"]
|
||||
|
||||
qt_env['CPPDEFINES'] = []
|
||||
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",
|
||||
"qt/maps/map_eta.cc", "qt/maps/map_instructions.cc"]
|
||||
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",
|
||||
"../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
|
||||
with open(File("translations/languages.json").abspath) as f:
|
||||
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)
|
||||
|
||||
# spinner and text window
|
||||
qt_env.Program("qt/text", ["qt/text.cc"], LIBS=qt_libs)
|
||||
qt_env.Program("qt/spinner", ["qt/spinner.cc"], LIBS=qt_libs)
|
||||
qt_env.Program("_text", ["qt/text.cc"], LIBS=qt_libs)
|
||||
qt_env.Program("_spinner", ["qt/spinner.cc"], LIBS=qt_libs)
|
||||
|
||||
# build main UI
|
||||
qt_env.Program("ui", qt_src + [asset_obj], LIBS=qt_libs)
|
||||
@@ -115,28 +97,25 @@ if GetOption('extras') and arch != "Darwin":
|
||||
# build updater UI
|
||||
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
|
||||
senv = qt_env.Clone()
|
||||
senv['LINKFLAGS'].append('-Wl,-strip-debug')
|
||||
|
||||
release = "release3"
|
||||
dashcam = "dashcam3"
|
||||
installers = [
|
||||
("openpilot", release),
|
||||
("openpilot_test", f"{release}-staging"),
|
||||
("openpilot_nightly", "nightly"),
|
||||
("openpilot_internal", "master"),
|
||||
("dashcam", dashcam),
|
||||
("dashcam_test", f"{dashcam}-staging"),
|
||||
]
|
||||
|
||||
cont = {}
|
||||
for brand in ("openpilot", "dashcam"):
|
||||
cont[brand] = senv.Command(f"installer/continue_{brand}.o", f"installer/continue_{brand}.sh",
|
||||
cont = senv.Command(f"installer/continue_openpilot.o", f"installer/continue_openpilot.sh",
|
||||
"ld -r -b binary -o $TARGET $SOURCE")
|
||||
for name, branch in installers:
|
||||
brand = "dashcam" if "dashcam" in branch else "openpilot"
|
||||
d = {'BRANCH': f"'\"{branch}\"'", 'BRAND': f"'\"{brand}\"'"}
|
||||
d = {'BRANCH': f"'\"{branch}\"'"}
|
||||
if "internal" in name:
|
||||
d['INTERNAL'] = "1"
|
||||
|
||||
@@ -145,7 +124,7 @@ if GetOption('extras') and arch != "Darwin":
|
||||
r.raise_for_status()
|
||||
d['SSH_KEYS'] = f'\\"{r.text.strip()}\\"'
|
||||
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
|
||||
assert f[0].get_size() < 350*1e3
|
||||
|
||||
|
||||
0
selfdrive/ui/__init__.py
Normal file
0
selfdrive/ui/__init__.py
Normal file
4
selfdrive/ui/installer/continue_openpilot.sh
Normal file
4
selfdrive/ui/installer/continue_openpilot.sh
Normal 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
4
selfdrive/ui/installer/installer.cc
Executable file → Normal file
@@ -33,8 +33,8 @@ const QString CACHE_PATH = "/data/openpilot.cache";
|
||||
#define INSTALL_PATH "/data/openpilot"
|
||||
#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_end[] asm("_binary_selfdrive_ui_installer_continue_" BRAND "_sh_end");
|
||||
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_openpilot_sh_end");
|
||||
|
||||
bool time_valid() {
|
||||
time_t rawtime;
|
||||
|
||||
0
selfdrive/ui/installer/installer.h
Executable file → Normal file
0
selfdrive/ui/installer/installer.h
Executable file → Normal file
0
selfdrive/ui/main.cc
Executable file → Normal file
0
selfdrive/ui/main.cc
Executable file → Normal file
50
selfdrive/ui/mui.cc
Normal file
50
selfdrive/ui/mui.cc
Normal 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
0
selfdrive/ui/qt/api.cc
Executable file → Normal file
0
selfdrive/ui/qt/api.h
Executable file → Normal file
0
selfdrive/ui/qt/api.h
Executable file → Normal file
165
selfdrive/ui/qt/body.cc
Executable file → Normal file
165
selfdrive/ui/qt/body.cc
Executable file → Normal file
@@ -6,51 +6,156 @@
|
||||
#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"
|
||||
RecordButton::RecordButton(QWidget *parent) : QPushButton(parent) {
|
||||
setCheckable(true);
|
||||
setChecked(false);
|
||||
setFixedSize(148, 148);
|
||||
|
||||
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(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 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.
|
||||
int x = (this->width() - comma_img.width()) / 2;
|
||||
int y = (this->height() - comma_img.height()) / 2;
|
||||
// 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);
|
||||
|
||||
// Draw the pixmap at the calculated position.
|
||||
painter.drawPixmap(x, y, comma_img);
|
||||
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::updateState(const UIState &s) {
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
28
selfdrive/ui/qt/body.h
Executable file → Normal file
28
selfdrive/ui/qt/body.h
Executable file → Normal file
@@ -3,21 +3,35 @@
|
||||
#include <QMovie>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QPixmap>
|
||||
#include <QProgressBar>
|
||||
#include <QSocketNotifier>
|
||||
#include <QVariantAnimation>
|
||||
#include <QWidget>
|
||||
|
||||
#include "common/util.h"
|
||||
#include "selfdrive/ui/ui.h"
|
||||
|
||||
class BodyWindow : public QWidget {
|
||||
class RecordButton : public QPushButton {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BodyWindow(QWidget* parent = 0);
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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) {
|
||||
}
|
||||
49
selfdrive/ui/qt/home.cc
Executable file → Normal file
49
selfdrive/ui/qt/home.cc
Executable file → Normal file
@@ -45,7 +45,7 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
|
||||
});
|
||||
slayout->addWidget(driver_view);
|
||||
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, sidebar, &Sidebar::offroadTransition);
|
||||
}
|
||||
@@ -66,37 +66,45 @@ void HomeWindow::updateState(const UIState &s) {
|
||||
body->setEnabled(true);
|
||||
slayout->setCurrentWidget(body);
|
||||
}
|
||||
|
||||
if (s.scene.started) {
|
||||
showDriverView(s.scene.driver_camera_timer >= 10, true);
|
||||
}
|
||||
}
|
||||
|
||||
void HomeWindow::offroadTransition(bool offroad) {
|
||||
body->setEnabled(false);
|
||||
sidebar->setVisible(false);
|
||||
sidebar->setVisible(offroad);
|
||||
if (offroad) {
|
||||
slayout->setCurrentWidget(body);
|
||||
slayout->setCurrentWidget(home);
|
||||
} else {
|
||||
slayout->setCurrentWidget(onroad);
|
||||
uiState()->scene.map_open = onroad->isMapVisible();
|
||||
}
|
||||
}
|
||||
|
||||
void HomeWindow::showDriverView(bool show) {
|
||||
void HomeWindow::showDriverView(bool show, bool started) {
|
||||
if (show) {
|
||||
emit closeSettings();
|
||||
slayout->setCurrentWidget(driver_view);
|
||||
sidebar->setVisible(show == false);
|
||||
} else {
|
||||
slayout->setCurrentWidget(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) {
|
||||
if (body->isVisible()) {
|
||||
showSidebar(true);
|
||||
slayout->setCurrentWidget(home);
|
||||
} else {
|
||||
// Handle sidebar collapsing
|
||||
if ((onroad->isVisible() || body->isVisible()) && (!sidebar->isVisible() || e->x() > sidebar->width())) {
|
||||
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->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) {
|
||||
left_widget->setCurrentIndex(prime ? 0 : 1);
|
||||
left_widget->setCurrentIndex(2);
|
||||
});
|
||||
|
||||
home_layout->addWidget(left_widget, 1);
|
||||
@@ -222,17 +230,6 @@ OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) {
|
||||
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) {
|
||||
@@ -245,8 +242,10 @@ void OffroadHome::hideEvent(QHideEvent *event) {
|
||||
}
|
||||
|
||||
void OffroadHome::refresh() {
|
||||
QString model = QString::fromStdString(params.get("ModelName"));
|
||||
|
||||
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();
|
||||
int alerts = alerts_widget->refresh();
|
||||
|
||||
8
selfdrive/ui/qt/home.h
Executable file → Normal file
8
selfdrive/ui/qt/home.h
Executable file → Normal file
@@ -33,7 +33,6 @@ private:
|
||||
Params params;
|
||||
|
||||
QTimer* timer;
|
||||
ElidedLabel* date;
|
||||
ElidedLabel* version;
|
||||
QStackedLayout* center_layout;
|
||||
UpdateAlert *update_widget;
|
||||
@@ -42,7 +41,7 @@ private:
|
||||
QPushButton* update_notif;
|
||||
|
||||
// FrogPilot variables
|
||||
QString modelName;
|
||||
ElidedLabel* date;
|
||||
};
|
||||
|
||||
class HomeWindow : public QWidget {
|
||||
@@ -57,7 +56,7 @@ signals:
|
||||
|
||||
public slots:
|
||||
void offroadTransition(bool offroad);
|
||||
void showDriverView(bool show);
|
||||
void showDriverView(bool show, bool started=false);
|
||||
void showSidebar(bool show);
|
||||
void showMapPanel(bool show);
|
||||
|
||||
@@ -73,6 +72,9 @@ private:
|
||||
DriverViewWindow *driver_view;
|
||||
QStackedLayout *slayout;
|
||||
|
||||
// FrogPilot variables
|
||||
Params params;
|
||||
|
||||
private slots:
|
||||
void updateState(const UIState &s);
|
||||
};
|
||||
|
||||
81
selfdrive/ui/qt/maps/map.cc
Executable file → Normal file
81
selfdrive/ui/qt/maps/map.cc
Executable file → Normal file
@@ -18,7 +18,7 @@ const float MAX_PITCH = 50;
|
||||
const float MIN_PITCH = 0;
|
||||
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);
|
||||
|
||||
map_overlay = new QWidget (this);
|
||||
@@ -57,10 +57,10 @@ void MapWindow::initLayers() {
|
||||
if (!m_map->layerExists("modelPathLayer")) {
|
||||
qDebug() << "Initializing modelPathLayer";
|
||||
QVariantMap modelPath;
|
||||
modelPath["id"] = "modelPathLayer";
|
||||
//modelPath["id"] = "modelPathLayer";
|
||||
modelPath["type"] = "line";
|
||||
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-width", 5.0);
|
||||
m_map->setLayoutProperty("modelPathLayer", "line-cap", "round");
|
||||
@@ -68,10 +68,9 @@ void MapWindow::initLayers() {
|
||||
if (!m_map->layerExists("navLayer")) {
|
||||
qDebug() << "Initializing navLayer";
|
||||
QVariantMap nav;
|
||||
nav["id"] = "navLayer";
|
||||
nav["type"] = "line";
|
||||
nav["source"] = "navSource";
|
||||
m_map->addLayer(nav, "road-intersection");
|
||||
m_map->addLayer("navLayer", nav, "road-intersection");
|
||||
|
||||
QVariantMap transition;
|
||||
transition["duration"] = 400; // ms
|
||||
@@ -84,10 +83,9 @@ void MapWindow::initLayers() {
|
||||
qDebug() << "Initializing pinLayer";
|
||||
m_map->addImage("default_marker", QImage("../assets/navigation/default_marker.svg"));
|
||||
QVariantMap pin;
|
||||
pin["id"] = "pinLayer";
|
||||
pin["type"] = "symbol";
|
||||
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-image", "default_marker");
|
||||
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"));
|
||||
|
||||
QVariantMap carPos;
|
||||
carPos["id"] = "carPosLayer";
|
||||
carPos["type"] = "symbol";
|
||||
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-image", "label-arrow");
|
||||
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
|
||||
m_map->setLayoutProperty("carPosLayer", "symbol-sort-key", 0);
|
||||
}
|
||||
|
||||
if (!m_map->layerExists("buildingsLayer")) {
|
||||
qDebug() << "Initializing buildingsLayer";
|
||||
QVariantMap buildings;
|
||||
@@ -121,7 +117,7 @@ void MapWindow::initLayers() {
|
||||
buildings["source-layer"] = "building";
|
||||
buildings["type"] = "fill-extrusion";
|
||||
buildings["minzoom"] = 15;
|
||||
m_map->addLayer(buildings);
|
||||
m_map->addLayer("buildingsLayer", buildings);
|
||||
m_map->setFilter("buildingsLayer", QVariantList({"==", "extrude", "true"}));
|
||||
|
||||
QVariantList fillExtrusionHight = {
|
||||
@@ -168,8 +164,7 @@ void MapWindow::updateState(const UIState &s) {
|
||||
if (sm.updated("modelV2")) {
|
||||
// set path color on change, and show map on rising edge of navigate on openpilot
|
||||
bool nav_enabled = sm["modelV2"].getModelV2().getNavEnabled() &&
|
||||
(sm["controlsState"].getControlsState().getEnabled() || sm["frogpilotCarControl"].getFrogpilotCarControl().getAlwaysOnLateral()) &&
|
||||
(!params.get("NavDestination").empty() || params.getInt("PrimeType") != 0);
|
||||
(sm["controlsState"].getControlsState().getEnabled() || sm["frogpilotCarControl"].getFrogpilotCarControl().getAlwaysOnLateral());
|
||||
if (nav_enabled != uiState()->scene.navigate_on_openpilot) {
|
||||
if (loaded_once) {
|
||||
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);
|
||||
|
||||
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]);
|
||||
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()) {
|
||||
auto nav_dest = coordinate_from_param("NavDestination");
|
||||
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) {
|
||||
// Update current location marker
|
||||
auto point = coordinate_to_collection(*last_position);
|
||||
QMapbox::Feature feature1(QMapbox::Feature::PointType, point, {}, {});
|
||||
QMapLibre::Feature feature1(QMapLibre::Feature::PointType, point, {}, {});
|
||||
QVariantMap carPosSource;
|
||||
carPosSource["type"] = "geojson";
|
||||
carPosSource["data"] = QVariant::fromValue<QMapbox::Feature>(feature1);
|
||||
carPosSource["data"] = QVariant::fromValue<QMapLibre::Feature>(feature1);
|
||||
m_map->updateSource("carPosSource", carPosSource);
|
||||
|
||||
// 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";
|
||||
auto route = sm["navRoute"].getNavRoute();
|
||||
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;
|
||||
navSource["type"] = "geojson";
|
||||
navSource["data"] = QVariant::fromValue<QMapbox::Feature>(feature);
|
||||
navSource["data"] = QVariant::fromValue<QMapLibre::Feature>(feature);
|
||||
m_map->updateSource("navSource", navSource);
|
||||
m_map->setLayoutProperty("navLayer", "visibility", "visible");
|
||||
|
||||
route_rcv_frame = sm.rcv_frame("navRoute");
|
||||
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) {
|
||||
@@ -301,24 +332,24 @@ void MapWindow::resizeGL(int w, int h) {
|
||||
}
|
||||
|
||||
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) {
|
||||
m_map->setCoordinateZoom(*last_position, MAX_ZOOM);
|
||||
} 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->setPitch(MIN_PITCH);
|
||||
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
|
||||
if (change == QMapboxGL::MapChange::MapChangeDidFinishLoadingStyle) {
|
||||
if (change == QMapLibre::Map::MapChange::MapChangeDidFinishLoadingStyle) {
|
||||
m_map->setTransitionOptions(0, 0);
|
||||
}
|
||||
if (change == QMapboxGL::MapChange::MapChangeDidFinishLoadingMap) {
|
||||
if (change == QMapLibre::Map::MapChange::MapChangeDidFinishLoadingMap) {
|
||||
loaded_once = true;
|
||||
}
|
||||
});
|
||||
@@ -426,10 +457,10 @@ void MapWindow::updateDestinationMarker() {
|
||||
auto nav_dest = coordinate_from_param("NavDestination");
|
||||
if (nav_dest.has_value()) {
|
||||
auto point = coordinate_to_collection(*nav_dest);
|
||||
QMapbox::Feature feature(QMapbox::Feature::PointType, point, {}, {});
|
||||
QMapLibre::Feature feature(QMapLibre::Feature::PointType, point, {}, {});
|
||||
QVariantMap pinSource;
|
||||
pinSource["type"] = "geojson";
|
||||
pinSource["data"] = QVariant::fromValue<QMapbox::Feature>(feature);
|
||||
pinSource["data"] = QVariant::fromValue<QMapLibre::Feature>(feature);
|
||||
m_map->updateSource("pinSource", pinSource);
|
||||
m_map->setPaintProperty("pinLayer", "visibility", "visible");
|
||||
} else {
|
||||
|
||||
17
selfdrive/ui/qt/maps/map.h
Executable file → Normal file
17
selfdrive/ui/qt/maps/map.h
Executable file → Normal file
@@ -6,7 +6,8 @@
|
||||
#include <QGestureEvent>
|
||||
#include <QLabel>
|
||||
#include <QMap>
|
||||
#include <QMapboxGL>
|
||||
#include <QMapLibre/Map>
|
||||
#include <QMapLibre/Settings>
|
||||
#include <QMouseEvent>
|
||||
#include <QOpenGLWidget>
|
||||
#include <QPixmap>
|
||||
@@ -27,7 +28,7 @@ class MapWindow : public QOpenGLWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MapWindow(const QMapboxGLSettings &);
|
||||
MapWindow(const QMapLibre::Settings &);
|
||||
~MapWindow();
|
||||
|
||||
private:
|
||||
@@ -35,8 +36,8 @@ private:
|
||||
void paintGL() final;
|
||||
void resizeGL(int w, int h) override;
|
||||
|
||||
QMapboxGLSettings m_settings;
|
||||
QScopedPointer<QMapboxGL> m_map;
|
||||
QMapLibre::Settings m_settings;
|
||||
QScopedPointer<QMapLibre::Map> m_map;
|
||||
|
||||
void initLayers();
|
||||
|
||||
@@ -56,8 +57,8 @@ private:
|
||||
int interaction_counter = 0;
|
||||
|
||||
// Position
|
||||
std::optional<QMapbox::Coordinate> last_valid_nav_dest;
|
||||
std::optional<QMapbox::Coordinate> last_position;
|
||||
std::optional<QMapLibre::Coordinate> last_valid_nav_dest;
|
||||
std::optional<QMapLibre::Coordinate> last_position;
|
||||
std::optional<float> last_bearing;
|
||||
FirstOrderFilter velocity_filter;
|
||||
bool locationd_valid = false;
|
||||
@@ -80,6 +81,10 @@ private:
|
||||
// FrogPilot variables
|
||||
Params params;
|
||||
|
||||
int previous_map_style;
|
||||
|
||||
uint64_t model_rcv_frame = 0;
|
||||
|
||||
private slots:
|
||||
void updateState(const UIState &s);
|
||||
|
||||
|
||||
0
selfdrive/ui/qt/maps/map_eta.cc
Executable file → Normal file
0
selfdrive/ui/qt/maps/map_eta.cc
Executable file → Normal file
0
selfdrive/ui/qt/maps/map_eta.h
Executable file → Normal file
0
selfdrive/ui/qt/maps/map_eta.h
Executable file → Normal file
37
selfdrive/ui/qt/maps/map_helpers.cc
Executable file → Normal file
37
selfdrive/ui/qt/maps/map_helpers.cc
Executable file → Normal file
@@ -16,24 +16,25 @@ QString get_mapbox_token() {
|
||||
return MAPBOX_TOKEN.isEmpty() ? CommaApi::create_jwt({}, 4 * 7 * 24 * 3600) : MAPBOX_TOKEN;
|
||||
}
|
||||
|
||||
QMapboxGLSettings get_mapbox_settings() {
|
||||
QMapboxGLSettings settings;
|
||||
QMapLibre::Settings get_mapbox_settings() {
|
||||
QMapLibre::Settings settings;
|
||||
settings.setProviderTemplate(QMapLibre::Settings::ProviderTemplate::MapboxProvider);
|
||||
|
||||
if (!Hardware::PC()) {
|
||||
settings.setCacheDatabasePath(MAPS_CACHE_PATH);
|
||||
settings.setCacheDatabaseMaximumSize(100 * 1024 * 1024);
|
||||
}
|
||||
settings.setApiBaseUrl(MAPS_HOST);
|
||||
settings.setAccessToken(get_mapbox_token());
|
||||
settings.setApiKey(get_mapbox_token());
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
QGeoCoordinate to_QGeoCoordinate(const QMapbox::Coordinate &in) {
|
||||
QGeoCoordinate to_QGeoCoordinate(const QMapLibre::Coordinate &in) {
|
||||
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 &positionECEF,
|
||||
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::Matrix3d ecef_from_local = euler2rot(orient);
|
||||
|
||||
QMapbox::Coordinates coordinates;
|
||||
QMapLibre::Coordinates coordinates;
|
||||
auto x = line.getX();
|
||||
auto y = line.getY();
|
||||
auto z = line.getZ();
|
||||
@@ -52,28 +53,28 @@ QMapbox::CoordinatesCollections model_to_collection(
|
||||
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) {
|
||||
QMapbox::Coordinates coordinates{c};
|
||||
return {QMapbox::CoordinatesCollection{coordinates}};
|
||||
QMapLibre::CoordinatesCollections coordinate_to_collection(const QMapLibre::Coordinate &c) {
|
||||
QMapLibre::Coordinates coordinates{c};
|
||||
return {QMapLibre::CoordinatesCollection{coordinates}};
|
||||
}
|
||||
|
||||
QMapbox::CoordinatesCollections capnp_coordinate_list_to_collection(const capnp::List<cereal::NavRoute::Coordinate>::Reader& coordinate_list) {
|
||||
QMapbox::Coordinates coordinates;
|
||||
QMapLibre::CoordinatesCollections capnp_coordinate_list_to_collection(const capnp::List<cereal::NavRoute::Coordinate>::Reader& coordinate_list) {
|
||||
QMapLibre::Coordinates coordinates;
|
||||
for (auto const &c : coordinate_list) {
|
||||
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) {
|
||||
QMapbox::Coordinates coordinates;
|
||||
QMapLibre::CoordinatesCollections coordinate_list_to_collection(const QList<QGeoCoordinate> &coordinate_list) {
|
||||
QMapLibre::Coordinates coordinates;
|
||||
for (auto &c : coordinate_list) {
|
||||
coordinates.push_back({c.latitude(), c.longitude()});
|
||||
}
|
||||
return {QMapbox::CoordinatesCollection{coordinates}};
|
||||
return {QMapLibre::CoordinatesCollection{coordinates}};
|
||||
}
|
||||
|
||||
QList<QGeoCoordinate> polyline_to_coordinate_list(const QString &polylineString) {
|
||||
@@ -118,7 +119,7 @@ QList<QGeoCoordinate> polyline_to_coordinate_list(const QString &polylineString)
|
||||
return path;
|
||||
}
|
||||
|
||||
std::optional<QMapbox::Coordinate> coordinate_from_param(const std::string ¶m) {
|
||||
std::optional<QMapLibre::Coordinate> coordinate_from_param(const std::string ¶m) {
|
||||
QString json_str = QString::fromStdString(Params().get(param));
|
||||
if (json_str.isEmpty()) return {};
|
||||
|
||||
@@ -127,7 +128,7 @@ std::optional<QMapbox::Coordinate> coordinate_from_param(const std::string ¶
|
||||
|
||||
QJsonObject json = doc.object();
|
||||
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;
|
||||
} else {
|
||||
return {};
|
||||
|
||||
17
selfdrive/ui/qt/maps/map_helpers.h
Executable file → Normal file
17
selfdrive/ui/qt/maps/map_helpers.h
Executable file → Normal file
@@ -3,8 +3,9 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <QMapLibre/Map>
|
||||
#include <QMapLibre/Settings>
|
||||
#include <eigen3/Eigen/Dense>
|
||||
#include <QMapboxGL>
|
||||
#include <QGeoCoordinate>
|
||||
|
||||
#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";
|
||||
|
||||
QString get_mapbox_token();
|
||||
QMapboxGLSettings get_mapbox_settings();
|
||||
QGeoCoordinate to_QGeoCoordinate(const QMapbox::Coordinate &in);
|
||||
QMapbox::CoordinatesCollections model_to_collection(
|
||||
QMapLibre::Settings get_mapbox_settings();
|
||||
QGeoCoordinate to_QGeoCoordinate(const QMapLibre::Coordinate &in);
|
||||
QMapLibre::CoordinatesCollections model_to_collection(
|
||||
const cereal::LiveLocationKalman::Measurement::Reader &calibratedOrientationECEF,
|
||||
const cereal::LiveLocationKalman::Measurement::Reader &positionECEF,
|
||||
const cereal::XYZTData::Reader &line);
|
||||
QMapbox::CoordinatesCollections coordinate_to_collection(const QMapbox::Coordinate &c);
|
||||
QMapbox::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_to_collection(const QMapLibre::Coordinate &c);
|
||||
QMapLibre::CoordinatesCollections capnp_coordinate_list_to_collection(const capnp::List<cereal::NavRoute::Coordinate>::Reader &coordinate_list);
|
||||
QMapLibre::CoordinatesCollections coordinate_list_to_collection(const QList<QGeoCoordinate> &coordinate_list);
|
||||
QList<QGeoCoordinate> polyline_to_coordinate_list(const QString &polylineString);
|
||||
std::optional<QMapbox::Coordinate> coordinate_from_param(const std::string ¶m);
|
||||
std::optional<QMapLibre::Coordinate> coordinate_from_param(const std::string ¶m);
|
||||
std::pair<QString, QString> map_format_distance(float d, bool is_metric);
|
||||
|
||||
0
selfdrive/ui/qt/maps/map_instructions.cc
Executable file → Normal file
0
selfdrive/ui/qt/maps/map_instructions.cc
Executable file → Normal file
0
selfdrive/ui/qt/maps/map_instructions.h
Executable file → Normal file
0
selfdrive/ui/qt/maps/map_instructions.h
Executable file → Normal file
7
selfdrive/ui/qt/maps/map_panel.cc
Executable file → Normal file
7
selfdrive/ui/qt/maps/map_panel.cc
Executable file → Normal file
@@ -8,7 +8,7 @@
|
||||
#include "selfdrive/ui/qt/util.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->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
@@ -41,8 +41,3 @@ void MapPanel::toggleMapSettings() {
|
||||
emit mapPanelRequested();
|
||||
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
5
selfdrive/ui/qt/maps/map_panel.h
Executable file → Normal file
@@ -1,15 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <QFrame>
|
||||
#include <QMapboxGL>
|
||||
#include <QMapLibre/Settings>
|
||||
#include <QStackedLayout>
|
||||
|
||||
class MapPanel : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MapPanel(const QMapboxGLSettings &settings, QWidget *parent = nullptr);
|
||||
void setVisible(bool visible);
|
||||
explicit MapPanel(const QMapLibre::Settings &settings, QWidget *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void mapPanelRequested();
|
||||
|
||||
14
selfdrive/ui/qt/maps/map_settings.cc
Executable file → Normal file
14
selfdrive/ui/qt/maps/map_settings.cc
Executable file → Normal file
@@ -62,13 +62,7 @@ MapSettings::MapSettings(bool closeable, QWidget *parent) : QFrame(parent) {
|
||||
title->setStyleSheet("color: #FFFFFF; font-size: 54px; font-weight: 600;");
|
||||
heading->addWidget(title);
|
||||
|
||||
// NOO without Prime IP extraction
|
||||
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;");
|
||||
heading->addWidget(subtitle);
|
||||
}
|
||||
@@ -99,6 +93,8 @@ MapSettings::MapSettings(bool closeable, QWidget *parent) : QFrame(parent) {
|
||||
|
||||
setStyleSheet("MapSettings { background-color: #333333; }");
|
||||
QObject::connect(NavManager::instance(), &NavManager::updated, this, &MapSettings::refresh);
|
||||
|
||||
wifi = new WifiManager(this);
|
||||
}
|
||||
|
||||
void MapSettings::showEvent(QShowEvent *event) {
|
||||
@@ -145,9 +141,9 @@ void MapSettings::refresh() {
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
|
||||
// NOO without Prime IP update
|
||||
if (notPrime) {
|
||||
ipAddress = QString("%1:8082").arg(wifi->getIp4Address());
|
||||
// Use IP for NOO without Prime
|
||||
if (!uiState()->hasPrime()) {
|
||||
QString ipAddress = QString("%1:8082").arg(wifi->getIp4Address());
|
||||
subtitle->setText(tr("Manage at %1").arg(ipAddress));
|
||||
}
|
||||
}
|
||||
|
||||
5
selfdrive/ui/qt/maps/map_settings.h
Executable file → Normal file
5
selfdrive/ui/qt/maps/map_settings.h
Executable file → Normal file
@@ -66,10 +66,9 @@ private:
|
||||
std::vector<DestinationWidget *> widgets;
|
||||
|
||||
// FrogPilot variables
|
||||
bool notPrime = Params().getInt("PrimeType") == 0;
|
||||
QLabel *subtitle;
|
||||
QString ipAddress;
|
||||
WifiManager *wifi = new WifiManager(this);
|
||||
|
||||
WifiManager *wifi;
|
||||
|
||||
signals:
|
||||
void closeSettings();
|
||||
|
||||
12
selfdrive/ui/qt/network/networking.cc
Executable file → Normal file
12
selfdrive/ui/qt/network/networking.cc
Executable file → Normal file
@@ -82,11 +82,11 @@ void Networking::connectToNetwork(const Network n) {
|
||||
if (wifi->isKnownConnection(n.ssid)) {
|
||||
wifi->activateWifiConnection(n.ssid);
|
||||
} else if (n.security_type == SecurityType::OPEN) {
|
||||
wifi->connect(n);
|
||||
wifi->connect(n, false);
|
||||
} 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);
|
||||
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);
|
||||
QString pass = InputDialog::getText(tr("Wrong password"), this, tr("for \"%1\"").arg(QString::fromUtf8(n.ssid)), true, 8);
|
||||
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);
|
||||
QObject::connect(tetheringToggle, &ToggleControl::toggleFlipped, this, &AdvancedNetworking::toggleTethering);
|
||||
if (params.getBool("TetheringEnabled")) {
|
||||
tetheringToggle->setVisualOn();
|
||||
tetheringToggle->refresh();
|
||||
uiState()->scene.tethering_enabled = true;
|
||||
}
|
||||
|
||||
@@ -196,9 +196,9 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
|
||||
hidden_network.ssid = ssid.toUtf8();
|
||||
if (!pass.isEmpty()) {
|
||||
hidden_network.security_type = SecurityType::WPA;
|
||||
wifi->connect(hidden_network, pass);
|
||||
wifi->connect(hidden_network, true, pass);
|
||||
} else {
|
||||
wifi->connect(hidden_network);
|
||||
wifi->connect(hidden_network, true);
|
||||
}
|
||||
emit requestWifiScreen();
|
||||
}
|
||||
|
||||
0
selfdrive/ui/qt/network/networking.h
Executable file → Normal file
0
selfdrive/ui/qt/network/networking.h
Executable file → Normal file
0
selfdrive/ui/qt/network/networkmanager.h
Executable file → Normal file
0
selfdrive/ui/qt/network/networkmanager.h
Executable file → Normal file
3
selfdrive/ui/qt/network/wifi_manager.cc
Executable file → Normal file
3
selfdrive/ui/qt/network/wifi_manager.cc
Executable file → Normal 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);
|
||||
forgetConnection(n.ssid); // Clear all connections that may already exist to the network we are connecting
|
||||
Connection connection;
|
||||
@@ -176,6 +176,7 @@ void WifiManager::connect(const Network &n, const QString &password, const QStri
|
||||
connection["connection"]["autoconnect-retries"] = 0;
|
||||
|
||||
connection["802-11-wireless"]["ssid"] = n.ssid;
|
||||
connection["802-11-wireless"]["hidden"] = is_hidden;
|
||||
connection["802-11-wireless"]["mode"] = "infrastructure";
|
||||
|
||||
if (n.security_type == SecurityType::WPA) {
|
||||
|
||||
2
selfdrive/ui/qt/network/wifi_manager.h
Executable file → Normal file
2
selfdrive/ui/qt/network/wifi_manager.h
Executable file → Normal file
@@ -52,7 +52,7 @@ public:
|
||||
std::optional<QDBusPendingCall> activateWifiConnection(const QString &ssid);
|
||||
NetworkType currentNetworkType();
|
||||
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
|
||||
void setTetheringEnabled(bool enabled);
|
||||
|
||||
0
selfdrive/ui/qt/offroad/driverview.cc
Executable file → Normal file
0
selfdrive/ui/qt/offroad/driverview.cc
Executable file → Normal file
0
selfdrive/ui/qt/offroad/driverview.h
Executable file → Normal file
0
selfdrive/ui/qt/offroad/driverview.h
Executable file → Normal file
0
selfdrive/ui/qt/offroad/experimental_mode.cc
Executable file → Normal file
0
selfdrive/ui/qt/offroad/experimental_mode.cc
Executable file → Normal file
0
selfdrive/ui/qt/offroad/experimental_mode.h
Executable file → Normal file
0
selfdrive/ui/qt/offroad/experimental_mode.h
Executable file → Normal file
14
selfdrive/ui/qt/offroad/onboarding.cc
Executable file → Normal file
14
selfdrive/ui/qt/offroad/onboarding.cc
Executable file → Normal file
@@ -183,8 +183,8 @@ void DeclinePage::showEvent(QShowEvent *event) {
|
||||
void OnboardingWindow::updateActiveScreen() {
|
||||
if (!accepted_terms) {
|
||||
setCurrentIndex(0);
|
||||
// } else if (!training_done && !params.getBool("Passive")) {
|
||||
// setCurrentIndex(1);
|
||||
} else if (!training_done) {
|
||||
setCurrentIndex(1);
|
||||
} else {
|
||||
emit onboardingDone();
|
||||
}
|
||||
@@ -199,7 +199,7 @@ OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) {
|
||||
TermsPage* terms = new TermsPage(this);
|
||||
addWidget(terms);
|
||||
connect(terms, &TermsPage::acceptedTerms, [=]() {
|
||||
Params().put("HasAcceptedTerms", current_terms_version);
|
||||
params.put("HasAcceptedTerms", current_terms_version);
|
||||
accepted_terms = true;
|
||||
updateActiveScreen();
|
||||
});
|
||||
@@ -209,7 +209,7 @@ OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) {
|
||||
addWidget(tr);
|
||||
connect(tr, &TrainingGuide::completedTraining, [=]() {
|
||||
training_done = true;
|
||||
Params().put("CompletedTrainingVersion", current_training_version);
|
||||
params.put("CompletedTrainingVersion", current_training_version);
|
||||
updateActiveScreen();
|
||||
});
|
||||
|
||||
@@ -230,11 +230,5 @@ OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) {
|
||||
background-color: #4F4F4F;
|
||||
}
|
||||
)");
|
||||
|
||||
// # Oscar sez
|
||||
Params().put("HasAcceptedTerms", current_terms_version);
|
||||
Params().put("CompletedTrainingVersion", current_training_version);
|
||||
accepted_terms = true;
|
||||
emit onboardingDone();
|
||||
updateActiveScreen();
|
||||
}
|
||||
|
||||
0
selfdrive/ui/qt/offroad/onboarding.h
Executable file → Normal file
0
selfdrive/ui/qt/offroad/onboarding.h
Executable file → Normal file
363
selfdrive/ui/qt/offroad/settings.cc
Executable file → Normal file
363
selfdrive/ui/qt/offroad/settings.cc
Executable file → Normal file
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
@@ -25,9 +27,9 @@
|
||||
#include "selfdrive/ui/qt/qt_window.h"
|
||||
|
||||
#include "selfdrive/frogpilot/navigation/ui/navigation_settings.h"
|
||||
#include "selfdrive/frogpilot/ui/control_settings.h"
|
||||
#include "selfdrive/frogpilot/ui/vehicle_settings.h"
|
||||
#include "selfdrive/frogpilot/ui/visual_settings.h"
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/control_settings.h"
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h"
|
||||
#include "selfdrive/frogpilot/ui/qt/offroad/visual_settings.h"
|
||||
|
||||
TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
|
||||
// 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")};
|
||||
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. "
|
||||
"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",
|
||||
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) {
|
||||
auto toggle = new ParamControl(param, title, desc, icon, this);
|
||||
|
||||
@@ -110,7 +117,7 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
|
||||
toggles[param.toStdString()] = toggle;
|
||||
|
||||
// insert longitudinal personality after NDOG toggle
|
||||
if (param == "DisengageOnAccelerator" && !params.getInt("AdjustablePersonalities")) {
|
||||
if (param == "DisengageOnAccelerator") {
|
||||
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 ¶m) {
|
||||
toggles[param.toStdString()]->showDescription();
|
||||
}
|
||||
@@ -138,6 +157,13 @@ void TogglesPanel::showEvent(QShowEvent *event) {
|
||||
}
|
||||
|
||||
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 op_long_toggle = toggles["ExperimentalLongitudinalEnabled"];
|
||||
const QString e2e_description = QString("%1<br>"
|
||||
@@ -173,7 +199,12 @@ void TogglesPanel::updateToggles() {
|
||||
op_long_toggle->setVisible(CP.getExperimentalLongitudinalAvailable() && !is_release);
|
||||
if (hasLongitudinalControl(CP)) {
|
||||
// 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);
|
||||
long_personality_setting->setEnabled(true);
|
||||
} else {
|
||||
@@ -225,7 +256,6 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
|
||||
});
|
||||
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"));
|
||||
connect(retrainingBtn, &ButtonControl::clicked, [=]() {
|
||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to review the training guide?"), tr("Review"), this)) {
|
||||
@@ -233,7 +263,6 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
|
||||
}
|
||||
});
|
||||
addItem(retrainingBtn);
|
||||
}
|
||||
|
||||
if (Hardware::TICI()) {
|
||||
auto regulatoryBtn = new ButtonControl(tr("Regulatory"), tr("VIEW"), "");
|
||||
@@ -258,44 +287,251 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
|
||||
addItem(translateBtn);
|
||||
|
||||
// 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.")
|
||||
);
|
||||
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;
|
||||
std::thread([&] {
|
||||
deleteDrivingDataBtn->setValue(tr("Deleting footage..."));
|
||||
std::system("rm -rf /data/media/0/realdata");
|
||||
deleteDrivingDataBtn->setValue(tr("Deleted!"));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
deleteDrivingDataBtn->setValue("");
|
||||
}).detach();
|
||||
});
|
||||
addItem(deleteFootageBtn);
|
||||
addItem(deleteDrivingDataBtn);
|
||||
|
||||
// Panda flashing button
|
||||
auto flashPandaBtn = new ButtonControl(tr("Flash Panda"), tr("FLASH"), "Use this button to troubleshoot and update the Panda device's firmware.");
|
||||
connect(flashPandaBtn, &ButtonControl::clicked, [this]() {
|
||||
if (!ConfirmationDialog::confirm(tr("Are you sure you want to flash the Panda?"), tr("Flash"), this)) return;
|
||||
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, [=]() {
|
||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to flash the Panda?"), tr("Flash"), this)) {
|
||||
std::thread([=]() {
|
||||
flashPandaBtn->setValue(tr("Flashing..."));
|
||||
|
||||
QProcess process;
|
||||
// Get Panda type
|
||||
SubMaster &sm = *(uiState()->sm);
|
||||
auto pandaStates = sm["pandaStates"].getPandaStates();
|
||||
// Choose recovery script based on Panda type
|
||||
if (pandaStates.size() != 0) {
|
||||
auto pandaType = pandaStates[0].getPandaType();
|
||||
bool isRedPanda = (pandaType == cereal::PandaState::PandaType::RED_PANDA ||
|
||||
pandaType == cereal::PandaState::PandaType::RED_PANDA_V2);
|
||||
QString recoveryScript = isRedPanda ? "./recover.sh" : "./recover.py";
|
||||
// Run recovery script and flash Panda
|
||||
|
||||
process.setWorkingDirectory("/data/openpilot/panda/board");
|
||||
process.start("/bin/sh", QStringList{"-c", recoveryScript});
|
||||
process.start("/bin/sh", QStringList{"-c", "./recover.py"});
|
||||
process.waitForFinished();
|
||||
process.start("/bin/sh", QStringList{"-c", "./flash.py"});
|
||||
process.waitForFinished();
|
||||
|
||||
process.setWorkingDirectory("/data/openpilot/panda/tests");
|
||||
process.start("/bin/sh", QStringList{"-c", "python reflash_internal_panda.py"});
|
||||
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);
|
||||
|
||||
// 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) {
|
||||
for (auto btn : findChildren<ButtonControl *>()) {
|
||||
btn->setEnabled(offroad);
|
||||
@@ -311,6 +547,11 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
|
||||
power_layout->addWidget(reboot_btn);
|
||||
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"));
|
||||
poweroff_btn->setObjectName("poweroff_btn");
|
||||
power_layout->addWidget(poweroff_btn);
|
||||
@@ -321,6 +562,8 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
|
||||
}
|
||||
|
||||
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:pressed { background-color: #4a4a4a; }
|
||||
#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() {
|
||||
if (!uiState()->engaged()) {
|
||||
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) {
|
||||
setCurrentPanel(0);
|
||||
}
|
||||
@@ -396,33 +660,34 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
|
||||
// setup two main layouts
|
||||
sidebar_widget = new QWidget;
|
||||
QVBoxLayout *sidebar_layout = new QVBoxLayout(sidebar_widget);
|
||||
sidebar_layout->setMargin(0);
|
||||
panel_widget = new QStackedWidget();
|
||||
|
||||
// close button
|
||||
QPushButton *close_btn = new QPushButton(tr("← Back"));
|
||||
close_btn->setStyleSheet(R"(
|
||||
QPushButton {
|
||||
font-size: 50px;
|
||||
padding-bottom: 0px;
|
||||
border 1px grey solid;
|
||||
color: white;
|
||||
border-radius: 25px;
|
||||
background-color: #292929;
|
||||
background: #292929;
|
||||
font-size: 50px;
|
||||
font-weight: 500;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #3B3B3B;
|
||||
color: #ADADAD;
|
||||
}
|
||||
)");
|
||||
close_btn->setFixedSize(300, 125);
|
||||
sidebar_layout->addSpacing(10);
|
||||
sidebar_layout->addWidget(close_btn, 0, Qt::AlignRight);
|
||||
QObject::connect(close_btn, &QPushButton::clicked, [this]() {
|
||||
if (frogPilotTogglesOpen) {
|
||||
frogPilotTogglesOpen = false;
|
||||
this->closeParentToggle();
|
||||
if (subParentToggleOpen) {
|
||||
closeSubParentToggle();
|
||||
subParentToggleOpen = false;
|
||||
} else if (parentToggleOpen) {
|
||||
closeParentToggle();
|
||||
parentToggleOpen = false;
|
||||
} else {
|
||||
this->closeSettings();
|
||||
closeSettings();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -435,20 +700,19 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
|
||||
QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription);
|
||||
QObject::connect(toggles, &TogglesPanel::updateMetric, this, &SettingsWindow::updateMetric);
|
||||
|
||||
// FrogPilotControlsPanel *frogpilotControls = new FrogPilotControlsPanel(this);
|
||||
// QObject::connect(frogpilotControls, &FrogPilotControlsPanel::closeParentToggle, this, [this]() {frogPilotTogglesOpen = false;});
|
||||
// QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openParentToggle, this, [this]() {frogPilotTogglesOpen = true;});
|
||||
FrogPilotControlsPanel *frogpilotControls = new FrogPilotControlsPanel(this);
|
||||
QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openSubParentToggle, this, [this]() {subParentToggleOpen = true;});
|
||||
QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openParentToggle, this, [this]() {parentToggleOpen = true;});
|
||||
|
||||
FrogPilotVisualsPanel *frogpilotVisuals = new FrogPilotVisualsPanel(this);
|
||||
QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::closeParentToggle, this, [this]() {frogPilotTogglesOpen = false;});
|
||||
QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::openParentToggle, this, [this]() {frogPilotTogglesOpen = true;});
|
||||
QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::openParentToggle, this, [this]() {parentToggleOpen = true;});
|
||||
|
||||
QList<QPair<QString, QWidget *>> panels = {
|
||||
{tr("Device"), device},
|
||||
{tr("Network"), new Networking(this)},
|
||||
{tr("Toggles"), toggles},
|
||||
{tr("Software"), new SoftwarePanel(this)},
|
||||
// {tr("Controls"), frogpilotControls},
|
||||
{tr("Controls"), frogpilotControls},
|
||||
{tr("Navigation"), new FrogPilotNavigationPanel(this)},
|
||||
{tr("Vehicles"), new FrogPilotVehiclesPanel(this)},
|
||||
{tr("Visuals"), frogpilotVisuals},
|
||||
@@ -484,21 +748,24 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
|
||||
ScrollView *panel_frame = new ScrollView(panel, this);
|
||||
panel_widget->addWidget(panel_frame);
|
||||
|
||||
if (name == tr("Controls") || name == tr("Visuals")) {
|
||||
if (name == tr("Controls")) {
|
||||
QScrollBar *scrollbar = panel_frame->verticalScrollBar();
|
||||
|
||||
QObject::connect(scrollbar, &QScrollBar::valueChanged, this, [this](int value) {
|
||||
if (!frogPilotTogglesOpen) {
|
||||
if (!parentToggleOpen) {
|
||||
previousScrollPosition = value;
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(scrollbar, &QScrollBar::rangeChanged, this, [this, panel_frame]() {
|
||||
if (!parentToggleOpen) {
|
||||
panel_frame->restorePosition(previousScrollPosition);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() {
|
||||
closeParentToggle();
|
||||
previousScrollPosition = 0;
|
||||
btn->setChecked(true);
|
||||
panel_widget->setCurrentWidget(w);
|
||||
|
||||
27
selfdrive/ui/qt/offroad/settings.h
Executable file → Normal file
27
selfdrive/ui/qt/offroad/settings.h
Executable file → Normal file
@@ -11,6 +11,7 @@
|
||||
#include <QWidget>
|
||||
|
||||
|
||||
#include "selfdrive/ui/ui.h"
|
||||
#include "selfdrive/ui/qt/util.h"
|
||||
#include "selfdrive/ui/qt/widgets/controls.h"
|
||||
|
||||
@@ -25,6 +26,9 @@ public:
|
||||
protected:
|
||||
void showEvent(QShowEvent *event) override;
|
||||
|
||||
// FrogPilot widgets
|
||||
void hideEvent(QHideEvent *event) override;
|
||||
|
||||
signals:
|
||||
void closeSettings();
|
||||
void reviewTrainingGuide();
|
||||
@@ -33,7 +37,9 @@ signals:
|
||||
|
||||
// FrogPilot signals
|
||||
void closeParentToggle();
|
||||
void closeSubParentToggle();
|
||||
void updateMetric();
|
||||
|
||||
private:
|
||||
QPushButton *sidebar_alert_widget;
|
||||
QWidget *sidebar_widget;
|
||||
@@ -41,7 +47,9 @@ private:
|
||||
QStackedWidget *panel_widget;
|
||||
|
||||
// FrogPilot variables
|
||||
bool frogPilotTogglesOpen;
|
||||
bool parentToggleOpen;
|
||||
bool subParentToggleOpen;
|
||||
|
||||
int previousScrollPosition;
|
||||
};
|
||||
|
||||
@@ -56,10 +64,14 @@ signals:
|
||||
private slots:
|
||||
void poweroff();
|
||||
void reboot();
|
||||
void softreboot();
|
||||
void updateCalibDescription();
|
||||
|
||||
private:
|
||||
Params params;
|
||||
|
||||
// FrogPilot variables
|
||||
Params paramsMemory{"/dev/shm/params"};
|
||||
};
|
||||
|
||||
class TogglesPanel : public ListWidget {
|
||||
@@ -75,6 +87,9 @@ signals:
|
||||
public slots:
|
||||
void expandToggleDescription(const QString ¶m);
|
||||
|
||||
private slots:
|
||||
void updateState(const UIState &s);
|
||||
|
||||
private:
|
||||
Params params;
|
||||
std::map<std::string, ParamControl*> toggles;
|
||||
@@ -97,7 +112,6 @@ private:
|
||||
|
||||
QLabel *onroadLbl;
|
||||
LabelControl *versionLbl;
|
||||
ButtonControl *errorLogBtn;
|
||||
ButtonControl *installBtn;
|
||||
ButtonControl *downloadBtn;
|
||||
ButtonControl *targetBranchBtn;
|
||||
@@ -106,11 +120,6 @@ private:
|
||||
ParamWatcher *fs_watch;
|
||||
|
||||
// FrogPilot variables
|
||||
void automaticUpdate();
|
||||
|
||||
ButtonControl *updateTime;
|
||||
|
||||
int deviceShutdown;
|
||||
int schedule;
|
||||
int time;
|
||||
Params paramsMemory{"/dev/shm/params"};
|
||||
UIScene &scene;
|
||||
};
|
||||
|
||||
152
selfdrive/ui/qt/offroad/software_settings.cc
Executable file → Normal file
152
selfdrive/ui/qt/offroad/software_settings.cc
Executable file → Normal file
@@ -2,8 +2,6 @@
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <QDebug>
|
||||
@@ -18,13 +16,14 @@
|
||||
#include "selfdrive/ui/qt/widgets/input.h"
|
||||
#include "system/hardware/hw.h"
|
||||
|
||||
#include "selfdrive/frogpilot/ui/qt/widgets/frogpilot_controls.h"
|
||||
|
||||
void SoftwarePanel::checkForUpdates() {
|
||||
std::system("pkill -SIGUSR1 -f selfdrive.updated");
|
||||
std::system("pkill -SIGUSR1 -f selfdrive.updated.updated");
|
||||
}
|
||||
|
||||
SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
||||
onroadLbl = new QLabel(tr("Updates are only downloaded while the car is off."));
|
||||
SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent), scene(uiState()->scene) {
|
||||
onroadLbl = new QLabel(tr("Updates are only downloaded while the car is off or in park."));
|
||||
onroadLbl->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left; padding-top: 30px; padding-bottom: 30px;");
|
||||
addItem(onroadLbl);
|
||||
|
||||
@@ -32,40 +31,17 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
||||
versionLbl = new LabelControl(tr("Current Version"), "");
|
||||
addItem(versionLbl);
|
||||
|
||||
// update scheduler
|
||||
std::vector<QString> scheduleOptions{tr("Manually"), tr("Daily"), tr("Weekly")};
|
||||
ButtonParamControl *preferredSchedule = new ButtonParamControl("UpdateSchedule", tr("Update Scheduler"),
|
||||
tr("Choose the update frequency for FrogPilot's automatic updates.\n\n"
|
||||
"This feature will handle the download, installation, and device reboot for a seamless 'Set and Forget' experience.\n\n"
|
||||
"Weekly updates start at midnight every Sunday."),
|
||||
"",
|
||||
scheduleOptions);
|
||||
schedule = params.getInt("UpdateSchedule");
|
||||
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);
|
||||
}
|
||||
// automatic updates toggle
|
||||
ParamControl *automaticUpdatesToggle = new ParamControl("AutomaticUpdates", tr("Automatically Update FrogPilot"),
|
||||
tr("FrogPilot will automatically update itself and it's assets when you're offroad and connected to Wi-Fi."), "");
|
||||
connect(automaticUpdatesToggle, &ToggleControl::toggleFlipped, [this]() {
|
||||
std::thread([this]() {
|
||||
paramsMemory.putBool("FrogPilotTogglesUpdated", true);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
paramsMemory.putBool("FrogPilotTogglesUpdated", false);
|
||||
}).detach();
|
||||
});
|
||||
time = params.getInt("UpdateTime");
|
||||
deviceShutdown = params.getInt("DeviceShutdown") * 3600;
|
||||
updateTime->setValue(hours[time]);
|
||||
addItem(updateTime);
|
||||
addItem(automaticUpdatesToggle);
|
||||
|
||||
// download update btn
|
||||
downloadBtn = new ButtonControl(tr("Download"), tr("CHECK"));
|
||||
@@ -74,8 +50,9 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
||||
if (downloadBtn->text() == tr("CHECK")) {
|
||||
checkForUpdates();
|
||||
} else {
|
||||
std::system("pkill -SIGHUP -f selfdrive.updated");
|
||||
std::system("pkill -SIGHUP -f selfdrive.updated.updated");
|
||||
}
|
||||
paramsMemory.putBool("ManualUpdateInitiated", true);
|
||||
});
|
||||
addItem(downloadBtn);
|
||||
|
||||
@@ -92,6 +69,11 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
||||
connect(targetBranchBtn, &ButtonControl::clicked, [=]() {
|
||||
auto current = params.get("GitBranch");
|
||||
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"}) {
|
||||
auto i = branches.indexOf(b);
|
||||
if (i >= 0) {
|
||||
@@ -122,7 +104,7 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
||||
addItem(uninstallBtn);
|
||||
|
||||
// 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, [=]() {
|
||||
std::string txt = util::read_file("/data/community/crashes/error.txt");
|
||||
ConfirmationDialog::rich(QString::fromStdString(txt), this);
|
||||
@@ -131,8 +113,6 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
||||
|
||||
fs_watch = new ParamWatcher(this);
|
||||
QObject::connect(fs_watch, &ParamWatcher::paramChanged, [=](const QString ¶m_name, const QString ¶m_value) {
|
||||
schedule = params.getInt("UpdateSchedule");
|
||||
time = params.getInt("UpdateTime");
|
||||
updateLabels();
|
||||
});
|
||||
|
||||
@@ -141,8 +121,6 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
||||
updateLabels();
|
||||
});
|
||||
|
||||
QObject::connect(uiState(), &UIState::uiUpdate, this, &SoftwarePanel::automaticUpdate);
|
||||
|
||||
updateLabels();
|
||||
}
|
||||
|
||||
@@ -160,16 +138,15 @@ void SoftwarePanel::updateLabels() {
|
||||
fs_watch->addParam("UpdaterState");
|
||||
fs_watch->addParam("UpdateAvailable");
|
||||
|
||||
fs_watch->addParam("UpdateSchedule");
|
||||
fs_watch->addParam("UpdateTime");
|
||||
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// updater only runs offroad
|
||||
onroadLbl->setVisible(is_onroad);
|
||||
downloadBtn->setVisible(!is_onroad);
|
||||
// updater only runs offroad or when parked
|
||||
bool parked = scene.parked;
|
||||
|
||||
onroadLbl->setVisible(is_onroad && !parked);
|
||||
downloadBtn->setVisible(!is_onroad || parked);
|
||||
|
||||
// download update
|
||||
QString updater_state = QString::fromStdString(params.get("UpdaterState"));
|
||||
@@ -201,82 +178,9 @@ void SoftwarePanel::updateLabels() {
|
||||
versionLbl->setText(QString::fromStdString(params.get("UpdaterCurrentDescription")));
|
||||
versionLbl->setDescription(QString::fromStdString(params.get("UpdaterCurrentReleaseNotes")));
|
||||
|
||||
installBtn->setVisible(!is_onroad && params.getBool("UpdateAvailable"));
|
||||
installBtn->setVisible((!is_onroad || parked) && params.getBool("UpdateAvailable"));
|
||||
installBtn->setValue(QString::fromStdString(params.get("UpdaterNewDescription")));
|
||||
installBtn->setDescription(QString::fromStdString(params.get("UpdaterNewReleaseNotes")));
|
||||
|
||||
updateTime->setVisible(params.getInt("UpdateSchedule"));
|
||||
|
||||
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(¤tTimeT);
|
||||
|
||||
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
0
selfdrive/ui/qt/offroad/text_view.qml
Executable file → Normal file
1285
selfdrive/ui/qt/onroad.cc
Executable file → Normal file
1285
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
132
selfdrive/ui/qt/onroad.h
Executable file → Normal file
@@ -2,7 +2,8 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QElapsedTimer>
|
||||
#include <QMovie>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QStackedLayout>
|
||||
#include <QWidget>
|
||||
@@ -16,7 +17,6 @@
|
||||
const int btn_size = 192;
|
||||
const int img_size = (btn_size / 4) * 3;
|
||||
|
||||
// FrogPilot global variables
|
||||
static double fps;
|
||||
|
||||
// ***** onroad widgets *****
|
||||
@@ -42,13 +42,14 @@ class Compass : public QWidget {
|
||||
public:
|
||||
explicit Compass(QWidget *parent = nullptr);
|
||||
|
||||
void initializeStaticElements();
|
||||
void updateState(int bearing_deg);
|
||||
void updateState();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
UIScene &scene;
|
||||
|
||||
int bearingDeg;
|
||||
int circleOffset;
|
||||
int compassSize;
|
||||
@@ -56,10 +57,37 @@ private:
|
||||
int innerCompass;
|
||||
int x;
|
||||
int y;
|
||||
|
||||
QPixmap compassInnerImg;
|
||||
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 {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -78,14 +106,24 @@ private:
|
||||
bool engageable;
|
||||
|
||||
// FrogPilot variables
|
||||
Params paramsMemory{"/dev/shm/params"};
|
||||
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 rotatingWheel;
|
||||
bool treeFiddyRandomEventTriggered;
|
||||
bool weebRandomEventTriggered;
|
||||
|
||||
int steeringAngleDeg;
|
||||
int wheelIcon;
|
||||
int wheelIconGif;
|
||||
int y_offset;
|
||||
};
|
||||
|
||||
@@ -102,28 +140,25 @@ private:
|
||||
QPixmap settings_img;
|
||||
};
|
||||
|
||||
// FrogPilot buttons
|
||||
class PersonalityButton : public QPushButton {
|
||||
public:
|
||||
explicit PersonalityButton(QWidget *parent = 0);
|
||||
class PedalIcons : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
void checkUpdate();
|
||||
void handleClick();
|
||||
public:
|
||||
explicit PedalIcons(QWidget *parent = 0);
|
||||
void updateState();
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
Params params;
|
||||
Params paramsMemory{"/dev/shm/params"};
|
||||
QPixmap brake_pedal_img;
|
||||
QPixmap gas_pedal_img;
|
||||
|
||||
UIScene &scene;
|
||||
|
||||
int personalityProfile = 0;
|
||||
bool accelerating;
|
||||
bool decelerating;
|
||||
|
||||
QElapsedTimer transitionTimer;
|
||||
|
||||
QVector<std::pair<QPixmap, QString>> profile_data;
|
||||
float acceleration;
|
||||
};
|
||||
|
||||
// container window for the NVG UI
|
||||
@@ -163,79 +198,86 @@ private:
|
||||
bool wide_cam_requested = false;
|
||||
|
||||
// FrogPilot widgets
|
||||
void initializeFrogPilotWidgets();
|
||||
void paintFrogPilotWidgets(QPainter &p);
|
||||
void updateFrogPilotWidgets();
|
||||
|
||||
void drawLeadInfo(QPainter &p);
|
||||
void drawSLCConfirmation(QPainter &p);
|
||||
void drawStatusBar(QPainter &p);
|
||||
void drawTurnSignals(QPainter &p);
|
||||
void initializeFrogPilotWidgets();
|
||||
void updateFrogPilotWidgets(QPainter &p);
|
||||
|
||||
// FrogPilot variables
|
||||
Params paramsMemory{"/dev/shm/params"};
|
||||
|
||||
UIScene &scene;
|
||||
|
||||
Compass *compass_img;
|
||||
PersonalityButton *personality_btn;
|
||||
DistanceButton *distance_btn;
|
||||
PedalIcons *pedal_icons;
|
||||
ScreenRecorder *recorder_btn;
|
||||
|
||||
QHBoxLayout *bottom_layout;
|
||||
|
||||
bool accelerationPath;
|
||||
bool adjacentPath;
|
||||
bool alwaysOnLateral;
|
||||
bool alwaysOnLateralActive;
|
||||
bool bigMapOpen;
|
||||
bool blindSpotLeft;
|
||||
bool blindSpotRight;
|
||||
bool compass;
|
||||
bool conditionalExperimental;
|
||||
bool experimentalMode;
|
||||
bool hideSpeed;
|
||||
bool leadInfo;
|
||||
bool mapOpen;
|
||||
bool muteDM;
|
||||
bool onroadAdjustableProfiles;
|
||||
bool reverseCruise;
|
||||
bool onroadDistanceButton;
|
||||
bool roadNameUI;
|
||||
bool showDriverCamera;
|
||||
bool showAlwaysOnLateralStatusBar;
|
||||
bool showConditionalExperimentalStatusBar;
|
||||
bool showSLCOffset;
|
||||
bool slcOverridden;
|
||||
bool speedLimitController;
|
||||
bool trafficModeActive;
|
||||
bool turnSignalLeft;
|
||||
bool turnSignalRight;
|
||||
bool useSI;
|
||||
bool useViennaSLCSign;
|
||||
double maxAcceleration;
|
||||
bool vtscControllingCurve;
|
||||
|
||||
float cruiseAdjustment;
|
||||
float distanceConversion;
|
||||
float laneDetectionWidth;
|
||||
float laneWidthLeft;
|
||||
float laneWidthRight;
|
||||
float slcOverriddenSpeed;
|
||||
float slcSpeedLimit;
|
||||
float slcSpeedLimitOffset;
|
||||
int bearingDeg;
|
||||
float speedConversion;
|
||||
|
||||
int alertSize;
|
||||
int cameraView;
|
||||
int conditionalSpeed;
|
||||
int conditionalSpeedLead;
|
||||
int conditionalStatus;
|
||||
int currentHolidayTheme;
|
||||
int customColors;
|
||||
int customSignals;
|
||||
int desiredFollow;
|
||||
int obstacleDistance;
|
||||
int obstacleDistanceStock;
|
||||
int stoppedEquivalence;
|
||||
int totalFrames = 8;
|
||||
QTimer *animationTimer;
|
||||
|
||||
QString leadDistanceUnit;
|
||||
QString leadSpeedUnit;
|
||||
|
||||
size_t animationFrameIndex;
|
||||
|
||||
inline QColor greenColor(int alpha = 242) { return QColor(23, 134, 68, alpha); }
|
||||
|
||||
std::unordered_map<int, std::pair<QString, std::pair<QColor, std::map<double, QBrush>>>> themeConfiguration;
|
||||
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::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:
|
||||
void paintGL() override;
|
||||
void initializeGL() override;
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void updateFrameMat() override;
|
||||
void drawLaneLines(QPainter &painter, const UIState *s);
|
||||
void drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd);
|
||||
void drawLead(QPainter &painter, const cereal::ModelDataV2::LeadDataV3::Reader &lead_data, const QPointF &vd, const float v_ego);
|
||||
void drawHud(QPainter &p);
|
||||
void drawDriverState(QPainter &painter, const UIState *s);
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
@@ -270,6 +312,8 @@ private:
|
||||
|
||||
// FrogPilot variables
|
||||
UIScene &scene;
|
||||
Params params;
|
||||
Params paramsMemory{"/dev/shm/params"};
|
||||
|
||||
QPoint timeoutPoint = QPoint(420, 69);
|
||||
QTimer clickTimer;
|
||||
|
||||
20
selfdrive/ui/qt/python_helpers.py
Normal file
20
selfdrive/ui/qt/python_helpers.py
Normal 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
0
selfdrive/ui/qt/qt_window.cc
Executable file → Normal file
0
selfdrive/ui/qt/qt_window.h
Executable file → Normal file
0
selfdrive/ui/qt/qt_window.h
Executable file → Normal file
0
selfdrive/ui/qt/request_repeater.cc
Executable file → Normal file
0
selfdrive/ui/qt/request_repeater.cc
Executable file → Normal file
0
selfdrive/ui/qt/request_repeater.h
Executable file → Normal file
0
selfdrive/ui/qt/request_repeater.h
Executable file → Normal file
9
selfdrive/ui/qt/setup/reset.cc
Executable file → Normal file
9
selfdrive/ui/qt/setup/reset.cc
Executable file → Normal file
@@ -57,7 +57,7 @@ Reset::Reset(ResetMode mode, QWidget *parent) : QWidget(parent) {
|
||||
|
||||
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->setStyleSheet("font-size: 80px; font-weight: light;");
|
||||
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."));
|
||||
}
|
||||
|
||||
// automatically start if we're just finishing up an ABL reset
|
||||
if (mode == ResetMode::FORMAT) {
|
||||
startReset();
|
||||
}
|
||||
|
||||
setStyleSheet(R"(
|
||||
* {
|
||||
font-family: Inter;
|
||||
@@ -129,8 +124,6 @@ int main(int argc, char *argv[]) {
|
||||
if (argc > 1) {
|
||||
if (strcmp(argv[1], "--recover") == 0) {
|
||||
mode = ResetMode::RECOVER;
|
||||
} else if (strcmp(argv[1], "--format") == 0) {
|
||||
mode = ResetMode::FORMAT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1
selfdrive/ui/qt/setup/reset.h
Executable file → Normal file
1
selfdrive/ui/qt/setup/reset.h
Executable file → Normal file
@@ -5,7 +5,6 @@
|
||||
enum ResetMode {
|
||||
USER_RESET, // user initiated a factory reset from openpilot
|
||||
RECOVER, // userdata is corrupt for some reason, give a chance to recover
|
||||
FORMAT, // finish up an ABL factory reset
|
||||
};
|
||||
|
||||
class Reset : public QWidget {
|
||||
|
||||
122
selfdrive/ui/qt/setup/setup.cc
Executable file → Normal file
122
selfdrive/ui/qt/setup/setup.cc
Executable file → Normal file
@@ -20,7 +20,7 @@
|
||||
#include "selfdrive/ui/qt/widgets/input.h"
|
||||
|
||||
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) {
|
||||
FILE *fp = fopen(fname, "rb");
|
||||
@@ -201,20 +201,7 @@ QWidget * Setup::network_setup() {
|
||||
QPushButton *cont = new QPushButton();
|
||||
cont->setObjectName("navBtn");
|
||||
cont->setProperty("primary", true);
|
||||
QObject::connect(cont, &QPushButton::clicked, [=]() {
|
||||
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);
|
||||
}
|
||||
});
|
||||
QObject::connect(cont, &QPushButton::clicked, this, &Setup::nextPage);
|
||||
blayout->addWidget(cont);
|
||||
|
||||
// setup timer for testing internet connection
|
||||
@@ -229,11 +216,11 @@ QWidget * Setup::network_setup() {
|
||||
}
|
||||
repaint();
|
||||
});
|
||||
request->sendRequest(DASHCAM_URL);
|
||||
request->sendRequest(OPENPILOT_URL);
|
||||
QTimer *timer = new QTimer(this);
|
||||
QObject::connect(timer, &QTimer::timeout, [=]() {
|
||||
if (!request->active() && cont->isVisible()) {
|
||||
request->sendRequest(DASHCAM_URL);
|
||||
request->sendRequest(OPENPILOT_URL);
|
||||
}
|
||||
});
|
||||
timer->start(1000);
|
||||
@@ -241,6 +228,106 @@ QWidget * Setup::network_setup() {
|
||||
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 *widget = new QWidget();
|
||||
QVBoxLayout *main_layout = new QVBoxLayout(widget);
|
||||
@@ -326,6 +413,7 @@ Setup::Setup(QWidget *parent) : QStackedWidget(parent) {
|
||||
|
||||
addWidget(getting_started());
|
||||
addWidget(network_setup());
|
||||
addWidget(software_selection());
|
||||
|
||||
downloading_widget = downloading();
|
||||
addWidget(downloading_widget);
|
||||
|
||||
1
selfdrive/ui/qt/setup/setup.h
Executable file → Normal file
1
selfdrive/ui/qt/setup/setup.h
Executable file → Normal file
@@ -17,6 +17,7 @@ private:
|
||||
QWidget *low_voltage();
|
||||
QWidget *getting_started();
|
||||
QWidget *network_setup();
|
||||
QWidget *software_selection();
|
||||
QWidget *downloading();
|
||||
QWidget *download_failed(QLabel *url, QLabel *body);
|
||||
|
||||
|
||||
31
selfdrive/ui/qt/setup/updater.cc
Executable file → Normal file
31
selfdrive/ui/qt/setup/updater.cc
Executable file → Normal file
@@ -54,12 +54,7 @@ Updater::Updater(const QString &updater_path, const QString &manifest_path, QWid
|
||||
background-color: #3049F4;
|
||||
}
|
||||
)");
|
||||
|
||||
QObject::connect(connect, &QPushButton::clicked, [=]() {
|
||||
countdownTimer->stop();
|
||||
setCurrentWidget(wifi);
|
||||
});
|
||||
|
||||
QObject::connect(install, &QPushButton::clicked, this, &Updater::installUpdate);
|
||||
hlayout->addWidget(install);
|
||||
}
|
||||
|
||||
@@ -119,20 +114,6 @@ Updater::Updater(const QString &updater_path, const QString &manifest_path, QWid
|
||||
addWidget(wifi);
|
||||
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"(
|
||||
* {
|
||||
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() {
|
||||
setCurrentWidget(progress);
|
||||
QObject::connect(&proc, &QProcess::readyReadStandardOutput, this, &Updater::readProgress);
|
||||
|
||||
3
selfdrive/ui/qt/setup/updater.h
Executable file → Normal file
3
selfdrive/ui/qt/setup/updater.h
Executable file → Normal file
@@ -26,7 +26,4 @@ private:
|
||||
QProgressBar *bar;
|
||||
QPushButton *reboot;
|
||||
QWidget *prompt, *wifi, *progress;
|
||||
QTimer *countdownTimer;
|
||||
int countdownValue;
|
||||
|
||||
};
|
||||
|
||||
109
selfdrive/ui/qt/sidebar.cc
Executable file → Normal file
109
selfdrive/ui/qt/sidebar.cc
Executable file → Normal file
@@ -43,16 +43,40 @@ Sidebar::Sidebar(QWidget *parent) : QFrame(parent), onroad(false), flag_pressed(
|
||||
isCPU = params.getBool("ShowCPU");
|
||||
isGPU = params.getBool("ShowGPU");
|
||||
|
||||
isIP = params.getBool("ShowIP");
|
||||
|
||||
isMemoryUsage = params.getBool("ShowMemoryUsage");
|
||||
isStorageLeft = params.getBool("ShowStorageLeft");
|
||||
isStorageUsed = params.getBool("ShowStorageUsed");
|
||||
|
||||
isNumericalTemp = params.getBool("NumericalTemp");
|
||||
isFahrenheit = params.getBool("Fahrenheit");
|
||||
holidayThemeConfiguration = {
|
||||
{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 = {
|
||||
{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)}}},
|
||||
{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];
|
||||
flag_img = flag_imgs[scene.custom_icons];
|
||||
settings_img = settings_imgs[scene.custom_icons];
|
||||
|
||||
currentColors = themeConfiguration[scene.custom_colors].second;
|
||||
}
|
||||
|
||||
@@ -78,35 +101,52 @@ void Sidebar::mousePressEvent(QMouseEvent *event) {
|
||||
// Declare the click boxes
|
||||
QRect cpuRect = {30, 496, 240, 126};
|
||||
QRect memoryRect = {30, 654, 240, 126};
|
||||
QRect networkRect = {30, 196, 240, 126};
|
||||
QRect tempRect = {30, 338, 240, 126};
|
||||
|
||||
static int showChip = 0;
|
||||
static int showMemory = 0;
|
||||
static int showNetwork = 0;
|
||||
static int showTemp = 0;
|
||||
|
||||
// Swap between the respective metrics upon tap
|
||||
if (cpuRect.contains(event->pos())) {
|
||||
showChip = (showChip + 1) % 3;
|
||||
|
||||
isCPU = (showChip == 1);
|
||||
isGPU = (showChip == 2);
|
||||
|
||||
params.putBoolNonBlocking("ShowCPU", isCPU);
|
||||
params.putBoolNonBlocking("ShowGPU", isGPU);
|
||||
|
||||
update();
|
||||
} else if (memoryRect.contains(event->pos())) {
|
||||
showMemory = (showMemory + 1) % 4;
|
||||
|
||||
isMemoryUsage = (showMemory == 1);
|
||||
isStorageLeft = (showMemory == 2);
|
||||
isStorageUsed = (showMemory == 3);
|
||||
|
||||
params.putBoolNonBlocking("ShowMemoryUsage", isMemoryUsage);
|
||||
params.putBoolNonBlocking("ShowStorageLeft", isStorageLeft);
|
||||
params.putBoolNonBlocking("ShowStorageUsed", isStorageUsed);
|
||||
|
||||
update();
|
||||
} else if (networkRect.contains(event->pos())) {
|
||||
showNetwork = (showNetwork + 1) % 2;
|
||||
isIP = (showNetwork == 1);
|
||||
params.putBoolNonBlocking("ShowIP", isIP);
|
||||
|
||||
update();
|
||||
} else if (tempRect.contains(event->pos())) {
|
||||
showTemp = (showTemp + 1) % 3;
|
||||
isNumericalTemp = (showTemp != 0);
|
||||
isFahrenheit = (showTemp == 2);
|
||||
params.putBoolNonBlocking("Fahrenheit", isFahrenheit);
|
||||
params.putBoolNonBlocking("NumericalTemp", isNumericalTemp);
|
||||
|
||||
scene.fahrenheit = showTemp == 2;
|
||||
scene.numerical_temp = showTemp != 0;
|
||||
|
||||
params.putBoolNonBlocking("Fahrenheit", showTemp == 2);
|
||||
params.putBoolNonBlocking("NumericalTemp", showTemp != 0);
|
||||
|
||||
update();
|
||||
} else if (onroad && home_btn.contains(event->pos())) {
|
||||
flag_pressed = true;
|
||||
@@ -147,17 +187,24 @@ void Sidebar::updateState(const UIState &s) {
|
||||
setProperty("netStrength", strength > 0 ? strength + 1 : 0);
|
||||
|
||||
// FrogPilot properties
|
||||
if (scene.current_holiday_theme != 0) {
|
||||
home_img = holiday_home_imgs[scene.current_holiday_theme];
|
||||
flag_img = holiday_flag_imgs[scene.current_holiday_theme];
|
||||
settings_img = holiday_settings_imgs[scene.current_holiday_theme];
|
||||
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();
|
||||
|
||||
bool isNumericalTemp = scene.numerical_temp;
|
||||
|
||||
int maxTempC = deviceState.getMaxTempC();
|
||||
QString max_temp = isFahrenheit ? QString::number(maxTempC * 9 / 5 + 32) + "°F" : QString::number(maxTempC) + "°C";
|
||||
QColor theme_color = currentColors[0];
|
||||
QString max_temp = scene.fahrenheit ? QString::number(maxTempC * 9 / 5 + 32) + "°F" : QString::number(maxTempC) + "°C";
|
||||
|
||||
// FrogPilot metrics
|
||||
if (isCPU || isGPU) {
|
||||
@@ -171,11 +218,11 @@ void Sidebar::updateState(const UIState &s) {
|
||||
QString metric = isGPU ? gpu : cpu;
|
||||
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) {
|
||||
cpuStatus = {{tr(isGPU ? "GPU" : "CPU"), metric}, danger_color};
|
||||
cpuStatus = {{isGPU ? tr("GPU") : tr("CPU"), metric}, danger_color};
|
||||
} 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));
|
||||
}
|
||||
@@ -186,10 +233,10 @@ void Sidebar::updateState(const UIState &s) {
|
||||
int storage_used = frogpilotDeviceState.getUsedSpace();
|
||||
|
||||
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) {
|
||||
ItemStatus memoryStatus = {{tr("MEMORY"), memory}, theme_color};
|
||||
ItemStatus memoryStatus = {{tr("MEMORY"), memory}, currentColors[0]};
|
||||
if (memory_usage >= 85) {
|
||||
memoryStatus = {{tr("MEMORY"), memory}, danger_color};
|
||||
} else if (memory_usage >= 70) {
|
||||
@@ -197,11 +244,11 @@ void Sidebar::updateState(const UIState &s) {
|
||||
}
|
||||
setProperty("memoryStatus", QVariant::fromValue(memoryStatus));
|
||||
} else {
|
||||
ItemStatus storageStatus = {{tr(isStorageLeft ? "LEFT" : "USED"), storage}, theme_color};
|
||||
if (10 <= storage_left && storage_left < 25) {
|
||||
storageStatus = {{tr(isStorageLeft ? "LEFT" : "USED"), storage}, warning_color};
|
||||
} else if (storage_left < 10) {
|
||||
storageStatus = {{tr(isStorageLeft ? "LEFT" : "USED"), storage}, danger_color};
|
||||
ItemStatus storageStatus = {{isStorageLeft ? tr("LEFT") : tr("USED"), storage}, currentColors[0]};
|
||||
if (25 > storage_left && storage_left >= 10) {
|
||||
storageStatus = {{isStorageLeft ? tr("LEFT") : tr("USED"), storage}, warning_color};
|
||||
} else if (10 > storage_left) {
|
||||
storageStatus = {{isStorageLeft ? tr("LEFT") : tr("USED"), storage}, danger_color};
|
||||
}
|
||||
setProperty("storageStatus", QVariant::fromValue(storageStatus));
|
||||
}
|
||||
@@ -213,7 +260,7 @@ void Sidebar::updateState(const UIState &s) {
|
||||
connectStatus = ItemStatus{{tr("CONNECT"), tr("OFFLINE")}, warning_color};
|
||||
} else {
|
||||
connectStatus = nanos_since_boot() - last_ping < 80e9
|
||||
? ItemStatus{{tr("CONNECT"), tr("ONLINE")}, theme_color}
|
||||
? ItemStatus{{tr("CONNECT"), tr("ONLINE")}, currentColors[0]}
|
||||
: ItemStatus{{tr("CONNECT"), tr("ERROR")}, danger_color};
|
||||
}
|
||||
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};
|
||||
auto ts = deviceState.getThermalStatus();
|
||||
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) {
|
||||
tempStatus = {{tr("TEMP"), isNumericalTemp ? max_temp : tr("OK")}, warning_color};
|
||||
}
|
||||
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) {
|
||||
pandaStatus = {{tr("NO"), tr("PANDA")}, danger_color};
|
||||
} else if (s.scene.started && !sm["liveLocationKalman"].getLiveLocationKalman().getGpsOK()) {
|
||||
@@ -253,14 +300,24 @@ void Sidebar::paintEvent(QPaintEvent *event) {
|
||||
// network
|
||||
int x = 58;
|
||||
const QColor gray(0x54, 0x54, 0x54);
|
||||
p.setFont(InterFont(35));
|
||||
|
||||
if (isIP) {
|
||||
p.setPen(QColor(0xff, 0xff, 0xff));
|
||||
p.save();
|
||||
p.setFont(InterFont(30));
|
||||
QRect ipBox = QRect(50, 196, 225, 27);
|
||||
p.drawText(ipBox, Qt::AlignLeft | Qt::AlignVCenter, uiState()->wifi->getIp4Address());
|
||||
p.restore();
|
||||
} else {
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
p.setBrush(i < net_strength ? Qt::white : gray);
|
||||
p.drawEllipse(x, 196, 27, 27);
|
||||
x += 37;
|
||||
}
|
||||
|
||||
p.setFont(InterFont(35));
|
||||
p.setPen(QColor(0xff, 0xff, 0xff));
|
||||
}
|
||||
|
||||
const QRect r = QRect(50, 247, 100, 50);
|
||||
p.drawText(r, Qt::AlignCenter, net_type);
|
||||
|
||||
|
||||
21
selfdrive/ui/qt/sidebar.h
Executable file → Normal file
21
selfdrive/ui/qt/sidebar.h
Executable file → Normal file
@@ -71,17 +71,22 @@ private:
|
||||
|
||||
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, QPixmap> flag_imgs;
|
||||
std::unordered_map<int, QPixmap> home_imgs;
|
||||
std::unordered_map<int, QPixmap> settings_imgs;
|
||||
std::vector<QColor> currentColors;
|
||||
|
||||
bool isCPU;
|
||||
bool isFahrenheit;
|
||||
bool isGPU;
|
||||
bool isMemoryUsage;
|
||||
bool isNumericalTemp;
|
||||
bool isStorageLeft;
|
||||
bool isStorageUsed;
|
||||
std::unordered_map<int, std::pair<QString, std::vector<QColor>>> holidayThemeConfiguration;
|
||||
std::unordered_map<int, QPixmap> holiday_flag_imgs;
|
||||
std::unordered_map<int, QPixmap> holiday_home_imgs;
|
||||
std::unordered_map<int, QPixmap> holiday_settings_imgs;
|
||||
|
||||
std::vector<QColor> currentColors;
|
||||
};
|
||||
|
||||
2
selfdrive/ui/qt/spinner.cc
Executable file → Normal file
2
selfdrive/ui/qt/spinner.cc
Executable file → Normal file
@@ -88,7 +88,7 @@ Spinner::Spinner(QWidget *parent) : QWidget(parent) {
|
||||
}
|
||||
QProgressBar::chunk {
|
||||
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
0
selfdrive/ui/qt/spinner.h
Executable file → Normal file
48
selfdrive/ui/qt/text.cc
Executable file → Normal file
48
selfdrive/ui/qt/text.cc
Executable file → Normal file
@@ -4,32 +4,11 @@
|
||||
#include <QScrollBar>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <QProcess>
|
||||
|
||||
#include "system/hardware/hw.h"
|
||||
#include "selfdrive/ui/qt/util.h"
|
||||
#include "selfdrive/ui/qt/qt_window.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[]) {
|
||||
initApp(argc, argv);
|
||||
@@ -52,36 +31,11 @@ int main(int argc, char *argv[]) {
|
||||
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();
|
||||
#ifdef __aarch64__
|
||||
btn->setText(QObject::tr("Reboot"));
|
||||
QObject::connect(btn, &QPushButton::clicked, [=]() {
|
||||
Hardware::reboot(); // bbot this is the dreaded crash reboot button
|
||||
Hardware::reboot();
|
||||
});
|
||||
#else
|
||||
btn->setText(QObject::tr("Exit"));
|
||||
|
||||
8
selfdrive/ui/qt/util.cc
Executable file → Normal file
8
selfdrive/ui/qt/util.cc
Executable file → Normal file
@@ -5,6 +5,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QHash>
|
||||
@@ -25,7 +26,7 @@ QString getVersion() {
|
||||
}
|
||||
|
||||
QString getBrand() {
|
||||
return Params().getBool("Passive") ? QObject::tr("dashcam") : QObject::tr("OscarPilot");
|
||||
return QObject::tr("FrogPilot");
|
||||
}
|
||||
|
||||
QString getUserAgent() {
|
||||
@@ -114,6 +115,11 @@ void initApp(int argc, char *argv[], bool disable_hidpi) {
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
0
selfdrive/ui/qt/util.h
Executable file → Normal file
0
selfdrive/ui/qt/util.h
Executable file → Normal file
3
selfdrive/ui/qt/widgets/cameraview.cc
Executable file → Normal file
3
selfdrive/ui/qt/widgets/cameraview.cc
Executable file → Normal file
@@ -41,6 +41,8 @@ const char frame_fragment_shader[] =
|
||||
"out vec4 colorOut;\n"
|
||||
"void main() {\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";
|
||||
#else
|
||||
#ifdef __APPLE__
|
||||
@@ -217,7 +219,6 @@ void CameraWidget::updateFrameMat() {
|
||||
if (active_stream_type == VISION_STREAM_DRIVER) {
|
||||
if (stream_width > 0 && stream_height > 0) {
|
||||
frame_mat = get_driver_view_transform(w, h, stream_width, stream_height);
|
||||
frame_mat.v[0] *= -1.0;
|
||||
}
|
||||
} else {
|
||||
// Project point at "infinity" to compute x and y offsets
|
||||
|
||||
0
selfdrive/ui/qt/widgets/cameraview.h
Executable file → Normal file
0
selfdrive/ui/qt/widgets/cameraview.h
Executable file → Normal file
0
selfdrive/ui/qt/widgets/controls.cc
Executable file → Normal file
0
selfdrive/ui/qt/widgets/controls.cc
Executable file → Normal file
27
selfdrive/ui/qt/widgets/controls.h
Executable file → Normal file
27
selfdrive/ui/qt/widgets/controls.h
Executable file → Normal file
@@ -127,15 +127,15 @@ public:
|
||||
QObject::connect(&toggle, &Toggle::stateChanged, this, &ToggleControl::toggleFlipped);
|
||||
}
|
||||
|
||||
void setVisualOn() {
|
||||
toggle.togglePosition();
|
||||
}
|
||||
|
||||
void setEnabled(bool enabled) {
|
||||
toggle.setEnabled(enabled);
|
||||
toggle.update();
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
toggle.togglePosition();
|
||||
}
|
||||
|
||||
signals:
|
||||
void toggleFlipped(bool state);
|
||||
|
||||
@@ -206,7 +206,7 @@ public:
|
||||
background-color: #4a4a4a;
|
||||
}
|
||||
QPushButton:checked:enabled {
|
||||
background-color: #0048FF;
|
||||
background-color: #33Ab4C;
|
||||
}
|
||||
QPushButton:disabled {
|
||||
color: #33E4E4E4;
|
||||
@@ -227,10 +227,8 @@ public:
|
||||
button_group->addButton(button, i);
|
||||
}
|
||||
|
||||
QObject::connect(button_group, QOverload<int, bool>::of(&QButtonGroup::buttonToggled), [=](int id, bool checked) {
|
||||
if (checked) {
|
||||
QObject::connect(button_group, QOverload<int>::of(&QButtonGroup::buttonClicked), [=](int 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:
|
||||
std::string key;
|
||||
Params params;
|
||||
|
||||
38
selfdrive/ui/qt/widgets/drive_stats.cc
Executable file → Normal file
38
selfdrive/ui/qt/widgets/drive_stats.cc
Executable file → Normal file
@@ -5,7 +5,6 @@
|
||||
#include <QJsonObject>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "common/params.h"
|
||||
#include "selfdrive/ui/qt/request_repeater.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) {
|
||||
metric_ = Params().getBool("IsMetric");
|
||||
metric_ = params.getBool("IsMetric");
|
||||
|
||||
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;
|
||||
grid_layout->setVerticalSpacing(10);
|
||||
grid_layout->setContentsMargins(0, 10, 0, 10);
|
||||
|
||||
int row = 0;
|
||||
grid_layout->addWidget(newLabel(title, "title"), row++, 0, 1, 3);
|
||||
grid_layout->addItem(new QSpacerItem(0, 50), row++, 0, 1, 1);
|
||||
grid_layout->addWidget(newLabel(title, FrogPilot ? "frogpilot_title" : "title"), row++, 0, 1, 3);
|
||||
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.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);
|
||||
|
||||
main_layout->addLayout(grid_layout);
|
||||
main_layout->addStretch(1);
|
||||
};
|
||||
|
||||
add_stats_layouts(tr("ALL TIME"), all_);
|
||||
main_layout->addStretch();
|
||||
add_stats_layouts(tr("PAST WEEK"), week_);
|
||||
add_stats_layouts(tr("FROGPILOT"), frogPilot_, true);
|
||||
|
||||
if (auto dongleId = getDongleId()) {
|
||||
QString url = CommaApi::BASE_URL + "/v1.1/devices/" + *dongleId + "/stats";
|
||||
@@ -57,13 +57,25 @@ DriveStats::DriveStats(QWidget* parent) : QFrame(parent) {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
QLabel[type="title"] { font-size: 51px; font-weight: 500; }
|
||||
QLabel[type="number"] { font-size: 78px; font-weight: 500; }
|
||||
QLabel[type="unit"] { font-size: 51px; font-weight: 300; color: #A0A0A0; }
|
||||
QLabel[type="title"] { font-size: 50px; font-weight: 500; }
|
||||
QLabel[type="frogpilot_title"] { font-size: 50px; font-weight: 500; color: #178643; }
|
||||
QLabel[type="number"] { font-size: 65px; font-weight: 400; }
|
||||
QLabel[type="unit"] { font-size: 50px; font-weight: 300; color: #A0A0A0; }
|
||||
)");
|
||||
}
|
||||
|
||||
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) {
|
||||
labels.routes->setText(QString::number((int)obj["routes"].toDouble()));
|
||||
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)));
|
||||
};
|
||||
|
||||
QJsonObject json = stats_.object();
|
||||
update(json["all"].toObject(), all_);
|
||||
update(json["week"].toObject(), week_);
|
||||
}
|
||||
@@ -89,9 +100,6 @@ void DriveStats::parseResponse(const QString& response, bool success) {
|
||||
}
|
||||
|
||||
void DriveStats::showEvent(QShowEvent* event) {
|
||||
bool metric = Params().getBool("IsMetric");
|
||||
if (metric_ != metric) {
|
||||
metric_ = metric;
|
||||
metric_ = params.getBool("IsMetric");
|
||||
updateStats();
|
||||
}
|
||||
}
|
||||
|
||||
6
selfdrive/ui/qt/widgets/drive_stats.h
Executable file → Normal file
6
selfdrive/ui/qt/widgets/drive_stats.h
Executable file → Normal file
@@ -3,6 +3,8 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QLabel>
|
||||
|
||||
#include "common/params.h"
|
||||
|
||||
class DriveStats : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -15,10 +17,12 @@ private:
|
||||
inline QString getDistanceUnit() const { return metric_ ? tr("KM") : tr("Miles"); }
|
||||
|
||||
bool metric_;
|
||||
Params params;
|
||||
Params paramsStorage{"/persist/params"};
|
||||
QJsonDocument stats_;
|
||||
struct StatsLabels {
|
||||
QLabel *routes, *distance, *distance_unit, *hours;
|
||||
} all_, week_;
|
||||
} all_, week_, frogPilot_;
|
||||
|
||||
private slots:
|
||||
void parseResponse(const QString &response, bool success);
|
||||
|
||||
0
selfdrive/ui/qt/widgets/input.cc
Executable file → Normal file
0
selfdrive/ui/qt/widgets/input.cc
Executable file → Normal file
0
selfdrive/ui/qt/widgets/input.h
Executable file → Normal file
0
selfdrive/ui/qt/widgets/input.h
Executable file → Normal file
0
selfdrive/ui/qt/widgets/keyboard.cc
Executable file → Normal file
0
selfdrive/ui/qt/widgets/keyboard.cc
Executable file → Normal file
0
selfdrive/ui/qt/widgets/keyboard.h
Executable file → Normal file
0
selfdrive/ui/qt/widgets/keyboard.h
Executable file → Normal file
8
selfdrive/ui/qt/widgets/offroad_alerts.cc
Executable file → Normal file
8
selfdrive/ui/qt/widgets/offroad_alerts.cc
Executable file → Normal file
@@ -37,13 +37,9 @@ AbstractAlert::AbstractAlert(bool hasRebootBtn, QWidget *parent) : QFrame(parent
|
||||
disable_check_btn->setFixedSize(625, 125);
|
||||
footer_layout->addWidget(disable_check_btn, 1, Qt::AlignBottom | Qt::AlignCenter);
|
||||
QObject::connect(disable_check_btn, &QPushButton::clicked, [=]() {
|
||||
if (!params.getBool("FireTheBabysitter")) {
|
||||
params.putBool("FireTheBabysitter", true);
|
||||
}
|
||||
if (!params.getBool("OfflineMode")) {
|
||||
params.putBool("SnoozeUpdate", true);
|
||||
params.putBool("DeviceManagement", true);
|
||||
params.putBool("OfflineMode", true);
|
||||
}
|
||||
Hardware::reboot();
|
||||
});
|
||||
QObject::connect(disable_check_btn, &QPushButton::clicked, this, &AbstractAlert::dismiss);
|
||||
disable_check_btn->setStyleSheet(R"(color: white; background-color: #4F4F4F;)");
|
||||
|
||||
0
selfdrive/ui/qt/widgets/offroad_alerts.h
Executable file → Normal file
0
selfdrive/ui/qt/widgets/offroad_alerts.h
Executable file → Normal file
0
selfdrive/ui/qt/widgets/prime.cc
Executable file → Normal file
0
selfdrive/ui/qt/widgets/prime.cc
Executable file → Normal file
0
selfdrive/ui/qt/widgets/prime.h
Executable file → Normal file
0
selfdrive/ui/qt/widgets/prime.h
Executable file → Normal file
0
selfdrive/ui/qt/widgets/scrollview.cc
Executable file → Normal file
0
selfdrive/ui/qt/widgets/scrollview.cc
Executable file → Normal file
1
selfdrive/ui/qt/widgets/scrollview.h
Executable file → Normal file
1
selfdrive/ui/qt/widgets/scrollview.h
Executable file → Normal file
@@ -10,6 +10,7 @@ public:
|
||||
|
||||
// FrogPilot functions
|
||||
void restorePosition(int previousScrollPosition);
|
||||
|
||||
protected:
|
||||
void hideEvent(QHideEvent *e) override;
|
||||
};
|
||||
|
||||
0
selfdrive/ui/qt/widgets/ssh_keys.cc
Executable file → Normal file
0
selfdrive/ui/qt/widgets/ssh_keys.cc
Executable file → Normal file
0
selfdrive/ui/qt/widgets/ssh_keys.h
Executable file → Normal file
0
selfdrive/ui/qt/widgets/ssh_keys.h
Executable file → Normal file
2
selfdrive/ui/qt/widgets/toggle.cc
Executable file → Normal file
2
selfdrive/ui/qt/widgets/toggle.cc
Executable file → Normal file
@@ -75,7 +75,7 @@ void Toggle::setEnabled(bool value) {
|
||||
enabled = value;
|
||||
if (value) {
|
||||
circleColor.setRgb(0xfafafa);
|
||||
green.setRgb(0x0048FF);
|
||||
green.setRgb(0x33ab4c);
|
||||
} else {
|
||||
circleColor.setRgb(0x888888);
|
||||
green.setRgb(0x227722);
|
||||
|
||||
0
selfdrive/ui/qt/widgets/toggle.h
Executable file → Normal file
0
selfdrive/ui/qt/widgets/toggle.h
Executable file → Normal file
31
selfdrive/ui/qt/widgets/wifi.cc
Executable file → Normal file
31
selfdrive/ui/qt/widgets/wifi.cc
Executable file → Normal file
@@ -81,6 +81,34 @@ WiFiPromptWidget::WiFiPromptWidget(QWidget *parent) : QFrame(parent) {
|
||||
}
|
||||
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"(
|
||||
WiFiPromptWidget {
|
||||
background-color: #333333;
|
||||
@@ -99,5 +127,6 @@ void WiFiPromptWidget::updateState(const UIState &s) {
|
||||
auto network_type = sm["deviceState"].getDeviceState().getNetworkType();
|
||||
auto uploading = network_type == cereal::DeviceState::NetworkType::WIFI ||
|
||||
network_type == cereal::DeviceState::NetworkType::ETHERNET;
|
||||
stack->setCurrentIndex(uploading ? 1 : 0);
|
||||
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
4
selfdrive/ui/qt/widgets/wifi.h
Executable file → Normal file
@@ -18,6 +18,10 @@ signals:
|
||||
public slots:
|
||||
void updateState(const UIState &s);
|
||||
|
||||
private:
|
||||
// FrogPilot variables
|
||||
Params params;
|
||||
|
||||
protected:
|
||||
QStackedLayout *stack;
|
||||
};
|
||||
|
||||
37
selfdrive/ui/qt/window.cc
Executable file → Normal file
37
selfdrive/ui/qt/window.cc
Executable file → Normal file
@@ -13,14 +13,14 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
|
||||
QObject::connect(homeWindow, &HomeWindow::openSettings, this, &MainWindow::openSettings);
|
||||
QObject::connect(homeWindow, &HomeWindow::closeSettings, this, &MainWindow::closeSettings);
|
||||
|
||||
oscarSettingsWindow = new OscarSettingsWindow(this);
|
||||
main_layout->addWidget(oscarSettingsWindow);
|
||||
QObject::connect(oscarSettingsWindow, &OscarSettingsWindow::closeSettings, this, &MainWindow::closeSettings);
|
||||
// QObject::connect(oscarSettingsWindow, &OscarSettingsWindow::reviewTrainingGuide, [=]() {
|
||||
// onboardingWindow->showTrainingGuide();
|
||||
// main_layout->setCurrentWidget(onboardingWindow);
|
||||
// });
|
||||
QObject::connect(oscarSettingsWindow, &OscarSettingsWindow::showDriverView, [=] {
|
||||
settingsWindow = new SettingsWindow(this);
|
||||
main_layout->addWidget(settingsWindow);
|
||||
QObject::connect(settingsWindow, &SettingsWindow::closeSettings, this, &MainWindow::closeSettings);
|
||||
QObject::connect(settingsWindow, &SettingsWindow::reviewTrainingGuide, [=]() {
|
||||
onboardingWindow->showTrainingGuide();
|
||||
main_layout->setCurrentWidget(onboardingWindow);
|
||||
});
|
||||
QObject::connect(settingsWindow, &SettingsWindow::showDriverView, [=] {
|
||||
homeWindow->showDriverView(true);
|
||||
});
|
||||
|
||||
@@ -38,11 +38,11 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
|
||||
closeSettings();
|
||||
}
|
||||
});
|
||||
// QObject::connect(device(), &Device::interactiveTimeout, [=]() {
|
||||
// if (main_layout->currentWidget() == oscarSettingsWindow) {
|
||||
// closeSettings();
|
||||
// }
|
||||
// });
|
||||
QObject::connect(device(), &Device::interactiveTimeout, [=]() {
|
||||
if (main_layout->currentWidget() == settingsWindow) {
|
||||
closeSettings();
|
||||
}
|
||||
});
|
||||
|
||||
// load fonts
|
||||
QFontDatabase::addApplicationFont("../assets/fonts/Inter-Black.ttf");
|
||||
@@ -66,8 +66,8 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
|
||||
}
|
||||
|
||||
void MainWindow::openSettings(int index, const QString ¶m) {
|
||||
main_layout->setCurrentWidget(oscarSettingsWindow);
|
||||
oscarSettingsWindow->setCurrentPanel(index, param);
|
||||
main_layout->setCurrentWidget(settingsWindow);
|
||||
settingsWindow->setCurrentPanel(index, param);
|
||||
}
|
||||
|
||||
void MainWindow::closeSettings() {
|
||||
@@ -93,12 +93,7 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
|
||||
case QEvent::MouseMove: {
|
||||
// ignore events when device is awakened by resetInteractiveTimeout
|
||||
ignore = !device()->isAwake();
|
||||
// if (main_layout->currentWidget() == oscarSettingsWindow) {
|
||||
// Not working...
|
||||
// device()->resetInteractiveTimeout(60 * 5); // 5 minute timeout if looking at settings window
|
||||
// } else {
|
||||
device()->resetInteractiveTimeout(); // Default 30 seconds otherwise
|
||||
// }
|
||||
device()->resetInteractiveTimeout(uiState()->scene.screen_timeout, uiState()->scene.screen_timeout_onroad);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
3
selfdrive/ui/qt/window.h
Executable file → Normal file
3
selfdrive/ui/qt/window.h
Executable file → Normal file
@@ -6,7 +6,6 @@
|
||||
#include "selfdrive/ui/qt/home.h"
|
||||
#include "selfdrive/ui/qt/offroad/onboarding.h"
|
||||
#include "selfdrive/ui/qt/offroad/settings.h"
|
||||
#include "selfdrive/oscarpilot/settings/settings.h"
|
||||
|
||||
class MainWindow : public QWidget {
|
||||
Q_OBJECT
|
||||
@@ -21,7 +20,7 @@ private:
|
||||
|
||||
QStackedLayout *main_layout;
|
||||
HomeWindow *homeWindow;
|
||||
OscarSettingsWindow *oscarSettingsWindow;
|
||||
SettingsWindow *settingsWindow;
|
||||
OnboardingWindow *onboardingWindow;
|
||||
|
||||
// FrogPilot variables
|
||||
|
||||
106
selfdrive/ui/soundd.py
Executable file → Normal file
106
selfdrive/ui/soundd.py
Executable file → Normal file
@@ -1,10 +1,9 @@
|
||||
import math
|
||||
import numpy as np
|
||||
import os
|
||||
import time
|
||||
import threading
|
||||
import wave
|
||||
|
||||
from typing import Dict, Optional, Tuple
|
||||
|
||||
from cereal import car, messaging
|
||||
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
|
||||
|
||||
|
||||
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.engage: ("engage.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.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):
|
||||
controls_missing = time.monotonic() - sm.rcv_time['controlsState']
|
||||
controls_missing = time.monotonic() - sm.recv_time['controlsState']
|
||||
|
||||
if controls_missing > CONTROLS_TIMEOUT:
|
||||
if sm['controlsState'].enabled and (controls_missing - CONTROLS_TIMEOUT) < 10:
|
||||
@@ -61,9 +70,20 @@ class Soundd:
|
||||
self.params = 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_volume = MIN_VOLUME
|
||||
@@ -74,13 +94,22 @@ class Soundd:
|
||||
self.spl_filter_weighted = FirstOrderFilter(0, 2.5, FILTER_DT, initialized=False)
|
||||
|
||||
def load_sounds(self):
|
||||
self.loaded_sounds: Dict[int, np.ndarray] = {}
|
||||
self.loaded_sounds: dict[int, np.ndarray] = {}
|
||||
|
||||
# Load all sounds
|
||||
for sound in sound_list:
|
||||
if sound == AudibleAlert.goat and not self.goat_scream:
|
||||
continue
|
||||
|
||||
filename, play_count, volume = sound_list[sound]
|
||||
|
||||
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.getsampwidth() == 2
|
||||
@@ -156,9 +185,15 @@ class Soundd:
|
||||
while True:
|
||||
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.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)
|
||||
|
||||
@@ -168,27 +203,66 @@ class Soundd:
|
||||
|
||||
# Update FrogPilot parameters
|
||||
if self.params_memory.get_bool("FrogPilotTogglesUpdated"):
|
||||
updateFrogPilotParams = threading.Thread(target=self.update_frogpilot_params)
|
||||
updateFrogPilotParams.start()
|
||||
self.update_frogpilot_params()
|
||||
|
||||
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_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 = {
|
||||
0: "stock",
|
||||
1: "frog_theme",
|
||||
2: "tesla_theme",
|
||||
3: "stalin_theme"
|
||||
}
|
||||
|
||||
theme_name = theme_configuration.get(custom_sounds, "stock")
|
||||
self.sound_directory = (f"{BASEDIR}/selfdrive/frogpilot/assets/custom_themes/{theme_name}/sounds/" if custom_sounds else f"{BASEDIR}/selfdrive/assets/sounds/")
|
||||
holiday_themes = custom_theme and self.params.get_bool("HolidayThemes")
|
||||
current_holiday_theme = self.params_memory.get_int("CurrentHolidayTheme") if holiday_themes else 0
|
||||
|
||||
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():
|
||||
s = Soundd()
|
||||
s.soundd_thread()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ -f /TICI ] && [ ! -f qt/spinner ]; then
|
||||
cp qt/spinner_larch64 qt/spinner
|
||||
if [ -f /TICI ] && [ ! -f _spinner ]; then
|
||||
cp qt/spinner_larch64 _spinner
|
||||
fi
|
||||
|
||||
exec ./qt/spinner "$1"
|
||||
exec ./_spinner "$1"
|
||||
|
||||
6
selfdrive/ui/tests/.gitignore
vendored
Normal file
6
selfdrive/ui/tests/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
test
|
||||
playsound
|
||||
test_sound
|
||||
test_translations
|
||||
ui_snapshot
|
||||
test_ui/report
|
||||
0
selfdrive/ui/tests/__init__.py
Normal file
0
selfdrive/ui/tests/__init__.py
Normal file
22
selfdrive/ui/tests/body.py
Normal file
22
selfdrive/ui/tests/body.py
Normal 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)
|
||||
18
selfdrive/ui/tests/create_test_translations.sh
Normal file
18
selfdrive/ui/tests/create_test_translations.sh
Normal 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
|
||||
36
selfdrive/ui/tests/cycle_offroad_alerts.py
Normal file
36
selfdrive/ui/tests/cycle_offroad_alerts.py
Normal 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)
|
||||
30
selfdrive/ui/tests/playsound.cc
Normal file
30
selfdrive/ui/tests/playsound.cc
Normal 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();
|
||||
}
|
||||
25
selfdrive/ui/tests/test_runner.cc
Normal file
25
selfdrive/ui/tests/test_runner.cc
Normal 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);
|
||||
}
|
||||
41
selfdrive/ui/tests/test_soundd.py
Normal file
41
selfdrive/ui/tests/test_soundd.py
Normal 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()
|
||||
48
selfdrive/ui/tests/test_translations.cc
Normal file
48
selfdrive/ui/tests/test_translations.cc
Normal 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
Reference in New Issue
Block a user