mirror of https://github.com/commaai/openpilot.git
Qt Offroad stats (#19498)
* probably broke a lot, need the commit though
* finally works
* make it work...
* ...
* kind of works
* better styling
* all should work
* tiny cleanup
* temp
* looks nicer
* create JWT in C++
* kilometers -> km
* use correct free methods
* dont put code in assert statement
* Build JWT payload dynamically
* get dongle id once
* include cleanup
* Remove qDebug
* Update drive_stats.cc
Github is a nice editor :)
* swap week and all and fix sconscript
* install openssl
* openssl include dirs on mac
* is this where openssl is?
* It's here
* small cleanup
Co-authored-by: Comma Device <device@comma.ai>
Co-authored-by: Willem Melching <willem.melching@gmail.com>
old-commit-hash: 5b26c97141
This commit is contained in:
parent
c81ac2c800
commit
9f0611507e
|
@ -89,10 +89,12 @@ else:
|
|||
"#cereal",
|
||||
"#selfdrive/common",
|
||||
"/usr/local/lib",
|
||||
"/usr/local/opt/openssl/lib",
|
||||
"/System/Library/Frameworks/OpenGL.framework/Libraries",
|
||||
]
|
||||
cflags += ["-DGL_SILENCE_DEPRECATION"]
|
||||
cxxflags += ["-DGL_SILENCE_DEPRECATION"]
|
||||
cpppath += ["/usr/local/opt/openssl/include"]
|
||||
else:
|
||||
libpath = [
|
||||
"#phonelibs/snpe/x86_64-linux-clang",
|
||||
|
@ -296,4 +298,3 @@ if arch != "Darwin":
|
|||
|
||||
if arch == "x86_64":
|
||||
SConscript(['tools/lib/index_log/SConscript'])
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import os
|
||||
BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))
|
||||
from pathlib import Path
|
||||
|
||||
from selfdrive.hardware import PC
|
||||
|
||||
BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))
|
||||
|
||||
if PC:
|
||||
PERSIST = os.path.join(BASEDIR, "persist")
|
||||
PERSIST = os.path.join(str(Path.home()), ".comma", "persist")
|
||||
else:
|
||||
PERSIST = "/persist"
|
||||
|
|
|
@ -21,19 +21,10 @@
|
|||
#include "common/utilpp.h"
|
||||
|
||||
|
||||
std::string getenv_default(const char* env_var, const char * suffix, const char* default_val) {
|
||||
const char* env_val = getenv(env_var);
|
||||
if (env_val != NULL){
|
||||
return std::string(env_val) + std::string(suffix);
|
||||
} else {
|
||||
return std::string(default_val);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(QCOM) || defined(QCOM2)
|
||||
const std::string default_params_path = "/data/params";
|
||||
#else
|
||||
const std::string default_params_path = getenv_default("HOME", "/.comma/params", "/data/params");
|
||||
const std::string default_params_path = util::getenv_default("HOME", "/.comma/params", "/data/params");
|
||||
#endif
|
||||
|
||||
#if defined(QCOM) || defined(QCOM2)
|
||||
|
|
|
@ -60,6 +60,16 @@ inline std::string readlink(std::string path) {
|
|||
return "";
|
||||
}
|
||||
|
||||
inline std::string getenv_default(const char* env_var, const char * suffix, const char* default_val) {
|
||||
const char* env_val = getenv(env_var);
|
||||
if (env_val != NULL){
|
||||
return std::string(env_val) + std::string(suffix);
|
||||
} else {
|
||||
return std::string(default_val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
struct unique_fd {
|
||||
|
|
|
@ -15,6 +15,7 @@ if arch in ["x86_64", "Darwin", "larch64"]:
|
|||
QT_BASE + "include/QtCore",
|
||||
QT_BASE + "include/QtDBus",
|
||||
QT_BASE + "include/QtMultimedia",
|
||||
QT_BASE + "include/QtNetwork",
|
||||
]
|
||||
qt_env["LINKFLAGS"] += ["-F" + QT_BASE + "lib"]
|
||||
else:
|
||||
|
@ -26,6 +27,7 @@ if arch in ["x86_64", "Darwin", "larch64"]:
|
|||
f"/usr/include/{real_arch}-linux-gnu/qt5/QtCore",
|
||||
f"/usr/include/{real_arch}-linux-gnu/qt5/QtDBus",
|
||||
f"/usr/include/{real_arch}-linux-gnu/qt5/QtMultimedia",
|
||||
f"/usr/include/{real_arch}-linux-gnu/qt5/QtNetwork",
|
||||
f"/usr/include/{real_arch}-linux-gnu/qt5/QtGui/5.5.1/QtGui",
|
||||
f"/usr/include/{real_arch}-linux-gnu/qt5/QtGui/5.12.8/QtGui",
|
||||
]
|
||||
|
@ -57,9 +59,9 @@ if qt_env is None:
|
|||
LINKFLAGS=linkflags,
|
||||
LIBS=libs)
|
||||
else:
|
||||
qt_libs = ["pthread"]
|
||||
qt_libs = ["pthread", "ssl", "crypto"]
|
||||
|
||||
qt_modules = ["Widgets", "Gui", "Core", "DBus", "Multimedia"]
|
||||
qt_modules = ["Widgets", "Gui", "Core", "DBus", "Multimedia", "Network"]
|
||||
|
||||
if arch == "larch64":
|
||||
qt_libs += ["GLESv2", "wayland-client"]
|
||||
|
@ -72,13 +74,13 @@ else:
|
|||
qt_libs += [f"Qt5{m}" for m in qt_modules]
|
||||
|
||||
|
||||
qt_env.Library("qt_widgets",
|
||||
["qt/qt_window.cc", "qt/qt_sound.cc", "qt/widgets/keyboard.cc", "qt/widgets/input_field.cc",
|
||||
"qt/offroad/wifi.cc", "qt/offroad/wifiManager.cc", "qt/widgets/toggle.cc"],
|
||||
widgets = qt_env.Library("qt_widgets",
|
||||
["qt/qt_window.cc", "qt/qt_sound.cc", "qt/widgets/keyboard.cc", "qt/widgets/input_field.cc", "qt/widgets/drive_stats.cc",
|
||||
"qt/offroad/wifi.cc", "qt/offroad/wifiManager.cc", "qt/widgets/toggle.cc", "qt/widgets/offroad_alerts.cc"],
|
||||
LIBS=qt_libs)
|
||||
qt_libs.append("qt_widgets")
|
||||
qt_libs.append(widgets)
|
||||
|
||||
qt_src = ["qt/ui.cc", "qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc", "qt/offroad/onboarding.cc", "qt/widgets/offroad_alerts.cc"] + src
|
||||
qt_src = ["qt/ui.cc", "qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc", "qt/offroad/onboarding.cc"] + src
|
||||
qt_env.Program("_ui", qt_src, LIBS=qt_libs + libs)
|
||||
|
||||
# spinner and text window
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "home.hpp"
|
||||
#include "paint.hpp"
|
||||
#include "qt_window.hpp"
|
||||
#include "widgets/drive_stats.hpp"
|
||||
|
||||
#define BACKLIGHT_DT 0.25
|
||||
#define BACKLIGHT_TS 2.00
|
||||
|
@ -44,9 +45,8 @@ OffroadHome::OffroadHome(QWidget *parent) : QWidget(parent) {
|
|||
QObject::connect(alert_notification, SIGNAL(released()), this, SLOT(openAlerts()));
|
||||
main_layout->addWidget(alert_notification, 0, Qt::AlignTop | Qt::AlignRight);
|
||||
|
||||
// center
|
||||
QLabel *drive = new QLabel("Drive me");
|
||||
drive->setStyleSheet(R"(font-size: 175px;)");
|
||||
DriveStats *drive = new DriveStats;
|
||||
drive->setFixedSize(1000,800);
|
||||
center_layout->addWidget(drive);
|
||||
|
||||
alerts_widget = new OffroadAlert();
|
||||
|
|
|
@ -99,7 +99,7 @@ void WifiUI::refresh() {
|
|||
if (!this->isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
wifi->request_scan();
|
||||
wifi->refreshNetworks();
|
||||
ipv4->setText(wifi->ipv4_address);
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
#include <iostream>
|
||||
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPixmap>
|
||||
#include <QPushButton>
|
||||
#include <QLineEdit>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QCryptographicHash>
|
||||
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/pem.h>
|
||||
|
||||
#include "drive_stats.hpp"
|
||||
#include "common/params.h"
|
||||
#include "common/utilpp.h"
|
||||
double MILE_TO_KM = 1.60934;
|
||||
|
||||
|
||||
#if defined(QCOM) || defined(QCOM2)
|
||||
const std::string private_key_path = "/persist/comma/id_rsa";
|
||||
#else
|
||||
const std::string private_key_path = util::getenv_default("HOME", "/.comma/persist/comma/id_rsa", "/persist/comma/id_rsa");
|
||||
#endif
|
||||
|
||||
QByteArray rsa_sign(QByteArray data){
|
||||
auto file = QFile(private_key_path.c_str());
|
||||
bool r = file.open(QIODevice::ReadOnly);
|
||||
assert(r);
|
||||
|
||||
auto key = file.readAll();
|
||||
|
||||
BIO *mem = BIO_new_mem_buf(key.data(), key.size());
|
||||
assert(mem);
|
||||
|
||||
RSA *rsa_private = PEM_read_bio_RSAPrivateKey(mem, NULL, NULL, NULL);
|
||||
assert(rsa_private);
|
||||
|
||||
auto sig = QByteArray();
|
||||
sig.resize(RSA_size(rsa_private));
|
||||
|
||||
unsigned int sig_len;
|
||||
int ret = RSA_sign(NID_sha256, (unsigned char*)data.data(), data.size(), (unsigned char*)sig.data(), &sig_len, rsa_private);
|
||||
|
||||
assert(ret == 1);
|
||||
assert(sig_len == sig.size());
|
||||
|
||||
BIO_free(mem);
|
||||
RSA_free(rsa_private);
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
QString create_jwt(QString dongle_id, int expiry=3600){
|
||||
QJsonObject header;
|
||||
header.insert("alg", "RS256");
|
||||
header.insert("typ", "JWT");
|
||||
|
||||
auto t = QDateTime::currentSecsSinceEpoch();
|
||||
QJsonObject payload;
|
||||
payload.insert("identity", dongle_id);
|
||||
payload.insert("nbf", t);
|
||||
payload.insert("iat", t);
|
||||
payload.insert("exp", t + expiry);
|
||||
|
||||
QString jwt =
|
||||
QJsonDocument(header).toJson(QJsonDocument::Compact).toBase64() +
|
||||
'.' +
|
||||
QJsonDocument(payload).toJson(QJsonDocument::Compact).toBase64();
|
||||
|
||||
auto hash = QCryptographicHash::hash(jwt.toUtf8(), QCryptographicHash::Sha256);
|
||||
auto sig = rsa_sign(hash);
|
||||
|
||||
jwt += '.' + sig.toBase64();
|
||||
|
||||
return jwt;
|
||||
}
|
||||
|
||||
QString bold(QString s) {
|
||||
return "<b>" + s + "</b>";
|
||||
}
|
||||
|
||||
QWidget *widget(QLayout *l){
|
||||
QWidget *q = new QWidget();
|
||||
q->setLayout(l);
|
||||
return q;
|
||||
}
|
||||
|
||||
QWidget *build_stat(QString name, int stat){
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
layout->addWidget(new QLabel(bold(QString("%1").arg(stat))), 1, Qt::AlignCenter);
|
||||
layout->addWidget(new QLabel(name),1, Qt::AlignCenter);
|
||||
return widget(layout);
|
||||
}
|
||||
|
||||
void DriveStats::replyFinished(QNetworkReply *l){
|
||||
QString answer = l->readAll();
|
||||
answer.chop(1);
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(answer.toUtf8());
|
||||
if (doc.isNull()) {
|
||||
qDebug() << "JSON Parse failed";
|
||||
}
|
||||
QString IsMetric = QString::fromStdString(Params().get("IsMetric"));
|
||||
bool metric = (IsMetric =="1");
|
||||
|
||||
QJsonObject json = doc.object();
|
||||
auto all = json["all"].toObject();
|
||||
auto week = json["week"].toObject();
|
||||
|
||||
QGridLayout *gl = new QGridLayout();
|
||||
|
||||
int all_distance = all["distance"].toDouble()*(metric ? MILE_TO_KM : 1);
|
||||
gl->addWidget(new QLabel(bold("ALL TIME")), 0, 0, 1, 3);
|
||||
gl->addWidget(build_stat("DRIVES", all["routes"].toDouble()), 1, 0, 3, 1);
|
||||
gl->addWidget(build_stat(metric ? "KM" : "MILES", all_distance), 1, 1, 3, 1);
|
||||
gl->addWidget(build_stat("HOURS", all["minutes"].toDouble() / 60), 1, 2, 3, 1);
|
||||
|
||||
QFrame *lineA = new QFrame;
|
||||
lineA->setFrameShape(QFrame::HLine);
|
||||
lineA->setFrameShadow(QFrame::Sunken);
|
||||
lineA->setProperty("class", "line");
|
||||
gl->addWidget(lineA, 5, 0, 1, 3);
|
||||
|
||||
int week_distance = week["distance"].toDouble()*(metric ? MILE_TO_KM : 1);
|
||||
gl->addWidget(new QLabel(bold("PAST WEEK")), 6, 0, 1, 3);
|
||||
gl->addWidget(build_stat("DRIVES", week["routes"].toDouble()), 7, 0, 3, 1);
|
||||
gl->addWidget(build_stat(metric ? "KM" : "MILES", week_distance), 7, 1, 3, 1);
|
||||
gl->addWidget(build_stat("HOURS", week["minutes"].toDouble() / 60), 7, 2, 3, 1);
|
||||
|
||||
|
||||
f->setLayout(gl);
|
||||
f->setStyleSheet(R"(
|
||||
[class="line"]{
|
||||
border: 2px solid white;
|
||||
}
|
||||
[class="outside"]{
|
||||
border-radius: 20px;
|
||||
border: 2px solid white;
|
||||
padding: 10px;
|
||||
}
|
||||
QLabel{
|
||||
font-size: 70px;
|
||||
font-weight: 200;
|
||||
}
|
||||
)");
|
||||
|
||||
}
|
||||
DriveStats::DriveStats(QWidget *parent) : QWidget(parent){
|
||||
f = new QFrame;
|
||||
f->setProperty("class", "outside");
|
||||
QVBoxLayout *v = new QVBoxLayout;
|
||||
v->addWidget(f);
|
||||
setLayout(v);
|
||||
|
||||
QString dongle_id = QString::fromStdString(Params().get("DongleId"));
|
||||
QString token = create_jwt(dongle_id);
|
||||
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
|
||||
connect(manager, &QNetworkAccessManager::finished, this, &DriveStats::replyFinished);
|
||||
|
||||
QNetworkRequest request;
|
||||
request.setUrl(QUrl("https://api.commadotai.com/v1.1/devices/" + dongle_id + "/stats"));
|
||||
request.setRawHeader("Authorization", ("JWT "+token).toUtf8());
|
||||
|
||||
manager->get(request);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QButtonGroup>
|
||||
#include <QVBoxLayout>
|
||||
#include <QStackedWidget>
|
||||
#include <QTimer>
|
||||
#include <QNetworkReply>
|
||||
|
||||
|
||||
class DriveStats : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DriveStats(QWidget *parent = 0);
|
||||
|
||||
private:
|
||||
QFrame *f;
|
||||
void replyFinished(QNetworkReply *l);
|
||||
};
|
|
@ -15,6 +15,7 @@ brew install capnp \
|
|||
libusb \
|
||||
libtool \
|
||||
llvm \
|
||||
openssl \
|
||||
pyenv \
|
||||
qt5 \
|
||||
zeromq
|
||||
|
|
Loading…
Reference in New Issue