FrogAi 659adb6457 openpilot v0.9.7 release
date: 2024-03-17T10:14:38
master commit: 7e9a909e0e57ecb31df4c87c5b9a06b1204fd034
2024-05-24 17:43:27 -07:00

488 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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;
list = curl_slist_append(list, ("X-openpilot-serial: " + Hardware::get_serial()).c_str());
char tmpfile[] = "/tmp/installer_XXXXXX";
FILE *fp = fdopen(mkstemp(tmpfile), "w");
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 devices 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, lets 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);
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);
// openpilot + custom radio buttons
QButtonGroup *group = new QButtonGroup(widget);
group->setExclusive(true);
QWidget *openpilot = radio_button(tr("openpilot"), group);
main_layout->addWidget(openpilot);
main_layout->addSpacing(30);
QWidget *custom = radio_button(tr("Custom Software"), group);
main_layout->addWidget(custom);
main_layout->addStretch();
// back + continue buttons
QHBoxLayout *blayout = new QHBoxLayout;
main_layout->addLayout(blayout);
blayout->setSpacing(50);
QPushButton *back = new QPushButton(tr("Back"));
back->setObjectName("navBtn");
QObject::connect(back, &QPushButton::clicked, this, &Setup::prevPage);
blayout->addWidget(back);
QPushButton *cont = new QPushButton(tr("Continue"));
cont->setObjectName("navBtn");
cont->setEnabled(false);
cont->setProperty("primary", true);
blayout->addWidget(cont);
QObject::connect(cont, &QPushButton::clicked, [=]() {
auto w = currentWidget();
QTimer::singleShot(0, [=]() {
setCurrentWidget(downloading_widget);
});
QString url = OPENPILOT_URL;
if (group->checkedButton() != openpilot) {
url = InputDialog::getText(tr("Enter URL"), this, tr("for Custom Software"));
}
if (!url.isEmpty()) {
QTimer::singleShot(1000, this, [=]() {
download(url);
});
} else {
setCurrentWidget(w);
}
});
connect(group, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), [=](QAbstractButton *btn) {
btn->setChecked(true);
cont->setEnabled(true);
});
return widget;
}
QWidget * Setup::downloading() {
QWidget *widget = new QWidget();
QVBoxLayout *main_layout = new QVBoxLayout(widget);
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();
}