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
commatwo_master
parent
c81ac2c800
commit
9f0611507e
10 changed files with 225 additions and 24 deletions
@ -1,8 +1,11 @@ |
|||||||
import os |
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 |
from selfdrive.hardware import PC |
||||||
|
|
||||||
|
BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../")) |
||||||
|
|
||||||
if PC: |
if PC: |
||||||
PERSIST = os.path.join(BASEDIR, "persist") |
PERSIST = os.path.join(str(Path.home()), ".comma", "persist") |
||||||
else: |
else: |
||||||
PERSIST = "/persist" |
PERSIST = "/persist" |
||||||
|
@ -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); |
||||||
|
}; |
Loading…
Reference in new issue