mirror of
https://github.com/infiniteCable2/openpilot.git
synced 2026-08-03 08:41:41 +08:00
87ff98dc7c
* ui: update names * Revert "ui: update names" This reverts commit 313510c8c0f112758ca1cc3e625d47ba47989bd5. * gone * init * Changed all occurrences of 'openpilot' to 'sunnypilot' All instances of 'openpilot' in the user interface, documentation, and prompts have been updated to 'sunnypilot'. These changes include source strings, warning messages, and the prompts shown to users. The adjustment is part of the rebranding process and aims to avoid any uncertainty or confusion for users. * vanish translations * translations --------- Co-authored-by: DevTekVE <devtekve@gmail.com>
490 lines
14 KiB
C++
490 lines
14 KiB
C++
#include "selfdrive/ui/qt/setup/setup.h"
|
||
|
||
#include <cstdio>
|
||
#include <cstdlib>
|
||
#include <sstream>
|
||
#include <string>
|
||
|
||
#include <QApplication>
|
||
#include <QLabel>
|
||
#include <QVBoxLayout>
|
||
|
||
#include <curl/curl.h>
|
||
|
||
#include "common/util.h"
|
||
#include "system/hardware/hw.h"
|
||
#include "selfdrive/ui/qt/api.h"
|
||
#include "selfdrive/ui/qt/qt_window.h"
|
||
#include "selfdrive/ui/qt/network/networking.h"
|
||
#include "selfdrive/ui/qt/util.h"
|
||
#include "selfdrive/ui/qt/widgets/input.h"
|
||
|
||
const std::string USER_AGENT = "AGNOSSetup-";
|
||
const QString OPENPILOT_URL = "https://openpilot.comma.ai";
|
||
|
||
bool is_elf(char *fname) {
|
||
FILE *fp = fopen(fname, "rb");
|
||
if (fp == NULL) {
|
||
return false;
|
||
}
|
||
char buf[4];
|
||
size_t n = fread(buf, 1, 4, fp);
|
||
fclose(fp);
|
||
return n == 4 && buf[0] == 0x7f && buf[1] == 'E' && buf[2] == 'L' && buf[3] == 'F';
|
||
}
|
||
|
||
void Setup::download(QString url) {
|
||
// autocomplete incomplete urls
|
||
if (QRegularExpression("^([^/.]+)/([^/]+)$").match(url).hasMatch()) {
|
||
url.prepend("https://installer.comma.ai/");
|
||
}
|
||
|
||
CURL *curl = curl_easy_init();
|
||
if (!curl) {
|
||
emit finished(url, tr("Something went wrong. Reboot the device."));
|
||
return;
|
||
}
|
||
|
||
auto version = util::read_file("/VERSION");
|
||
|
||
struct curl_slist *list = NULL;
|
||
std::string header = "X-openpilot-serial: " + Hardware::get_serial();
|
||
list = curl_slist_append(list, header.c_str());
|
||
|
||
char tmpfile[] = "/tmp/installer_XXXXXX";
|
||
FILE *fp = fdopen(mkstemp(tmpfile), "wb");
|
||
|
||
curl_easy_setopt(curl, CURLOPT_URL, url.toStdString().c_str());
|
||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
|
||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
|
||
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||
curl_easy_setopt(curl, CURLOPT_USERAGENT, (USER_AGENT + version).c_str());
|
||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
|
||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
|
||
|
||
int ret = curl_easy_perform(curl);
|
||
long res_status = 0;
|
||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_status);
|
||
|
||
if (ret != CURLE_OK || res_status != 200) {
|
||
emit finished(url, tr("Ensure the entered URL is valid, and the device’s internet connection is good."));
|
||
} else if (!is_elf(tmpfile)) {
|
||
emit finished(url, tr("No custom software found at this URL."));
|
||
} else {
|
||
rename(tmpfile, "/tmp/installer");
|
||
|
||
FILE *fp_url = fopen("/tmp/installer_url", "w");
|
||
fprintf(fp_url, "%s", url.toStdString().c_str());
|
||
fclose(fp_url);
|
||
|
||
emit finished(url);
|
||
}
|
||
|
||
curl_slist_free_all(list);
|
||
curl_easy_cleanup(curl);
|
||
fclose(fp);
|
||
}
|
||
|
||
QWidget * Setup::low_voltage() {
|
||
QWidget *widget = new QWidget();
|
||
QVBoxLayout *main_layout = new QVBoxLayout(widget);
|
||
main_layout->setContentsMargins(55, 0, 55, 55);
|
||
main_layout->setSpacing(0);
|
||
|
||
// inner text layout: warning icon, title, and body
|
||
QVBoxLayout *inner_layout = new QVBoxLayout();
|
||
inner_layout->setContentsMargins(110, 144, 365, 0);
|
||
main_layout->addLayout(inner_layout);
|
||
|
||
QLabel *triangle = new QLabel();
|
||
triangle->setPixmap(QPixmap(ASSET_PATH + "offroad/icon_warning.png"));
|
||
inner_layout->addWidget(triangle, 0, Qt::AlignTop | Qt::AlignLeft);
|
||
inner_layout->addSpacing(80);
|
||
|
||
QLabel *title = new QLabel(tr("WARNING: Low Voltage"));
|
||
title->setStyleSheet("font-size: 90px; font-weight: 500; color: #FF594F;");
|
||
inner_layout->addWidget(title, 0, Qt::AlignTop | Qt::AlignLeft);
|
||
|
||
inner_layout->addSpacing(25);
|
||
|
||
QLabel *body = new QLabel(tr("Power your device in a car with a harness or proceed at your own risk."));
|
||
body->setWordWrap(true);
|
||
body->setAlignment(Qt::AlignTop | Qt::AlignLeft);
|
||
body->setStyleSheet("font-size: 80px; font-weight: 300;");
|
||
inner_layout->addWidget(body);
|
||
|
||
inner_layout->addStretch();
|
||
|
||
// power off + continue buttons
|
||
QHBoxLayout *blayout = new QHBoxLayout();
|
||
blayout->setSpacing(50);
|
||
main_layout->addLayout(blayout, 0);
|
||
|
||
QPushButton *poweroff = new QPushButton(tr("Power off"));
|
||
poweroff->setObjectName("navBtn");
|
||
blayout->addWidget(poweroff);
|
||
QObject::connect(poweroff, &QPushButton::clicked, this, [=]() {
|
||
Hardware::poweroff();
|
||
});
|
||
|
||
QPushButton *cont = new QPushButton(tr("Continue"));
|
||
cont->setObjectName("navBtn");
|
||
blayout->addWidget(cont);
|
||
QObject::connect(cont, &QPushButton::clicked, this, &Setup::nextPage);
|
||
|
||
return widget;
|
||
}
|
||
|
||
QWidget * Setup::getting_started() {
|
||
QWidget *widget = new QWidget();
|
||
|
||
QHBoxLayout *main_layout = new QHBoxLayout(widget);
|
||
main_layout->setMargin(0);
|
||
|
||
QVBoxLayout *vlayout = new QVBoxLayout();
|
||
vlayout->setContentsMargins(165, 280, 100, 0);
|
||
main_layout->addLayout(vlayout);
|
||
|
||
QLabel *title = new QLabel(tr("Getting Started"));
|
||
title->setStyleSheet("font-size: 90px; font-weight: 500;");
|
||
vlayout->addWidget(title, 0, Qt::AlignTop | Qt::AlignLeft);
|
||
|
||
vlayout->addSpacing(90);
|
||
QLabel *desc = new QLabel(tr("Before we get on the road, let’s finish installation and cover some details."));
|
||
desc->setWordWrap(true);
|
||
desc->setStyleSheet("font-size: 80px; font-weight: 300;");
|
||
vlayout->addWidget(desc, 0, Qt::AlignTop | Qt::AlignLeft);
|
||
|
||
vlayout->addStretch();
|
||
|
||
QPushButton *btn = new QPushButton();
|
||
btn->setIcon(QIcon(":/img_continue_triangle.svg"));
|
||
btn->setIconSize(QSize(54, 106));
|
||
btn->setFixedSize(310, 1080);
|
||
btn->setProperty("primary", true);
|
||
btn->setStyleSheet("border: none;");
|
||
main_layout->addWidget(btn, 0, Qt::AlignRight);
|
||
QObject::connect(btn, &QPushButton::clicked, this, &Setup::nextPage);
|
||
|
||
return widget;
|
||
}
|
||
|
||
QWidget * Setup::network_setup() {
|
||
QWidget *widget = new QWidget();
|
||
QVBoxLayout *main_layout = new QVBoxLayout(widget);
|
||
main_layout->setContentsMargins(55, 50, 55, 50);
|
||
|
||
// title
|
||
QLabel *title = new QLabel(tr("Connect to Wi-Fi"));
|
||
title->setStyleSheet("font-size: 90px; font-weight: 500;");
|
||
main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop);
|
||
|
||
main_layout->addSpacing(25);
|
||
|
||
// wifi widget
|
||
Networking *networking = new Networking(this, false);
|
||
networking->setStyleSheet("Networking {background-color: #292929; border-radius: 13px;}");
|
||
main_layout->addWidget(networking, 1);
|
||
|
||
main_layout->addSpacing(35);
|
||
|
||
// 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();
|
||
cont->setObjectName("navBtn");
|
||
cont->setProperty("primary", true);
|
||
cont->setEnabled(false);
|
||
QObject::connect(cont, &QPushButton::clicked, this, &Setup::nextPage);
|
||
blayout->addWidget(cont);
|
||
|
||
// setup timer for testing internet connection
|
||
HttpRequest *request = new HttpRequest(this, false, 2500);
|
||
QObject::connect(request, &HttpRequest::requestDone, [=](const QString &, bool success) {
|
||
cont->setEnabled(success);
|
||
if (success) {
|
||
const bool wifi = networking->wifi->currentNetworkType() == NetworkType::WIFI;
|
||
cont->setText(wifi ? tr("Continue") : tr("Continue without Wi-Fi"));
|
||
} else {
|
||
cont->setText(tr("Waiting for internet"));
|
||
}
|
||
repaint();
|
||
});
|
||
request->sendRequest(OPENPILOT_URL);
|
||
QTimer *timer = new QTimer(this);
|
||
QObject::connect(timer, &QTimer::timeout, [=]() {
|
||
if (!request->active() && cont->isVisible()) {
|
||
request->sendRequest(OPENPILOT_URL);
|
||
}
|
||
});
|
||
timer->start(1000);
|
||
|
||
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);
|
||
|
||
// sunnypilot + custom radio buttons
|
||
QButtonGroup *group = new QButtonGroup(widget);
|
||
group->setExclusive(true);
|
||
|
||
QWidget *openpilot = radio_button(tr("sunnypilot"), group);
|
||
main_layout->addWidget(openpilot);
|
||
|
||
main_layout->addSpacing(30);
|
||
|
||
QWidget *custom = radio_button(tr("Custom Software"), group);
|
||
main_layout->addWidget(custom);
|
||
|
||
main_layout->addStretch();
|
||
|
||
// back + continue buttons
|
||
QHBoxLayout *blayout = new QHBoxLayout;
|
||
main_layout->addLayout(blayout);
|
||
blayout->setSpacing(50);
|
||
|
||
QPushButton *back = new QPushButton(tr("Back"));
|
||
back->setObjectName("navBtn");
|
||
QObject::connect(back, &QPushButton::clicked, this, &Setup::prevPage);
|
||
blayout->addWidget(back);
|
||
|
||
QPushButton *cont = new QPushButton(tr("Continue"));
|
||
cont->setObjectName("navBtn");
|
||
cont->setEnabled(false);
|
||
cont->setProperty("primary", true);
|
||
blayout->addWidget(cont);
|
||
|
||
QObject::connect(cont, &QPushButton::clicked, [=]() {
|
||
auto w = currentWidget();
|
||
QTimer::singleShot(0, [=]() {
|
||
setCurrentWidget(downloading_widget);
|
||
});
|
||
QString url = OPENPILOT_URL;
|
||
if (group->checkedButton() != openpilot) {
|
||
url = InputDialog::getText(tr("Enter URL"), this, tr("for Custom Software"));
|
||
}
|
||
if (!url.isEmpty()) {
|
||
QTimer::singleShot(1000, this, [=]() {
|
||
download(url);
|
||
});
|
||
} else {
|
||
setCurrentWidget(w);
|
||
}
|
||
});
|
||
|
||
connect(group, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), [=](QAbstractButton *btn) {
|
||
btn->setChecked(true);
|
||
cont->setEnabled(true);
|
||
});
|
||
|
||
return widget;
|
||
}
|
||
|
||
QWidget * Setup::downloading() {
|
||
QWidget *widget = new QWidget();
|
||
QVBoxLayout *main_layout = new QVBoxLayout(widget);
|
||
QLabel *txt = new QLabel(tr("Downloading..."));
|
||
txt->setStyleSheet("font-size: 90px; font-weight: 500;");
|
||
main_layout->addWidget(txt, 0, Qt::AlignCenter);
|
||
return widget;
|
||
}
|
||
|
||
QWidget * Setup::download_failed(QLabel *url, QLabel *body) {
|
||
QWidget *widget = new QWidget();
|
||
QVBoxLayout *main_layout = new QVBoxLayout(widget);
|
||
main_layout->setContentsMargins(55, 185, 55, 55);
|
||
main_layout->setSpacing(0);
|
||
|
||
QLabel *title = new QLabel(tr("Download Failed"));
|
||
title->setStyleSheet("font-size: 90px; font-weight: 500;");
|
||
main_layout->addWidget(title, 0, Qt::AlignTop | Qt::AlignLeft);
|
||
|
||
main_layout->addSpacing(67);
|
||
|
||
url->setWordWrap(true);
|
||
url->setAlignment(Qt::AlignTop | Qt::AlignLeft);
|
||
url->setStyleSheet("font-family: \"JetBrains Mono\"; font-size: 64px; font-weight: 400; margin-right: 100px;");
|
||
main_layout->addWidget(url);
|
||
|
||
main_layout->addSpacing(48);
|
||
|
||
body->setWordWrap(true);
|
||
body->setAlignment(Qt::AlignTop | Qt::AlignLeft);
|
||
body->setStyleSheet("font-size: 80px; font-weight: 300; margin-right: 100px;");
|
||
main_layout->addWidget(body);
|
||
|
||
main_layout->addStretch();
|
||
|
||
// reboot + start over buttons
|
||
QHBoxLayout *blayout = new QHBoxLayout();
|
||
blayout->setSpacing(50);
|
||
main_layout->addLayout(blayout, 0);
|
||
|
||
QPushButton *reboot = new QPushButton(tr("Reboot device"));
|
||
reboot->setObjectName("navBtn");
|
||
blayout->addWidget(reboot);
|
||
QObject::connect(reboot, &QPushButton::clicked, this, [=]() {
|
||
Hardware::reboot();
|
||
});
|
||
|
||
QPushButton *restart = new QPushButton(tr("Start over"));
|
||
restart->setObjectName("navBtn");
|
||
restart->setProperty("primary", true);
|
||
blayout->addWidget(restart);
|
||
QObject::connect(restart, &QPushButton::clicked, this, [=]() {
|
||
setCurrentIndex(1);
|
||
});
|
||
|
||
widget->setStyleSheet(R"(
|
||
QLabel {
|
||
margin-left: 117;
|
||
}
|
||
)");
|
||
return widget;
|
||
}
|
||
|
||
void Setup::prevPage() {
|
||
setCurrentIndex(currentIndex() - 1);
|
||
}
|
||
|
||
void Setup::nextPage() {
|
||
setCurrentIndex(currentIndex() + 1);
|
||
}
|
||
|
||
Setup::Setup(QWidget *parent) : QStackedWidget(parent) {
|
||
if (std::getenv("MULTILANG")) {
|
||
selectLanguage();
|
||
}
|
||
|
||
std::stringstream buffer;
|
||
buffer << std::ifstream("/sys/class/hwmon/hwmon1/in1_input").rdbuf();
|
||
float voltage = (float)std::atoi(buffer.str().c_str()) / 1000.;
|
||
if (voltage < 7) {
|
||
addWidget(low_voltage());
|
||
}
|
||
|
||
addWidget(getting_started());
|
||
addWidget(network_setup());
|
||
addWidget(software_selection());
|
||
|
||
downloading_widget = downloading();
|
||
addWidget(downloading_widget);
|
||
|
||
QLabel *url_label = new QLabel();
|
||
QLabel *body_label = new QLabel();
|
||
failed_widget = download_failed(url_label, body_label);
|
||
addWidget(failed_widget);
|
||
|
||
QObject::connect(this, &Setup::finished, [=](const QString &url, const QString &error) {
|
||
qDebug() << "finished" << url << error;
|
||
if (error.isEmpty()) {
|
||
// hide setup on success
|
||
QTimer::singleShot(3000, this, &QWidget::hide);
|
||
} else {
|
||
url_label->setText(url);
|
||
body_label->setText(error);
|
||
setCurrentWidget(failed_widget);
|
||
}
|
||
});
|
||
|
||
// TODO: revisit pressed bg color
|
||
setStyleSheet(R"(
|
||
* {
|
||
color: white;
|
||
font-family: Inter;
|
||
}
|
||
Setup {
|
||
background-color: black;
|
||
}
|
||
QPushButton#navBtn {
|
||
height: 160;
|
||
font-size: 55px;
|
||
font-weight: 400;
|
||
border-radius: 10px;
|
||
background-color: #333333;
|
||
}
|
||
QPushButton#navBtn:disabled, QPushButton[primary='true']:disabled {
|
||
color: #808080;
|
||
background-color: #333333;
|
||
}
|
||
QPushButton#navBtn:pressed {
|
||
background-color: #444444;
|
||
}
|
||
QPushButton[primary='true'], #navBtn[primary='true'] {
|
||
background-color: #465BEA;
|
||
}
|
||
QPushButton[primary='true']:pressed, #navBtn:pressed[primary='true'] {
|
||
background-color: #3049F4;
|
||
}
|
||
)");
|
||
}
|
||
|
||
void Setup::selectLanguage() {
|
||
QMap<QString, QString> langs = getSupportedLanguages();
|
||
QString selection = MultiOptionDialog::getSelection(tr("Select a language"), langs.keys(), "", this);
|
||
if (!selection.isEmpty()) {
|
||
QString selectedLang = langs[selection];
|
||
Params().put("LanguageSetting", selectedLang.toStdString());
|
||
if (translator.load(":/" + selectedLang)) {
|
||
qApp->installTranslator(&translator);
|
||
}
|
||
}
|
||
}
|
||
|
||
int main(int argc, char *argv[]) {
|
||
QApplication a(argc, argv);
|
||
Setup setup;
|
||
setMainWindow(&setup);
|
||
return a.exec();
|
||
}
|