qt replay (#20602)
* initial commit, works * remove nui * working again * visionipc * cleanup * cleanup * moving VisionIpcServer to Unlogger class * works * tab cleanup * headless mode * headless mode works * working headless mode * gitignore update * small unlogger refactor * refactor param in UIState * works, very slow, hacks * cleanup * works * cleanup * cleanup * unused * works for whole route * nicer * a little nicer * different threshold * maintains 1 segment window * works with public api * comments * networkTimer works * cleanup * unified HttpRequest * tabs * tabs * comments' * gitignore * gitignore * only on PC * same line else * no changes in home.cc * scons * update scons * works * revert mainc.c * revert home * else * just api + problem with api send * works * include cleanup * general json fail * whitespace * remove active * adding request repeater * removing comments * tabs * update comment * cereal * fix * trailing new lines * grammar * if whitespace * indentation * Update selfdrive/ui/SConscript Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com> * Update selfdrive/ui/qt/request_repeater.cc Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com> * works * sort by dir * no blockSignal * replay is now QOBject * cant take const char * rename inner it * get width and height from frame readeR * resolve TODO * seek in next pr * spaces * ui stuff * fix CI * remove comments * no repalce * trim segment fix * remove seek from stream * no cache key * final changes' * fix Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>pull/20740/head
parent
0fe155b7c3
commit
19d962cdf3
26 changed files with 267 additions and 440 deletions
@ -0,0 +1,12 @@ |
||||
#include "request_repeater.hpp" |
||||
|
||||
RequestRepeater::RequestRepeater(QObject *parent, QString requestURL, const QString &cache_key, int period_seconds, bool disableWithScreen) : |
||||
HttpRequest(parent, requestURL, cache_key), disableWithScreen(disableWithScreen) { |
||||
QTimer* timer = new QTimer(this); |
||||
QObject::connect(timer, &QTimer::timeout, [=](){ |
||||
if (!GLWindow::ui_state.scene.started && reply == NULL && (GLWindow::ui_state.awake || !disableWithScreen)) { |
||||
sendRequest(requestURL); |
||||
} |
||||
}); |
||||
timer->start(period_seconds * 1000); |
||||
} |
@ -0,0 +1,9 @@ |
||||
#include "api.hpp" |
||||
#include "home.hpp" |
||||
|
||||
class RequestRepeater : public HttpRequest { |
||||
|
||||
public: |
||||
RequestRepeater(QObject *parent, QString requestURL, const QString &cache_key = "", int period_seconds = 0, bool disableWithScreen = true); |
||||
bool disableWithScreen; |
||||
}; |
@ -0,0 +1,18 @@ |
||||
#include <QApplication> |
||||
|
||||
#include "replay.hpp" |
||||
|
||||
int main(int argc, char *argv[]){ |
||||
QApplication a(argc, argv); |
||||
|
||||
QString route(argv[1]); |
||||
if (route == "") { |
||||
printf("Usage: ./replay \"route\"\n"); |
||||
return 1; |
||||
} |
||||
|
||||
Replay *replay = new Replay(route, 0); |
||||
replay->stream(0); |
||||
|
||||
return a.exec(); |
||||
} |
@ -0,0 +1,71 @@ |
||||
#include "replay.hpp" |
||||
|
||||
Replay::Replay(QString route_, int seek) : route(route_) { |
||||
unlogger = new Unlogger(&events, &events_lock, &frs, seek); |
||||
current_segment = 0; |
||||
|
||||
http = new HttpRequest(this, "https://api.commadotai.com/v1/route/" + route + "/files"); |
||||
QObject::connect(http, SIGNAL(receivedResponse(QString)), this, SLOT(parseResponse(QString))); |
||||
} |
||||
|
||||
void Replay::parseResponse(QString response){ |
||||
response = response.trimmed(); |
||||
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8()); |
||||
|
||||
if (doc.isNull()) { |
||||
qDebug() << "JSON Parse failed"; |
||||
return; |
||||
} |
||||
|
||||
camera_paths = doc["cameras"].toArray(); |
||||
log_paths = doc["logs"].toArray(); |
||||
|
||||
// add first segment
|
||||
addSegment(0); |
||||
} |
||||
|
||||
void Replay::addSegment(int i){ |
||||
if (lrs.find(i) != lrs.end()) { |
||||
return; |
||||
} |
||||
|
||||
QThread* thread = new QThread; |
||||
|
||||
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); |
||||
QObject::connect(thread, SIGNAL (started()), lrs[i], SLOT (process())); |
||||
thread->start(); |
||||
|
||||
QString camera_fn = this->camera_paths.at(i).toString(); |
||||
frs.insert(i, new FrameReader(qPrintable(camera_fn))); |
||||
} |
||||
|
||||
void Replay::trimSegment(int n){ |
||||
event_sizes.enqueue(events.size() - event_sizes.last()); |
||||
auto first = events.begin(); |
||||
|
||||
for(int i = 0 ; i < n ; i++){ |
||||
int remove = event_sizes.dequeue(); |
||||
for(int j = 0 ; j < remove ; j++){ |
||||
first = events.erase(first); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void Replay::stream(SubMaster *sm){ |
||||
QThread* thread = new QThread; |
||||
unlogger->moveToThread(thread); |
||||
QObject::connect(thread, &QThread::started, [=](){ |
||||
unlogger->process(sm); |
||||
}); |
||||
thread->start(); |
||||
|
||||
QObject::connect(unlogger, &Unlogger::loadSegment, [=](){ |
||||
addSegment(++current_segment); |
||||
if (current_segment > 1) { |
||||
trimSegment(1); |
||||
} |
||||
}); |
||||
} |
@ -0,0 +1,48 @@ |
||||
#pragma once |
||||
|
||||
#include <QFile> |
||||
#include <QQueue> |
||||
#include <QJsonArray> |
||||
#include <QJsonObject> |
||||
#include <QJsonDocument> |
||||
|
||||
#include <capnp/dynamic.h> |
||||
|
||||
#include "qt/api.hpp" |
||||
#include "Unlogger.hpp" |
||||
#include "FileReader.hpp" |
||||
#include "FrameReader.hpp" |
||||
#include "visionipc_server.h" |
||||
|
||||
class Replay : public QObject { |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
Replay(QString route_, int seek); |
||||
void stream(SubMaster *sm = nullptr); |
||||
void addSegment(int i); |
||||
void trimSegment(int n); |
||||
QJsonArray camera_paths; |
||||
QJsonArray log_paths; |
||||
|
||||
QQueue<int> event_sizes; |
||||
|
||||
public slots: |
||||
void parseResponse(QString response); |
||||
|
||||
protected: |
||||
Unlogger *unlogger; |
||||
|
||||
private: |
||||
QString route; |
||||
|
||||
QReadWriteLock events_lock; |
||||
Events events; |
||||
|
||||
QMap<int, LogReader*> lrs; |
||||
QMap<int, FrameReader*> frs; |
||||
HttpRequest *http; |
||||
|
||||
int current_segment; |
||||
}; |
||||
|
@ -1,8 +0,0 @@ |
||||
.*.swp |
||||
*.o |
||||
_nui |
||||
moc_* |
||||
nui.app/* |
||||
routes.json |
||||
_nui.app |
||||
|
@ -1,11 +0,0 @@ |
||||
Import('qt_env', 'messaging') |
||||
|
||||
qt_env['CPPPATH'] += ["#tools/clib"] |
||||
qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"] |
||||
|
||||
libs = [messaging, 'avutil', 'avcodec', 'avformat', 'bz2', 'capnp', 'kj', |
||||
'pthread', 'swscale', 'zmq'] |
||||
|
||||
qt_env.Program("_nui", |
||||
['main.cpp', 'Unlogger.cpp', 'FileReader.cpp', '../clib/FrameReader.cpp'], |
||||
LIBS=qt_env['LIBS'] + libs) |
@ -1,14 +0,0 @@ |
||||
#!/usr/bin/env python3 |
||||
import json |
||||
import sys |
||||
|
||||
from tools.lib.route import Route |
||||
|
||||
route_name = sys.argv[1] |
||||
routes = Route(route_name) |
||||
data_dump = { |
||||
"camera": routes.camera_paths(), |
||||
"logs": routes.log_paths() |
||||
} |
||||
|
||||
json.dump(data_dump, open("routes.json", "w")) |
@ -1,262 +0,0 @@ |
||||
#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(); |
||||
} |
||||
|
@ -1,18 +0,0 @@ |
||||
#!/bin/bash -e |
||||
|
||||
if [ $# -gt 0 ]; then |
||||
if [ "$INTERNAL" = 1 ]; then |
||||
./_nui "$1" |
||||
else |
||||
./get_files_comma_api.py $1 |
||||
if [ -f ./_nui ]; then |
||||
./_nui use_api |
||||
elif [ -f _nui.app/Contents/MacOS/_nui ]; then |
||||
./_nui.app/Contents/MacOS/_nui use_api |
||||
else |
||||
echo "nui not found, please build it" |
||||
fi |
||||
fi |
||||
else |
||||
echo "Please Enter a Route" |
||||
fi |
@ -1 +0,0 @@ |
||||
test |
@ -1,14 +0,0 @@ |
||||
#include "../../clib/FrameReader.hpp" |
||||
#include "TestFrameReader.hpp" |
||||
|
||||
void TestFrameReader::frameread() { |
||||
QElapsedTimer t; |
||||
t.start(); |
||||
FrameReader fr("3a5d6ac1c23e5536/2019-10-29--10-06-58/2/fcamera.hevc"); |
||||
fr.get(2); |
||||
//QThread::sleep(10);
|
||||
qDebug() << t.nsecsElapsed()*1e-9 << "seconds"; |
||||
} |
||||
|
||||
QTEST_MAIN(TestFrameReader) |
||||
|
@ -1,8 +0,0 @@ |
||||
#include <QtTest/QtTest> |
||||
|
||||
class TestFrameReader : public QObject { |
||||
Q_OBJECT |
||||
private slots: |
||||
void frameread(); |
||||
}; |
||||
|
@ -1,16 +0,0 @@ |
||||
###################################################################### |
||||
# Automatically generated by qmake (3.0) Thu Oct 31 16:05:48 2019 |
||||
###################################################################### |
||||
|
||||
QT += testlib |
||||
TEMPLATE = app |
||||
TARGET = test |
||||
INCLUDEPATH += . ../ |
||||
|
||||
# Input |
||||
SOURCES += TestFrameReader.cpp ../../clib/FrameReader.cpp |
||||
HEADERS = TestFrameReader.hpp ../../clib/FrameReader.hpp |
||||
|
||||
CONFIG += c++14 |
||||
|
||||
LIBS += -lavformat -lavcodec -lavutil -lswscale |
Loading…
Reference in new issue