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.*
|
translations/main_test_en.*
|
||||||
|
|
||||||
|
_text
|
||||||
|
_spinner
|
||||||
|
|
||||||
ui
|
ui
|
||||||
|
mui
|
||||||
watch3
|
watch3
|
||||||
installer/installers/*
|
installer/installers/*
|
||||||
qt/text
|
|
||||||
qt/spinner
|
|
||||||
qt/setup/setup
|
qt/setup/setup
|
||||||
qt/setup/reset
|
qt/setup/reset
|
||||||
qt/setup/wifi
|
qt/setup/wifi
|
||||||
|
|||||||
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']
|
maps = arch in ['larch64', 'aarch64', 'x86_64']
|
||||||
|
|
||||||
if maps and arch != 'larch64':
|
|
||||||
rpath = [Dir(f"#third_party/mapbox-gl-native-qt/{arch}").srcnode().abspath]
|
|
||||||
qt_env["RPATH"] += rpath
|
|
||||||
|
|
||||||
if arch == "Darwin":
|
if arch == "Darwin":
|
||||||
del base_libs[base_libs.index('OpenCL')]
|
del base_libs[base_libs.index('OpenCL')]
|
||||||
qt_env['FRAMEWORKS'] += ['OpenCL']
|
qt_env['FRAMEWORKS'] += ['OpenCL']
|
||||||
|
|
||||||
|
# FIXME: remove this once we're on 5.15 (24.04)
|
||||||
|
qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"]
|
||||||
|
|
||||||
qt_util = qt_env.Library("qt_util", ["#selfdrive/ui/qt/api.cc", "#selfdrive/ui/qt/util.cc"], LIBS=base_libs)
|
qt_util = qt_env.Library("qt_util", ["#selfdrive/ui/qt/api.cc", "#selfdrive/ui/qt/util.cc"], LIBS=base_libs)
|
||||||
widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/drive_stats.cc", "qt/widgets/wifi.cc",
|
widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/drive_stats.cc", "qt/widgets/wifi.cc",
|
||||||
"qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc",
|
"qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc",
|
||||||
"qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc",
|
"qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc",
|
||||||
"qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc",
|
"qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc",
|
||||||
"qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc",
|
"qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc",
|
||||||
"../frogpilot/ui/frogpilot_functions.cc", "../frogpilot/navigation/ui/navigation_settings.cc",
|
"../frogpilot/ui/qt/widgets/frogpilot_controls.cc", "../frogpilot/navigation/ui/navigation_settings.cc",
|
||||||
"../frogpilot/ui/control_settings.cc", "../frogpilot/ui/vehicle_settings.cc",
|
"../frogpilot/ui/qt/offroad/control_settings.cc", "../frogpilot/ui/qt/offroad/vehicle_settings.cc",
|
||||||
"../frogpilot/ui/visual_settings.cc",
|
"../frogpilot/ui/qt/offroad/visual_settings.cc"]
|
||||||
"../oscarpilot/settings/settings.cc", "../oscarpilot/settings/basic.cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
qt_env['CPPDEFINES'] = []
|
qt_env['CPPDEFINES'] = []
|
||||||
if maps:
|
if maps:
|
||||||
base_libs += ['qmapboxgl']
|
base_libs += ['QMapLibre']
|
||||||
widgets_src += ["qt/maps/map_helpers.cc", "qt/maps/map_settings.cc", "qt/maps/map.cc", "qt/maps/map_panel.cc",
|
widgets_src += ["qt/maps/map_helpers.cc", "qt/maps/map_settings.cc", "qt/maps/map.cc", "qt/maps/map_panel.cc",
|
||||||
"qt/maps/map_eta.cc", "qt/maps/map_instructions.cc"]
|
"qt/maps/map_eta.cc", "qt/maps/map_instructions.cc"]
|
||||||
qt_env['CPPDEFINES'] += ["ENABLE_MAPS"]
|
qt_env['CPPDEFINES'] += ["ENABLE_MAPS"]
|
||||||
@@ -48,21 +45,6 @@ qt_src = ["main.cc", "qt/sidebar.cc", "qt/onroad.cc", "qt/body.cc",
|
|||||||
"qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc",
|
"qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc",
|
||||||
"../frogpilot/screenrecorder/omx_encoder.cc", "../frogpilot/screenrecorder/screenrecorder.cc"]
|
"../frogpilot/screenrecorder/omx_encoder.cc", "../frogpilot/screenrecorder/screenrecorder.cc"]
|
||||||
|
|
||||||
def is_running_on_wsl2():
|
|
||||||
try:
|
|
||||||
with open('/proc/version', 'r') as f:
|
|
||||||
return 'WSL2' in f.read()
|
|
||||||
except FileNotFoundError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if is_running_on_wsl2():
|
|
||||||
qt_env.Append(CXXFLAGS=['-DWSL2'])
|
|
||||||
base_libs.remove('OmxCore')
|
|
||||||
qt_libs.remove('OmxCore')
|
|
||||||
qt_src.remove("../frogpilot/screenrecorder/screenrecorder.cc")
|
|
||||||
qt_src.remove("../frogpilot/screenrecorder/omx_encoder.cc")
|
|
||||||
print("Building for WSL2. Removing Screen Recorder")
|
|
||||||
|
|
||||||
# build translation files
|
# build translation files
|
||||||
with open(File("translations/languages.json").abspath) as f:
|
with open(File("translations/languages.json").abspath) as f:
|
||||||
languages = json.loads(f.read())
|
languages = json.loads(f.read())
|
||||||
@@ -94,8 +76,8 @@ asset_obj = qt_env.Object("assets", assets)
|
|||||||
qt_env.SharedLibrary("qt/python_helpers", ["qt/qt_window.cc"], LIBS=qt_libs)
|
qt_env.SharedLibrary("qt/python_helpers", ["qt/qt_window.cc"], LIBS=qt_libs)
|
||||||
|
|
||||||
# spinner and text window
|
# spinner and text window
|
||||||
qt_env.Program("qt/text", ["qt/text.cc"], LIBS=qt_libs)
|
qt_env.Program("_text", ["qt/text.cc"], LIBS=qt_libs)
|
||||||
qt_env.Program("qt/spinner", ["qt/spinner.cc"], LIBS=qt_libs)
|
qt_env.Program("_spinner", ["qt/spinner.cc"], LIBS=qt_libs)
|
||||||
|
|
||||||
# build main UI
|
# build main UI
|
||||||
qt_env.Program("ui", qt_src + [asset_obj], LIBS=qt_libs)
|
qt_env.Program("ui", qt_src + [asset_obj], LIBS=qt_libs)
|
||||||
@@ -115,28 +97,25 @@ if GetOption('extras') and arch != "Darwin":
|
|||||||
# build updater UI
|
# build updater UI
|
||||||
qt_env.Program("qt/setup/updater", ["qt/setup/updater.cc", asset_obj], LIBS=qt_libs)
|
qt_env.Program("qt/setup/updater", ["qt/setup/updater.cc", asset_obj], LIBS=qt_libs)
|
||||||
|
|
||||||
|
# build mui
|
||||||
|
qt_env.Program("mui", ["mui.cc"], LIBS=qt_libs)
|
||||||
|
|
||||||
# build installers
|
# build installers
|
||||||
senv = qt_env.Clone()
|
senv = qt_env.Clone()
|
||||||
senv['LINKFLAGS'].append('-Wl,-strip-debug')
|
senv['LINKFLAGS'].append('-Wl,-strip-debug')
|
||||||
|
|
||||||
release = "release3"
|
release = "release3"
|
||||||
dashcam = "dashcam3"
|
|
||||||
installers = [
|
installers = [
|
||||||
("openpilot", release),
|
("openpilot", release),
|
||||||
("openpilot_test", f"{release}-staging"),
|
("openpilot_test", f"{release}-staging"),
|
||||||
("openpilot_nightly", "nightly"),
|
("openpilot_nightly", "nightly"),
|
||||||
("openpilot_internal", "master"),
|
("openpilot_internal", "master"),
|
||||||
("dashcam", dashcam),
|
|
||||||
("dashcam_test", f"{dashcam}-staging"),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
cont = {}
|
cont = senv.Command(f"installer/continue_openpilot.o", f"installer/continue_openpilot.sh",
|
||||||
for brand in ("openpilot", "dashcam"):
|
|
||||||
cont[brand] = senv.Command(f"installer/continue_{brand}.o", f"installer/continue_{brand}.sh",
|
|
||||||
"ld -r -b binary -o $TARGET $SOURCE")
|
"ld -r -b binary -o $TARGET $SOURCE")
|
||||||
for name, branch in installers:
|
for name, branch in installers:
|
||||||
brand = "dashcam" if "dashcam" in branch else "openpilot"
|
d = {'BRANCH': f"'\"{branch}\"'"}
|
||||||
d = {'BRANCH': f"'\"{branch}\"'", 'BRAND': f"'\"{brand}\"'"}
|
|
||||||
if "internal" in name:
|
if "internal" in name:
|
||||||
d['INTERNAL'] = "1"
|
d['INTERNAL'] = "1"
|
||||||
|
|
||||||
@@ -145,7 +124,7 @@ if GetOption('extras') and arch != "Darwin":
|
|||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
d['SSH_KEYS'] = f'\\"{r.text.strip()}\\"'
|
d['SSH_KEYS'] = f'\\"{r.text.strip()}\\"'
|
||||||
obj = senv.Object(f"installer/installers/installer_{name}.o", ["installer/installer.cc"], CPPDEFINES=d)
|
obj = senv.Object(f"installer/installers/installer_{name}.o", ["installer/installer.cc"], CPPDEFINES=d)
|
||||||
f = senv.Program(f"installer/installers/installer_{name}", [obj, cont[brand]], LIBS=qt_libs)
|
f = senv.Program(f"installer/installers/installer_{name}", [obj, cont], LIBS=qt_libs)
|
||||||
# keep installers small
|
# keep installers small
|
||||||
assert f[0].get_size() < 350*1e3
|
assert f[0].get_size() < 350*1e3
|
||||||
|
|
||||||
|
|||||||
0
selfdrive/ui/__init__.py
Normal file
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 INSTALL_PATH "/data/openpilot"
|
||||||
#define TMP_INSTALL_PATH "/data/tmppilot"
|
#define TMP_INSTALL_PATH "/data/tmppilot"
|
||||||
|
|
||||||
extern const uint8_t str_continue[] asm("_binary_selfdrive_ui_installer_continue_" BRAND "_sh_start");
|
extern const uint8_t str_continue[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_start");
|
||||||
extern const uint8_t str_continue_end[] asm("_binary_selfdrive_ui_installer_continue_" BRAND "_sh_end");
|
extern const uint8_t str_continue_end[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_end");
|
||||||
|
|
||||||
bool time_valid() {
|
bool time_valid() {
|
||||||
time_t rawtime;
|
time_t rawtime;
|
||||||
|
|||||||
0
selfdrive/ui/installer/installer.h
Executable file → Normal file
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 <QPainter>
|
||||||
#include <QStackedLayout>
|
#include <QStackedLayout>
|
||||||
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QGridLayout>
|
|
||||||
#include <QString>
|
|
||||||
#include <QTransform>
|
|
||||||
#include <QPixmap>
|
|
||||||
|
|
||||||
#include "common/params.h"
|
#include "common/params.h"
|
||||||
#include "common/timing.h"
|
#include "common/timing.h"
|
||||||
|
|
||||||
#include "system/hardware/hw.h"
|
RecordButton::RecordButton(QWidget *parent) : QPushButton(parent) {
|
||||||
#include "selfdrive/ui/qt/qt_window.h"
|
setCheckable(true);
|
||||||
#include "selfdrive/ui/qt/util.h"
|
setChecked(false);
|
||||||
|
setFixedSize(148, 148);
|
||||||
|
|
||||||
BodyWindow::BodyWindow(QWidget *parent) : QWidget(parent) {
|
QObject::connect(this, &QPushButton::toggled, [=]() {
|
||||||
QGridLayout *layout = new QGridLayout(this);
|
setEnabled(false);
|
||||||
layout->setSpacing(0);
|
});
|
||||||
layout->setMargin(200);
|
|
||||||
|
|
||||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
|
||||||
|
|
||||||
setStyleSheet(R"(
|
|
||||||
BodyWindow {
|
|
||||||
background-color: blue;
|
|
||||||
}
|
}
|
||||||
)");
|
|
||||||
|
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);
|
QObject::connect(uiState(), &UIState::uiUpdate, this, &BodyWindow::updateState);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BodyWindow::paintEvent(QPaintEvent *event) {
|
void BodyWindow::paintEvent(QPaintEvent *event) {
|
||||||
QPainter painter(this);
|
QPainter p(this);
|
||||||
|
p.setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
QPixmap comma_img = loadPixmap("../assets/oscarpilot_ready.png");
|
p.fillRect(rect(), QColor(0, 0, 0));
|
||||||
|
|
||||||
// Calculate the top-left position to center the image in the window.
|
// battery outline + detail
|
||||||
int x = (this->width() - comma_img.width()) / 2;
|
p.translate(width() - 136, 16);
|
||||||
int y = (this->height() - comma_img.height()) / 2;
|
const QColor gray = QColor("#737373");
|
||||||
|
p.setBrush(Qt::NoBrush);
|
||||||
|
p.setPen(QPen(gray, 4, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||||
|
p.drawRoundedRect(2, 2, 78, 36, 8, 8);
|
||||||
|
|
||||||
// Draw the pixmap at the calculated position.
|
p.setPen(Qt::NoPen);
|
||||||
painter.drawPixmap(x, y, comma_img);
|
p.setBrush(gray);
|
||||||
|
p.drawRoundedRect(84, 12, 6, 16, 4, 4);
|
||||||
|
p.drawRect(84, 12, 3, 16);
|
||||||
|
|
||||||
|
// battery level
|
||||||
|
double fuel = std::clamp(fuel_filter.x(), 0.2f, 1.0f);
|
||||||
|
const int m = 5; // manual margin since we can't do an inner border
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.setBrush(fuel > 0.25 ? QColor("#32D74B") : QColor("#FF453A"));
|
||||||
|
p.drawRoundedRect(2 + m, 2 + m, (78 - 2*m)*fuel, 36 - 2*m, 4, 4);
|
||||||
|
|
||||||
|
// 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) {
|
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 <QMovie>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QPixmap>
|
|
||||||
#include <QProgressBar>
|
|
||||||
#include <QSocketNotifier>
|
|
||||||
#include <QVariantAnimation>
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
#include "common/util.h"
|
#include "common/util.h"
|
||||||
#include "selfdrive/ui/ui.h"
|
#include "selfdrive/ui/ui.h"
|
||||||
|
|
||||||
class BodyWindow : public QWidget {
|
class RecordButton : public QPushButton {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BodyWindow(QWidget* parent = 0);
|
RecordButton(QWidget* parent = 0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void paintEvent(QPaintEvent*) override;
|
void paintEvent(QPaintEvent*) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BodyWindow : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
BodyWindow(QWidget* parent = 0);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool charging = false;
|
||||||
|
uint64_t last_button = 0;
|
||||||
|
FirstOrderFilter fuel_filter;
|
||||||
|
QLabel *face;
|
||||||
|
QMovie *awake, *sleep;
|
||||||
|
RecordButton *btn;
|
||||||
|
void paintEvent(QPaintEvent*) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void updateState(const UIState &s);
|
void updateState(const UIState &s);
|
||||||
void offroadTransition(bool onroad);
|
void offroadTransition(bool onroad);
|
||||||
|
|||||||
@@ -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);
|
slayout->addWidget(driver_view);
|
||||||
setAttribute(Qt::WA_NoSystemBackground);
|
setAttribute(Qt::WA_NoSystemBackground);
|
||||||
// QObject::connect(uiState(), &UIState::uiUpdate, this, &HomeWindow::updateState);
|
QObject::connect(uiState(), &UIState::uiUpdate, this, &HomeWindow::updateState);
|
||||||
QObject::connect(uiState(), &UIState::offroadTransition, this, &HomeWindow::offroadTransition);
|
QObject::connect(uiState(), &UIState::offroadTransition, this, &HomeWindow::offroadTransition);
|
||||||
QObject::connect(uiState(), &UIState::offroadTransition, sidebar, &Sidebar::offroadTransition);
|
QObject::connect(uiState(), &UIState::offroadTransition, sidebar, &Sidebar::offroadTransition);
|
||||||
}
|
}
|
||||||
@@ -66,37 +66,45 @@ void HomeWindow::updateState(const UIState &s) {
|
|||||||
body->setEnabled(true);
|
body->setEnabled(true);
|
||||||
slayout->setCurrentWidget(body);
|
slayout->setCurrentWidget(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s.scene.started) {
|
||||||
|
showDriverView(s.scene.driver_camera_timer >= 10, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HomeWindow::offroadTransition(bool offroad) {
|
void HomeWindow::offroadTransition(bool offroad) {
|
||||||
body->setEnabled(false);
|
body->setEnabled(false);
|
||||||
sidebar->setVisible(false);
|
sidebar->setVisible(offroad);
|
||||||
if (offroad) {
|
if (offroad) {
|
||||||
slayout->setCurrentWidget(body);
|
slayout->setCurrentWidget(home);
|
||||||
} else {
|
} else {
|
||||||
slayout->setCurrentWidget(onroad);
|
slayout->setCurrentWidget(onroad);
|
||||||
|
uiState()->scene.map_open = onroad->isMapVisible();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HomeWindow::showDriverView(bool show) {
|
void HomeWindow::showDriverView(bool show, bool started) {
|
||||||
if (show) {
|
if (show) {
|
||||||
emit closeSettings();
|
emit closeSettings();
|
||||||
slayout->setCurrentWidget(driver_view);
|
slayout->setCurrentWidget(driver_view);
|
||||||
|
sidebar->setVisible(show == false);
|
||||||
} else {
|
} else {
|
||||||
slayout->setCurrentWidget(body);
|
if (started) {
|
||||||
|
slayout->setCurrentWidget(onroad);
|
||||||
|
sidebar->setVisible(params.getBool("Sidebar"));
|
||||||
|
} else {
|
||||||
|
slayout->setCurrentWidget(home);
|
||||||
|
sidebar->setVisible(show == false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sidebar->setVisible(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HomeWindow::mousePressEvent(QMouseEvent* e) {
|
void HomeWindow::mousePressEvent(QMouseEvent* e) {
|
||||||
if (body->isVisible()) {
|
|
||||||
showSidebar(true);
|
|
||||||
slayout->setCurrentWidget(home);
|
|
||||||
} else {
|
|
||||||
// Handle sidebar collapsing
|
// Handle sidebar collapsing
|
||||||
if ((onroad->isVisible() || body->isVisible()) && (!sidebar->isVisible() || e->x() > sidebar->width())) {
|
if ((onroad->isVisible() || body->isVisible()) && (!sidebar->isVisible() || e->x() > sidebar->width())) {
|
||||||
sidebar->setVisible(!sidebar->isVisible() && !onroad->isMapVisible());
|
sidebar->setVisible(!sidebar->isVisible() && !onroad->isMapVisible());
|
||||||
}
|
uiState()->scene.map_open = onroad->isMapVisible();
|
||||||
|
params.putBool("Sidebar", sidebar->isVisible());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,9 +173,9 @@ OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) {
|
|||||||
left_widget->addWidget(new DriveStats);
|
left_widget->addWidget(new DriveStats);
|
||||||
left_widget->setStyleSheet("border-radius: 10px;");
|
left_widget->setStyleSheet("border-radius: 10px;");
|
||||||
|
|
||||||
left_widget->setCurrentIndex(params.getBool("DriveStats") ? 2 : uiState()->hasPrime() ? 0 : 1);
|
left_widget->setCurrentIndex(2);
|
||||||
connect(uiState(), &UIState::primeChanged, [=](bool prime) {
|
connect(uiState(), &UIState::primeChanged, [=](bool prime) {
|
||||||
left_widget->setCurrentIndex(prime ? 0 : 1);
|
left_widget->setCurrentIndex(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
home_layout->addWidget(left_widget, 1);
|
home_layout->addWidget(left_widget, 1);
|
||||||
@@ -222,17 +230,6 @@ OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) {
|
|||||||
font-size: 55px;
|
font-size: 55px;
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
|
|
||||||
// Set the model name
|
|
||||||
std::map<int, QString> MODEL_NAME {
|
|
||||||
{0, "New Delhi"},
|
|
||||||
{1, "Blue Diamond V1"},
|
|
||||||
{2, "Blue Diamond V2"},
|
|
||||||
{3, "Farmville"},
|
|
||||||
{4, "New Lemon Pie"},
|
|
||||||
};
|
|
||||||
|
|
||||||
modelName = MODEL_NAME[params.getInt("Model")];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OffroadHome::showEvent(QShowEvent *event) {
|
void OffroadHome::showEvent(QShowEvent *event) {
|
||||||
@@ -245,8 +242,10 @@ void OffroadHome::hideEvent(QHideEvent *event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OffroadHome::refresh() {
|
void OffroadHome::refresh() {
|
||||||
|
QString model = QString::fromStdString(params.get("ModelName"));
|
||||||
|
|
||||||
date->setText(QLocale(uiState()->language.mid(5)).toString(QDateTime::currentDateTime(), "dddd, MMMM d"));
|
date->setText(QLocale(uiState()->language.mid(5)).toString(QDateTime::currentDateTime(), "dddd, MMMM d"));
|
||||||
version->setText(getBrand() + " v" + getVersion().left(14).trimmed() + " - " + modelName);
|
version->setText(getBrand() + " v" + getVersion().left(14).trimmed() + " - " + model);
|
||||||
|
|
||||||
bool updateAvailable = update_widget->refresh();
|
bool updateAvailable = update_widget->refresh();
|
||||||
int alerts = alerts_widget->refresh();
|
int alerts = alerts_widget->refresh();
|
||||||
|
|||||||
8
selfdrive/ui/qt/home.h
Executable file → Normal file
8
selfdrive/ui/qt/home.h
Executable file → Normal file
@@ -33,7 +33,6 @@ private:
|
|||||||
Params params;
|
Params params;
|
||||||
|
|
||||||
QTimer* timer;
|
QTimer* timer;
|
||||||
ElidedLabel* date;
|
|
||||||
ElidedLabel* version;
|
ElidedLabel* version;
|
||||||
QStackedLayout* center_layout;
|
QStackedLayout* center_layout;
|
||||||
UpdateAlert *update_widget;
|
UpdateAlert *update_widget;
|
||||||
@@ -42,7 +41,7 @@ private:
|
|||||||
QPushButton* update_notif;
|
QPushButton* update_notif;
|
||||||
|
|
||||||
// FrogPilot variables
|
// FrogPilot variables
|
||||||
QString modelName;
|
ElidedLabel* date;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HomeWindow : public QWidget {
|
class HomeWindow : public QWidget {
|
||||||
@@ -57,7 +56,7 @@ signals:
|
|||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void offroadTransition(bool offroad);
|
void offroadTransition(bool offroad);
|
||||||
void showDriverView(bool show);
|
void showDriverView(bool show, bool started=false);
|
||||||
void showSidebar(bool show);
|
void showSidebar(bool show);
|
||||||
void showMapPanel(bool show);
|
void showMapPanel(bool show);
|
||||||
|
|
||||||
@@ -73,6 +72,9 @@ private:
|
|||||||
DriverViewWindow *driver_view;
|
DriverViewWindow *driver_view;
|
||||||
QStackedLayout *slayout;
|
QStackedLayout *slayout;
|
||||||
|
|
||||||
|
// FrogPilot variables
|
||||||
|
Params params;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void updateState(const UIState &s);
|
void updateState(const UIState &s);
|
||||||
};
|
};
|
||||||
|
|||||||
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 MIN_PITCH = 0;
|
||||||
const float MAP_SCALE = 2;
|
const float MAP_SCALE = 2;
|
||||||
|
|
||||||
MapWindow::MapWindow(const QMapboxGLSettings &settings) : m_settings(settings), velocity_filter(0, 10, 0.05, false) {
|
MapWindow::MapWindow(const QMapLibre::Settings &settings) : m_settings(settings), velocity_filter(0, 10, 0.05, false) {
|
||||||
QObject::connect(uiState(), &UIState::uiUpdate, this, &MapWindow::updateState);
|
QObject::connect(uiState(), &UIState::uiUpdate, this, &MapWindow::updateState);
|
||||||
|
|
||||||
map_overlay = new QWidget (this);
|
map_overlay = new QWidget (this);
|
||||||
@@ -57,10 +57,10 @@ void MapWindow::initLayers() {
|
|||||||
if (!m_map->layerExists("modelPathLayer")) {
|
if (!m_map->layerExists("modelPathLayer")) {
|
||||||
qDebug() << "Initializing modelPathLayer";
|
qDebug() << "Initializing modelPathLayer";
|
||||||
QVariantMap modelPath;
|
QVariantMap modelPath;
|
||||||
modelPath["id"] = "modelPathLayer";
|
//modelPath["id"] = "modelPathLayer";
|
||||||
modelPath["type"] = "line";
|
modelPath["type"] = "line";
|
||||||
modelPath["source"] = "modelPathSource";
|
modelPath["source"] = "modelPathSource";
|
||||||
m_map->addLayer(modelPath);
|
m_map->addLayer("modelPathLayer", modelPath);
|
||||||
m_map->setPaintProperty("modelPathLayer", "line-color", QColor("red"));
|
m_map->setPaintProperty("modelPathLayer", "line-color", QColor("red"));
|
||||||
m_map->setPaintProperty("modelPathLayer", "line-width", 5.0);
|
m_map->setPaintProperty("modelPathLayer", "line-width", 5.0);
|
||||||
m_map->setLayoutProperty("modelPathLayer", "line-cap", "round");
|
m_map->setLayoutProperty("modelPathLayer", "line-cap", "round");
|
||||||
@@ -68,10 +68,9 @@ void MapWindow::initLayers() {
|
|||||||
if (!m_map->layerExists("navLayer")) {
|
if (!m_map->layerExists("navLayer")) {
|
||||||
qDebug() << "Initializing navLayer";
|
qDebug() << "Initializing navLayer";
|
||||||
QVariantMap nav;
|
QVariantMap nav;
|
||||||
nav["id"] = "navLayer";
|
|
||||||
nav["type"] = "line";
|
nav["type"] = "line";
|
||||||
nav["source"] = "navSource";
|
nav["source"] = "navSource";
|
||||||
m_map->addLayer(nav, "road-intersection");
|
m_map->addLayer("navLayer", nav, "road-intersection");
|
||||||
|
|
||||||
QVariantMap transition;
|
QVariantMap transition;
|
||||||
transition["duration"] = 400; // ms
|
transition["duration"] = 400; // ms
|
||||||
@@ -84,10 +83,9 @@ void MapWindow::initLayers() {
|
|||||||
qDebug() << "Initializing pinLayer";
|
qDebug() << "Initializing pinLayer";
|
||||||
m_map->addImage("default_marker", QImage("../assets/navigation/default_marker.svg"));
|
m_map->addImage("default_marker", QImage("../assets/navigation/default_marker.svg"));
|
||||||
QVariantMap pin;
|
QVariantMap pin;
|
||||||
pin["id"] = "pinLayer";
|
|
||||||
pin["type"] = "symbol";
|
pin["type"] = "symbol";
|
||||||
pin["source"] = "pinSource";
|
pin["source"] = "pinSource";
|
||||||
m_map->addLayer(pin);
|
m_map->addLayer("pinLayer", pin);
|
||||||
m_map->setLayoutProperty("pinLayer", "icon-pitch-alignment", "viewport");
|
m_map->setLayoutProperty("pinLayer", "icon-pitch-alignment", "viewport");
|
||||||
m_map->setLayoutProperty("pinLayer", "icon-image", "default_marker");
|
m_map->setLayoutProperty("pinLayer", "icon-image", "default_marker");
|
||||||
m_map->setLayoutProperty("pinLayer", "icon-ignore-placement", true);
|
m_map->setLayoutProperty("pinLayer", "icon-ignore-placement", true);
|
||||||
@@ -100,10 +98,9 @@ void MapWindow::initLayers() {
|
|||||||
m_map->addImage("label-arrow", QImage("../assets/images/triangle.svg"));
|
m_map->addImage("label-arrow", QImage("../assets/images/triangle.svg"));
|
||||||
|
|
||||||
QVariantMap carPos;
|
QVariantMap carPos;
|
||||||
carPos["id"] = "carPosLayer";
|
|
||||||
carPos["type"] = "symbol";
|
carPos["type"] = "symbol";
|
||||||
carPos["source"] = "carPosSource";
|
carPos["source"] = "carPosSource";
|
||||||
m_map->addLayer(carPos);
|
m_map->addLayer("carPosLayer", carPos);
|
||||||
m_map->setLayoutProperty("carPosLayer", "icon-pitch-alignment", "map");
|
m_map->setLayoutProperty("carPosLayer", "icon-pitch-alignment", "map");
|
||||||
m_map->setLayoutProperty("carPosLayer", "icon-image", "label-arrow");
|
m_map->setLayoutProperty("carPosLayer", "icon-image", "label-arrow");
|
||||||
m_map->setLayoutProperty("carPosLayer", "icon-size", 0.5);
|
m_map->setLayoutProperty("carPosLayer", "icon-size", 0.5);
|
||||||
@@ -112,7 +109,6 @@ void MapWindow::initLayers() {
|
|||||||
// TODO: remove, symbol-sort-key does not seem to matter outside of each layer
|
// TODO: remove, symbol-sort-key does not seem to matter outside of each layer
|
||||||
m_map->setLayoutProperty("carPosLayer", "symbol-sort-key", 0);
|
m_map->setLayoutProperty("carPosLayer", "symbol-sort-key", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_map->layerExists("buildingsLayer")) {
|
if (!m_map->layerExists("buildingsLayer")) {
|
||||||
qDebug() << "Initializing buildingsLayer";
|
qDebug() << "Initializing buildingsLayer";
|
||||||
QVariantMap buildings;
|
QVariantMap buildings;
|
||||||
@@ -121,7 +117,7 @@ void MapWindow::initLayers() {
|
|||||||
buildings["source-layer"] = "building";
|
buildings["source-layer"] = "building";
|
||||||
buildings["type"] = "fill-extrusion";
|
buildings["type"] = "fill-extrusion";
|
||||||
buildings["minzoom"] = 15;
|
buildings["minzoom"] = 15;
|
||||||
m_map->addLayer(buildings);
|
m_map->addLayer("buildingsLayer", buildings);
|
||||||
m_map->setFilter("buildingsLayer", QVariantList({"==", "extrude", "true"}));
|
m_map->setFilter("buildingsLayer", QVariantList({"==", "extrude", "true"}));
|
||||||
|
|
||||||
QVariantList fillExtrusionHight = {
|
QVariantList fillExtrusionHight = {
|
||||||
@@ -168,8 +164,7 @@ void MapWindow::updateState(const UIState &s) {
|
|||||||
if (sm.updated("modelV2")) {
|
if (sm.updated("modelV2")) {
|
||||||
// set path color on change, and show map on rising edge of navigate on openpilot
|
// set path color on change, and show map on rising edge of navigate on openpilot
|
||||||
bool nav_enabled = sm["modelV2"].getModelV2().getNavEnabled() &&
|
bool nav_enabled = sm["modelV2"].getModelV2().getNavEnabled() &&
|
||||||
(sm["controlsState"].getControlsState().getEnabled() || sm["frogpilotCarControl"].getFrogpilotCarControl().getAlwaysOnLateral()) &&
|
(sm["controlsState"].getControlsState().getEnabled() || sm["frogpilotCarControl"].getFrogpilotCarControl().getAlwaysOnLateral());
|
||||||
(!params.get("NavDestination").empty() || params.getInt("PrimeType") != 0);
|
|
||||||
if (nav_enabled != uiState()->scene.navigate_on_openpilot) {
|
if (nav_enabled != uiState()->scene.navigate_on_openpilot) {
|
||||||
if (loaded_once) {
|
if (loaded_once) {
|
||||||
m_map->setPaintProperty("navLayer", "line-color", getNavPathColor(nav_enabled));
|
m_map->setPaintProperty("navLayer", "line-color", getNavPathColor(nav_enabled));
|
||||||
@@ -194,12 +189,24 @@ void MapWindow::updateState(const UIState &s) {
|
|||||||
locationd_valid = (locationd_pos.getValid() && locationd_orientation.getValid() && locationd_velocity.getValid() && pos_accurate_enough);
|
locationd_valid = (locationd_pos.getValid() && locationd_orientation.getValid() && locationd_velocity.getValid() && pos_accurate_enough);
|
||||||
|
|
||||||
if (locationd_valid) {
|
if (locationd_valid) {
|
||||||
last_position = QMapbox::Coordinate(locationd_pos.getValue()[0], locationd_pos.getValue()[1]);
|
last_position = QMapLibre::Coordinate(locationd_pos.getValue()[0], locationd_pos.getValue()[1]);
|
||||||
last_bearing = RAD2DEG(locationd_orientation.getValue()[2]);
|
last_bearing = RAD2DEG(locationd_orientation.getValue()[2]);
|
||||||
velocity_filter.update(std::max(10.0, locationd_velocity.getValue()[0]));
|
velocity_filter.update(std::max(10.0, locationd_velocity.getValue()[0]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Credit to jakethesnake420
|
||||||
|
if (loaded_once && (sm.rcv_frame("uiPlan") != model_rcv_frame)) {
|
||||||
|
auto locationd_location = sm["liveLocationKalman"].getLiveLocationKalman();
|
||||||
|
auto model_path = model_to_collection(locationd_location.getCalibratedOrientationECEF(), locationd_location.getPositionECEF(), sm["uiPlan"].getUiPlan().getPosition());
|
||||||
|
QMapLibre::Feature model_path_feature(QMapLibre::Feature::LineStringType, model_path, {}, {});
|
||||||
|
QVariantMap modelV2Path;
|
||||||
|
modelV2Path["type"] = "geojson";
|
||||||
|
modelV2Path["data"] = QVariant::fromValue<QMapLibre::Feature>(model_path_feature);
|
||||||
|
m_map->updateSource("modelPathSource", modelV2Path);
|
||||||
|
model_rcv_frame = sm.rcv_frame("uiPlan");
|
||||||
|
}
|
||||||
|
|
||||||
if (sm.updated("navRoute") && sm["navRoute"].getNavRoute().getCoordinates().size()) {
|
if (sm.updated("navRoute") && sm["navRoute"].getNavRoute().getCoordinates().size()) {
|
||||||
auto nav_dest = coordinate_from_param("NavDestination");
|
auto nav_dest = coordinate_from_param("NavDestination");
|
||||||
bool allow_open = std::exchange(last_valid_nav_dest, nav_dest) != nav_dest &&
|
bool allow_open = std::exchange(last_valid_nav_dest, nav_dest) != nav_dest &&
|
||||||
@@ -231,10 +238,10 @@ void MapWindow::updateState(const UIState &s) {
|
|||||||
if (locationd_valid) {
|
if (locationd_valid) {
|
||||||
// Update current location marker
|
// Update current location marker
|
||||||
auto point = coordinate_to_collection(*last_position);
|
auto point = coordinate_to_collection(*last_position);
|
||||||
QMapbox::Feature feature1(QMapbox::Feature::PointType, point, {}, {});
|
QMapLibre::Feature feature1(QMapLibre::Feature::PointType, point, {}, {});
|
||||||
QVariantMap carPosSource;
|
QVariantMap carPosSource;
|
||||||
carPosSource["type"] = "geojson";
|
carPosSource["type"] = "geojson";
|
||||||
carPosSource["data"] = QVariant::fromValue<QMapbox::Feature>(feature1);
|
carPosSource["data"] = QVariant::fromValue<QMapLibre::Feature>(feature1);
|
||||||
m_map->updateSource("carPosSource", carPosSource);
|
m_map->updateSource("carPosSource", carPosSource);
|
||||||
|
|
||||||
// Map bearing isn't updated when interacting, keep location marker up to date
|
// Map bearing isn't updated when interacting, keep location marker up to date
|
||||||
@@ -275,16 +282,40 @@ void MapWindow::updateState(const UIState &s) {
|
|||||||
qWarning() << "Updating navLayer with new route";
|
qWarning() << "Updating navLayer with new route";
|
||||||
auto route = sm["navRoute"].getNavRoute();
|
auto route = sm["navRoute"].getNavRoute();
|
||||||
auto route_points = capnp_coordinate_list_to_collection(route.getCoordinates());
|
auto route_points = capnp_coordinate_list_to_collection(route.getCoordinates());
|
||||||
QMapbox::Feature feature(QMapbox::Feature::LineStringType, route_points, {}, {});
|
QMapLibre::Feature feature(QMapLibre::Feature::LineStringType, route_points, {}, {});
|
||||||
QVariantMap navSource;
|
QVariantMap navSource;
|
||||||
navSource["type"] = "geojson";
|
navSource["type"] = "geojson";
|
||||||
navSource["data"] = QVariant::fromValue<QMapbox::Feature>(feature);
|
navSource["data"] = QVariant::fromValue<QMapLibre::Feature>(feature);
|
||||||
m_map->updateSource("navSource", navSource);
|
m_map->updateSource("navSource", navSource);
|
||||||
m_map->setLayoutProperty("navLayer", "visibility", "visible");
|
m_map->setLayoutProperty("navLayer", "visibility", "visible");
|
||||||
|
|
||||||
route_rcv_frame = sm.rcv_frame("navRoute");
|
route_rcv_frame = sm.rcv_frame("navRoute");
|
||||||
updateDestinationMarker();
|
updateDestinationMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Map Styling - Credit goes to OPKR!
|
||||||
|
int map_style = uiState()->scene.map_style;
|
||||||
|
|
||||||
|
if (map_style != previous_map_style) {
|
||||||
|
std::unordered_map<int, std::string> styleUrls = {
|
||||||
|
{0, "mapbox://styles/commaai/clkqztk0f00ou01qyhsa5bzpj"}, // Stock openpilot
|
||||||
|
{1, "mapbox://styles/mapbox/streets-v11"}, // Mapbox Streets
|
||||||
|
{2, "mapbox://styles/mapbox/outdoors-v11"}, // Mapbox Outdoors
|
||||||
|
{3, "mapbox://styles/mapbox/light-v10"}, // Mapbox Light
|
||||||
|
{4, "mapbox://styles/mapbox/dark-v10"}, // Mapbox Dark
|
||||||
|
{5, "mapbox://styles/mapbox/satellite-v9"}, // Mapbox Satellite
|
||||||
|
{6, "mapbox://styles/mapbox/satellite-streets-v11"}, // Mapbox Satellite Streets
|
||||||
|
{7, "mapbox://styles/mapbox/navigation-day-v1"}, // Mapbox Navigation Day
|
||||||
|
{8, "mapbox://styles/mapbox/navigation-night-v1"}, // Mapbox Navigation Night
|
||||||
|
{9, "mapbox://styles/mapbox/traffic-night-v2"}, // Mapbox Traffic Night
|
||||||
|
{10, "mapbox://styles/mike854/clt0hm8mw01ok01p4blkr27jp"}, // mike854's (Satellite hybrid)
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<int, std::string>::iterator it = styleUrls.find(map_style);
|
||||||
|
m_map->setStyleUrl(QString::fromStdString(it->second));
|
||||||
|
}
|
||||||
|
|
||||||
|
previous_map_style = map_style;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapWindow::setError(const QString &err_str) {
|
void MapWindow::setError(const QString &err_str) {
|
||||||
@@ -301,24 +332,24 @@ void MapWindow::resizeGL(int w, int h) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MapWindow::initializeGL() {
|
void MapWindow::initializeGL() {
|
||||||
m_map.reset(new QMapboxGL(this, m_settings, size(), 1));
|
m_map.reset(new QMapLibre::Map(this, m_settings, size(), 1));
|
||||||
|
|
||||||
if (last_position) {
|
if (last_position) {
|
||||||
m_map->setCoordinateZoom(*last_position, MAX_ZOOM);
|
m_map->setCoordinateZoom(*last_position, MAX_ZOOM);
|
||||||
} else {
|
} else {
|
||||||
m_map->setCoordinateZoom(QMapbox::Coordinate(64.31990695292795, -149.79038934046247), MIN_ZOOM);
|
m_map->setCoordinateZoom(QMapLibre::Coordinate(64.31990695292795, -149.79038934046247), MIN_ZOOM);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_map->setMargins({0, 350, 0, 50});
|
m_map->setMargins({0, 350, 0, 50});
|
||||||
m_map->setPitch(MIN_PITCH);
|
m_map->setPitch(MIN_PITCH);
|
||||||
m_map->setStyleUrl("mapbox://styles/commaai/clkqztk0f00ou01qyhsa5bzpj");
|
m_map->setStyleUrl("mapbox://styles/commaai/clkqztk0f00ou01qyhsa5bzpj");
|
||||||
|
|
||||||
QObject::connect(m_map.data(), &QMapboxGL::mapChanged, [=](QMapboxGL::MapChange change) {
|
QObject::connect(m_map.data(), &QMapLibre::Map::mapChanged, [=](QMapLibre::Map::MapChange change) {
|
||||||
// set global animation duration to 0 ms so visibility changes are instant
|
// set global animation duration to 0 ms so visibility changes are instant
|
||||||
if (change == QMapboxGL::MapChange::MapChangeDidFinishLoadingStyle) {
|
if (change == QMapLibre::Map::MapChange::MapChangeDidFinishLoadingStyle) {
|
||||||
m_map->setTransitionOptions(0, 0);
|
m_map->setTransitionOptions(0, 0);
|
||||||
}
|
}
|
||||||
if (change == QMapboxGL::MapChange::MapChangeDidFinishLoadingMap) {
|
if (change == QMapLibre::Map::MapChange::MapChangeDidFinishLoadingMap) {
|
||||||
loaded_once = true;
|
loaded_once = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -426,10 +457,10 @@ void MapWindow::updateDestinationMarker() {
|
|||||||
auto nav_dest = coordinate_from_param("NavDestination");
|
auto nav_dest = coordinate_from_param("NavDestination");
|
||||||
if (nav_dest.has_value()) {
|
if (nav_dest.has_value()) {
|
||||||
auto point = coordinate_to_collection(*nav_dest);
|
auto point = coordinate_to_collection(*nav_dest);
|
||||||
QMapbox::Feature feature(QMapbox::Feature::PointType, point, {}, {});
|
QMapLibre::Feature feature(QMapLibre::Feature::PointType, point, {}, {});
|
||||||
QVariantMap pinSource;
|
QVariantMap pinSource;
|
||||||
pinSource["type"] = "geojson";
|
pinSource["type"] = "geojson";
|
||||||
pinSource["data"] = QVariant::fromValue<QMapbox::Feature>(feature);
|
pinSource["data"] = QVariant::fromValue<QMapLibre::Feature>(feature);
|
||||||
m_map->updateSource("pinSource", pinSource);
|
m_map->updateSource("pinSource", pinSource);
|
||||||
m_map->setPaintProperty("pinLayer", "visibility", "visible");
|
m_map->setPaintProperty("pinLayer", "visibility", "visible");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
17
selfdrive/ui/qt/maps/map.h
Executable file → Normal file
17
selfdrive/ui/qt/maps/map.h
Executable file → Normal file
@@ -6,7 +6,8 @@
|
|||||||
#include <QGestureEvent>
|
#include <QGestureEvent>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QMapboxGL>
|
#include <QMapLibre/Map>
|
||||||
|
#include <QMapLibre/Settings>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
#include <QOpenGLWidget>
|
#include <QOpenGLWidget>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
@@ -27,7 +28,7 @@ class MapWindow : public QOpenGLWidget {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MapWindow(const QMapboxGLSettings &);
|
MapWindow(const QMapLibre::Settings &);
|
||||||
~MapWindow();
|
~MapWindow();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -35,8 +36,8 @@ private:
|
|||||||
void paintGL() final;
|
void paintGL() final;
|
||||||
void resizeGL(int w, int h) override;
|
void resizeGL(int w, int h) override;
|
||||||
|
|
||||||
QMapboxGLSettings m_settings;
|
QMapLibre::Settings m_settings;
|
||||||
QScopedPointer<QMapboxGL> m_map;
|
QScopedPointer<QMapLibre::Map> m_map;
|
||||||
|
|
||||||
void initLayers();
|
void initLayers();
|
||||||
|
|
||||||
@@ -56,8 +57,8 @@ private:
|
|||||||
int interaction_counter = 0;
|
int interaction_counter = 0;
|
||||||
|
|
||||||
// Position
|
// Position
|
||||||
std::optional<QMapbox::Coordinate> last_valid_nav_dest;
|
std::optional<QMapLibre::Coordinate> last_valid_nav_dest;
|
||||||
std::optional<QMapbox::Coordinate> last_position;
|
std::optional<QMapLibre::Coordinate> last_position;
|
||||||
std::optional<float> last_bearing;
|
std::optional<float> last_bearing;
|
||||||
FirstOrderFilter velocity_filter;
|
FirstOrderFilter velocity_filter;
|
||||||
bool locationd_valid = false;
|
bool locationd_valid = false;
|
||||||
@@ -80,6 +81,10 @@ private:
|
|||||||
// FrogPilot variables
|
// FrogPilot variables
|
||||||
Params params;
|
Params params;
|
||||||
|
|
||||||
|
int previous_map_style;
|
||||||
|
|
||||||
|
uint64_t model_rcv_frame = 0;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void updateState(const UIState &s);
|
void updateState(const UIState &s);
|
||||||
|
|
||||||
|
|||||||
0
selfdrive/ui/qt/maps/map_eta.cc
Executable file → Normal file
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;
|
return MAPBOX_TOKEN.isEmpty() ? CommaApi::create_jwt({}, 4 * 7 * 24 * 3600) : MAPBOX_TOKEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
QMapboxGLSettings get_mapbox_settings() {
|
QMapLibre::Settings get_mapbox_settings() {
|
||||||
QMapboxGLSettings settings;
|
QMapLibre::Settings settings;
|
||||||
|
settings.setProviderTemplate(QMapLibre::Settings::ProviderTemplate::MapboxProvider);
|
||||||
|
|
||||||
if (!Hardware::PC()) {
|
if (!Hardware::PC()) {
|
||||||
settings.setCacheDatabasePath(MAPS_CACHE_PATH);
|
settings.setCacheDatabasePath(MAPS_CACHE_PATH);
|
||||||
settings.setCacheDatabaseMaximumSize(100 * 1024 * 1024);
|
settings.setCacheDatabaseMaximumSize(100 * 1024 * 1024);
|
||||||
}
|
}
|
||||||
settings.setApiBaseUrl(MAPS_HOST);
|
settings.setApiBaseUrl(MAPS_HOST);
|
||||||
settings.setAccessToken(get_mapbox_token());
|
settings.setApiKey(get_mapbox_token());
|
||||||
|
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
QGeoCoordinate to_QGeoCoordinate(const QMapbox::Coordinate &in) {
|
QGeoCoordinate to_QGeoCoordinate(const QMapLibre::Coordinate &in) {
|
||||||
return QGeoCoordinate(in.first, in.second);
|
return QGeoCoordinate(in.first, in.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
QMapbox::CoordinatesCollections model_to_collection(
|
QMapLibre::CoordinatesCollections model_to_collection(
|
||||||
const cereal::LiveLocationKalman::Measurement::Reader &calibratedOrientationECEF,
|
const cereal::LiveLocationKalman::Measurement::Reader &calibratedOrientationECEF,
|
||||||
const cereal::LiveLocationKalman::Measurement::Reader &positionECEF,
|
const cereal::LiveLocationKalman::Measurement::Reader &positionECEF,
|
||||||
const cereal::XYZTData::Reader &line){
|
const cereal::XYZTData::Reader &line){
|
||||||
@@ -42,7 +43,7 @@ QMapbox::CoordinatesCollections model_to_collection(
|
|||||||
Eigen::Vector3d orient(calibratedOrientationECEF.getValue()[0], calibratedOrientationECEF.getValue()[1], calibratedOrientationECEF.getValue()[2]);
|
Eigen::Vector3d orient(calibratedOrientationECEF.getValue()[0], calibratedOrientationECEF.getValue()[1], calibratedOrientationECEF.getValue()[2]);
|
||||||
Eigen::Matrix3d ecef_from_local = euler2rot(orient);
|
Eigen::Matrix3d ecef_from_local = euler2rot(orient);
|
||||||
|
|
||||||
QMapbox::Coordinates coordinates;
|
QMapLibre::Coordinates coordinates;
|
||||||
auto x = line.getX();
|
auto x = line.getX();
|
||||||
auto y = line.getY();
|
auto y = line.getY();
|
||||||
auto z = line.getZ();
|
auto z = line.getZ();
|
||||||
@@ -52,28 +53,28 @@ QMapbox::CoordinatesCollections model_to_collection(
|
|||||||
coordinates.push_back({point_geodetic.lat, point_geodetic.lon});
|
coordinates.push_back({point_geodetic.lat, point_geodetic.lon});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {QMapbox::CoordinatesCollection{coordinates}};
|
return {QMapLibre::CoordinatesCollection{coordinates}};
|
||||||
}
|
}
|
||||||
|
|
||||||
QMapbox::CoordinatesCollections coordinate_to_collection(const QMapbox::Coordinate &c) {
|
QMapLibre::CoordinatesCollections coordinate_to_collection(const QMapLibre::Coordinate &c) {
|
||||||
QMapbox::Coordinates coordinates{c};
|
QMapLibre::Coordinates coordinates{c};
|
||||||
return {QMapbox::CoordinatesCollection{coordinates}};
|
return {QMapLibre::CoordinatesCollection{coordinates}};
|
||||||
}
|
}
|
||||||
|
|
||||||
QMapbox::CoordinatesCollections capnp_coordinate_list_to_collection(const capnp::List<cereal::NavRoute::Coordinate>::Reader& coordinate_list) {
|
QMapLibre::CoordinatesCollections capnp_coordinate_list_to_collection(const capnp::List<cereal::NavRoute::Coordinate>::Reader& coordinate_list) {
|
||||||
QMapbox::Coordinates coordinates;
|
QMapLibre::Coordinates coordinates;
|
||||||
for (auto const &c : coordinate_list) {
|
for (auto const &c : coordinate_list) {
|
||||||
coordinates.push_back({c.getLatitude(), c.getLongitude()});
|
coordinates.push_back({c.getLatitude(), c.getLongitude()});
|
||||||
}
|
}
|
||||||
return {QMapbox::CoordinatesCollection{coordinates}};
|
return {QMapLibre::CoordinatesCollection{coordinates}};
|
||||||
}
|
}
|
||||||
|
|
||||||
QMapbox::CoordinatesCollections coordinate_list_to_collection(const QList<QGeoCoordinate> &coordinate_list) {
|
QMapLibre::CoordinatesCollections coordinate_list_to_collection(const QList<QGeoCoordinate> &coordinate_list) {
|
||||||
QMapbox::Coordinates coordinates;
|
QMapLibre::Coordinates coordinates;
|
||||||
for (auto &c : coordinate_list) {
|
for (auto &c : coordinate_list) {
|
||||||
coordinates.push_back({c.latitude(), c.longitude()});
|
coordinates.push_back({c.latitude(), c.longitude()});
|
||||||
}
|
}
|
||||||
return {QMapbox::CoordinatesCollection{coordinates}};
|
return {QMapLibre::CoordinatesCollection{coordinates}};
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QGeoCoordinate> polyline_to_coordinate_list(const QString &polylineString) {
|
QList<QGeoCoordinate> polyline_to_coordinate_list(const QString &polylineString) {
|
||||||
@@ -118,7 +119,7 @@ QList<QGeoCoordinate> polyline_to_coordinate_list(const QString &polylineString)
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<QMapbox::Coordinate> coordinate_from_param(const std::string ¶m) {
|
std::optional<QMapLibre::Coordinate> coordinate_from_param(const std::string ¶m) {
|
||||||
QString json_str = QString::fromStdString(Params().get(param));
|
QString json_str = QString::fromStdString(Params().get(param));
|
||||||
if (json_str.isEmpty()) return {};
|
if (json_str.isEmpty()) return {};
|
||||||
|
|
||||||
@@ -127,7 +128,7 @@ std::optional<QMapbox::Coordinate> coordinate_from_param(const std::string ¶
|
|||||||
|
|
||||||
QJsonObject json = doc.object();
|
QJsonObject json = doc.object();
|
||||||
if (json["latitude"].isDouble() && json["longitude"].isDouble()) {
|
if (json["latitude"].isDouble() && json["longitude"].isDouble()) {
|
||||||
QMapbox::Coordinate coord(json["latitude"].toDouble(), json["longitude"].toDouble());
|
QMapLibre::Coordinate coord(json["latitude"].toDouble(), json["longitude"].toDouble());
|
||||||
return coord;
|
return coord;
|
||||||
} else {
|
} else {
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
17
selfdrive/ui/qt/maps/map_helpers.h
Executable file → Normal file
17
selfdrive/ui/qt/maps/map_helpers.h
Executable file → Normal file
@@ -3,8 +3,9 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <QMapLibre/Map>
|
||||||
|
#include <QMapLibre/Settings>
|
||||||
#include <eigen3/Eigen/Dense>
|
#include <eigen3/Eigen/Dense>
|
||||||
#include <QMapboxGL>
|
|
||||||
#include <QGeoCoordinate>
|
#include <QGeoCoordinate>
|
||||||
|
|
||||||
#include "common/params.h"
|
#include "common/params.h"
|
||||||
@@ -18,15 +19,15 @@ const QString MAPS_HOST = util::getenv("MAPS_HOST", MAPBOX_TOKEN.isEmpty() ? "ht
|
|||||||
const QString MAPS_CACHE_PATH = "/data/mbgl-cache-navd.db";
|
const QString MAPS_CACHE_PATH = "/data/mbgl-cache-navd.db";
|
||||||
|
|
||||||
QString get_mapbox_token();
|
QString get_mapbox_token();
|
||||||
QMapboxGLSettings get_mapbox_settings();
|
QMapLibre::Settings get_mapbox_settings();
|
||||||
QGeoCoordinate to_QGeoCoordinate(const QMapbox::Coordinate &in);
|
QGeoCoordinate to_QGeoCoordinate(const QMapLibre::Coordinate &in);
|
||||||
QMapbox::CoordinatesCollections model_to_collection(
|
QMapLibre::CoordinatesCollections model_to_collection(
|
||||||
const cereal::LiveLocationKalman::Measurement::Reader &calibratedOrientationECEF,
|
const cereal::LiveLocationKalman::Measurement::Reader &calibratedOrientationECEF,
|
||||||
const cereal::LiveLocationKalman::Measurement::Reader &positionECEF,
|
const cereal::LiveLocationKalman::Measurement::Reader &positionECEF,
|
||||||
const cereal::XYZTData::Reader &line);
|
const cereal::XYZTData::Reader &line);
|
||||||
QMapbox::CoordinatesCollections coordinate_to_collection(const QMapbox::Coordinate &c);
|
QMapLibre::CoordinatesCollections coordinate_to_collection(const QMapLibre::Coordinate &c);
|
||||||
QMapbox::CoordinatesCollections capnp_coordinate_list_to_collection(const capnp::List<cereal::NavRoute::Coordinate>::Reader &coordinate_list);
|
QMapLibre::CoordinatesCollections capnp_coordinate_list_to_collection(const capnp::List<cereal::NavRoute::Coordinate>::Reader &coordinate_list);
|
||||||
QMapbox::CoordinatesCollections coordinate_list_to_collection(const QList<QGeoCoordinate> &coordinate_list);
|
QMapLibre::CoordinatesCollections coordinate_list_to_collection(const QList<QGeoCoordinate> &coordinate_list);
|
||||||
QList<QGeoCoordinate> polyline_to_coordinate_list(const QString &polylineString);
|
QList<QGeoCoordinate> polyline_to_coordinate_list(const QString &polylineString);
|
||||||
std::optional<QMapbox::Coordinate> coordinate_from_param(const std::string ¶m);
|
std::optional<QMapLibre::Coordinate> coordinate_from_param(const std::string ¶m);
|
||||||
std::pair<QString, QString> map_format_distance(float d, bool is_metric);
|
std::pair<QString, QString> map_format_distance(float d, bool is_metric);
|
||||||
|
|||||||
0
selfdrive/ui/qt/maps/map_instructions.cc
Executable file → Normal file
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/qt/util.h"
|
||||||
#include "selfdrive/ui/ui.h"
|
#include "selfdrive/ui/ui.h"
|
||||||
|
|
||||||
MapPanel::MapPanel(const QMapboxGLSettings &mapboxSettings, QWidget *parent) : QFrame(parent) {
|
MapPanel::MapPanel(const QMapLibre::Settings &mapboxSettings, QWidget *parent) : QFrame(parent) {
|
||||||
content_stack = new QStackedLayout(this);
|
content_stack = new QStackedLayout(this);
|
||||||
content_stack->setContentsMargins(0, 0, 0, 0);
|
content_stack->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
@@ -41,8 +41,3 @@ void MapPanel::toggleMapSettings() {
|
|||||||
emit mapPanelRequested();
|
emit mapPanelRequested();
|
||||||
show();
|
show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapPanel::setVisible(bool visible) {
|
|
||||||
QFrame::setVisible(visible);
|
|
||||||
uiState()->scene.map_open = visible;
|
|
||||||
}
|
|
||||||
|
|||||||
5
selfdrive/ui/qt/maps/map_panel.h
Executable file → Normal file
5
selfdrive/ui/qt/maps/map_panel.h
Executable file → Normal file
@@ -1,15 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QFrame>
|
#include <QFrame>
|
||||||
#include <QMapboxGL>
|
#include <QMapLibre/Settings>
|
||||||
#include <QStackedLayout>
|
#include <QStackedLayout>
|
||||||
|
|
||||||
class MapPanel : public QFrame {
|
class MapPanel : public QFrame {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MapPanel(const QMapboxGLSettings &settings, QWidget *parent = nullptr);
|
explicit MapPanel(const QMapLibre::Settings &settings, QWidget *parent = nullptr);
|
||||||
void setVisible(bool visible);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void mapPanelRequested();
|
void mapPanelRequested();
|
||||||
|
|||||||
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;");
|
title->setStyleSheet("color: #FFFFFF; font-size: 54px; font-weight: 600;");
|
||||||
heading->addWidget(title);
|
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 = new QLabel(tr("Manage at connect.comma.ai"), this);
|
||||||
}
|
|
||||||
subtitle->setStyleSheet("color: #A0A0A0; font-size: 40px; font-weight: 300;");
|
subtitle->setStyleSheet("color: #A0A0A0; font-size: 40px; font-weight: 300;");
|
||||||
heading->addWidget(subtitle);
|
heading->addWidget(subtitle);
|
||||||
}
|
}
|
||||||
@@ -99,6 +93,8 @@ MapSettings::MapSettings(bool closeable, QWidget *parent) : QFrame(parent) {
|
|||||||
|
|
||||||
setStyleSheet("MapSettings { background-color: #333333; }");
|
setStyleSheet("MapSettings { background-color: #333333; }");
|
||||||
QObject::connect(NavManager::instance(), &NavManager::updated, this, &MapSettings::refresh);
|
QObject::connect(NavManager::instance(), &NavManager::updated, this, &MapSettings::refresh);
|
||||||
|
|
||||||
|
wifi = new WifiManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapSettings::showEvent(QShowEvent *event) {
|
void MapSettings::showEvent(QShowEvent *event) {
|
||||||
@@ -145,9 +141,9 @@ void MapSettings::refresh() {
|
|||||||
|
|
||||||
setUpdatesEnabled(true);
|
setUpdatesEnabled(true);
|
||||||
|
|
||||||
// NOO without Prime IP update
|
// Use IP for NOO without Prime
|
||||||
if (notPrime) {
|
if (!uiState()->hasPrime()) {
|
||||||
ipAddress = QString("%1:8082").arg(wifi->getIp4Address());
|
QString ipAddress = QString("%1:8082").arg(wifi->getIp4Address());
|
||||||
subtitle->setText(tr("Manage at %1").arg(ipAddress));
|
subtitle->setText(tr("Manage at %1").arg(ipAddress));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
selfdrive/ui/qt/maps/map_settings.h
Executable file → Normal file
5
selfdrive/ui/qt/maps/map_settings.h
Executable file → Normal file
@@ -66,10 +66,9 @@ private:
|
|||||||
std::vector<DestinationWidget *> widgets;
|
std::vector<DestinationWidget *> widgets;
|
||||||
|
|
||||||
// FrogPilot variables
|
// FrogPilot variables
|
||||||
bool notPrime = Params().getInt("PrimeType") == 0;
|
|
||||||
QLabel *subtitle;
|
QLabel *subtitle;
|
||||||
QString ipAddress;
|
|
||||||
WifiManager *wifi = new WifiManager(this);
|
WifiManager *wifi;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void closeSettings();
|
void closeSettings();
|
||||||
|
|||||||
12
selfdrive/ui/qt/network/networking.cc
Executable file → Normal file
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)) {
|
if (wifi->isKnownConnection(n.ssid)) {
|
||||||
wifi->activateWifiConnection(n.ssid);
|
wifi->activateWifiConnection(n.ssid);
|
||||||
} else if (n.security_type == SecurityType::OPEN) {
|
} else if (n.security_type == SecurityType::OPEN) {
|
||||||
wifi->connect(n);
|
wifi->connect(n, false);
|
||||||
} else if (n.security_type == SecurityType::WPA) {
|
} else if (n.security_type == SecurityType::WPA) {
|
||||||
QString pass = InputDialog::getText(tr("Enter password"), this, tr("for \"%1\"").arg(QString::fromUtf8(n.ssid)), true, 8);
|
QString pass = InputDialog::getText(tr("Enter password"), this, tr("for \"%1\"").arg(QString::fromUtf8(n.ssid)), true, 8);
|
||||||
if (!pass.isEmpty()) {
|
if (!pass.isEmpty()) {
|
||||||
wifi->connect(n, pass);
|
wifi->connect(n, false, pass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,7 +96,7 @@ void Networking::wrongPassword(const QString &ssid) {
|
|||||||
const Network &n = wifi->seenNetworks.value(ssid);
|
const Network &n = wifi->seenNetworks.value(ssid);
|
||||||
QString pass = InputDialog::getText(tr("Wrong password"), this, tr("for \"%1\"").arg(QString::fromUtf8(n.ssid)), true, 8);
|
QString pass = InputDialog::getText(tr("Wrong password"), this, tr("for \"%1\"").arg(QString::fromUtf8(n.ssid)), true, 8);
|
||||||
if (!pass.isEmpty()) {
|
if (!pass.isEmpty()) {
|
||||||
wifi->connect(n, pass);
|
wifi->connect(n, false, pass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,7 +131,7 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
|
|||||||
list->addItem(tetheringToggle);
|
list->addItem(tetheringToggle);
|
||||||
QObject::connect(tetheringToggle, &ToggleControl::toggleFlipped, this, &AdvancedNetworking::toggleTethering);
|
QObject::connect(tetheringToggle, &ToggleControl::toggleFlipped, this, &AdvancedNetworking::toggleTethering);
|
||||||
if (params.getBool("TetheringEnabled")) {
|
if (params.getBool("TetheringEnabled")) {
|
||||||
tetheringToggle->setVisualOn();
|
tetheringToggle->refresh();
|
||||||
uiState()->scene.tethering_enabled = true;
|
uiState()->scene.tethering_enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,9 +196,9 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
|
|||||||
hidden_network.ssid = ssid.toUtf8();
|
hidden_network.ssid = ssid.toUtf8();
|
||||||
if (!pass.isEmpty()) {
|
if (!pass.isEmpty()) {
|
||||||
hidden_network.security_type = SecurityType::WPA;
|
hidden_network.security_type = SecurityType::WPA;
|
||||||
wifi->connect(hidden_network, pass);
|
wifi->connect(hidden_network, true, pass);
|
||||||
} else {
|
} else {
|
||||||
wifi->connect(hidden_network);
|
wifi->connect(hidden_network, true);
|
||||||
}
|
}
|
||||||
emit requestWifiScreen();
|
emit requestWifiScreen();
|
||||||
}
|
}
|
||||||
|
|||||||
0
selfdrive/ui/qt/network/networking.h
Executable file → Normal file
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);
|
setCurrentConnecting(n.ssid);
|
||||||
forgetConnection(n.ssid); // Clear all connections that may already exist to the network we are connecting
|
forgetConnection(n.ssid); // Clear all connections that may already exist to the network we are connecting
|
||||||
Connection connection;
|
Connection connection;
|
||||||
@@ -176,6 +176,7 @@ void WifiManager::connect(const Network &n, const QString &password, const QStri
|
|||||||
connection["connection"]["autoconnect-retries"] = 0;
|
connection["connection"]["autoconnect-retries"] = 0;
|
||||||
|
|
||||||
connection["802-11-wireless"]["ssid"] = n.ssid;
|
connection["802-11-wireless"]["ssid"] = n.ssid;
|
||||||
|
connection["802-11-wireless"]["hidden"] = is_hidden;
|
||||||
connection["802-11-wireless"]["mode"] = "infrastructure";
|
connection["802-11-wireless"]["mode"] = "infrastructure";
|
||||||
|
|
||||||
if (n.security_type == SecurityType::WPA) {
|
if (n.security_type == SecurityType::WPA) {
|
||||||
|
|||||||
2
selfdrive/ui/qt/network/wifi_manager.h
Executable file → Normal file
2
selfdrive/ui/qt/network/wifi_manager.h
Executable file → Normal file
@@ -52,7 +52,7 @@ public:
|
|||||||
std::optional<QDBusPendingCall> activateWifiConnection(const QString &ssid);
|
std::optional<QDBusPendingCall> activateWifiConnection(const QString &ssid);
|
||||||
NetworkType currentNetworkType();
|
NetworkType currentNetworkType();
|
||||||
void updateGsmSettings(bool roaming, QString apn, bool metered);
|
void updateGsmSettings(bool roaming, QString apn, bool metered);
|
||||||
void connect(const Network &ssid, const QString &password = {}, const QString &username = {});
|
void connect(const Network &ssid, const bool is_hidden = false, const QString &password = {}, const QString &username = {});
|
||||||
|
|
||||||
// Tethering functions
|
// Tethering functions
|
||||||
void setTetheringEnabled(bool enabled);
|
void setTetheringEnabled(bool enabled);
|
||||||
|
|||||||
0
selfdrive/ui/qt/offroad/driverview.cc
Executable file → Normal file
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() {
|
void OnboardingWindow::updateActiveScreen() {
|
||||||
if (!accepted_terms) {
|
if (!accepted_terms) {
|
||||||
setCurrentIndex(0);
|
setCurrentIndex(0);
|
||||||
// } else if (!training_done && !params.getBool("Passive")) {
|
} else if (!training_done) {
|
||||||
// setCurrentIndex(1);
|
setCurrentIndex(1);
|
||||||
} else {
|
} else {
|
||||||
emit onboardingDone();
|
emit onboardingDone();
|
||||||
}
|
}
|
||||||
@@ -199,7 +199,7 @@ OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) {
|
|||||||
TermsPage* terms = new TermsPage(this);
|
TermsPage* terms = new TermsPage(this);
|
||||||
addWidget(terms);
|
addWidget(terms);
|
||||||
connect(terms, &TermsPage::acceptedTerms, [=]() {
|
connect(terms, &TermsPage::acceptedTerms, [=]() {
|
||||||
Params().put("HasAcceptedTerms", current_terms_version);
|
params.put("HasAcceptedTerms", current_terms_version);
|
||||||
accepted_terms = true;
|
accepted_terms = true;
|
||||||
updateActiveScreen();
|
updateActiveScreen();
|
||||||
});
|
});
|
||||||
@@ -209,7 +209,7 @@ OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) {
|
|||||||
addWidget(tr);
|
addWidget(tr);
|
||||||
connect(tr, &TrainingGuide::completedTraining, [=]() {
|
connect(tr, &TrainingGuide::completedTraining, [=]() {
|
||||||
training_done = true;
|
training_done = true;
|
||||||
Params().put("CompletedTrainingVersion", current_training_version);
|
params.put("CompletedTrainingVersion", current_training_version);
|
||||||
updateActiveScreen();
|
updateActiveScreen();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -230,11 +230,5 @@ OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) {
|
|||||||
background-color: #4F4F4F;
|
background-color: #4F4F4F;
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
|
|
||||||
// # Oscar sez
|
|
||||||
Params().put("HasAcceptedTerms", current_terms_version);
|
|
||||||
Params().put("CompletedTrainingVersion", current_training_version);
|
|
||||||
accepted_terms = true;
|
|
||||||
emit onboardingDone();
|
|
||||||
updateActiveScreen();
|
updateActiveScreen();
|
||||||
}
|
}
|
||||||
|
|||||||
0
selfdrive/ui/qt/offroad/onboarding.h
Executable file → Normal file
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 <cassert>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -25,9 +27,9 @@
|
|||||||
#include "selfdrive/ui/qt/qt_window.h"
|
#include "selfdrive/ui/qt/qt_window.h"
|
||||||
|
|
||||||
#include "selfdrive/frogpilot/navigation/ui/navigation_settings.h"
|
#include "selfdrive/frogpilot/navigation/ui/navigation_settings.h"
|
||||||
#include "selfdrive/frogpilot/ui/control_settings.h"
|
#include "selfdrive/frogpilot/ui/qt/offroad/control_settings.h"
|
||||||
#include "selfdrive/frogpilot/ui/vehicle_settings.h"
|
#include "selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h"
|
||||||
#include "selfdrive/frogpilot/ui/visual_settings.h"
|
#include "selfdrive/frogpilot/ui/qt/offroad/visual_settings.h"
|
||||||
|
|
||||||
TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
|
TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
|
||||||
// param, title, desc, icon
|
// param, title, desc, icon
|
||||||
@@ -97,9 +99,14 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
|
|||||||
std::vector<QString> longi_button_texts{tr("Aggressive"), tr("Standard"), tr("Relaxed")};
|
std::vector<QString> longi_button_texts{tr("Aggressive"), tr("Standard"), tr("Relaxed")};
|
||||||
long_personality_setting = new ButtonParamControl("LongitudinalPersonality", tr("Driving Personality"),
|
long_personality_setting = new ButtonParamControl("LongitudinalPersonality", tr("Driving Personality"),
|
||||||
tr("Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. "
|
tr("Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. "
|
||||||
"In relaxed mode openpilot will stay further away from lead cars."),
|
"In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with "
|
||||||
|
"your steering wheel distance button."),
|
||||||
"../assets/offroad/icon_speed_limit.png",
|
"../assets/offroad/icon_speed_limit.png",
|
||||||
longi_button_texts);
|
longi_button_texts);
|
||||||
|
|
||||||
|
// set up uiState update for personality setting
|
||||||
|
QObject::connect(uiState(), &UIState::uiUpdate, this, &TogglesPanel::updateState);
|
||||||
|
|
||||||
for (auto &[param, title, desc, icon] : toggle_defs) {
|
for (auto &[param, title, desc, icon] : toggle_defs) {
|
||||||
auto toggle = new ParamControl(param, title, desc, icon, this);
|
auto toggle = new ParamControl(param, title, desc, icon, this);
|
||||||
|
|
||||||
@@ -110,7 +117,7 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
|
|||||||
toggles[param.toStdString()] = toggle;
|
toggles[param.toStdString()] = toggle;
|
||||||
|
|
||||||
// insert longitudinal personality after NDOG toggle
|
// insert longitudinal personality after NDOG toggle
|
||||||
if (param == "DisengageOnAccelerator" && !params.getInt("AdjustablePersonalities")) {
|
if (param == "DisengageOnAccelerator") {
|
||||||
addItem(long_personality_setting);
|
addItem(long_personality_setting);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,6 +136,18 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TogglesPanel::updateState(const UIState &s) {
|
||||||
|
const SubMaster &sm = *(s.sm);
|
||||||
|
|
||||||
|
if (sm.updated("controlsState")) {
|
||||||
|
auto personality = sm["controlsState"].getControlsState().getPersonality();
|
||||||
|
if (personality != s.scene.personality && s.scene.started && isVisible()) {
|
||||||
|
long_personality_setting->setCheckedButton(static_cast<int>(personality));
|
||||||
|
}
|
||||||
|
uiState()->scene.personality = personality;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TogglesPanel::expandToggleDescription(const QString ¶m) {
|
void TogglesPanel::expandToggleDescription(const QString ¶m) {
|
||||||
toggles[param.toStdString()]->showDescription();
|
toggles[param.toStdString()]->showDescription();
|
||||||
}
|
}
|
||||||
@@ -138,6 +157,13 @@ void TogglesPanel::showEvent(QShowEvent *event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TogglesPanel::updateToggles() {
|
void TogglesPanel::updateToggles() {
|
||||||
|
auto disengage_on_accelerator_toggle = toggles["DisengageOnAccelerator"];
|
||||||
|
disengage_on_accelerator_toggle->setVisible(!params.getBool("AlwaysOnLateral"));
|
||||||
|
auto driver_camera_toggle = toggles["RecordFront"];
|
||||||
|
driver_camera_toggle->setVisible(!(params.getBool("DeviceManagement") && params.getBool("NoLogging") && params.getBool("NoUploads")));
|
||||||
|
auto nav_settings_left_toggle = toggles["NavSettingLeftSide"];
|
||||||
|
nav_settings_left_toggle->setVisible(!params.getBool("FullMap"));
|
||||||
|
|
||||||
auto experimental_mode_toggle = toggles["ExperimentalMode"];
|
auto experimental_mode_toggle = toggles["ExperimentalMode"];
|
||||||
auto op_long_toggle = toggles["ExperimentalLongitudinalEnabled"];
|
auto op_long_toggle = toggles["ExperimentalLongitudinalEnabled"];
|
||||||
const QString e2e_description = QString("%1<br>"
|
const QString e2e_description = QString("%1<br>"
|
||||||
@@ -173,7 +199,12 @@ void TogglesPanel::updateToggles() {
|
|||||||
op_long_toggle->setVisible(CP.getExperimentalLongitudinalAvailable() && !is_release);
|
op_long_toggle->setVisible(CP.getExperimentalLongitudinalAvailable() && !is_release);
|
||||||
if (hasLongitudinalControl(CP)) {
|
if (hasLongitudinalControl(CP)) {
|
||||||
// normal description and toggle
|
// normal description and toggle
|
||||||
experimental_mode_toggle->setEnabled(!params.getBool("ConditionalExperimental"));
|
bool conditional_experimental = params.getBool("ConditionalExperimental");
|
||||||
|
if (conditional_experimental) {
|
||||||
|
params.putBool("ExperimentalMode", true);
|
||||||
|
experimental_mode_toggle->refresh();
|
||||||
|
}
|
||||||
|
experimental_mode_toggle->setEnabled(!conditional_experimental);
|
||||||
experimental_mode_toggle->setDescription(e2e_description);
|
experimental_mode_toggle->setDescription(e2e_description);
|
||||||
long_personality_setting->setEnabled(true);
|
long_personality_setting->setEnabled(true);
|
||||||
} else {
|
} else {
|
||||||
@@ -225,7 +256,6 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
|
|||||||
});
|
});
|
||||||
addItem(resetCalibBtn);
|
addItem(resetCalibBtn);
|
||||||
|
|
||||||
if (!params.getBool("Passive")) {
|
|
||||||
auto retrainingBtn = new ButtonControl(tr("Review Training Guide"), tr("REVIEW"), tr("Review the rules, features, and limitations of openpilot"));
|
auto retrainingBtn = new ButtonControl(tr("Review Training Guide"), tr("REVIEW"), tr("Review the rules, features, and limitations of openpilot"));
|
||||||
connect(retrainingBtn, &ButtonControl::clicked, [=]() {
|
connect(retrainingBtn, &ButtonControl::clicked, [=]() {
|
||||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to review the training guide?"), tr("Review"), this)) {
|
if (ConfirmationDialog::confirm(tr("Are you sure you want to review the training guide?"), tr("Review"), this)) {
|
||||||
@@ -233,7 +263,6 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
addItem(retrainingBtn);
|
addItem(retrainingBtn);
|
||||||
}
|
|
||||||
|
|
||||||
if (Hardware::TICI()) {
|
if (Hardware::TICI()) {
|
||||||
auto regulatoryBtn = new ButtonControl(tr("Regulatory"), tr("VIEW"), "");
|
auto regulatoryBtn = new ButtonControl(tr("Regulatory"), tr("VIEW"), "");
|
||||||
@@ -258,44 +287,251 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
|
|||||||
addItem(translateBtn);
|
addItem(translateBtn);
|
||||||
|
|
||||||
// Delete driving footage button
|
// Delete driving footage button
|
||||||
auto deleteFootageBtn = new ButtonControl(tr("Delete Driving Data"), tr("DELETE"), tr("This button provides a swift and secure way to permanently delete all "
|
auto deleteDrivingDataBtn = new ButtonControl(tr("Delete Driving Data"), tr("DELETE"), tr("This button provides a swift and secure way to permanently delete all "
|
||||||
"stored driving footage and data from your device. Ideal for maintaining privacy or freeing up space.")
|
"stored driving footage and data from your device. Ideal for maintaining privacy or freeing up space.")
|
||||||
);
|
);
|
||||||
connect(deleteFootageBtn, &ButtonControl::clicked, [this]() {
|
connect(deleteDrivingDataBtn, &ButtonControl::clicked, [=]() {
|
||||||
if (!ConfirmationDialog::confirm(tr("Are you sure you want to permanently delete all of your driving footage and data?"), tr("Delete"), this)) return;
|
if (!ConfirmationDialog::confirm(tr("Are you sure you want to permanently delete all of your driving footage and data?"), tr("Delete"), this)) return;
|
||||||
std::thread([&] {
|
std::thread([&] {
|
||||||
|
deleteDrivingDataBtn->setValue(tr("Deleting footage..."));
|
||||||
std::system("rm -rf /data/media/0/realdata");
|
std::system("rm -rf /data/media/0/realdata");
|
||||||
|
deleteDrivingDataBtn->setValue(tr("Deleted!"));
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||||
|
deleteDrivingDataBtn->setValue("");
|
||||||
}).detach();
|
}).detach();
|
||||||
});
|
});
|
||||||
addItem(deleteFootageBtn);
|
addItem(deleteDrivingDataBtn);
|
||||||
|
|
||||||
// Panda flashing button
|
// Panda flashing button
|
||||||
auto flashPandaBtn = new ButtonControl(tr("Flash Panda"), tr("FLASH"), "Use this button to troubleshoot and update the Panda device's firmware.");
|
auto flashPandaBtn = new ButtonControl(tr("Flash Panda"), tr("FLASH"), tr("Use this button to troubleshoot and update the Panda device's firmware."));
|
||||||
connect(flashPandaBtn, &ButtonControl::clicked, [this]() {
|
connect(flashPandaBtn, &ButtonControl::clicked, [=]() {
|
||||||
if (!ConfirmationDialog::confirm(tr("Are you sure you want to flash the Panda?"), tr("Flash"), this)) return;
|
if (ConfirmationDialog::confirm(tr("Are you sure you want to flash the Panda?"), tr("Flash"), this)) {
|
||||||
|
std::thread([=]() {
|
||||||
|
flashPandaBtn->setValue(tr("Flashing..."));
|
||||||
|
|
||||||
QProcess process;
|
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.setWorkingDirectory("/data/openpilot/panda/board");
|
||||||
process.start("/bin/sh", QStringList{"-c", recoveryScript});
|
process.start("/bin/sh", QStringList{"-c", "./recover.py"});
|
||||||
process.waitForFinished();
|
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);
|
addItem(flashPandaBtn);
|
||||||
|
|
||||||
|
// Reset toggle button
|
||||||
|
auto resetTogglesBtn = new ButtonControl(tr("Reset Toggle Settings"), tr("RESET"), tr("Reset your toggle settings back to default."));
|
||||||
|
connect(resetTogglesBtn, &ButtonControl::clicked, [=]() {
|
||||||
|
if (!ConfirmationDialog::confirm(tr("Are you sure you want to completely reset your toggle settings? This is irreversible!"), tr("Reset"), this)) return;
|
||||||
|
std::thread([&] {
|
||||||
|
resetTogglesBtn->setValue(tr("Resetting toggles..."));
|
||||||
|
|
||||||
|
std::system("find /data/params -type f ! -name 'FrogPilotDrives' ! -name 'FrogPilotMinutes' ! -name 'FrogPilotKilometers' -exec rm {} +");
|
||||||
|
std::system("find /persist/params -type f ! -name 'FrogPilotDrives' ! -name 'FrogPilotMinutes' ! -name 'FrogPilotKilometers' -exec rm {} +");
|
||||||
|
|
||||||
|
Hardware::soft_reboot();
|
||||||
|
}).detach();
|
||||||
|
});
|
||||||
|
addItem(resetTogglesBtn);
|
||||||
|
|
||||||
|
// Backup FrogPilot
|
||||||
|
std::vector<QString> frogpilotBackupOptions{tr("Backup"), tr("Delete"), tr("Restore")};
|
||||||
|
FrogPilotButtonsControl *frogpilotBackup = new FrogPilotButtonsControl(tr("FrogPilot Backups"), tr("Backup, delete, or restore your FrogPilot backups."), "", frogpilotBackupOptions);
|
||||||
|
|
||||||
|
connect(frogpilotBackup, &FrogPilotButtonsControl::buttonClicked, [=](int id) {
|
||||||
|
QDir backupDir("/data/backups");
|
||||||
|
|
||||||
|
if (id == 0) {
|
||||||
|
QString nameSelection = InputDialog::getText(tr("Name your backup"), this, "", false, 1);
|
||||||
|
if (!nameSelection.isEmpty()) {
|
||||||
|
std::thread([=]() {
|
||||||
|
frogpilotBackup->setValue(tr("Backing up..."));
|
||||||
|
|
||||||
|
std::string fullBackupPath = backupDir.absolutePath().toStdString() + "/" + nameSelection.toStdString();
|
||||||
|
|
||||||
|
std::ostringstream commandStream;
|
||||||
|
commandStream << "mkdir -p " << std::quoted(fullBackupPath)
|
||||||
|
<< " && rsync -av /data/openpilot/ " << std::quoted(fullBackupPath + "/");
|
||||||
|
std::string command = commandStream.str();
|
||||||
|
|
||||||
|
int result = std::system(command.c_str());
|
||||||
|
if (result == 0) {
|
||||||
|
std::cout << "Backup successful to " << fullBackupPath << std::endl;
|
||||||
|
frogpilotBackup->setValue(tr("Success!"));
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||||
|
frogpilotBackup->setValue("");
|
||||||
|
} else {
|
||||||
|
std::cerr << "Backup failed with error code: " << result << std::endl;
|
||||||
|
frogpilotBackup->setValue(tr("Failed..."));
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||||
|
frogpilotBackup->setValue("");
|
||||||
|
}
|
||||||
|
}).detach();
|
||||||
|
}
|
||||||
|
} else if (id == 1) {
|
||||||
|
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
|
||||||
|
QString selection = MultiOptionDialog::getSelection(tr("Select a backup to delete"), backupNames, "", this);
|
||||||
|
if (!selection.isEmpty()) {
|
||||||
|
if (!ConfirmationDialog::confirm(tr("Are you sure you want to delete this backup?"), tr("Delete"), this)) return;
|
||||||
|
std::thread([=]() {
|
||||||
|
frogpilotBackup->setValue(tr("Deleting..."));
|
||||||
|
QDir dirToDelete(backupDir.absoluteFilePath(selection));
|
||||||
|
if (dirToDelete.removeRecursively()) {
|
||||||
|
frogpilotBackup->setValue(tr("Deleted!"));
|
||||||
|
} else {
|
||||||
|
frogpilotBackup->setValue(tr("Failed..."));
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||||
|
frogpilotBackup->setValue("");
|
||||||
|
}).detach();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
|
||||||
|
QString selection = MultiOptionDialog::getSelection(tr("Select a restore point"), backupNames, "", this);
|
||||||
|
if (!selection.isEmpty()) {
|
||||||
|
if (!ConfirmationDialog::confirm(tr("Are you sure you want to restore this version of FrogPilot?"), tr("Restore"), this)) return;
|
||||||
|
std::thread([=]() {
|
||||||
|
frogpilotBackup->setValue(tr("Restoring..."));
|
||||||
|
|
||||||
|
std::string sourcePath = backupDir.absolutePath().toStdString() + "/" + selection.toStdString();
|
||||||
|
std::string targetPath = "/data/safe_staging/finalized";
|
||||||
|
std::string consistentFilePath = targetPath + "/.overlay_consistent";
|
||||||
|
|
||||||
|
std::ostringstream commandStream;
|
||||||
|
commandStream << "rsync -av --delete --exclude='.overlay_consistent' " << std::quoted(sourcePath + "/") << " " << std::quoted(targetPath + "/");
|
||||||
|
std::string command = commandStream.str();
|
||||||
|
int result = std::system(command.c_str());
|
||||||
|
|
||||||
|
if (result == 0) {
|
||||||
|
std::cout << "Restore successful from " << sourcePath << " to " << targetPath << std::endl;
|
||||||
|
std::ofstream consistentFile(consistentFilePath);
|
||||||
|
if (consistentFile) {
|
||||||
|
consistentFile.close();
|
||||||
|
std::cout << ".overlay_consistent file created successfully." << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Failed to create .overlay_consistent file." << std::endl;
|
||||||
|
frogpilotBackup->setValue(tr("Failed..."));
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||||
|
frogpilotBackup->setValue("");
|
||||||
|
}
|
||||||
|
Hardware::soft_reboot();
|
||||||
|
} else {
|
||||||
|
std::cerr << "Restore failed with error code: " << result << std::endl;
|
||||||
|
}
|
||||||
|
}).detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
addItem(frogpilotBackup);
|
||||||
|
|
||||||
|
// Backup toggles
|
||||||
|
std::vector<QString> toggleBackupOptions{tr("Backup"), tr("Delete"), tr("Restore")};
|
||||||
|
FrogPilotButtonsControl *toggleBackup = new FrogPilotButtonsControl(tr("Toggle Backups"), tr("Backup, delete, or restore your toggle backups."), "", toggleBackupOptions);
|
||||||
|
|
||||||
|
connect(toggleBackup, &FrogPilotButtonsControl::buttonClicked, [=](int id) {
|
||||||
|
QDir backupDir("/data/toggle_backups");
|
||||||
|
|
||||||
|
if (id == 0) {
|
||||||
|
QString nameSelection = InputDialog::getText(tr("Name your backup"), this, "", false, 1);
|
||||||
|
if (!nameSelection.isEmpty()) {
|
||||||
|
std::thread([=]() {
|
||||||
|
toggleBackup->setValue(tr("Backing up..."));
|
||||||
|
|
||||||
|
std::string fullBackupPath = backupDir.absolutePath().toStdString() + "/" + nameSelection.toStdString() + "/";
|
||||||
|
|
||||||
|
std::ostringstream commandStream;
|
||||||
|
commandStream << "mkdir -p " << std::quoted(fullBackupPath)
|
||||||
|
<< " && rsync -av /data/params/d/ " << std::quoted(fullBackupPath);
|
||||||
|
std::string command = commandStream.str();
|
||||||
|
|
||||||
|
int result = std::system(command.c_str());
|
||||||
|
if (result == 0) {
|
||||||
|
std::cout << "Backup successful to " << fullBackupPath << std::endl;
|
||||||
|
toggleBackup->setValue(tr("Success!"));
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||||
|
toggleBackup->setValue("");
|
||||||
|
} else {
|
||||||
|
std::cerr << "Backup failed with error code: " << result << std::endl;
|
||||||
|
toggleBackup->setValue(tr("Failed..."));
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||||
|
toggleBackup->setValue("");
|
||||||
|
}
|
||||||
|
}).detach();
|
||||||
|
}
|
||||||
|
} else if (id == 1) {
|
||||||
|
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
|
||||||
|
QString selection = MultiOptionDialog::getSelection(tr("Select a backup to delete"), backupNames, "", this);
|
||||||
|
if (!selection.isEmpty()) {
|
||||||
|
if (!ConfirmationDialog::confirm(tr("Are you sure you want to delete this backup?"), tr("Delete"), this)) return;
|
||||||
|
std::thread([=]() {
|
||||||
|
toggleBackup->setValue(tr("Deleting..."));
|
||||||
|
QDir dirToDelete(backupDir.absoluteFilePath(selection));
|
||||||
|
if (dirToDelete.removeRecursively()) {
|
||||||
|
toggleBackup->setValue(tr("Deleted!"));
|
||||||
|
} else {
|
||||||
|
toggleBackup->setValue(tr("Failed..."));
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||||
|
toggleBackup->setValue("");
|
||||||
|
}).detach();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
|
||||||
|
QString selection = MultiOptionDialog::getSelection(tr("Select a restore point"), backupNames, "", this);
|
||||||
|
if (!selection.isEmpty()) {
|
||||||
|
if (!ConfirmationDialog::confirm(tr("Are you sure you want to restore this toggle backup?"), tr("Restore"), this)) return;
|
||||||
|
std::thread([=]() {
|
||||||
|
toggleBackup->setValue(tr("Restoring..."));
|
||||||
|
|
||||||
|
std::string sourcePath = backupDir.absolutePath().toStdString() + "/" + selection.toStdString() + "/";
|
||||||
|
std::string targetPath = "/data/params/d/";
|
||||||
|
|
||||||
|
std::ostringstream commandStream;
|
||||||
|
commandStream << "rsync -av --delete " << std::quoted(sourcePath) << " " << std::quoted(targetPath);
|
||||||
|
std::string command = commandStream.str();
|
||||||
|
|
||||||
|
int result = std::system(command.c_str());
|
||||||
|
|
||||||
|
if (result == 0) {
|
||||||
|
std::cout << "Restore successful from " << sourcePath << " to " << targetPath << std::endl;
|
||||||
|
toggleBackup->setValue(tr("Success!"));
|
||||||
|
paramsMemory.putBool("FrogPilotTogglesUpdated", true);
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||||
|
toggleBackup->setValue("");
|
||||||
|
paramsMemory.putBool("FrogPilotTogglesUpdated", false);
|
||||||
|
} else {
|
||||||
|
std::cerr << "Restore failed with error code: " << result << std::endl;
|
||||||
|
toggleBackup->setValue(tr("Failed..."));
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||||
|
toggleBackup->setValue("");
|
||||||
|
}
|
||||||
|
}).detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
addItem(toggleBackup);
|
||||||
|
|
||||||
|
auto lockDoorsButton = new ButtonControl(tr("Lock Doors"), tr("LOCK"), tr("Use this button to lock the doors on Toyota/Lexus vehicles."));
|
||||||
|
connect(lockDoorsButton, &ButtonControl::clicked, [this]() {
|
||||||
|
QProcess *process = new QProcess(this);
|
||||||
|
QString scriptPath = "/data/openpilot/frogpilot/controls/lib/lock_doors.py";
|
||||||
|
QStringList arguments{"--lock"};
|
||||||
|
process->start("python3", QStringList() << scriptPath << arguments);
|
||||||
|
});
|
||||||
|
addItem(lockDoorsButton);
|
||||||
|
|
||||||
QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
|
QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
|
||||||
for (auto btn : findChildren<ButtonControl *>()) {
|
for (auto btn : findChildren<ButtonControl *>()) {
|
||||||
btn->setEnabled(offroad);
|
btn->setEnabled(offroad);
|
||||||
@@ -311,6 +547,11 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
|
|||||||
power_layout->addWidget(reboot_btn);
|
power_layout->addWidget(reboot_btn);
|
||||||
QObject::connect(reboot_btn, &QPushButton::clicked, this, &DevicePanel::reboot);
|
QObject::connect(reboot_btn, &QPushButton::clicked, this, &DevicePanel::reboot);
|
||||||
|
|
||||||
|
QPushButton *softreboot_btn = new QPushButton(tr("Soft Reboot"));
|
||||||
|
softreboot_btn->setObjectName("softreboot_btn");
|
||||||
|
power_layout->addWidget(softreboot_btn);
|
||||||
|
QObject::connect(softreboot_btn, &QPushButton::clicked, this, &DevicePanel::softreboot);
|
||||||
|
|
||||||
QPushButton *poweroff_btn = new QPushButton(tr("Power Off"));
|
QPushButton *poweroff_btn = new QPushButton(tr("Power Off"));
|
||||||
poweroff_btn->setObjectName("poweroff_btn");
|
poweroff_btn->setObjectName("poweroff_btn");
|
||||||
power_layout->addWidget(poweroff_btn);
|
power_layout->addWidget(poweroff_btn);
|
||||||
@@ -321,6 +562,8 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setStyleSheet(R"(
|
setStyleSheet(R"(
|
||||||
|
#softreboot_btn { height: 120px; border-radius: 15px; background-color: #e2e22c; }
|
||||||
|
#softreboot_btn:pressed { background-color: #ffe224; }
|
||||||
#reboot_btn { height: 120px; border-radius: 15px; background-color: #393939; }
|
#reboot_btn { height: 120px; border-radius: 15px; background-color: #393939; }
|
||||||
#reboot_btn:pressed { background-color: #4a4a4a; }
|
#reboot_btn:pressed { background-color: #4a4a4a; }
|
||||||
#poweroff_btn { height: 120px; border-radius: 15px; background-color: #E22C2C; }
|
#poweroff_btn { height: 120px; border-radius: 15px; background-color: #E22C2C; }
|
||||||
@@ -366,6 +609,18 @@ void DevicePanel::reboot() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DevicePanel::softreboot() {
|
||||||
|
if (!uiState()->engaged()) {
|
||||||
|
if (ConfirmationDialog::confirm(tr("Are you sure you want to soft reboot?"), tr("Soft Reboot"), this)) {
|
||||||
|
if (!uiState()->engaged()) {
|
||||||
|
params.putBool("DoSoftReboot", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ConfirmationDialog::alert(tr("Disengage to Soft Reboot"), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DevicePanel::poweroff() {
|
void DevicePanel::poweroff() {
|
||||||
if (!uiState()->engaged()) {
|
if (!uiState()->engaged()) {
|
||||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to power off?"), tr("Power Off"), this)) {
|
if (ConfirmationDialog::confirm(tr("Are you sure you want to power off?"), tr("Power Off"), this)) {
|
||||||
@@ -379,6 +634,15 @@ void DevicePanel::poweroff() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsWindow::hideEvent(QHideEvent *event) {
|
||||||
|
closeParentToggle();
|
||||||
|
|
||||||
|
parentToggleOpen = false;
|
||||||
|
subParentToggleOpen = false;
|
||||||
|
|
||||||
|
previousScrollPosition = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsWindow::showEvent(QShowEvent *event) {
|
void SettingsWindow::showEvent(QShowEvent *event) {
|
||||||
setCurrentPanel(0);
|
setCurrentPanel(0);
|
||||||
}
|
}
|
||||||
@@ -396,33 +660,34 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
|
|||||||
// setup two main layouts
|
// setup two main layouts
|
||||||
sidebar_widget = new QWidget;
|
sidebar_widget = new QWidget;
|
||||||
QVBoxLayout *sidebar_layout = new QVBoxLayout(sidebar_widget);
|
QVBoxLayout *sidebar_layout = new QVBoxLayout(sidebar_widget);
|
||||||
sidebar_layout->setMargin(0);
|
|
||||||
panel_widget = new QStackedWidget();
|
panel_widget = new QStackedWidget();
|
||||||
|
|
||||||
// close button
|
// close button
|
||||||
QPushButton *close_btn = new QPushButton(tr("← Back"));
|
QPushButton *close_btn = new QPushButton(tr("← Back"));
|
||||||
close_btn->setStyleSheet(R"(
|
close_btn->setStyleSheet(R"(
|
||||||
QPushButton {
|
QPushButton {
|
||||||
font-size: 50px;
|
color: white;
|
||||||
padding-bottom: 0px;
|
|
||||||
border 1px grey solid;
|
|
||||||
border-radius: 25px;
|
border-radius: 25px;
|
||||||
background-color: #292929;
|
background: #292929;
|
||||||
|
font-size: 50px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
QPushButton:pressed {
|
QPushButton:pressed {
|
||||||
background-color: #3B3B3B;
|
color: #ADADAD;
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
close_btn->setFixedSize(300, 125);
|
close_btn->setFixedSize(300, 125);
|
||||||
sidebar_layout->addSpacing(10);
|
sidebar_layout->addSpacing(10);
|
||||||
sidebar_layout->addWidget(close_btn, 0, Qt::AlignRight);
|
sidebar_layout->addWidget(close_btn, 0, Qt::AlignRight);
|
||||||
QObject::connect(close_btn, &QPushButton::clicked, [this]() {
|
QObject::connect(close_btn, &QPushButton::clicked, [this]() {
|
||||||
if (frogPilotTogglesOpen) {
|
if (subParentToggleOpen) {
|
||||||
frogPilotTogglesOpen = false;
|
closeSubParentToggle();
|
||||||
this->closeParentToggle();
|
subParentToggleOpen = false;
|
||||||
|
} else if (parentToggleOpen) {
|
||||||
|
closeParentToggle();
|
||||||
|
parentToggleOpen = false;
|
||||||
} else {
|
} else {
|
||||||
this->closeSettings();
|
closeSettings();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -435,20 +700,19 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
|
|||||||
QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription);
|
QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription);
|
||||||
QObject::connect(toggles, &TogglesPanel::updateMetric, this, &SettingsWindow::updateMetric);
|
QObject::connect(toggles, &TogglesPanel::updateMetric, this, &SettingsWindow::updateMetric);
|
||||||
|
|
||||||
// FrogPilotControlsPanel *frogpilotControls = new FrogPilotControlsPanel(this);
|
FrogPilotControlsPanel *frogpilotControls = new FrogPilotControlsPanel(this);
|
||||||
// QObject::connect(frogpilotControls, &FrogPilotControlsPanel::closeParentToggle, this, [this]() {frogPilotTogglesOpen = false;});
|
QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openSubParentToggle, this, [this]() {subParentToggleOpen = true;});
|
||||||
// QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openParentToggle, this, [this]() {frogPilotTogglesOpen = true;});
|
QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openParentToggle, this, [this]() {parentToggleOpen = true;});
|
||||||
|
|
||||||
FrogPilotVisualsPanel *frogpilotVisuals = new FrogPilotVisualsPanel(this);
|
FrogPilotVisualsPanel *frogpilotVisuals = new FrogPilotVisualsPanel(this);
|
||||||
QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::closeParentToggle, this, [this]() {frogPilotTogglesOpen = false;});
|
QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::openParentToggle, this, [this]() {parentToggleOpen = true;});
|
||||||
QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::openParentToggle, this, [this]() {frogPilotTogglesOpen = true;});
|
|
||||||
|
|
||||||
QList<QPair<QString, QWidget *>> panels = {
|
QList<QPair<QString, QWidget *>> panels = {
|
||||||
{tr("Device"), device},
|
{tr("Device"), device},
|
||||||
{tr("Network"), new Networking(this)},
|
{tr("Network"), new Networking(this)},
|
||||||
{tr("Toggles"), toggles},
|
{tr("Toggles"), toggles},
|
||||||
{tr("Software"), new SoftwarePanel(this)},
|
{tr("Software"), new SoftwarePanel(this)},
|
||||||
// {tr("Controls"), frogpilotControls},
|
{tr("Controls"), frogpilotControls},
|
||||||
{tr("Navigation"), new FrogPilotNavigationPanel(this)},
|
{tr("Navigation"), new FrogPilotNavigationPanel(this)},
|
||||||
{tr("Vehicles"), new FrogPilotVehiclesPanel(this)},
|
{tr("Vehicles"), new FrogPilotVehiclesPanel(this)},
|
||||||
{tr("Visuals"), frogpilotVisuals},
|
{tr("Visuals"), frogpilotVisuals},
|
||||||
@@ -484,21 +748,24 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
|
|||||||
ScrollView *panel_frame = new ScrollView(panel, this);
|
ScrollView *panel_frame = new ScrollView(panel, this);
|
||||||
panel_widget->addWidget(panel_frame);
|
panel_widget->addWidget(panel_frame);
|
||||||
|
|
||||||
if (name == tr("Controls") || name == tr("Visuals")) {
|
if (name == tr("Controls")) {
|
||||||
QScrollBar *scrollbar = panel_frame->verticalScrollBar();
|
QScrollBar *scrollbar = panel_frame->verticalScrollBar();
|
||||||
|
|
||||||
QObject::connect(scrollbar, &QScrollBar::valueChanged, this, [this](int value) {
|
QObject::connect(scrollbar, &QScrollBar::valueChanged, this, [this](int value) {
|
||||||
if (!frogPilotTogglesOpen) {
|
if (!parentToggleOpen) {
|
||||||
previousScrollPosition = value;
|
previousScrollPosition = value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
QObject::connect(scrollbar, &QScrollBar::rangeChanged, this, [this, panel_frame]() {
|
QObject::connect(scrollbar, &QScrollBar::rangeChanged, this, [this, panel_frame]() {
|
||||||
|
if (!parentToggleOpen) {
|
||||||
panel_frame->restorePosition(previousScrollPosition);
|
panel_frame->restorePosition(previousScrollPosition);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() {
|
QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() {
|
||||||
|
closeParentToggle();
|
||||||
previousScrollPosition = 0;
|
previousScrollPosition = 0;
|
||||||
btn->setChecked(true);
|
btn->setChecked(true);
|
||||||
panel_widget->setCurrentWidget(w);
|
panel_widget->setCurrentWidget(w);
|
||||||
|
|||||||
27
selfdrive/ui/qt/offroad/settings.h
Executable file → Normal file
27
selfdrive/ui/qt/offroad/settings.h
Executable file → Normal file
@@ -11,6 +11,7 @@
|
|||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
|
||||||
|
#include "selfdrive/ui/ui.h"
|
||||||
#include "selfdrive/ui/qt/util.h"
|
#include "selfdrive/ui/qt/util.h"
|
||||||
#include "selfdrive/ui/qt/widgets/controls.h"
|
#include "selfdrive/ui/qt/widgets/controls.h"
|
||||||
|
|
||||||
@@ -25,6 +26,9 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
void showEvent(QShowEvent *event) override;
|
void showEvent(QShowEvent *event) override;
|
||||||
|
|
||||||
|
// FrogPilot widgets
|
||||||
|
void hideEvent(QHideEvent *event) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void closeSettings();
|
void closeSettings();
|
||||||
void reviewTrainingGuide();
|
void reviewTrainingGuide();
|
||||||
@@ -33,7 +37,9 @@ signals:
|
|||||||
|
|
||||||
// FrogPilot signals
|
// FrogPilot signals
|
||||||
void closeParentToggle();
|
void closeParentToggle();
|
||||||
|
void closeSubParentToggle();
|
||||||
void updateMetric();
|
void updateMetric();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPushButton *sidebar_alert_widget;
|
QPushButton *sidebar_alert_widget;
|
||||||
QWidget *sidebar_widget;
|
QWidget *sidebar_widget;
|
||||||
@@ -41,7 +47,9 @@ private:
|
|||||||
QStackedWidget *panel_widget;
|
QStackedWidget *panel_widget;
|
||||||
|
|
||||||
// FrogPilot variables
|
// FrogPilot variables
|
||||||
bool frogPilotTogglesOpen;
|
bool parentToggleOpen;
|
||||||
|
bool subParentToggleOpen;
|
||||||
|
|
||||||
int previousScrollPosition;
|
int previousScrollPosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -56,10 +64,14 @@ signals:
|
|||||||
private slots:
|
private slots:
|
||||||
void poweroff();
|
void poweroff();
|
||||||
void reboot();
|
void reboot();
|
||||||
|
void softreboot();
|
||||||
void updateCalibDescription();
|
void updateCalibDescription();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Params params;
|
Params params;
|
||||||
|
|
||||||
|
// FrogPilot variables
|
||||||
|
Params paramsMemory{"/dev/shm/params"};
|
||||||
};
|
};
|
||||||
|
|
||||||
class TogglesPanel : public ListWidget {
|
class TogglesPanel : public ListWidget {
|
||||||
@@ -75,6 +87,9 @@ signals:
|
|||||||
public slots:
|
public slots:
|
||||||
void expandToggleDescription(const QString ¶m);
|
void expandToggleDescription(const QString ¶m);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void updateState(const UIState &s);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Params params;
|
Params params;
|
||||||
std::map<std::string, ParamControl*> toggles;
|
std::map<std::string, ParamControl*> toggles;
|
||||||
@@ -97,7 +112,6 @@ private:
|
|||||||
|
|
||||||
QLabel *onroadLbl;
|
QLabel *onroadLbl;
|
||||||
LabelControl *versionLbl;
|
LabelControl *versionLbl;
|
||||||
ButtonControl *errorLogBtn;
|
|
||||||
ButtonControl *installBtn;
|
ButtonControl *installBtn;
|
||||||
ButtonControl *downloadBtn;
|
ButtonControl *downloadBtn;
|
||||||
ButtonControl *targetBranchBtn;
|
ButtonControl *targetBranchBtn;
|
||||||
@@ -106,11 +120,6 @@ private:
|
|||||||
ParamWatcher *fs_watch;
|
ParamWatcher *fs_watch;
|
||||||
|
|
||||||
// FrogPilot variables
|
// FrogPilot variables
|
||||||
void automaticUpdate();
|
Params paramsMemory{"/dev/shm/params"};
|
||||||
|
UIScene &scene;
|
||||||
ButtonControl *updateTime;
|
|
||||||
|
|
||||||
int deviceShutdown;
|
|
||||||
int schedule;
|
|
||||||
int time;
|
|
||||||
};
|
};
|
||||||
|
|||||||
152
selfdrive/ui/qt/offroad/software_settings.cc
Executable file → Normal file
152
selfdrive/ui/qt/offroad/software_settings.cc
Executable file → Normal file
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <iomanip>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
@@ -18,13 +16,14 @@
|
|||||||
#include "selfdrive/ui/qt/widgets/input.h"
|
#include "selfdrive/ui/qt/widgets/input.h"
|
||||||
#include "system/hardware/hw.h"
|
#include "system/hardware/hw.h"
|
||||||
|
|
||||||
|
#include "selfdrive/frogpilot/ui/qt/widgets/frogpilot_controls.h"
|
||||||
|
|
||||||
void SoftwarePanel::checkForUpdates() {
|
void SoftwarePanel::checkForUpdates() {
|
||||||
std::system("pkill -SIGUSR1 -f selfdrive.updated");
|
std::system("pkill -SIGUSR1 -f selfdrive.updated.updated");
|
||||||
}
|
}
|
||||||
|
|
||||||
SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent), scene(uiState()->scene) {
|
||||||
onroadLbl = new QLabel(tr("Updates are only downloaded while the car is off."));
|
onroadLbl = new QLabel(tr("Updates are only downloaded while the car is off or in park."));
|
||||||
onroadLbl->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left; padding-top: 30px; padding-bottom: 30px;");
|
onroadLbl->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left; padding-top: 30px; padding-bottom: 30px;");
|
||||||
addItem(onroadLbl);
|
addItem(onroadLbl);
|
||||||
|
|
||||||
@@ -32,40 +31,17 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
|||||||
versionLbl = new LabelControl(tr("Current Version"), "");
|
versionLbl = new LabelControl(tr("Current Version"), "");
|
||||||
addItem(versionLbl);
|
addItem(versionLbl);
|
||||||
|
|
||||||
// update scheduler
|
// automatic updates toggle
|
||||||
std::vector<QString> scheduleOptions{tr("Manually"), tr("Daily"), tr("Weekly")};
|
ParamControl *automaticUpdatesToggle = new ParamControl("AutomaticUpdates", tr("Automatically Update FrogPilot"),
|
||||||
ButtonParamControl *preferredSchedule = new ButtonParamControl("UpdateSchedule", tr("Update Scheduler"),
|
tr("FrogPilot will automatically update itself and it's assets when you're offroad and connected to Wi-Fi."), "");
|
||||||
tr("Choose the update frequency for FrogPilot's automatic updates.\n\n"
|
connect(automaticUpdatesToggle, &ToggleControl::toggleFlipped, [this]() {
|
||||||
"This feature will handle the download, installation, and device reboot for a seamless 'Set and Forget' experience.\n\n"
|
std::thread([this]() {
|
||||||
"Weekly updates start at midnight every Sunday."),
|
paramsMemory.putBool("FrogPilotTogglesUpdated", true);
|
||||||
"",
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
scheduleOptions);
|
paramsMemory.putBool("FrogPilotTogglesUpdated", false);
|
||||||
schedule = params.getInt("UpdateSchedule");
|
}).detach();
|
||||||
addItem(preferredSchedule);
|
|
||||||
|
|
||||||
updateTime = new ButtonControl(tr("Update Time"), tr("SELECT"));
|
|
||||||
QStringList hours;
|
|
||||||
for (int h = 0; h < 24; h++) {
|
|
||||||
int displayHour = (h % 12 == 0) ? 12 : h % 12;
|
|
||||||
QString meridiem = (h < 12) ? "AM" : "PM";
|
|
||||||
hours << QString("%1:00 %2").arg(displayHour).arg(meridiem)
|
|
||||||
<< QString("%1:30 %2").arg(displayHour).arg(meridiem);
|
|
||||||
}
|
|
||||||
QObject::connect(updateTime, &ButtonControl::clicked, [=]() {
|
|
||||||
int currentHourIndex = params.getInt("UpdateTime");
|
|
||||||
QString currentHourLabel = hours[currentHourIndex];
|
|
||||||
|
|
||||||
QString selection = MultiOptionDialog::getSelection(tr("Select a time to automatically update"), hours, currentHourLabel, this);
|
|
||||||
if (!selection.isEmpty()) {
|
|
||||||
int selectedHourIndex = hours.indexOf(selection);
|
|
||||||
params.putInt("UpdateTime", selectedHourIndex);
|
|
||||||
updateTime->setValue(selection);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
time = params.getInt("UpdateTime");
|
addItem(automaticUpdatesToggle);
|
||||||
deviceShutdown = params.getInt("DeviceShutdown") * 3600;
|
|
||||||
updateTime->setValue(hours[time]);
|
|
||||||
addItem(updateTime);
|
|
||||||
|
|
||||||
// download update btn
|
// download update btn
|
||||||
downloadBtn = new ButtonControl(tr("Download"), tr("CHECK"));
|
downloadBtn = new ButtonControl(tr("Download"), tr("CHECK"));
|
||||||
@@ -74,8 +50,9 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
|||||||
if (downloadBtn->text() == tr("CHECK")) {
|
if (downloadBtn->text() == tr("CHECK")) {
|
||||||
checkForUpdates();
|
checkForUpdates();
|
||||||
} else {
|
} else {
|
||||||
std::system("pkill -SIGHUP -f selfdrive.updated");
|
std::system("pkill -SIGHUP -f selfdrive.updated.updated");
|
||||||
}
|
}
|
||||||
|
paramsMemory.putBool("ManualUpdateInitiated", true);
|
||||||
});
|
});
|
||||||
addItem(downloadBtn);
|
addItem(downloadBtn);
|
||||||
|
|
||||||
@@ -92,6 +69,11 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
|||||||
connect(targetBranchBtn, &ButtonControl::clicked, [=]() {
|
connect(targetBranchBtn, &ButtonControl::clicked, [=]() {
|
||||||
auto current = params.get("GitBranch");
|
auto current = params.get("GitBranch");
|
||||||
QStringList branches = QString::fromStdString(params.get("UpdaterAvailableBranches")).split(",");
|
QStringList branches = QString::fromStdString(params.get("UpdaterAvailableBranches")).split(",");
|
||||||
|
if (!Params("/persist/params").getBool("FrogsGoMoo")) {
|
||||||
|
branches.removeAll("FrogPilot-Development");
|
||||||
|
branches.removeAll("FrogPilot-New");
|
||||||
|
branches.removeAll("MAKE-PRS-HERE");
|
||||||
|
}
|
||||||
for (QString b : {current.c_str(), "devel-staging", "devel", "nightly", "master-ci", "master"}) {
|
for (QString b : {current.c_str(), "devel-staging", "devel", "nightly", "master-ci", "master"}) {
|
||||||
auto i = branches.indexOf(b);
|
auto i = branches.indexOf(b);
|
||||||
if (i >= 0) {
|
if (i >= 0) {
|
||||||
@@ -122,7 +104,7 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
|||||||
addItem(uninstallBtn);
|
addItem(uninstallBtn);
|
||||||
|
|
||||||
// error log button
|
// error log button
|
||||||
errorLogBtn = new ButtonControl(tr("Error Log"), tr("VIEW"), "View the error log for debugging purposes when openpilot crashes.");
|
auto errorLogBtn = new ButtonControl(tr("Error Log"), tr("VIEW"), tr("View the error log for openpilot crashes."));
|
||||||
connect(errorLogBtn, &ButtonControl::clicked, [=]() {
|
connect(errorLogBtn, &ButtonControl::clicked, [=]() {
|
||||||
std::string txt = util::read_file("/data/community/crashes/error.txt");
|
std::string txt = util::read_file("/data/community/crashes/error.txt");
|
||||||
ConfirmationDialog::rich(QString::fromStdString(txt), this);
|
ConfirmationDialog::rich(QString::fromStdString(txt), this);
|
||||||
@@ -131,8 +113,6 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
|||||||
|
|
||||||
fs_watch = new ParamWatcher(this);
|
fs_watch = new ParamWatcher(this);
|
||||||
QObject::connect(fs_watch, &ParamWatcher::paramChanged, [=](const QString ¶m_name, const QString ¶m_value) {
|
QObject::connect(fs_watch, &ParamWatcher::paramChanged, [=](const QString ¶m_name, const QString ¶m_value) {
|
||||||
schedule = params.getInt("UpdateSchedule");
|
|
||||||
time = params.getInt("UpdateTime");
|
|
||||||
updateLabels();
|
updateLabels();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -141,8 +121,6 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
|||||||
updateLabels();
|
updateLabels();
|
||||||
});
|
});
|
||||||
|
|
||||||
QObject::connect(uiState(), &UIState::uiUpdate, this, &SoftwarePanel::automaticUpdate);
|
|
||||||
|
|
||||||
updateLabels();
|
updateLabels();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,16 +138,15 @@ void SoftwarePanel::updateLabels() {
|
|||||||
fs_watch->addParam("UpdaterState");
|
fs_watch->addParam("UpdaterState");
|
||||||
fs_watch->addParam("UpdateAvailable");
|
fs_watch->addParam("UpdateAvailable");
|
||||||
|
|
||||||
fs_watch->addParam("UpdateSchedule");
|
|
||||||
fs_watch->addParam("UpdateTime");
|
|
||||||
|
|
||||||
if (!isVisible()) {
|
if (!isVisible()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// updater only runs offroad
|
// updater only runs offroad or when parked
|
||||||
onroadLbl->setVisible(is_onroad);
|
bool parked = scene.parked;
|
||||||
downloadBtn->setVisible(!is_onroad);
|
|
||||||
|
onroadLbl->setVisible(is_onroad && !parked);
|
||||||
|
downloadBtn->setVisible(!is_onroad || parked);
|
||||||
|
|
||||||
// download update
|
// download update
|
||||||
QString updater_state = QString::fromStdString(params.get("UpdaterState"));
|
QString updater_state = QString::fromStdString(params.get("UpdaterState"));
|
||||||
@@ -201,82 +178,9 @@ void SoftwarePanel::updateLabels() {
|
|||||||
versionLbl->setText(QString::fromStdString(params.get("UpdaterCurrentDescription")));
|
versionLbl->setText(QString::fromStdString(params.get("UpdaterCurrentDescription")));
|
||||||
versionLbl->setDescription(QString::fromStdString(params.get("UpdaterCurrentReleaseNotes")));
|
versionLbl->setDescription(QString::fromStdString(params.get("UpdaterCurrentReleaseNotes")));
|
||||||
|
|
||||||
installBtn->setVisible(!is_onroad && params.getBool("UpdateAvailable"));
|
installBtn->setVisible((!is_onroad || parked) && params.getBool("UpdateAvailable"));
|
||||||
installBtn->setValue(QString::fromStdString(params.get("UpdaterNewDescription")));
|
installBtn->setValue(QString::fromStdString(params.get("UpdaterNewDescription")));
|
||||||
installBtn->setDescription(QString::fromStdString(params.get("UpdaterNewReleaseNotes")));
|
installBtn->setDescription(QString::fromStdString(params.get("UpdaterNewReleaseNotes")));
|
||||||
|
|
||||||
updateTime->setVisible(params.getInt("UpdateSchedule"));
|
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoftwarePanel::automaticUpdate() {
|
|
||||||
static int timer = 0;
|
|
||||||
static std::chrono::system_clock::time_point lastOffroadTime;
|
|
||||||
|
|
||||||
if (!is_onroad) {
|
|
||||||
timer = (timer == 0) ? 0 : std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - lastOffroadTime).count();
|
|
||||||
lastOffroadTime = std::chrono::system_clock::now();
|
|
||||||
} else {
|
|
||||||
timer = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isWifiConnected = (*uiState()->sm)["deviceState"].getDeviceState().getNetworkType() == cereal::DeviceState::NetworkType::WIFI;
|
|
||||||
if (schedule == 0 || is_onroad || !isWifiConnected || isVisible()) return;
|
|
||||||
|
|
||||||
static bool isDownloadCompleted = false;
|
|
||||||
if (isDownloadCompleted && params.getBool("UpdateAvailable")) {
|
|
||||||
params.putBool(timer > deviceShutdown ? "DoShutdown" : "DoReboot", true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int updateHour = time / 2;
|
|
||||||
int updateMinute = (time % 2) * 30;
|
|
||||||
|
|
||||||
if (updateHour >= 1 && updateHour <= 11 && time >= 24) {
|
|
||||||
updateHour += 12;
|
|
||||||
} else if (updateHour == 12 && time < 24) {
|
|
||||||
updateHour = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::time_t currentTimeT = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
|
||||||
std::tm now = *std::localtime(¤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 <memory>
|
||||||
|
|
||||||
#include <QElapsedTimer>
|
#include <QMovie>
|
||||||
|
#include <QLabel>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QStackedLayout>
|
#include <QStackedLayout>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
@@ -16,7 +17,6 @@
|
|||||||
const int btn_size = 192;
|
const int btn_size = 192;
|
||||||
const int img_size = (btn_size / 4) * 3;
|
const int img_size = (btn_size / 4) * 3;
|
||||||
|
|
||||||
// FrogPilot global variables
|
|
||||||
static double fps;
|
static double fps;
|
||||||
|
|
||||||
// ***** onroad widgets *****
|
// ***** onroad widgets *****
|
||||||
@@ -42,13 +42,14 @@ class Compass : public QWidget {
|
|||||||
public:
|
public:
|
||||||
explicit Compass(QWidget *parent = nullptr);
|
explicit Compass(QWidget *parent = nullptr);
|
||||||
|
|
||||||
void initializeStaticElements();
|
void updateState();
|
||||||
void updateState(int bearing_deg);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *event) override;
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
UIScene &scene;
|
||||||
|
|
||||||
int bearingDeg;
|
int bearingDeg;
|
||||||
int circleOffset;
|
int circleOffset;
|
||||||
int compassSize;
|
int compassSize;
|
||||||
@@ -56,10 +57,37 @@ private:
|
|||||||
int innerCompass;
|
int innerCompass;
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
|
|
||||||
QPixmap compassInnerImg;
|
QPixmap compassInnerImg;
|
||||||
QPixmap staticElements;
|
QPixmap staticElements;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DistanceButton : public QPushButton {
|
||||||
|
public:
|
||||||
|
explicit DistanceButton(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
void buttonPressed();
|
||||||
|
void buttonReleased();
|
||||||
|
void updateState();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Params paramsMemory{"/dev/shm/params"};
|
||||||
|
|
||||||
|
UIScene &scene;
|
||||||
|
|
||||||
|
bool trafficModeActive;
|
||||||
|
|
||||||
|
int personality;
|
||||||
|
|
||||||
|
QElapsedTimer transitionTimer;
|
||||||
|
|
||||||
|
QVector<std::pair<QPixmap, QString>> profile_data;
|
||||||
|
QVector<std::pair<QPixmap, QString>> profile_data_kaofui;
|
||||||
|
};
|
||||||
|
|
||||||
class ExperimentalButton : public QPushButton {
|
class ExperimentalButton : public QPushButton {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -78,14 +106,24 @@ private:
|
|||||||
bool engageable;
|
bool engageable;
|
||||||
|
|
||||||
// FrogPilot variables
|
// FrogPilot variables
|
||||||
|
Params paramsMemory{"/dev/shm/params"};
|
||||||
UIScene &scene;
|
UIScene &scene;
|
||||||
|
|
||||||
std::map<int, QPixmap> wheelImages;
|
QMap<int, QPixmap> wheelImages;
|
||||||
|
QMap<int, QMovie*> wheelImagesGif;
|
||||||
|
|
||||||
|
QMovie engage_gif;
|
||||||
|
QLabel *gifLabel;
|
||||||
|
|
||||||
|
bool docRandomEventTriggered;
|
||||||
bool firefoxRandomEventTriggered;
|
bool firefoxRandomEventTriggered;
|
||||||
bool rotatingWheel;
|
bool rotatingWheel;
|
||||||
|
bool treeFiddyRandomEventTriggered;
|
||||||
|
bool weebRandomEventTriggered;
|
||||||
|
|
||||||
int steeringAngleDeg;
|
int steeringAngleDeg;
|
||||||
int wheelIcon;
|
int wheelIcon;
|
||||||
|
int wheelIconGif;
|
||||||
int y_offset;
|
int y_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -102,28 +140,25 @@ private:
|
|||||||
QPixmap settings_img;
|
QPixmap settings_img;
|
||||||
};
|
};
|
||||||
|
|
||||||
// FrogPilot buttons
|
class PedalIcons : public QWidget {
|
||||||
class PersonalityButton : public QPushButton {
|
Q_OBJECT
|
||||||
public:
|
|
||||||
explicit PersonalityButton(QWidget *parent = 0);
|
|
||||||
|
|
||||||
void checkUpdate();
|
public:
|
||||||
void handleClick();
|
explicit PedalIcons(QWidget *parent = 0);
|
||||||
void updateState();
|
void updateState();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void paintEvent(QPaintEvent *event) override;
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
Params params;
|
QPixmap brake_pedal_img;
|
||||||
Params paramsMemory{"/dev/shm/params"};
|
QPixmap gas_pedal_img;
|
||||||
|
|
||||||
UIScene &scene;
|
UIScene &scene;
|
||||||
|
|
||||||
int personalityProfile = 0;
|
bool accelerating;
|
||||||
|
bool decelerating;
|
||||||
|
|
||||||
QElapsedTimer transitionTimer;
|
float acceleration;
|
||||||
|
|
||||||
QVector<std::pair<QPixmap, QString>> profile_data;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// container window for the NVG UI
|
// container window for the NVG UI
|
||||||
@@ -163,79 +198,86 @@ private:
|
|||||||
bool wide_cam_requested = false;
|
bool wide_cam_requested = false;
|
||||||
|
|
||||||
// FrogPilot widgets
|
// FrogPilot widgets
|
||||||
|
void initializeFrogPilotWidgets();
|
||||||
|
void paintFrogPilotWidgets(QPainter &p);
|
||||||
|
void updateFrogPilotWidgets();
|
||||||
|
|
||||||
void drawLeadInfo(QPainter &p);
|
void drawLeadInfo(QPainter &p);
|
||||||
|
void drawSLCConfirmation(QPainter &p);
|
||||||
void drawStatusBar(QPainter &p);
|
void drawStatusBar(QPainter &p);
|
||||||
void drawTurnSignals(QPainter &p);
|
void drawTurnSignals(QPainter &p);
|
||||||
void initializeFrogPilotWidgets();
|
|
||||||
void updateFrogPilotWidgets(QPainter &p);
|
|
||||||
|
|
||||||
// FrogPilot variables
|
// FrogPilot variables
|
||||||
Params paramsMemory{"/dev/shm/params"};
|
Params paramsMemory{"/dev/shm/params"};
|
||||||
|
|
||||||
UIScene &scene;
|
UIScene &scene;
|
||||||
|
|
||||||
Compass *compass_img;
|
Compass *compass_img;
|
||||||
PersonalityButton *personality_btn;
|
DistanceButton *distance_btn;
|
||||||
|
PedalIcons *pedal_icons;
|
||||||
ScreenRecorder *recorder_btn;
|
ScreenRecorder *recorder_btn;
|
||||||
|
|
||||||
QHBoxLayout *bottom_layout;
|
QHBoxLayout *bottom_layout;
|
||||||
|
|
||||||
bool accelerationPath;
|
bool alwaysOnLateralActive;
|
||||||
bool adjacentPath;
|
bool bigMapOpen;
|
||||||
bool alwaysOnLateral;
|
|
||||||
bool blindSpotLeft;
|
bool blindSpotLeft;
|
||||||
bool blindSpotRight;
|
bool blindSpotRight;
|
||||||
bool compass;
|
bool compass;
|
||||||
bool conditionalExperimental;
|
|
||||||
bool experimentalMode;
|
bool experimentalMode;
|
||||||
bool hideSpeed;
|
|
||||||
bool leadInfo;
|
bool leadInfo;
|
||||||
bool mapOpen;
|
bool mapOpen;
|
||||||
bool muteDM;
|
bool onroadDistanceButton;
|
||||||
bool onroadAdjustableProfiles;
|
|
||||||
bool reverseCruise;
|
|
||||||
bool roadNameUI;
|
bool roadNameUI;
|
||||||
bool showDriverCamera;
|
bool showAlwaysOnLateralStatusBar;
|
||||||
|
bool showConditionalExperimentalStatusBar;
|
||||||
bool showSLCOffset;
|
bool showSLCOffset;
|
||||||
bool slcOverridden;
|
bool slcOverridden;
|
||||||
|
bool speedLimitController;
|
||||||
|
bool trafficModeActive;
|
||||||
bool turnSignalLeft;
|
bool turnSignalLeft;
|
||||||
bool turnSignalRight;
|
bool turnSignalRight;
|
||||||
bool useSI;
|
|
||||||
bool useViennaSLCSign;
|
bool useViennaSLCSign;
|
||||||
double maxAcceleration;
|
bool vtscControllingCurve;
|
||||||
|
|
||||||
float cruiseAdjustment;
|
float cruiseAdjustment;
|
||||||
|
float distanceConversion;
|
||||||
|
float laneDetectionWidth;
|
||||||
float laneWidthLeft;
|
float laneWidthLeft;
|
||||||
float laneWidthRight;
|
float laneWidthRight;
|
||||||
float slcOverriddenSpeed;
|
|
||||||
float slcSpeedLimit;
|
|
||||||
float slcSpeedLimitOffset;
|
float slcSpeedLimitOffset;
|
||||||
int bearingDeg;
|
float speedConversion;
|
||||||
|
|
||||||
|
int alertSize;
|
||||||
int cameraView;
|
int cameraView;
|
||||||
int conditionalSpeed;
|
|
||||||
int conditionalSpeedLead;
|
|
||||||
int conditionalStatus;
|
int conditionalStatus;
|
||||||
|
int currentHolidayTheme;
|
||||||
int customColors;
|
int customColors;
|
||||||
int customSignals;
|
int customSignals;
|
||||||
int desiredFollow;
|
|
||||||
int obstacleDistance;
|
int obstacleDistance;
|
||||||
int obstacleDistanceStock;
|
int obstacleDistanceStock;
|
||||||
int stoppedEquivalence;
|
|
||||||
int totalFrames = 8;
|
int totalFrames = 8;
|
||||||
QTimer *animationTimer;
|
|
||||||
|
QString leadDistanceUnit;
|
||||||
|
QString leadSpeedUnit;
|
||||||
|
|
||||||
size_t animationFrameIndex;
|
size_t animationFrameIndex;
|
||||||
|
|
||||||
inline QColor greenColor(int alpha = 242) { return QColor(23, 134, 68, alpha); }
|
std::unordered_map<int, std::tuple<QString, QColor, std::map<double, QBrush>>> themeConfiguration;
|
||||||
|
std::unordered_map<int, std::tuple<QString, QColor, std::map<double, QBrush>>> holidayThemeConfiguration;
|
||||||
std::unordered_map<int, std::pair<QString, std::pair<QColor, std::map<double, QBrush>>>> themeConfiguration;
|
|
||||||
std::vector<QPixmap> signalImgVector;
|
std::vector<QPixmap> signalImgVector;
|
||||||
|
|
||||||
|
QTimer *animationTimer;
|
||||||
|
|
||||||
|
inline QColor blueColor(int alpha = 255) { return QColor(0, 150, 255, alpha); }
|
||||||
|
inline QColor greenColor(int alpha = 242) { return QColor(23, 134, 68, alpha); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintGL() override;
|
void paintGL() override;
|
||||||
void initializeGL() override;
|
void initializeGL() override;
|
||||||
void showEvent(QShowEvent *event) override;
|
void showEvent(QShowEvent *event) override;
|
||||||
void updateFrameMat() override;
|
void updateFrameMat() override;
|
||||||
void drawLaneLines(QPainter &painter, const UIState *s);
|
void drawLaneLines(QPainter &painter, const UIState *s);
|
||||||
void drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd);
|
void drawLead(QPainter &painter, const cereal::ModelDataV2::LeadDataV3::Reader &lead_data, const QPointF &vd, const float v_ego);
|
||||||
void drawHud(QPainter &p);
|
void drawHud(QPainter &p);
|
||||||
void drawDriverState(QPainter &painter, const UIState *s);
|
void drawDriverState(QPainter &painter, const UIState *s);
|
||||||
void paintEvent(QPaintEvent *event) override;
|
void paintEvent(QPaintEvent *event) override;
|
||||||
@@ -270,6 +312,8 @@ private:
|
|||||||
|
|
||||||
// FrogPilot variables
|
// FrogPilot variables
|
||||||
UIScene &scene;
|
UIScene &scene;
|
||||||
|
Params params;
|
||||||
|
Params paramsMemory{"/dev/shm/params"};
|
||||||
|
|
||||||
QPoint timeoutPoint = QPoint(420, 69);
|
QPoint timeoutPoint = QPoint(420, 69);
|
||||||
QTimer clickTimer;
|
QTimer clickTimer;
|
||||||
|
|||||||
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);
|
main_layout->addSpacing(60);
|
||||||
|
|
||||||
body = new QLabel(tr("Press confirm to erase all content and settings. Press cancel to resume boot."));
|
body = new QLabel(tr("System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot."));
|
||||||
body->setWordWrap(true);
|
body->setWordWrap(true);
|
||||||
body->setStyleSheet("font-size: 80px; font-weight: light;");
|
body->setStyleSheet("font-size: 80px; font-weight: light;");
|
||||||
main_layout->addWidget(body, 1, Qt::AlignTop | Qt::AlignLeft);
|
main_layout->addWidget(body, 1, Qt::AlignTop | Qt::AlignLeft);
|
||||||
@@ -97,11 +97,6 @@ Reset::Reset(ResetMode mode, QWidget *parent) : QWidget(parent) {
|
|||||||
body->setText(tr("Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device."));
|
body->setText(tr("Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device."));
|
||||||
}
|
}
|
||||||
|
|
||||||
// automatically start if we're just finishing up an ABL reset
|
|
||||||
if (mode == ResetMode::FORMAT) {
|
|
||||||
startReset();
|
|
||||||
}
|
|
||||||
|
|
||||||
setStyleSheet(R"(
|
setStyleSheet(R"(
|
||||||
* {
|
* {
|
||||||
font-family: Inter;
|
font-family: Inter;
|
||||||
@@ -129,8 +124,6 @@ int main(int argc, char *argv[]) {
|
|||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
if (strcmp(argv[1], "--recover") == 0) {
|
if (strcmp(argv[1], "--recover") == 0) {
|
||||||
mode = ResetMode::RECOVER;
|
mode = ResetMode::RECOVER;
|
||||||
} else if (strcmp(argv[1], "--format") == 0) {
|
|
||||||
mode = ResetMode::FORMAT;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
selfdrive/ui/qt/setup/reset.h
Executable file → Normal file
1
selfdrive/ui/qt/setup/reset.h
Executable file → Normal file
@@ -5,7 +5,6 @@
|
|||||||
enum ResetMode {
|
enum ResetMode {
|
||||||
USER_RESET, // user initiated a factory reset from openpilot
|
USER_RESET, // user initiated a factory reset from openpilot
|
||||||
RECOVER, // userdata is corrupt for some reason, give a chance to recover
|
RECOVER, // userdata is corrupt for some reason, give a chance to recover
|
||||||
FORMAT, // finish up an ABL factory reset
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Reset : public QWidget {
|
class Reset : public QWidget {
|
||||||
|
|||||||
122
selfdrive/ui/qt/setup/setup.cc
Executable file → Normal file
122
selfdrive/ui/qt/setup/setup.cc
Executable file → Normal file
@@ -20,7 +20,7 @@
|
|||||||
#include "selfdrive/ui/qt/widgets/input.h"
|
#include "selfdrive/ui/qt/widgets/input.h"
|
||||||
|
|
||||||
const std::string USER_AGENT = "AGNOSSetup-";
|
const std::string USER_AGENT = "AGNOSSetup-";
|
||||||
const QString DASHCAM_URL = "https://dashcam.comma.ai";
|
const QString OPENPILOT_URL = "https://openpilot.comma.ai";
|
||||||
|
|
||||||
bool is_elf(char *fname) {
|
bool is_elf(char *fname) {
|
||||||
FILE *fp = fopen(fname, "rb");
|
FILE *fp = fopen(fname, "rb");
|
||||||
@@ -201,20 +201,7 @@ QWidget * Setup::network_setup() {
|
|||||||
QPushButton *cont = new QPushButton();
|
QPushButton *cont = new QPushButton();
|
||||||
cont->setObjectName("navBtn");
|
cont->setObjectName("navBtn");
|
||||||
cont->setProperty("primary", true);
|
cont->setProperty("primary", true);
|
||||||
QObject::connect(cont, &QPushButton::clicked, [=]() {
|
QObject::connect(cont, &QPushButton::clicked, this, &Setup::nextPage);
|
||||||
auto w = currentWidget();
|
|
||||||
QTimer::singleShot(0, [=]() {
|
|
||||||
setCurrentWidget(downloading_widget);
|
|
||||||
});
|
|
||||||
QString url = InputDialog::getText(tr("Enter URL"), this, tr("for Custom Software"));
|
|
||||||
if (!url.isEmpty()) {
|
|
||||||
QTimer::singleShot(1000, this, [=]() {
|
|
||||||
download(url);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setCurrentWidget(w);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
blayout->addWidget(cont);
|
blayout->addWidget(cont);
|
||||||
|
|
||||||
// setup timer for testing internet connection
|
// setup timer for testing internet connection
|
||||||
@@ -229,11 +216,11 @@ QWidget * Setup::network_setup() {
|
|||||||
}
|
}
|
||||||
repaint();
|
repaint();
|
||||||
});
|
});
|
||||||
request->sendRequest(DASHCAM_URL);
|
request->sendRequest(OPENPILOT_URL);
|
||||||
QTimer *timer = new QTimer(this);
|
QTimer *timer = new QTimer(this);
|
||||||
QObject::connect(timer, &QTimer::timeout, [=]() {
|
QObject::connect(timer, &QTimer::timeout, [=]() {
|
||||||
if (!request->active() && cont->isVisible()) {
|
if (!request->active() && cont->isVisible()) {
|
||||||
request->sendRequest(DASHCAM_URL);
|
request->sendRequest(OPENPILOT_URL);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
timer->start(1000);
|
timer->start(1000);
|
||||||
@@ -241,6 +228,106 @@ QWidget * Setup::network_setup() {
|
|||||||
return widget;
|
return widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QWidget * radio_button(QString title, QButtonGroup *group) {
|
||||||
|
QPushButton *btn = new QPushButton(title);
|
||||||
|
btn->setCheckable(true);
|
||||||
|
group->addButton(btn);
|
||||||
|
btn->setStyleSheet(R"(
|
||||||
|
QPushButton {
|
||||||
|
height: 230;
|
||||||
|
padding-left: 100px;
|
||||||
|
padding-right: 100px;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 80px;
|
||||||
|
font-weight: 400;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: #4F4F4F;
|
||||||
|
}
|
||||||
|
QPushButton:checked {
|
||||||
|
background-color: #465BEA;
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
|
||||||
|
// checkmark icon
|
||||||
|
QPixmap pix(":/img_circled_check.svg");
|
||||||
|
btn->setIcon(pix);
|
||||||
|
btn->setIconSize(QSize(0, 0));
|
||||||
|
btn->setLayoutDirection(Qt::RightToLeft);
|
||||||
|
QObject::connect(btn, &QPushButton::toggled, [=](bool checked) {
|
||||||
|
btn->setIconSize(checked ? QSize(104, 104) : QSize(0, 0));
|
||||||
|
});
|
||||||
|
return btn;
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget * Setup::software_selection() {
|
||||||
|
QWidget *widget = new QWidget();
|
||||||
|
QVBoxLayout *main_layout = new QVBoxLayout(widget);
|
||||||
|
main_layout->setContentsMargins(55, 50, 55, 50);
|
||||||
|
main_layout->setSpacing(0);
|
||||||
|
|
||||||
|
// title
|
||||||
|
QLabel *title = new QLabel(tr("Choose Software to Install"));
|
||||||
|
title->setStyleSheet("font-size: 90px; font-weight: 500;");
|
||||||
|
main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop);
|
||||||
|
|
||||||
|
main_layout->addSpacing(50);
|
||||||
|
|
||||||
|
// openpilot + custom radio buttons
|
||||||
|
QButtonGroup *group = new QButtonGroup(widget);
|
||||||
|
group->setExclusive(true);
|
||||||
|
|
||||||
|
QWidget *openpilot = radio_button(tr("openpilot"), group);
|
||||||
|
main_layout->addWidget(openpilot);
|
||||||
|
|
||||||
|
main_layout->addSpacing(30);
|
||||||
|
|
||||||
|
QWidget *custom = radio_button(tr("Custom Software"), group);
|
||||||
|
main_layout->addWidget(custom);
|
||||||
|
|
||||||
|
main_layout->addStretch();
|
||||||
|
|
||||||
|
// back + continue buttons
|
||||||
|
QHBoxLayout *blayout = new QHBoxLayout;
|
||||||
|
main_layout->addLayout(blayout);
|
||||||
|
blayout->setSpacing(50);
|
||||||
|
|
||||||
|
QPushButton *back = new QPushButton(tr("Back"));
|
||||||
|
back->setObjectName("navBtn");
|
||||||
|
QObject::connect(back, &QPushButton::clicked, this, &Setup::prevPage);
|
||||||
|
blayout->addWidget(back);
|
||||||
|
|
||||||
|
QPushButton *cont = new QPushButton(tr("Continue"));
|
||||||
|
cont->setObjectName("navBtn");
|
||||||
|
cont->setEnabled(false);
|
||||||
|
cont->setProperty("primary", true);
|
||||||
|
blayout->addWidget(cont);
|
||||||
|
|
||||||
|
QObject::connect(cont, &QPushButton::clicked, [=]() {
|
||||||
|
auto w = currentWidget();
|
||||||
|
QTimer::singleShot(0, [=]() {
|
||||||
|
setCurrentWidget(downloading_widget);
|
||||||
|
});
|
||||||
|
QString url = OPENPILOT_URL;
|
||||||
|
if (group->checkedButton() != openpilot) {
|
||||||
|
url = InputDialog::getText(tr("Enter URL"), this, tr("for Custom Software"));
|
||||||
|
}
|
||||||
|
if (!url.isEmpty()) {
|
||||||
|
QTimer::singleShot(1000, this, [=]() {
|
||||||
|
download(url);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setCurrentWidget(w);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(group, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), [=](QAbstractButton *btn) {
|
||||||
|
btn->setChecked(true);
|
||||||
|
cont->setEnabled(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
|
||||||
QWidget * Setup::downloading() {
|
QWidget * Setup::downloading() {
|
||||||
QWidget *widget = new QWidget();
|
QWidget *widget = new QWidget();
|
||||||
QVBoxLayout *main_layout = new QVBoxLayout(widget);
|
QVBoxLayout *main_layout = new QVBoxLayout(widget);
|
||||||
@@ -326,6 +413,7 @@ Setup::Setup(QWidget *parent) : QStackedWidget(parent) {
|
|||||||
|
|
||||||
addWidget(getting_started());
|
addWidget(getting_started());
|
||||||
addWidget(network_setup());
|
addWidget(network_setup());
|
||||||
|
addWidget(software_selection());
|
||||||
|
|
||||||
downloading_widget = downloading();
|
downloading_widget = downloading();
|
||||||
addWidget(downloading_widget);
|
addWidget(downloading_widget);
|
||||||
|
|||||||
1
selfdrive/ui/qt/setup/setup.h
Executable file → Normal file
1
selfdrive/ui/qt/setup/setup.h
Executable file → Normal file
@@ -17,6 +17,7 @@ private:
|
|||||||
QWidget *low_voltage();
|
QWidget *low_voltage();
|
||||||
QWidget *getting_started();
|
QWidget *getting_started();
|
||||||
QWidget *network_setup();
|
QWidget *network_setup();
|
||||||
|
QWidget *software_selection();
|
||||||
QWidget *downloading();
|
QWidget *downloading();
|
||||||
QWidget *download_failed(QLabel *url, QLabel *body);
|
QWidget *download_failed(QLabel *url, QLabel *body);
|
||||||
|
|
||||||
|
|||||||
31
selfdrive/ui/qt/setup/updater.cc
Executable file → Normal file
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;
|
background-color: #3049F4;
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
|
QObject::connect(install, &QPushButton::clicked, this, &Updater::installUpdate);
|
||||||
QObject::connect(connect, &QPushButton::clicked, [=]() {
|
|
||||||
countdownTimer->stop();
|
|
||||||
setCurrentWidget(wifi);
|
|
||||||
});
|
|
||||||
|
|
||||||
hlayout->addWidget(install);
|
hlayout->addWidget(install);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,20 +114,6 @@ Updater::Updater(const QString &updater_path, const QString &manifest_path, QWid
|
|||||||
addWidget(wifi);
|
addWidget(wifi);
|
||||||
addWidget(progress);
|
addWidget(progress);
|
||||||
|
|
||||||
// Initialize the countdown timer and value
|
|
||||||
countdownValue = 5; // 5 seconds countdown
|
|
||||||
countdownTimer = new QTimer(this);
|
|
||||||
countdownTimer->setInterval(1000); // 1 second interval
|
|
||||||
|
|
||||||
// Connect the timer's timeout signal to update the countdown and button text
|
|
||||||
QObject::connect(countdownTimer, &QTimer::timeout, this, &Updater::updateCountdown);
|
|
||||||
|
|
||||||
// Start the countdown
|
|
||||||
countdownTimer->start();
|
|
||||||
|
|
||||||
// Set initial button text
|
|
||||||
install->setText(tr("Install (5)"));
|
|
||||||
|
|
||||||
setStyleSheet(R"(
|
setStyleSheet(R"(
|
||||||
* {
|
* {
|
||||||
color: white;
|
color: white;
|
||||||
@@ -163,16 +144,6 @@ Updater::Updater(const QString &updater_path, const QString &manifest_path, QWid
|
|||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Updater::updateCountdown() {
|
|
||||||
countdownValue--;
|
|
||||||
if (countdownValue > 0) {
|
|
||||||
install->setText(tr("Install (%1)").arg(countdownValue));
|
|
||||||
} else {
|
|
||||||
countdownTimer->stop();
|
|
||||||
installUpdate(); // Assuming this is the method that starts the update
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Updater::installUpdate() {
|
void Updater::installUpdate() {
|
||||||
setCurrentWidget(progress);
|
setCurrentWidget(progress);
|
||||||
QObject::connect(&proc, &QProcess::readyReadStandardOutput, this, &Updater::readProgress);
|
QObject::connect(&proc, &QProcess::readyReadStandardOutput, this, &Updater::readProgress);
|
||||||
|
|||||||
3
selfdrive/ui/qt/setup/updater.h
Executable file → Normal file
3
selfdrive/ui/qt/setup/updater.h
Executable file → Normal file
@@ -26,7 +26,4 @@ private:
|
|||||||
QProgressBar *bar;
|
QProgressBar *bar;
|
||||||
QPushButton *reboot;
|
QPushButton *reboot;
|
||||||
QWidget *prompt, *wifi, *progress;
|
QWidget *prompt, *wifi, *progress;
|
||||||
QTimer *countdownTimer;
|
|
||||||
int countdownValue;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
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");
|
isCPU = params.getBool("ShowCPU");
|
||||||
isGPU = params.getBool("ShowGPU");
|
isGPU = params.getBool("ShowGPU");
|
||||||
|
|
||||||
|
isIP = params.getBool("ShowIP");
|
||||||
|
|
||||||
isMemoryUsage = params.getBool("ShowMemoryUsage");
|
isMemoryUsage = params.getBool("ShowMemoryUsage");
|
||||||
isStorageLeft = params.getBool("ShowStorageLeft");
|
isStorageLeft = params.getBool("ShowStorageLeft");
|
||||||
isStorageUsed = params.getBool("ShowStorageUsed");
|
isStorageUsed = params.getBool("ShowStorageUsed");
|
||||||
|
|
||||||
isNumericalTemp = params.getBool("NumericalTemp");
|
holidayThemeConfiguration = {
|
||||||
isFahrenheit = params.getBool("Fahrenheit");
|
{0, {"stock", {QColor(255, 255, 255)}}},
|
||||||
|
{1, {"april_fools", {QColor(255, 165, 0)}}},
|
||||||
|
{2, {"christmas", {QColor(0, 72, 255)}}},
|
||||||
|
{3, {"cinco_de_mayo", {QColor(0, 104, 71)}}},
|
||||||
|
{4, {"easter", {QColor(200, 150, 200)}}},
|
||||||
|
{5, {"fourth_of_july", {QColor(0, 72, 255)}}},
|
||||||
|
{6, {"halloween", {QColor(255, 0, 0)}}},
|
||||||
|
{7, {"new_years_day", {QColor(23, 134, 68)}}},
|
||||||
|
{8, {"st_patricks_day", {QColor(0, 128, 0)}}},
|
||||||
|
{9, {"thanksgiving", {QColor(255, 0, 0)}}},
|
||||||
|
{10, {"valentines_day", {QColor(23, 134, 68)}}},
|
||||||
|
{11, {"world_frog_day", {QColor(23, 134, 68)}}},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto &[key, themeData] : holidayThemeConfiguration) {
|
||||||
|
QString &themeName = themeData.first;
|
||||||
|
QString base = themeName == "stock" ? "../assets/images" : QString("../frogpilot/assets/holiday_themes/%1/images").arg(themeName);
|
||||||
|
std::vector<QString> paths = {base + "/button_home.png", base + "/button_flag.png", base + "/button_settings.png"};
|
||||||
|
|
||||||
|
holiday_home_imgs[key] = loadPixmap(paths[0], home_btn.size());
|
||||||
|
holiday_flag_imgs[key] = loadPixmap(paths[1], home_btn.size());
|
||||||
|
holiday_settings_imgs[key] = loadPixmap(paths[2], settings_btn.size(), Qt::IgnoreAspectRatio);
|
||||||
|
}
|
||||||
|
|
||||||
themeConfiguration = {
|
themeConfiguration = {
|
||||||
{0, {"stock", {QColor(255, 255, 255)}}},
|
{0, {"stock", {QColor(255, 255, 255)}}},
|
||||||
{1, {"frog_theme", {QColor(0, 72, 255)}}},
|
{1, {"frog_theme", {QColor(23, 134, 68)}}},
|
||||||
{2, {"tesla_theme", {QColor(0, 72, 255)}}},
|
{2, {"tesla_theme", {QColor(0, 72, 255)}}},
|
||||||
{3, {"stalin_theme", {QColor(255, 0, 0)}}}
|
{3, {"stalin_theme", {QColor(255, 0, 0)}}}
|
||||||
};
|
};
|
||||||
@@ -70,7 +94,6 @@ Sidebar::Sidebar(QWidget *parent) : QFrame(parent), onroad(false), flag_pressed(
|
|||||||
home_img = home_imgs[scene.custom_icons];
|
home_img = home_imgs[scene.custom_icons];
|
||||||
flag_img = flag_imgs[scene.custom_icons];
|
flag_img = flag_imgs[scene.custom_icons];
|
||||||
settings_img = settings_imgs[scene.custom_icons];
|
settings_img = settings_imgs[scene.custom_icons];
|
||||||
|
|
||||||
currentColors = themeConfiguration[scene.custom_colors].second;
|
currentColors = themeConfiguration[scene.custom_colors].second;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,35 +101,52 @@ void Sidebar::mousePressEvent(QMouseEvent *event) {
|
|||||||
// Declare the click boxes
|
// Declare the click boxes
|
||||||
QRect cpuRect = {30, 496, 240, 126};
|
QRect cpuRect = {30, 496, 240, 126};
|
||||||
QRect memoryRect = {30, 654, 240, 126};
|
QRect memoryRect = {30, 654, 240, 126};
|
||||||
|
QRect networkRect = {30, 196, 240, 126};
|
||||||
QRect tempRect = {30, 338, 240, 126};
|
QRect tempRect = {30, 338, 240, 126};
|
||||||
|
|
||||||
static int showChip = 0;
|
static int showChip = 0;
|
||||||
static int showMemory = 0;
|
static int showMemory = 0;
|
||||||
|
static int showNetwork = 0;
|
||||||
static int showTemp = 0;
|
static int showTemp = 0;
|
||||||
|
|
||||||
// Swap between the respective metrics upon tap
|
// Swap between the respective metrics upon tap
|
||||||
if (cpuRect.contains(event->pos())) {
|
if (cpuRect.contains(event->pos())) {
|
||||||
showChip = (showChip + 1) % 3;
|
showChip = (showChip + 1) % 3;
|
||||||
|
|
||||||
isCPU = (showChip == 1);
|
isCPU = (showChip == 1);
|
||||||
isGPU = (showChip == 2);
|
isGPU = (showChip == 2);
|
||||||
|
|
||||||
params.putBoolNonBlocking("ShowCPU", isCPU);
|
params.putBoolNonBlocking("ShowCPU", isCPU);
|
||||||
params.putBoolNonBlocking("ShowGPU", isGPU);
|
params.putBoolNonBlocking("ShowGPU", isGPU);
|
||||||
|
|
||||||
update();
|
update();
|
||||||
} else if (memoryRect.contains(event->pos())) {
|
} else if (memoryRect.contains(event->pos())) {
|
||||||
showMemory = (showMemory + 1) % 4;
|
showMemory = (showMemory + 1) % 4;
|
||||||
|
|
||||||
isMemoryUsage = (showMemory == 1);
|
isMemoryUsage = (showMemory == 1);
|
||||||
isStorageLeft = (showMemory == 2);
|
isStorageLeft = (showMemory == 2);
|
||||||
isStorageUsed = (showMemory == 3);
|
isStorageUsed = (showMemory == 3);
|
||||||
|
|
||||||
params.putBoolNonBlocking("ShowMemoryUsage", isMemoryUsage);
|
params.putBoolNonBlocking("ShowMemoryUsage", isMemoryUsage);
|
||||||
params.putBoolNonBlocking("ShowStorageLeft", isStorageLeft);
|
params.putBoolNonBlocking("ShowStorageLeft", isStorageLeft);
|
||||||
params.putBoolNonBlocking("ShowStorageUsed", isStorageUsed);
|
params.putBoolNonBlocking("ShowStorageUsed", isStorageUsed);
|
||||||
|
|
||||||
|
update();
|
||||||
|
} else if (networkRect.contains(event->pos())) {
|
||||||
|
showNetwork = (showNetwork + 1) % 2;
|
||||||
|
isIP = (showNetwork == 1);
|
||||||
|
params.putBoolNonBlocking("ShowIP", isIP);
|
||||||
|
|
||||||
update();
|
update();
|
||||||
} else if (tempRect.contains(event->pos())) {
|
} else if (tempRect.contains(event->pos())) {
|
||||||
showTemp = (showTemp + 1) % 3;
|
showTemp = (showTemp + 1) % 3;
|
||||||
isNumericalTemp = (showTemp != 0);
|
|
||||||
isFahrenheit = (showTemp == 2);
|
scene.fahrenheit = showTemp == 2;
|
||||||
params.putBoolNonBlocking("Fahrenheit", isFahrenheit);
|
scene.numerical_temp = showTemp != 0;
|
||||||
params.putBoolNonBlocking("NumericalTemp", isNumericalTemp);
|
|
||||||
|
params.putBoolNonBlocking("Fahrenheit", showTemp == 2);
|
||||||
|
params.putBoolNonBlocking("NumericalTemp", showTemp != 0);
|
||||||
|
|
||||||
update();
|
update();
|
||||||
} else if (onroad && home_btn.contains(event->pos())) {
|
} else if (onroad && home_btn.contains(event->pos())) {
|
||||||
flag_pressed = true;
|
flag_pressed = true;
|
||||||
@@ -147,17 +187,24 @@ void Sidebar::updateState(const UIState &s) {
|
|||||||
setProperty("netStrength", strength > 0 ? strength + 1 : 0);
|
setProperty("netStrength", strength > 0 ? strength + 1 : 0);
|
||||||
|
|
||||||
// FrogPilot properties
|
// FrogPilot properties
|
||||||
|
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];
|
home_img = home_imgs[scene.custom_icons];
|
||||||
flag_img = flag_imgs[scene.custom_icons];
|
flag_img = flag_imgs[scene.custom_icons];
|
||||||
settings_img = settings_imgs[scene.custom_icons];
|
settings_img = settings_imgs[scene.custom_icons];
|
||||||
|
|
||||||
currentColors = themeConfiguration[scene.custom_colors].second;
|
currentColors = themeConfiguration[scene.custom_colors].second;
|
||||||
|
}
|
||||||
|
|
||||||
auto frogpilotDeviceState = sm["frogpilotDeviceState"].getFrogpilotDeviceState();
|
auto frogpilotDeviceState = sm["frogpilotDeviceState"].getFrogpilotDeviceState();
|
||||||
|
|
||||||
|
bool isNumericalTemp = scene.numerical_temp;
|
||||||
|
|
||||||
int maxTempC = deviceState.getMaxTempC();
|
int maxTempC = deviceState.getMaxTempC();
|
||||||
QString max_temp = isFahrenheit ? QString::number(maxTempC * 9 / 5 + 32) + "°F" : QString::number(maxTempC) + "°C";
|
QString max_temp = scene.fahrenheit ? QString::number(maxTempC * 9 / 5 + 32) + "°F" : QString::number(maxTempC) + "°C";
|
||||||
QColor theme_color = currentColors[0];
|
|
||||||
|
|
||||||
// FrogPilot metrics
|
// FrogPilot metrics
|
||||||
if (isCPU || isGPU) {
|
if (isCPU || isGPU) {
|
||||||
@@ -171,11 +218,11 @@ void Sidebar::updateState(const UIState &s) {
|
|||||||
QString metric = isGPU ? gpu : cpu;
|
QString metric = isGPU ? gpu : cpu;
|
||||||
int usage = isGPU ? gpu_usage : cpu_usage;
|
int usage = isGPU ? gpu_usage : cpu_usage;
|
||||||
|
|
||||||
ItemStatus cpuStatus = {{tr(isGPU ? "GPU" : "CPU"), metric}, theme_color};
|
ItemStatus cpuStatus = {{isGPU ? tr("GPU") : tr("CPU"), metric}, currentColors[0]};
|
||||||
if (usage >= 85) {
|
if (usage >= 85) {
|
||||||
cpuStatus = {{tr(isGPU ? "GPU" : "CPU"), metric}, danger_color};
|
cpuStatus = {{isGPU ? tr("GPU") : tr("CPU"), metric}, danger_color};
|
||||||
} else if (usage >= 70) {
|
} else if (usage >= 70) {
|
||||||
cpuStatus = {{tr(isGPU ? "GPU" : "CPU"), metric}, warning_color};
|
cpuStatus = {{isGPU ? tr("GPU") : tr("CPU"), metric}, warning_color};
|
||||||
}
|
}
|
||||||
setProperty("cpuStatus", QVariant::fromValue(cpuStatus));
|
setProperty("cpuStatus", QVariant::fromValue(cpuStatus));
|
||||||
}
|
}
|
||||||
@@ -186,10 +233,10 @@ void Sidebar::updateState(const UIState &s) {
|
|||||||
int storage_used = frogpilotDeviceState.getUsedSpace();
|
int storage_used = frogpilotDeviceState.getUsedSpace();
|
||||||
|
|
||||||
QString memory = QString::number(memory_usage) + "%";
|
QString memory = QString::number(memory_usage) + "%";
|
||||||
QString storage = QString::number(isStorageLeft ? storage_left : storage_used) + " GB";
|
QString storage = QString::number(isStorageLeft ? storage_left : storage_used) + tr(" GB");
|
||||||
|
|
||||||
if (isMemoryUsage) {
|
if (isMemoryUsage) {
|
||||||
ItemStatus memoryStatus = {{tr("MEMORY"), memory}, theme_color};
|
ItemStatus memoryStatus = {{tr("MEMORY"), memory}, currentColors[0]};
|
||||||
if (memory_usage >= 85) {
|
if (memory_usage >= 85) {
|
||||||
memoryStatus = {{tr("MEMORY"), memory}, danger_color};
|
memoryStatus = {{tr("MEMORY"), memory}, danger_color};
|
||||||
} else if (memory_usage >= 70) {
|
} else if (memory_usage >= 70) {
|
||||||
@@ -197,11 +244,11 @@ void Sidebar::updateState(const UIState &s) {
|
|||||||
}
|
}
|
||||||
setProperty("memoryStatus", QVariant::fromValue(memoryStatus));
|
setProperty("memoryStatus", QVariant::fromValue(memoryStatus));
|
||||||
} else {
|
} else {
|
||||||
ItemStatus storageStatus = {{tr(isStorageLeft ? "LEFT" : "USED"), storage}, theme_color};
|
ItemStatus storageStatus = {{isStorageLeft ? tr("LEFT") : tr("USED"), storage}, currentColors[0]};
|
||||||
if (10 <= storage_left && storage_left < 25) {
|
if (25 > storage_left && storage_left >= 10) {
|
||||||
storageStatus = {{tr(isStorageLeft ? "LEFT" : "USED"), storage}, warning_color};
|
storageStatus = {{isStorageLeft ? tr("LEFT") : tr("USED"), storage}, warning_color};
|
||||||
} else if (storage_left < 10) {
|
} else if (10 > storage_left) {
|
||||||
storageStatus = {{tr(isStorageLeft ? "LEFT" : "USED"), storage}, danger_color};
|
storageStatus = {{isStorageLeft ? tr("LEFT") : tr("USED"), storage}, danger_color};
|
||||||
}
|
}
|
||||||
setProperty("storageStatus", QVariant::fromValue(storageStatus));
|
setProperty("storageStatus", QVariant::fromValue(storageStatus));
|
||||||
}
|
}
|
||||||
@@ -213,7 +260,7 @@ void Sidebar::updateState(const UIState &s) {
|
|||||||
connectStatus = ItemStatus{{tr("CONNECT"), tr("OFFLINE")}, warning_color};
|
connectStatus = ItemStatus{{tr("CONNECT"), tr("OFFLINE")}, warning_color};
|
||||||
} else {
|
} else {
|
||||||
connectStatus = nanos_since_boot() - last_ping < 80e9
|
connectStatus = nanos_since_boot() - last_ping < 80e9
|
||||||
? ItemStatus{{tr("CONNECT"), tr("ONLINE")}, theme_color}
|
? ItemStatus{{tr("CONNECT"), tr("ONLINE")}, currentColors[0]}
|
||||||
: ItemStatus{{tr("CONNECT"), tr("ERROR")}, danger_color};
|
: ItemStatus{{tr("CONNECT"), tr("ERROR")}, danger_color};
|
||||||
}
|
}
|
||||||
setProperty("connectStatus", QVariant::fromValue(connectStatus));
|
setProperty("connectStatus", QVariant::fromValue(connectStatus));
|
||||||
@@ -221,13 +268,13 @@ void Sidebar::updateState(const UIState &s) {
|
|||||||
ItemStatus tempStatus = {{tr("TEMP"), isNumericalTemp ? max_temp : tr("HIGH")}, danger_color};
|
ItemStatus tempStatus = {{tr("TEMP"), isNumericalTemp ? max_temp : tr("HIGH")}, danger_color};
|
||||||
auto ts = deviceState.getThermalStatus();
|
auto ts = deviceState.getThermalStatus();
|
||||||
if (ts == cereal::DeviceState::ThermalStatus::GREEN) {
|
if (ts == cereal::DeviceState::ThermalStatus::GREEN) {
|
||||||
tempStatus = {{tr("TEMP"), isNumericalTemp ? max_temp : tr("GOOD")}, theme_color};
|
tempStatus = {{tr("TEMP"), isNumericalTemp ? max_temp : tr("GOOD")}, currentColors[0]};
|
||||||
} else if (ts == cereal::DeviceState::ThermalStatus::YELLOW) {
|
} else if (ts == cereal::DeviceState::ThermalStatus::YELLOW) {
|
||||||
tempStatus = {{tr("TEMP"), isNumericalTemp ? max_temp : tr("OK")}, warning_color};
|
tempStatus = {{tr("TEMP"), isNumericalTemp ? max_temp : tr("OK")}, warning_color};
|
||||||
}
|
}
|
||||||
setProperty("tempStatus", QVariant::fromValue(tempStatus));
|
setProperty("tempStatus", QVariant::fromValue(tempStatus));
|
||||||
|
|
||||||
ItemStatus pandaStatus = {{tr("VEHICLE"), tr("ONLINE")}, theme_color};
|
ItemStatus pandaStatus = {{tr("VEHICLE"), tr("ONLINE")}, currentColors[0]};
|
||||||
if (s.scene.pandaType == cereal::PandaState::PandaType::UNKNOWN) {
|
if (s.scene.pandaType == cereal::PandaState::PandaType::UNKNOWN) {
|
||||||
pandaStatus = {{tr("NO"), tr("PANDA")}, danger_color};
|
pandaStatus = {{tr("NO"), tr("PANDA")}, danger_color};
|
||||||
} else if (s.scene.started && !sm["liveLocationKalman"].getLiveLocationKalman().getGpsOK()) {
|
} else if (s.scene.started && !sm["liveLocationKalman"].getLiveLocationKalman().getGpsOK()) {
|
||||||
@@ -253,14 +300,24 @@ void Sidebar::paintEvent(QPaintEvent *event) {
|
|||||||
// network
|
// network
|
||||||
int x = 58;
|
int x = 58;
|
||||||
const QColor gray(0x54, 0x54, 0x54);
|
const QColor gray(0x54, 0x54, 0x54);
|
||||||
|
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) {
|
for (int i = 0; i < 5; ++i) {
|
||||||
p.setBrush(i < net_strength ? Qt::white : gray);
|
p.setBrush(i < net_strength ? Qt::white : gray);
|
||||||
p.drawEllipse(x, 196, 27, 27);
|
p.drawEllipse(x, 196, 27, 27);
|
||||||
x += 37;
|
x += 37;
|
||||||
}
|
}
|
||||||
|
|
||||||
p.setFont(InterFont(35));
|
|
||||||
p.setPen(QColor(0xff, 0xff, 0xff));
|
p.setPen(QColor(0xff, 0xff, 0xff));
|
||||||
|
}
|
||||||
|
|
||||||
const QRect r = QRect(50, 247, 100, 50);
|
const QRect r = QRect(50, 247, 100, 50);
|
||||||
p.drawText(r, Qt::AlignCenter, net_type);
|
p.drawText(r, Qt::AlignCenter, net_type);
|
||||||
|
|
||||||
|
|||||||
21
selfdrive/ui/qt/sidebar.h
Executable file → Normal file
21
selfdrive/ui/qt/sidebar.h
Executable file → Normal file
@@ -71,17 +71,22 @@ private:
|
|||||||
|
|
||||||
ItemStatus cpu_status, memory_status, storage_status;
|
ItemStatus cpu_status, memory_status, storage_status;
|
||||||
|
|
||||||
|
bool isCPU;
|
||||||
|
bool isGPU;
|
||||||
|
bool isIP;
|
||||||
|
bool isMemoryUsage;
|
||||||
|
bool isStorageLeft;
|
||||||
|
bool isStorageUsed;
|
||||||
|
|
||||||
std::unordered_map<int, std::pair<QString, std::vector<QColor>>> themeConfiguration;
|
std::unordered_map<int, std::pair<QString, std::vector<QColor>>> themeConfiguration;
|
||||||
std::unordered_map<int, QPixmap> flag_imgs;
|
std::unordered_map<int, QPixmap> flag_imgs;
|
||||||
std::unordered_map<int, QPixmap> home_imgs;
|
std::unordered_map<int, QPixmap> home_imgs;
|
||||||
std::unordered_map<int, QPixmap> settings_imgs;
|
std::unordered_map<int, QPixmap> settings_imgs;
|
||||||
std::vector<QColor> currentColors;
|
|
||||||
|
|
||||||
bool isCPU;
|
std::unordered_map<int, std::pair<QString, std::vector<QColor>>> holidayThemeConfiguration;
|
||||||
bool isFahrenheit;
|
std::unordered_map<int, QPixmap> holiday_flag_imgs;
|
||||||
bool isGPU;
|
std::unordered_map<int, QPixmap> holiday_home_imgs;
|
||||||
bool isMemoryUsage;
|
std::unordered_map<int, QPixmap> holiday_settings_imgs;
|
||||||
bool isNumericalTemp;
|
|
||||||
bool isStorageLeft;
|
std::vector<QColor> currentColors;
|
||||||
bool isStorageUsed;
|
|
||||||
};
|
};
|
||||||
|
|||||||
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 {
|
QProgressBar::chunk {
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background-color: rgba(179, 0, 0, 255);
|
background-color: rgba(23, 134, 68, 255);
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
|
|
||||||
|
|||||||
0
selfdrive/ui/qt/spinner.h
Executable file → Normal file
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 <QScrollBar>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QProcess>
|
|
||||||
|
|
||||||
#include "system/hardware/hw.h"
|
#include "system/hardware/hw.h"
|
||||||
#include "selfdrive/ui/qt/util.h"
|
#include "selfdrive/ui/qt/util.h"
|
||||||
#include "selfdrive/ui/qt/qt_window.h"
|
#include "selfdrive/ui/qt/qt_window.h"
|
||||||
#include "selfdrive/ui/qt/widgets/scrollview.h"
|
#include "selfdrive/ui/qt/widgets/scrollview.h"
|
||||||
#include "selfdrive/ui/qt/network/wifi_manager.h"
|
|
||||||
|
|
||||||
// wifi connection screen
|
|
||||||
// wifi = new QWidget;
|
|
||||||
// {
|
|
||||||
// QVBoxLayout *layout = new QVBoxLayout(wifi);
|
|
||||||
// layout->setContentsMargins(100, 100, 100, 100);
|
|
||||||
|
|
||||||
// Networking *networking = new Networking(this, false);
|
|
||||||
// networking->setStyleSheet("Networking { background-color: #292929; border-radius: 13px; }");
|
|
||||||
// layout->addWidget(networking, 1);
|
|
||||||
|
|
||||||
// QPushButton *back = new QPushButton(tr("Back"));
|
|
||||||
// back->setObjectName("navBtn");
|
|
||||||
// back->setStyleSheet("padding-left: 60px; padding-right: 60px;");
|
|
||||||
// QObject::connect(back, &QPushButton::clicked, [=]() {
|
|
||||||
// setCurrentWidget(prompt);
|
|
||||||
// });
|
|
||||||
// layout->addWidget(back, 0, Qt::AlignLeft);
|
|
||||||
// }
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
initApp(argc, argv);
|
initApp(argc, argv);
|
||||||
@@ -52,36 +31,11 @@ int main(int argc, char *argv[]) {
|
|||||||
scroll->verticalScrollBar()->setValue(scroll->verticalScrollBar()->maximum());
|
scroll->verticalScrollBar()->setValue(scroll->verticalScrollBar()->maximum());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
QPushButton *btnupdate = new QPushButton();
|
|
||||||
#ifdef __aarch64__
|
|
||||||
btnupdate->setText(QObject::tr("Update"));
|
|
||||||
QObject::connect(btnupdate, &QPushButton::clicked, [=]() {
|
|
||||||
QProcess process;
|
|
||||||
label->setText("Attempting to connect to wifi");
|
|
||||||
// TODO: make this work, then copy the compiled binary into git
|
|
||||||
// wifi = new WifiManager(null);
|
|
||||||
// connect(wifi, &WifiManager::refreshSignal, [=]() {
|
|
||||||
// label->setText("Performing update");
|
|
||||||
// process.setWorkingDirectory("/data/openpilot/");
|
|
||||||
// process.start("/bin/bash", QStringList{"-c", "update.sh"});
|
|
||||||
// process.waitForFinished();
|
|
||||||
// label->setText("Rebooting");
|
|
||||||
// Hardware::reboot();
|
|
||||||
// });
|
|
||||||
// wifi->start();
|
|
||||||
});
|
|
||||||
#else
|
|
||||||
btnupdate->setText(QObject::tr("Exit"));
|
|
||||||
QObject::connect(btnupdate, &QPushButton::clicked, &a, &QApplication::quit);
|
|
||||||
#endif
|
|
||||||
main_layout->addWidget(btnupdate, 0, 0, Qt::AlignLeft | Qt::AlignBottom);
|
|
||||||
|
|
||||||
QPushButton *btn = new QPushButton();
|
QPushButton *btn = new QPushButton();
|
||||||
#ifdef __aarch64__
|
#ifdef __aarch64__
|
||||||
btn->setText(QObject::tr("Reboot"));
|
btn->setText(QObject::tr("Reboot"));
|
||||||
QObject::connect(btn, &QPushButton::clicked, [=]() {
|
QObject::connect(btn, &QPushButton::clicked, [=]() {
|
||||||
Hardware::reboot(); // bbot this is the dreaded crash reboot button
|
Hardware::reboot();
|
||||||
});
|
});
|
||||||
#else
|
#else
|
||||||
btn->setText(QObject::tr("Exit"));
|
btn->setText(QObject::tr("Exit"));
|
||||||
|
|||||||
8
selfdrive/ui/qt/util.cc
Executable file → Normal file
8
selfdrive/ui/qt/util.cc
Executable file → Normal file
@@ -5,6 +5,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
@@ -25,7 +26,7 @@ QString getVersion() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString getBrand() {
|
QString getBrand() {
|
||||||
return Params().getBool("Passive") ? QObject::tr("dashcam") : QObject::tr("OscarPilot");
|
return QObject::tr("FrogPilot");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString getUserAgent() {
|
QString getUserAgent() {
|
||||||
@@ -114,6 +115,11 @@ void initApp(int argc, char *argv[], bool disable_hidpi) {
|
|||||||
|
|
||||||
qputenv("QT_DBL_CLICK_DIST", QByteArray::number(150));
|
qputenv("QT_DBL_CLICK_DIST", QByteArray::number(150));
|
||||||
|
|
||||||
|
// ensure the current dir matches the exectuable's directory
|
||||||
|
QApplication tmp(argc, argv);
|
||||||
|
QString appDir = QCoreApplication::applicationDirPath();
|
||||||
|
QDir::setCurrent(appDir);
|
||||||
|
|
||||||
setQtSurfaceFormat();
|
setQtSurfaceFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
0
selfdrive/ui/qt/util.h
Executable file → Normal file
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"
|
"out vec4 colorOut;\n"
|
||||||
"void main() {\n"
|
"void main() {\n"
|
||||||
" colorOut = texture(uTexture, vTexCoord);\n"
|
" colorOut = texture(uTexture, vTexCoord);\n"
|
||||||
|
// gamma to improve worst case visibility when dark
|
||||||
|
" colorOut.rgb = pow(colorOut.rgb, vec3(1.0/1.28));\n"
|
||||||
"}\n";
|
"}\n";
|
||||||
#else
|
#else
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
@@ -217,7 +219,6 @@ void CameraWidget::updateFrameMat() {
|
|||||||
if (active_stream_type == VISION_STREAM_DRIVER) {
|
if (active_stream_type == VISION_STREAM_DRIVER) {
|
||||||
if (stream_width > 0 && stream_height > 0) {
|
if (stream_width > 0 && stream_height > 0) {
|
||||||
frame_mat = get_driver_view_transform(w, h, stream_width, stream_height);
|
frame_mat = get_driver_view_transform(w, h, stream_width, stream_height);
|
||||||
frame_mat.v[0] *= -1.0;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Project point at "infinity" to compute x and y offsets
|
// Project point at "infinity" to compute x and y offsets
|
||||||
|
|||||||
0
selfdrive/ui/qt/widgets/cameraview.h
Executable file → Normal file
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);
|
QObject::connect(&toggle, &Toggle::stateChanged, this, &ToggleControl::toggleFlipped);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setVisualOn() {
|
|
||||||
toggle.togglePosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setEnabled(bool enabled) {
|
void setEnabled(bool enabled) {
|
||||||
toggle.setEnabled(enabled);
|
toggle.setEnabled(enabled);
|
||||||
toggle.update();
|
toggle.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void refresh() {
|
||||||
|
toggle.togglePosition();
|
||||||
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void toggleFlipped(bool state);
|
void toggleFlipped(bool state);
|
||||||
|
|
||||||
@@ -206,7 +206,7 @@ public:
|
|||||||
background-color: #4a4a4a;
|
background-color: #4a4a4a;
|
||||||
}
|
}
|
||||||
QPushButton:checked:enabled {
|
QPushButton:checked:enabled {
|
||||||
background-color: #0048FF;
|
background-color: #33Ab4C;
|
||||||
}
|
}
|
||||||
QPushButton:disabled {
|
QPushButton:disabled {
|
||||||
color: #33E4E4E4;
|
color: #33E4E4E4;
|
||||||
@@ -227,10 +227,8 @@ public:
|
|||||||
button_group->addButton(button, i);
|
button_group->addButton(button, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject::connect(button_group, QOverload<int, bool>::of(&QButtonGroup::buttonToggled), [=](int id, bool checked) {
|
QObject::connect(button_group, QOverload<int>::of(&QButtonGroup::buttonClicked), [=](int id) {
|
||||||
if (checked) {
|
|
||||||
params.put(key, std::to_string(id));
|
params.put(key, std::to_string(id));
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +238,19 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setCheckedButton(int id) {
|
||||||
|
button_group->button(id)->setChecked(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh() {
|
||||||
|
int value = atoi(params.get(key).c_str());
|
||||||
|
button_group->button(value)->setChecked(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void showEvent(QShowEvent *event) override {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string key;
|
std::string key;
|
||||||
Params params;
|
Params params;
|
||||||
|
|||||||
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 <QJsonObject>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "common/params.h"
|
|
||||||
#include "selfdrive/ui/qt/request_repeater.h"
|
#include "selfdrive/ui/qt/request_repeater.h"
|
||||||
#include "selfdrive/ui/qt/util.h"
|
#include "selfdrive/ui/qt/util.h"
|
||||||
|
|
||||||
@@ -16,19 +15,19 @@ static QLabel* newLabel(const QString& text, const QString &type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DriveStats::DriveStats(QWidget* parent) : QFrame(parent) {
|
DriveStats::DriveStats(QWidget* parent) : QFrame(parent) {
|
||||||
metric_ = Params().getBool("IsMetric");
|
metric_ = params.getBool("IsMetric");
|
||||||
|
|
||||||
QVBoxLayout* main_layout = new QVBoxLayout(this);
|
QVBoxLayout* main_layout = new QVBoxLayout(this);
|
||||||
main_layout->setContentsMargins(50, 50, 50, 60);
|
main_layout->setContentsMargins(50, 25, 50, 20);
|
||||||
|
|
||||||
auto add_stats_layouts = [=](const QString &title, StatsLabels& labels) {
|
auto add_stats_layouts = [=](const QString &title, StatsLabels& labels, bool FrogPilot=false) {
|
||||||
QGridLayout* grid_layout = new QGridLayout;
|
QGridLayout* grid_layout = new QGridLayout;
|
||||||
grid_layout->setVerticalSpacing(10);
|
grid_layout->setVerticalSpacing(10);
|
||||||
grid_layout->setContentsMargins(0, 10, 0, 10);
|
grid_layout->setContentsMargins(0, 10, 0, 10);
|
||||||
|
|
||||||
int row = 0;
|
int row = 0;
|
||||||
grid_layout->addWidget(newLabel(title, "title"), row++, 0, 1, 3);
|
grid_layout->addWidget(newLabel(title, FrogPilot ? "frogpilot_title" : "title"), row++, 0, 1, 3);
|
||||||
grid_layout->addItem(new QSpacerItem(0, 50), row++, 0, 1, 1);
|
grid_layout->addItem(new QSpacerItem(0, 10), row++, 0, 1, 1);
|
||||||
|
|
||||||
grid_layout->addWidget(labels.routes = newLabel("0", "number"), row, 0, Qt::AlignLeft);
|
grid_layout->addWidget(labels.routes = newLabel("0", "number"), row, 0, Qt::AlignLeft);
|
||||||
grid_layout->addWidget(labels.distance = newLabel("0", "number"), row, 1, Qt::AlignLeft);
|
grid_layout->addWidget(labels.distance = newLabel("0", "number"), row, 1, Qt::AlignLeft);
|
||||||
@@ -39,11 +38,12 @@ DriveStats::DriveStats(QWidget* parent) : QFrame(parent) {
|
|||||||
grid_layout->addWidget(newLabel(tr("Hours"), "unit"), row + 1, 2, Qt::AlignLeft);
|
grid_layout->addWidget(newLabel(tr("Hours"), "unit"), row + 1, 2, Qt::AlignLeft);
|
||||||
|
|
||||||
main_layout->addLayout(grid_layout);
|
main_layout->addLayout(grid_layout);
|
||||||
|
main_layout->addStretch(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
add_stats_layouts(tr("ALL TIME"), all_);
|
add_stats_layouts(tr("ALL TIME"), all_);
|
||||||
main_layout->addStretch();
|
|
||||||
add_stats_layouts(tr("PAST WEEK"), week_);
|
add_stats_layouts(tr("PAST WEEK"), week_);
|
||||||
|
add_stats_layouts(tr("FROGPILOT"), frogPilot_, true);
|
||||||
|
|
||||||
if (auto dongleId = getDongleId()) {
|
if (auto dongleId = getDongleId()) {
|
||||||
QString url = CommaApi::BASE_URL + "/v1.1/devices/" + *dongleId + "/stats";
|
QString url = CommaApi::BASE_URL + "/v1.1/devices/" + *dongleId + "/stats";
|
||||||
@@ -57,13 +57,25 @@ DriveStats::DriveStats(QWidget* parent) : QFrame(parent) {
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QLabel[type="title"] { font-size: 51px; font-weight: 500; }
|
QLabel[type="title"] { font-size: 50px; font-weight: 500; }
|
||||||
QLabel[type="number"] { font-size: 78px; font-weight: 500; }
|
QLabel[type="frogpilot_title"] { font-size: 50px; font-weight: 500; color: #178643; }
|
||||||
QLabel[type="unit"] { font-size: 51px; font-weight: 300; color: #A0A0A0; }
|
QLabel[type="number"] { font-size: 65px; font-weight: 400; }
|
||||||
|
QLabel[type="unit"] { font-size: 50px; font-weight: 300; color: #A0A0A0; }
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
void DriveStats::updateStats() {
|
void DriveStats::updateStats() {
|
||||||
|
QJsonObject json = stats_.object();
|
||||||
|
|
||||||
|
auto updateFrogPilot = [this](const QJsonObject& obj, StatsLabels& labels) {
|
||||||
|
labels.routes->setText(QString::number(paramsStorage.getInt("FrogPilotDrives")));
|
||||||
|
labels.distance->setText(QString::number(int(paramsStorage.getFloat("FrogPilotKilometers") * (metric_ ? 1 : KM_TO_MILE))));
|
||||||
|
labels.distance_unit->setText(getDistanceUnit());
|
||||||
|
labels.hours->setText(QString::number(int(paramsStorage.getFloat("FrogPilotMinutes") / 60)));
|
||||||
|
};
|
||||||
|
|
||||||
|
updateFrogPilot(json["frogpilot"].toObject(), frogPilot_);
|
||||||
|
|
||||||
auto update = [=](const QJsonObject& obj, StatsLabels& labels) {
|
auto update = [=](const QJsonObject& obj, StatsLabels& labels) {
|
||||||
labels.routes->setText(QString::number((int)obj["routes"].toDouble()));
|
labels.routes->setText(QString::number((int)obj["routes"].toDouble()));
|
||||||
labels.distance->setText(QString::number(int(obj["distance"].toDouble() * (metric_ ? MILE_TO_KM : 1))));
|
labels.distance->setText(QString::number(int(obj["distance"].toDouble() * (metric_ ? MILE_TO_KM : 1))));
|
||||||
@@ -71,7 +83,6 @@ void DriveStats::updateStats() {
|
|||||||
labels.hours->setText(QString::number((int)(obj["minutes"].toDouble() / 60)));
|
labels.hours->setText(QString::number((int)(obj["minutes"].toDouble() / 60)));
|
||||||
};
|
};
|
||||||
|
|
||||||
QJsonObject json = stats_.object();
|
|
||||||
update(json["all"].toObject(), all_);
|
update(json["all"].toObject(), all_);
|
||||||
update(json["week"].toObject(), week_);
|
update(json["week"].toObject(), week_);
|
||||||
}
|
}
|
||||||
@@ -89,9 +100,6 @@ void DriveStats::parseResponse(const QString& response, bool success) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DriveStats::showEvent(QShowEvent* event) {
|
void DriveStats::showEvent(QShowEvent* event) {
|
||||||
bool metric = Params().getBool("IsMetric");
|
metric_ = params.getBool("IsMetric");
|
||||||
if (metric_ != metric) {
|
|
||||||
metric_ = metric;
|
|
||||||
updateStats();
|
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 <QJsonDocument>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
|
||||||
|
#include "common/params.h"
|
||||||
|
|
||||||
class DriveStats : public QFrame {
|
class DriveStats : public QFrame {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -15,10 +17,12 @@ private:
|
|||||||
inline QString getDistanceUnit() const { return metric_ ? tr("KM") : tr("Miles"); }
|
inline QString getDistanceUnit() const { return metric_ ? tr("KM") : tr("Miles"); }
|
||||||
|
|
||||||
bool metric_;
|
bool metric_;
|
||||||
|
Params params;
|
||||||
|
Params paramsStorage{"/persist/params"};
|
||||||
QJsonDocument stats_;
|
QJsonDocument stats_;
|
||||||
struct StatsLabels {
|
struct StatsLabels {
|
||||||
QLabel *routes, *distance, *distance_unit, *hours;
|
QLabel *routes, *distance, *distance_unit, *hours;
|
||||||
} all_, week_;
|
} all_, week_, frogPilot_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void parseResponse(const QString &response, bool success);
|
void parseResponse(const QString &response, bool success);
|
||||||
|
|||||||
0
selfdrive/ui/qt/widgets/input.cc
Executable file → Normal file
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);
|
disable_check_btn->setFixedSize(625, 125);
|
||||||
footer_layout->addWidget(disable_check_btn, 1, Qt::AlignBottom | Qt::AlignCenter);
|
footer_layout->addWidget(disable_check_btn, 1, Qt::AlignBottom | Qt::AlignCenter);
|
||||||
QObject::connect(disable_check_btn, &QPushButton::clicked, [=]() {
|
QObject::connect(disable_check_btn, &QPushButton::clicked, [=]() {
|
||||||
if (!params.getBool("FireTheBabysitter")) {
|
params.putBool("SnoozeUpdate", true);
|
||||||
params.putBool("FireTheBabysitter", true);
|
params.putBool("DeviceManagement", true);
|
||||||
}
|
|
||||||
if (!params.getBool("OfflineMode")) {
|
|
||||||
params.putBool("OfflineMode", true);
|
params.putBool("OfflineMode", true);
|
||||||
}
|
|
||||||
Hardware::reboot();
|
|
||||||
});
|
});
|
||||||
QObject::connect(disable_check_btn, &QPushButton::clicked, this, &AbstractAlert::dismiss);
|
QObject::connect(disable_check_btn, &QPushButton::clicked, this, &AbstractAlert::dismiss);
|
||||||
disable_check_btn->setStyleSheet(R"(color: white; background-color: #4F4F4F;)");
|
disable_check_btn->setStyleSheet(R"(color: white; background-color: #4F4F4F;)");
|
||||||
|
|||||||
0
selfdrive/ui/qt/widgets/offroad_alerts.h
Executable file → Normal file
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
|
// FrogPilot functions
|
||||||
void restorePosition(int previousScrollPosition);
|
void restorePosition(int previousScrollPosition);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void hideEvent(QHideEvent *e) override;
|
void hideEvent(QHideEvent *e) override;
|
||||||
};
|
};
|
||||||
|
|||||||
0
selfdrive/ui/qt/widgets/ssh_keys.cc
Executable file → Normal file
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;
|
enabled = value;
|
||||||
if (value) {
|
if (value) {
|
||||||
circleColor.setRgb(0xfafafa);
|
circleColor.setRgb(0xfafafa);
|
||||||
green.setRgb(0x0048FF);
|
green.setRgb(0x33ab4c);
|
||||||
} else {
|
} else {
|
||||||
circleColor.setRgb(0x888888);
|
circleColor.setRgb(0x888888);
|
||||||
green.setRgb(0x227722);
|
green.setRgb(0x227722);
|
||||||
|
|||||||
0
selfdrive/ui/qt/widgets/toggle.h
Executable file → Normal file
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);
|
stack->addWidget(uploading);
|
||||||
|
|
||||||
|
QWidget *notUploading = new QWidget;
|
||||||
|
QVBoxLayout *not_uploading_layout = new QVBoxLayout(notUploading);
|
||||||
|
not_uploading_layout->setContentsMargins(64, 56, 64, 56);
|
||||||
|
not_uploading_layout->setSpacing(36);
|
||||||
|
{
|
||||||
|
QHBoxLayout *title_layout = new QHBoxLayout;
|
||||||
|
{
|
||||||
|
QLabel *title = new QLabel(tr("Uploading disabled"));
|
||||||
|
title->setStyleSheet("font-size: 64px; font-weight: 600;");
|
||||||
|
title->setWordWrap(true);
|
||||||
|
title->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||||
|
title_layout->addWidget(title);
|
||||||
|
title_layout->addStretch();
|
||||||
|
|
||||||
|
QLabel *icon = new QLabel;
|
||||||
|
QPixmap pixmap("../frogpilot/assets/other_images/icon_wifi_uploading_disabled.svg");
|
||||||
|
icon->setPixmap(pixmap.scaledToWidth(120, Qt::SmoothTransformation));
|
||||||
|
title_layout->addWidget(icon);
|
||||||
|
}
|
||||||
|
not_uploading_layout->addLayout(title_layout);
|
||||||
|
|
||||||
|
QLabel *desc = new QLabel(tr("Toggle off the 'Disable Uploading' toggle to enable uploads."));
|
||||||
|
desc->setStyleSheet("font-size: 48px; font-weight: 400;");
|
||||||
|
desc->setWordWrap(true);
|
||||||
|
not_uploading_layout->addWidget(desc);
|
||||||
|
}
|
||||||
|
stack->addWidget(notUploading);
|
||||||
|
|
||||||
setStyleSheet(R"(
|
setStyleSheet(R"(
|
||||||
WiFiPromptWidget {
|
WiFiPromptWidget {
|
||||||
background-color: #333333;
|
background-color: #333333;
|
||||||
@@ -99,5 +127,6 @@ void WiFiPromptWidget::updateState(const UIState &s) {
|
|||||||
auto network_type = sm["deviceState"].getDeviceState().getNetworkType();
|
auto network_type = sm["deviceState"].getDeviceState().getNetworkType();
|
||||||
auto uploading = network_type == cereal::DeviceState::NetworkType::WIFI ||
|
auto uploading = network_type == cereal::DeviceState::NetworkType::WIFI ||
|
||||||
network_type == cereal::DeviceState::NetworkType::ETHERNET;
|
network_type == cereal::DeviceState::NetworkType::ETHERNET;
|
||||||
stack->setCurrentIndex(uploading ? 1 : 0);
|
bool uploading_disabled = params.getBool("DeviceManagement") && params.getBool("NoUploads");
|
||||||
|
stack->setCurrentIndex(uploading_disabled ? 2 : uploading ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|||||||
4
selfdrive/ui/qt/widgets/wifi.h
Executable file → Normal file
4
selfdrive/ui/qt/widgets/wifi.h
Executable file → Normal file
@@ -18,6 +18,10 @@ signals:
|
|||||||
public slots:
|
public slots:
|
||||||
void updateState(const UIState &s);
|
void updateState(const UIState &s);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// FrogPilot variables
|
||||||
|
Params params;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QStackedLayout *stack;
|
QStackedLayout *stack;
|
||||||
};
|
};
|
||||||
|
|||||||
37
selfdrive/ui/qt/window.cc
Executable file → Normal file
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::openSettings, this, &MainWindow::openSettings);
|
||||||
QObject::connect(homeWindow, &HomeWindow::closeSettings, this, &MainWindow::closeSettings);
|
QObject::connect(homeWindow, &HomeWindow::closeSettings, this, &MainWindow::closeSettings);
|
||||||
|
|
||||||
oscarSettingsWindow = new OscarSettingsWindow(this);
|
settingsWindow = new SettingsWindow(this);
|
||||||
main_layout->addWidget(oscarSettingsWindow);
|
main_layout->addWidget(settingsWindow);
|
||||||
QObject::connect(oscarSettingsWindow, &OscarSettingsWindow::closeSettings, this, &MainWindow::closeSettings);
|
QObject::connect(settingsWindow, &SettingsWindow::closeSettings, this, &MainWindow::closeSettings);
|
||||||
// QObject::connect(oscarSettingsWindow, &OscarSettingsWindow::reviewTrainingGuide, [=]() {
|
QObject::connect(settingsWindow, &SettingsWindow::reviewTrainingGuide, [=]() {
|
||||||
// onboardingWindow->showTrainingGuide();
|
onboardingWindow->showTrainingGuide();
|
||||||
// main_layout->setCurrentWidget(onboardingWindow);
|
main_layout->setCurrentWidget(onboardingWindow);
|
||||||
// });
|
});
|
||||||
QObject::connect(oscarSettingsWindow, &OscarSettingsWindow::showDriverView, [=] {
|
QObject::connect(settingsWindow, &SettingsWindow::showDriverView, [=] {
|
||||||
homeWindow->showDriverView(true);
|
homeWindow->showDriverView(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -38,11 +38,11 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
|
|||||||
closeSettings();
|
closeSettings();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// QObject::connect(device(), &Device::interactiveTimeout, [=]() {
|
QObject::connect(device(), &Device::interactiveTimeout, [=]() {
|
||||||
// if (main_layout->currentWidget() == oscarSettingsWindow) {
|
if (main_layout->currentWidget() == settingsWindow) {
|
||||||
// closeSettings();
|
closeSettings();
|
||||||
// }
|
}
|
||||||
// });
|
});
|
||||||
|
|
||||||
// load fonts
|
// load fonts
|
||||||
QFontDatabase::addApplicationFont("../assets/fonts/Inter-Black.ttf");
|
QFontDatabase::addApplicationFont("../assets/fonts/Inter-Black.ttf");
|
||||||
@@ -66,8 +66,8 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::openSettings(int index, const QString ¶m) {
|
void MainWindow::openSettings(int index, const QString ¶m) {
|
||||||
main_layout->setCurrentWidget(oscarSettingsWindow);
|
main_layout->setCurrentWidget(settingsWindow);
|
||||||
oscarSettingsWindow->setCurrentPanel(index, param);
|
settingsWindow->setCurrentPanel(index, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::closeSettings() {
|
void MainWindow::closeSettings() {
|
||||||
@@ -93,12 +93,7 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
|
|||||||
case QEvent::MouseMove: {
|
case QEvent::MouseMove: {
|
||||||
// ignore events when device is awakened by resetInteractiveTimeout
|
// ignore events when device is awakened by resetInteractiveTimeout
|
||||||
ignore = !device()->isAwake();
|
ignore = !device()->isAwake();
|
||||||
// if (main_layout->currentWidget() == oscarSettingsWindow) {
|
device()->resetInteractiveTimeout(uiState()->scene.screen_timeout, uiState()->scene.screen_timeout_onroad);
|
||||||
// Not working...
|
|
||||||
// device()->resetInteractiveTimeout(60 * 5); // 5 minute timeout if looking at settings window
|
|
||||||
// } else {
|
|
||||||
device()->resetInteractiveTimeout(); // Default 30 seconds otherwise
|
|
||||||
// }
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
3
selfdrive/ui/qt/window.h
Executable file → Normal file
3
selfdrive/ui/qt/window.h
Executable file → Normal file
@@ -6,7 +6,6 @@
|
|||||||
#include "selfdrive/ui/qt/home.h"
|
#include "selfdrive/ui/qt/home.h"
|
||||||
#include "selfdrive/ui/qt/offroad/onboarding.h"
|
#include "selfdrive/ui/qt/offroad/onboarding.h"
|
||||||
#include "selfdrive/ui/qt/offroad/settings.h"
|
#include "selfdrive/ui/qt/offroad/settings.h"
|
||||||
#include "selfdrive/oscarpilot/settings/settings.h"
|
|
||||||
|
|
||||||
class MainWindow : public QWidget {
|
class MainWindow : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -21,7 +20,7 @@ private:
|
|||||||
|
|
||||||
QStackedLayout *main_layout;
|
QStackedLayout *main_layout;
|
||||||
HomeWindow *homeWindow;
|
HomeWindow *homeWindow;
|
||||||
OscarSettingsWindow *oscarSettingsWindow;
|
SettingsWindow *settingsWindow;
|
||||||
OnboardingWindow *onboardingWindow;
|
OnboardingWindow *onboardingWindow;
|
||||||
|
|
||||||
// FrogPilot variables
|
// FrogPilot variables
|
||||||
|
|||||||
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 math
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
import threading
|
|
||||||
import wave
|
import wave
|
||||||
|
|
||||||
from typing import Dict, Optional, Tuple
|
|
||||||
|
|
||||||
from cereal import car, messaging
|
from cereal import car, messaging
|
||||||
from openpilot.common.basedir import BASEDIR
|
from openpilot.common.basedir import BASEDIR
|
||||||
@@ -29,7 +28,7 @@ DB_SCALE = 30 # AMBIENT_DB + DB_SCALE is where MAX_VOLUME is applied
|
|||||||
AudibleAlert = car.CarControl.HUDControl.AudibleAlert
|
AudibleAlert = car.CarControl.HUDControl.AudibleAlert
|
||||||
|
|
||||||
|
|
||||||
sound_list: Dict[int, Tuple[str, Optional[int], float]] = {
|
sound_list: dict[int, tuple[str, int | None, float]] = {
|
||||||
# AudibleAlert, file name, play count (none for infinite)
|
# AudibleAlert, file name, play count (none for infinite)
|
||||||
AudibleAlert.engage: ("engage.wav", 1, MAX_VOLUME),
|
AudibleAlert.engage: ("engage.wav", 1, MAX_VOLUME),
|
||||||
AudibleAlert.disengage: ("disengage.wav", 1, MAX_VOLUME),
|
AudibleAlert.disengage: ("disengage.wav", 1, MAX_VOLUME),
|
||||||
@@ -42,11 +41,21 @@ sound_list: Dict[int, Tuple[str, Optional[int], float]] = {
|
|||||||
AudibleAlert.warningSoft: ("warning_soft.wav", None, MAX_VOLUME),
|
AudibleAlert.warningSoft: ("warning_soft.wav", None, MAX_VOLUME),
|
||||||
AudibleAlert.warningImmediate: ("warning_immediate.wav", None, MAX_VOLUME),
|
AudibleAlert.warningImmediate: ("warning_immediate.wav", None, MAX_VOLUME),
|
||||||
|
|
||||||
AudibleAlert.firefox: ("firefox.wav", None, MAX_VOLUME),
|
# Random Events
|
||||||
|
AudibleAlert.angry: ("angry.wav", 1, MAX_VOLUME),
|
||||||
|
AudibleAlert.doc: ("doc.wav", 1, MAX_VOLUME),
|
||||||
|
AudibleAlert.fart: ("fart.wav", 1, MAX_VOLUME),
|
||||||
|
AudibleAlert.firefox: ("firefox.wav", 1, MAX_VOLUME),
|
||||||
|
AudibleAlert.nessie: ("nessie.wav", 1, MAX_VOLUME),
|
||||||
|
AudibleAlert.noice: ("noice.wav", 1, MAX_VOLUME),
|
||||||
|
AudibleAlert.uwu: ("uwu.wav", 1, MAX_VOLUME),
|
||||||
|
|
||||||
|
# Other
|
||||||
|
AudibleAlert.goat: ("goat.wav", None, MAX_VOLUME),
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_controls_timeout_alert(sm):
|
def check_controls_timeout_alert(sm):
|
||||||
controls_missing = time.monotonic() - sm.rcv_time['controlsState']
|
controls_missing = time.monotonic() - sm.recv_time['controlsState']
|
||||||
|
|
||||||
if controls_missing > CONTROLS_TIMEOUT:
|
if controls_missing > CONTROLS_TIMEOUT:
|
||||||
if sm['controlsState'].enabled and (controls_missing - CONTROLS_TIMEOUT) < 10:
|
if sm['controlsState'].enabled and (controls_missing - CONTROLS_TIMEOUT) < 10:
|
||||||
@@ -61,9 +70,20 @@ class Soundd:
|
|||||||
self.params = Params()
|
self.params = Params()
|
||||||
self.params_memory = Params("/dev/shm/params")
|
self.params_memory = Params("/dev/shm/params")
|
||||||
|
|
||||||
self.update_frogpilot_params()
|
self.previous_sound_directory = None
|
||||||
|
self.random_events_directory = BASEDIR + "/selfdrive/frogpilot/assets/random_events/sounds/"
|
||||||
|
|
||||||
self.load_sounds()
|
self.random_events_map = {
|
||||||
|
AudibleAlert.angry: MAX_VOLUME,
|
||||||
|
AudibleAlert.doc: MAX_VOLUME,
|
||||||
|
AudibleAlert.fart: MAX_VOLUME,
|
||||||
|
AudibleAlert.firefox: MAX_VOLUME,
|
||||||
|
AudibleAlert.nessie: MAX_VOLUME,
|
||||||
|
AudibleAlert.noice: MAX_VOLUME,
|
||||||
|
AudibleAlert.uwu: MAX_VOLUME,
|
||||||
|
}
|
||||||
|
|
||||||
|
self.update_frogpilot_params()
|
||||||
|
|
||||||
self.current_alert = AudibleAlert.none
|
self.current_alert = AudibleAlert.none
|
||||||
self.current_volume = MIN_VOLUME
|
self.current_volume = MIN_VOLUME
|
||||||
@@ -74,13 +94,22 @@ class Soundd:
|
|||||||
self.spl_filter_weighted = FirstOrderFilter(0, 2.5, FILTER_DT, initialized=False)
|
self.spl_filter_weighted = FirstOrderFilter(0, 2.5, FILTER_DT, initialized=False)
|
||||||
|
|
||||||
def load_sounds(self):
|
def load_sounds(self):
|
||||||
self.loaded_sounds: Dict[int, np.ndarray] = {}
|
self.loaded_sounds: dict[int, np.ndarray] = {}
|
||||||
|
|
||||||
# Load all sounds
|
# Load all sounds
|
||||||
for sound in sound_list:
|
for sound in sound_list:
|
||||||
|
if sound == AudibleAlert.goat and not self.goat_scream:
|
||||||
|
continue
|
||||||
|
|
||||||
filename, play_count, volume = sound_list[sound]
|
filename, play_count, volume = sound_list[sound]
|
||||||
|
|
||||||
|
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')
|
wavefile = wave.open(self.sound_directory + filename, 'r')
|
||||||
|
except FileNotFoundError:
|
||||||
|
wavefile = wave.open(BASEDIR + "/selfdrive/assets/sounds/" + filename, 'r')
|
||||||
|
|
||||||
assert wavefile.getnchannels() == 1
|
assert wavefile.getnchannels() == 1
|
||||||
assert wavefile.getsampwidth() == 2
|
assert wavefile.getsampwidth() == 2
|
||||||
@@ -156,9 +185,15 @@ class Soundd:
|
|||||||
while True:
|
while True:
|
||||||
sm.update(0)
|
sm.update(0)
|
||||||
|
|
||||||
if sm.updated['microphone'] and self.current_alert == AudibleAlert.none: # only update volume filter when not playing alert
|
if sm.updated['microphone'] and self.current_alert == AudibleAlert.none and not self.alert_volume_control: # only update volume filter when not playing alert
|
||||||
self.spl_filter_weighted.update(sm["microphone"].soundPressureWeightedDb)
|
self.spl_filter_weighted.update(sm["microphone"].soundPressureWeightedDb)
|
||||||
self.current_volume = self.calculate_volume(float(self.spl_filter_weighted.x)) if not self.silent_mode else 0
|
self.current_volume = self.calculate_volume(float(self.spl_filter_weighted.x))
|
||||||
|
|
||||||
|
elif self.alert_volume_control and self.current_alert in self.volume_map:
|
||||||
|
self.current_volume = self.volume_map[self.current_alert] / 100.0
|
||||||
|
|
||||||
|
elif self.current_alert in self.random_events_map:
|
||||||
|
self.current_volume = self.random_events_map[self.current_alert]
|
||||||
|
|
||||||
self.get_audible_alert(sm)
|
self.get_audible_alert(sm)
|
||||||
|
|
||||||
@@ -168,27 +203,66 @@ class Soundd:
|
|||||||
|
|
||||||
# Update FrogPilot parameters
|
# Update FrogPilot parameters
|
||||||
if self.params_memory.get_bool("FrogPilotTogglesUpdated"):
|
if self.params_memory.get_bool("FrogPilotTogglesUpdated"):
|
||||||
updateFrogPilotParams = threading.Thread(target=self.update_frogpilot_params)
|
self.update_frogpilot_params()
|
||||||
updateFrogPilotParams.start()
|
|
||||||
|
|
||||||
def update_frogpilot_params(self):
|
def update_frogpilot_params(self):
|
||||||
self.silent_mode = self.params.get_bool("SilentMode")
|
self.alert_volume_control = self.params.get_bool("AlertVolumeControl")
|
||||||
|
|
||||||
|
self.volume_map = {
|
||||||
|
AudibleAlert.engage: self.params.get_int("EngageVolume"),
|
||||||
|
AudibleAlert.disengage: self.params.get_int("DisengageVolume"),
|
||||||
|
AudibleAlert.refuse: self.params.get_int("RefuseVolume"),
|
||||||
|
|
||||||
|
AudibleAlert.prompt: self.params.get_int("PromptVolume"),
|
||||||
|
AudibleAlert.promptRepeat: self.params.get_int("PromptVolume"),
|
||||||
|
AudibleAlert.promptDistracted: self.params.get_int("PromptDistractedVolume"),
|
||||||
|
|
||||||
|
AudibleAlert.warningSoft: self.params.get_int("WarningSoftVolume"),
|
||||||
|
AudibleAlert.warningImmediate: self.params.get_int("WarningImmediateVolume"),
|
||||||
|
|
||||||
|
AudibleAlert.goat: self.params.get_int("PromptVolume"),
|
||||||
|
}
|
||||||
|
|
||||||
custom_theme = self.params.get_bool("CustomTheme")
|
custom_theme = self.params.get_bool("CustomTheme")
|
||||||
custom_sounds = self.params.get_int("CustomSounds") if custom_theme else 0
|
custom_sounds = self.params.get_int("CustomSounds") if custom_theme else 0
|
||||||
|
self.goat_scream = custom_sounds == 1 and self.params.get_bool("GoatScream")
|
||||||
|
|
||||||
theme_configuration = {
|
theme_configuration = {
|
||||||
0: "stock",
|
|
||||||
1: "frog_theme",
|
1: "frog_theme",
|
||||||
2: "tesla_theme",
|
2: "tesla_theme",
|
||||||
3: "stalin_theme"
|
3: "stalin_theme"
|
||||||
}
|
}
|
||||||
|
|
||||||
theme_name = theme_configuration.get(custom_sounds, "stock")
|
holiday_themes = custom_theme and self.params.get_bool("HolidayThemes")
|
||||||
self.sound_directory = (f"{BASEDIR}/selfdrive/frogpilot/assets/custom_themes/{theme_name}/sounds/" if custom_sounds else f"{BASEDIR}/selfdrive/assets/sounds/")
|
current_holiday_theme = self.params_memory.get_int("CurrentHolidayTheme") if holiday_themes else 0
|
||||||
|
|
||||||
|
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.load_sounds()
|
||||||
|
|
||||||
|
self.previous_sound_directory = self.sound_directory
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
s = Soundd()
|
s = Soundd()
|
||||||
s.soundd_thread()
|
s.soundd_thread()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
if [ -f /TICI ] && [ ! -f qt/spinner ]; then
|
if [ -f /TICI ] && [ ! -f _spinner ]; then
|
||||||
cp qt/spinner_larch64 qt/spinner
|
cp qt/spinner_larch64 _spinner
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exec ./qt/spinner "$1"
|
exec ./_spinner "$1"
|
||||||
|
|||||||
6
selfdrive/ui/tests/.gitignore
vendored
Normal file
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