openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

262 lines
6.7 KiB

#include <QApplication>
#include <QWidget>
#include <QString>
#include <QTimer>
#include <QPushButton>
#include <QGraphicsScene>
#include <QPainter>
#include <QThread>
#include <QMouseEvent>
#include <QReadWriteLock>
#include <QLineEdit>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDebug>
#include <stdlib.h>
#include <QTextStream>
#include "FileReader.hpp"
#include "Unlogger.hpp"
#include "FrameReader.hpp"
class Window : public QWidget {
public:
Window(QString route_, int seek, int use_api);
bool addSegment(int i);
QJsonArray camera_paths;
QJsonArray log_paths;
int use_api;
protected:
void keyPressEvent(QKeyEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void paintEvent(QPaintEvent *event) override;
uint64_t ct;
Unlogger *unlogger;
private:
int timeToPixel(uint64_t ns);
uint64_t pixelToTime(int px);
QString route;
QReadWriteLock events_lock;
Events events;
int last_event_size = 0;
QMap<int, LogReader*> lrs;
QMap<int, FrameReader*> frs;
// cache the bar
QPixmap *px = NULL;
int seg_add = 0;
QLineEdit *timeLE;
};
Window::Window(QString route_, int seek, int use_api_) : route(route_), use_api(use_api_) {
timeLE = new QLineEdit(this);
timeLE->setPlaceholderText("Placeholder Text");
timeLE->move(50, 650);
QThread* thread = new QThread;
unlogger = new Unlogger(&events, &events_lock, &frs, seek);
unlogger->moveToThread(thread);
connect(thread, SIGNAL (started()), unlogger, SLOT (process()));
connect(unlogger, SIGNAL (elapsed()), this, SLOT (update()));
thread->start();
if (use_api) {
QString settings;
QFile file;
file.setFileName("routes.json");
file.open(QIODevice::ReadOnly | QIODevice::Text);
settings = file.readAll();
file.close();
QJsonDocument sd = QJsonDocument::fromJson(settings.toUtf8());
qWarning() << sd.isNull(); // <- print false :)
QJsonObject sett2 = sd.object();
this->camera_paths = sett2.value("camera").toArray();
this->log_paths = sett2.value("logs").toArray();
}
this->setFocusPolicy(Qt::StrongFocus);
// add the first segment
addSegment(seek/60);
}
bool Window::addSegment(int i) {
if (lrs.find(i) == lrs.end()) {
QString fn = QString("http://data.comma.life/%1/%2/rlog.bz2").arg(route).arg(i);
QThread* thread = new QThread;
if (!use_api) {
lrs.insert(i, new LogReader(fn, &events, &events_lock, &unlogger->eidx));
} else {
QString log_fn = this->log_paths.at(i).toString();
lrs.insert(i, new LogReader(log_fn, &events, &events_lock, &unlogger->eidx));
}
lrs[i]->moveToThread(thread);
connect(thread, SIGNAL (started()), lrs[i], SLOT (process()));
thread->start();
//connect(lrs[i], SIGNAL (finished()), this, SLOT (update()));
QString frn = QString("http://data.comma.life/%1/%2/fcamera.hevc").arg(route).arg(i);
if (!use_api) {
frs.insert(i, new FrameReader(qPrintable(frn)));
} else {
QString camera_fn = this->camera_paths.at(i).toString();
frs.insert(i, new FrameReader(qPrintable(camera_fn)));
}
return true;
}
return false;
}
#define PIXELS_PER_SEC 0.5
int Window::timeToPixel(uint64_t ns) {
// TODO: make this dynamic
return int(ns*1e-9*PIXELS_PER_SEC+0.5);
}
uint64_t Window::pixelToTime(int px) {
// TODO: make this dynamic
//printf("%d\n", px);
return ((px+0.5)/PIXELS_PER_SEC) * 1e9;
}
void Window::keyPressEvent(QKeyEvent *event) {
printf("keypress: %x\n", event->key());
if (event->key() == Qt::Key_Space) unlogger->togglePause();
}
void Window::mousePressEvent(QMouseEvent *event) {
//printf("mouse event\n");
if (event->button() == Qt::LeftButton) {
uint64_t t0 = events.begin().key();
uint64_t tt = pixelToTime(event->x());
int seg = int((tt*1e-9)/60);
printf("segment %d\n", seg);
addSegment(seg);
//printf("seek to %lu\n", t0+tt);
unlogger->setSeekRequest(t0+tt);
}
this->update();
}
void Window::paintEvent(QPaintEvent *event) {
if (events.size() == 0) return;
QElapsedTimer timer;
timer.start();
uint64_t t0 = events.begin().key();
//p.drawRect(0, 0, 600, 100);
// TODO: we really don't have to redraw this every time, only on updates to events
float vEgo = 0.;
int this_event_size = events.size();
if (last_event_size != this_event_size) {
if (px != NULL) delete px;
px = new QPixmap(1920, 600);
px->fill(QColor(0xd8, 0xd8, 0xd8));
QPainter tt(px);
tt.setBrush(Qt::cyan);
int lt = -1;
int lvv = 0;
for (auto e : events) {
auto type = e.which();
//printf("%lld %d\n", e.getLogMonoTime()-t0, type);
if (type == cereal::Event::CAR_STATE) {
vEgo = e.getCarState().getVEgo();
} else if (type == cereal::Event::CONTROLS_STATE) {
auto controlsState = e.getControlsState();
uint64_t t = (e.getLogMonoTime()-t0);
int enabled = controlsState.getState() == cereal::ControlsState::OpenpilotState::ENABLED;
int rt = timeToPixel(t); // 250 ms per pixel
if (rt != lt) {
int vv = vEgo*8.0;
if (lt != -1) {
tt.setPen(Qt::red);
tt.drawLine(lt, 300-lvv, rt, 300-vv);
if (enabled) {
tt.setPen(Qt::green);
} else {
tt.setPen(Qt::blue);
}
tt.drawLine(rt, 300, rt, 600);
}
lt = rt;
lvv = vv;
}
}
}
tt.end();
last_event_size = this_event_size;
if (lrs.find(seg_add) != lrs.end() && lrs[seg_add]->is_done) {
while (!addSegment(++seg_add));
}
}
QPainter p(this);
if (px != NULL) p.drawPixmap(0, 0, 1920, 600, *px);
p.setBrush(Qt::cyan);
uint64_t ct = unlogger->getCurrentTime();
if (ct != 0) {
addSegment((((ct-t0)*1e-9)/60)+1);
int rrt = timeToPixel(ct-t0);
p.drawRect(rrt-1, 0, 2, 600);
timeLE->setText(QString("%1").arg((ct-t0)*1e-9, '8', 'f', 2));
}
p.end();
if (timer.elapsed() > 50) {
qDebug() << "paint in" << timer.elapsed() << "ms";
}
}
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QString route(argv[1]);
int use_api = QString::compare(QString("use_api"), route, Qt::CaseInsensitive) == 0;
int seek = QString(argv[2]).toInt();
printf("seek: %d\n", seek);
route = route.replace("|", "/");
if (route == "") {
printf("usage %s: <route>\n", argv[0]);
exit(0);
//route = "3a5d6ac1c23e5536/2019-10-29--10-06-58";
//route = "0006c839f32a6f99/2019-02-18--06-21-29";
//route = "02ec6bea180a4d36/2019-10-25--10-18-09";
}
Window window(route, seek, use_api);
window.resize(1920, 800);
window.setWindowTitle("nui unlogger");
window.show();
return app.exec();
}