mirror of
https://github.com/infiniteCable2/openpilot.git
synced 2026-10-01 03:53:41 +08:00
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> old-commit-hash: 19d962cdf37b80523deba6518057f2e860f65fee
This commit is contained in:
@@ -30,15 +30,17 @@ public:
|
||||
int getRGBSize() { return width*height*3; }
|
||||
void loaderThread();
|
||||
void cacherThread();
|
||||
|
||||
//TODO: get this from the actual frame
|
||||
int width = 1164;
|
||||
int height = 874;
|
||||
|
||||
private:
|
||||
AVFormatContext *pFormatCtx = NULL;
|
||||
AVCodecContext *pCodecCtx = NULL;
|
||||
|
||||
struct SwsContext *sws_ctx = NULL;
|
||||
|
||||
int width = 1164;
|
||||
int height = 874;
|
||||
|
||||
std::vector<AVPacket *> pkts;
|
||||
|
||||
std::thread *t;
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
.*.swp
|
||||
*.o
|
||||
_nui
|
||||
moc_*
|
||||
nui.app/*
|
||||
routes.json
|
||||
_nui.app
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
#include "FileReader.hpp"
|
||||
#include "FrameReader.hpp"
|
||||
|
||||
#include <QtNetwork>
|
||||
|
||||
FileReader::FileReader(const QString& file_) : file(file_) {
|
||||
}
|
||||
|
||||
void FileReader::process() {
|
||||
timer.start();
|
||||
QString str = file.simplified();
|
||||
str.replace(" ", "");
|
||||
startRequest(QUrl(str));
|
||||
}
|
||||
|
||||
void FileReader::startRequest(const QUrl &url) {
|
||||
qnam = new QNetworkAccessManager;
|
||||
reply = qnam->get(QNetworkRequest(url));
|
||||
connect(reply, &QNetworkReply::finished, this, &FileReader::httpFinished);
|
||||
connect(reply, &QIODevice::readyRead, this, &FileReader::readyRead);
|
||||
qDebug() << "requesting" << url;
|
||||
}
|
||||
|
||||
void FileReader::httpFinished() {
|
||||
if (reply->error()) {
|
||||
qWarning() << reply->errorString();
|
||||
}
|
||||
|
||||
const QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
||||
if (!redirectionTarget.isNull()) {
|
||||
const QUrl redirectedUrl = redirectionTarget.toUrl();
|
||||
//qDebug() << "redirected to" << redirectedUrl;
|
||||
startRequest(redirectedUrl);
|
||||
} else {
|
||||
qDebug() << "done in" << timer.elapsed() << "ms";
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
void FileReader::readyRead() {
|
||||
QByteArray dat = reply->readAll();
|
||||
printf("got http ready read: %d\n", dat.size());
|
||||
}
|
||||
|
||||
FileReader::~FileReader() {
|
||||
|
||||
}
|
||||
|
||||
LogReader::LogReader(const QString& file, Events *events_, QReadWriteLock* events_lock_, QMap<int, QPair<int, int> > *eidx_) :
|
||||
FileReader(file), events(events_), events_lock(events_lock_), eidx(eidx_) {
|
||||
bStream.next_in = NULL;
|
||||
bStream.avail_in = 0;
|
||||
bStream.bzalloc = NULL;
|
||||
bStream.bzfree = NULL;
|
||||
bStream.opaque = NULL;
|
||||
|
||||
int ret = BZ2_bzDecompressInit(&bStream, 0, 0);
|
||||
if (ret != BZ_OK) qWarning() << "bz2 init failed";
|
||||
|
||||
// start with 64MB buffer
|
||||
raw.resize(1024*1024*64);
|
||||
|
||||
// auto increment?
|
||||
bStream.next_out = raw.data();
|
||||
bStream.avail_out = raw.size();
|
||||
|
||||
// parsed no events yet
|
||||
event_offset = 0;
|
||||
|
||||
parser = new std::thread([&]() {
|
||||
while (1) {
|
||||
mergeEvents(cdled.get());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
LogReader::~LogReader() {
|
||||
delete parser;
|
||||
}
|
||||
|
||||
void LogReader::mergeEvents(int dled) {
|
||||
auto amsg = kj::arrayPtr((const capnp::word*)(raw.data() + event_offset), (dled-event_offset)/sizeof(capnp::word));
|
||||
Events events_local;
|
||||
QMap<int, QPair<int, int> > eidx_local;
|
||||
|
||||
while (amsg.size() > 0) {
|
||||
try {
|
||||
capnp::FlatArrayMessageReader cmsg = capnp::FlatArrayMessageReader(amsg);
|
||||
|
||||
// this needed? it is
|
||||
capnp::FlatArrayMessageReader *tmsg =
|
||||
new capnp::FlatArrayMessageReader(kj::arrayPtr(amsg.begin(), cmsg.getEnd()));
|
||||
|
||||
amsg = kj::arrayPtr(cmsg.getEnd(), amsg.end());
|
||||
|
||||
cereal::Event::Reader event = tmsg->getRoot<cereal::Event>();
|
||||
events_local.insert(event.getLogMonoTime(), event);
|
||||
|
||||
// hack
|
||||
// TODO: rewrite with callback
|
||||
if (event.which() == cereal::Event::ROAD_ENCODE_IDX) {
|
||||
auto ee = event.getRoadEncodeIdx();
|
||||
eidx_local.insert(ee.getFrameId(), qMakePair(ee.getSegmentNum(), ee.getSegmentId()));
|
||||
}
|
||||
|
||||
// increment
|
||||
event_offset = (char*)cmsg.getEnd() - raw.data();
|
||||
} catch (const kj::Exception& e) {
|
||||
// partial messages trigger this
|
||||
//qDebug() << e.getDescription().cStr();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// merge in events
|
||||
// TODO: add lock
|
||||
events_lock->lockForWrite();
|
||||
*events += events_local;
|
||||
eidx->unite(eidx_local);
|
||||
events_lock->unlock();
|
||||
|
||||
printf("parsed %d into %d events with offset %d\n", dled, events->size(), event_offset);
|
||||
}
|
||||
|
||||
void LogReader::readyRead() {
|
||||
QByteArray dat = reply->readAll();
|
||||
|
||||
bStream.next_in = dat.data();
|
||||
bStream.avail_in = dat.size();
|
||||
|
||||
while (bStream.avail_in > 0) {
|
||||
int ret = BZ2_bzDecompress(&bStream);
|
||||
if (ret != BZ_OK && ret != BZ_STREAM_END) {
|
||||
qWarning() << "bz2 decompress failed";
|
||||
break;
|
||||
}
|
||||
qDebug() << "got" << dat.size() << "with" << bStream.avail_out << "size" << raw.size();
|
||||
}
|
||||
|
||||
int dled = raw.size() - bStream.avail_out;
|
||||
cdled.put(dled);
|
||||
}
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QWidget>
|
||||
#include <QVector>
|
||||
#include <QMultiMap>
|
||||
#include <QElapsedTimer>
|
||||
#include <QReadWriteLock>
|
||||
|
||||
#include <bzlib.h>
|
||||
|
||||
#include <kj/io.h>
|
||||
#include <capnp/serialize.h>
|
||||
|
||||
#include "cereal/gen/cpp/log.capnp.h"
|
||||
|
||||
#include <thread>
|
||||
#include "channel.hpp"
|
||||
|
||||
class FileReader : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FileReader(const QString& file_);
|
||||
void startRequest(const QUrl &url);
|
||||
~FileReader();
|
||||
virtual void readyRead();
|
||||
void httpFinished();
|
||||
virtual void done() {};
|
||||
|
||||
public slots:
|
||||
void process();
|
||||
|
||||
protected:
|
||||
QNetworkReply *reply;
|
||||
|
||||
private:
|
||||
QNetworkAccessManager *qnam;
|
||||
QElapsedTimer timer;
|
||||
QString file;
|
||||
};
|
||||
|
||||
typedef QMultiMap<uint64_t, cereal::Event::Reader> Events;
|
||||
|
||||
class LogReader : public FileReader {
|
||||
Q_OBJECT
|
||||
public:
|
||||
LogReader(const QString& file, Events *, QReadWriteLock* events_lock_, QMap<int, QPair<int, int> > *eidx_);
|
||||
~LogReader();
|
||||
|
||||
void readyRead();
|
||||
void done() { is_done = true; };
|
||||
bool is_done = false;
|
||||
|
||||
private:
|
||||
bz_stream bStream;
|
||||
|
||||
// backing store
|
||||
QByteArray raw;
|
||||
|
||||
std::thread *parser;
|
||||
int event_offset;
|
||||
channel<int> cdled;
|
||||
|
||||
// global
|
||||
void mergeEvents(int dled);
|
||||
Events *events;
|
||||
QReadWriteLock* events_lock;
|
||||
QMap<int, QPair<int, int> > *eidx;
|
||||
};
|
||||
@@ -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,185 +0,0 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <capnp/dynamic.h>
|
||||
#include <capnp/schema.h>
|
||||
|
||||
// include the dynamic struct
|
||||
#include "cereal/gen/cpp/log.capnp.c++"
|
||||
#include "cereal/gen/cpp/car.capnp.c++"
|
||||
#include "cereal/gen/cpp/legacy.capnp.c++"
|
||||
#include "cereal/services.h"
|
||||
|
||||
#include "Unlogger.hpp"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
static inline uint64_t nanos_since_boot() {
|
||||
struct timespec t;
|
||||
#ifdef __APPLE__
|
||||
clock_gettime(CLOCK_REALTIME, &t);
|
||||
#else
|
||||
clock_gettime(CLOCK_BOOTTIME, &t);
|
||||
#endif
|
||||
return t.tv_sec * 1000000000ULL + t.tv_nsec;
|
||||
}
|
||||
|
||||
|
||||
Unlogger::Unlogger(Events *events_, QReadWriteLock* events_lock_, QMap<int, FrameReader*> *frs_, int seek)
|
||||
: events(events_), events_lock(events_lock_), frs(frs_) {
|
||||
ctx = Context::create();
|
||||
|
||||
seek_request = seek*1e9;
|
||||
|
||||
QStringList block = QString(getenv("BLOCK")).split(",");
|
||||
qDebug() << "blocklist" << block;
|
||||
|
||||
QStringList allow = QString(getenv("ALLOW")).split(",");
|
||||
qDebug() << "allowlist" << allow;
|
||||
|
||||
for (const auto& it : services) {
|
||||
std::string name = it.name;
|
||||
if (allow[0].size() > 0 && !allow.contains(name.c_str())) {
|
||||
qDebug() << "not allowing" << name.c_str();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (block.contains(name.c_str())) {
|
||||
qDebug() << "blocking" << name.c_str();
|
||||
continue;
|
||||
}
|
||||
|
||||
PubSocket *sock = PubSocket::create(ctx, name);
|
||||
if (sock == NULL) {
|
||||
qDebug() << "FAILED" << name.c_str();
|
||||
continue;
|
||||
}
|
||||
|
||||
qDebug() << name.c_str();
|
||||
|
||||
for (auto field: capnp::Schema::from<cereal::Event>().getFields()) {
|
||||
std::string tname = field.getProto().getName();
|
||||
|
||||
if (tname == name) {
|
||||
// TODO: I couldn't figure out how to get the which, only the index, hence this hack
|
||||
int type = field.getIndex();
|
||||
if (type > 67) type--; // valid
|
||||
type--; // logMonoTime
|
||||
|
||||
//qDebug() << "here" << tname.c_str() << type << cereal::Event::CONTROLS_STATE;
|
||||
socks.insert(type, sock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Unlogger::process() {
|
||||
qDebug() << "hello from unlogger thread";
|
||||
while (events->size() == 0) {
|
||||
qDebug() << "waiting for events";
|
||||
QThread::sleep(1);
|
||||
}
|
||||
qDebug() << "got events";
|
||||
|
||||
// TODO: hack
|
||||
if (seek_request != 0) {
|
||||
seek_request += events->begin().key();
|
||||
while (events->lowerBound(seek_request) == events->end()) {
|
||||
qDebug() << "waiting for desired time";
|
||||
QThread::sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
|
||||
uint64_t last_elapsed = 0;
|
||||
|
||||
// loops
|
||||
while (1) {
|
||||
uint64_t t0 = (events->begin()+1).key();
|
||||
uint64_t t0r = timer.nsecsElapsed();
|
||||
qDebug() << "unlogging at" << t0;
|
||||
|
||||
auto eit = events->lowerBound(t0);
|
||||
while (eit != events->end()) {
|
||||
while (paused) {
|
||||
QThread::usleep(1000);
|
||||
t0 = eit->getLogMonoTime();
|
||||
t0r = timer.nsecsElapsed();
|
||||
}
|
||||
|
||||
if (seek_request != 0) {
|
||||
t0 = seek_request;
|
||||
qDebug() << "seeking to" << t0;
|
||||
t0r = timer.nsecsElapsed();
|
||||
eit = events->lowerBound(t0);
|
||||
seek_request = 0;
|
||||
if (eit == events->end()) {
|
||||
qWarning() << "seek off end";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (abs(((long long)tc-(long long)last_elapsed)) > 50e6) {
|
||||
//qDebug() << "elapsed";
|
||||
emit elapsed();
|
||||
last_elapsed = tc;
|
||||
}
|
||||
|
||||
auto e = *eit;
|
||||
auto type = e.which();
|
||||
uint64_t tm = e.getLogMonoTime();
|
||||
auto it = socks.find(type);
|
||||
tc = tm;
|
||||
if (it != socks.end()) {
|
||||
long etime = tm-t0;
|
||||
long rtime = timer.nsecsElapsed() - t0r;
|
||||
long us_behind = ((etime-rtime)*1e-3)+0.5;
|
||||
if (us_behind > 0) {
|
||||
if (us_behind > 1e6) {
|
||||
qWarning() << "OVER ONE SECOND BEHIND, HACKING" << us_behind;
|
||||
us_behind = 0;
|
||||
t0 = tm;
|
||||
t0r = timer.nsecsElapsed();
|
||||
}
|
||||
QThread::usleep(us_behind);
|
||||
//qDebug() << "sleeping" << us_behind << etime << timer.nsecsElapsed();
|
||||
}
|
||||
|
||||
capnp::MallocMessageBuilder msg;
|
||||
msg.setRoot(e);
|
||||
|
||||
auto ee = msg.getRoot<cereal::Event>();
|
||||
ee.setLogMonoTime(nanos_since_boot());
|
||||
|
||||
if (e.which() == cereal::Event::ROAD_CAMERA_STATE) {
|
||||
auto fr = msg.getRoot<cereal::Event>().getRoadCameraState();
|
||||
|
||||
// TODO: better way?
|
||||
auto it = eidx.find(fr.getFrameId());
|
||||
if (it != eidx.end()) {
|
||||
auto pp = *it;
|
||||
//qDebug() << fr.getRoadCameraStateId() << pp;
|
||||
|
||||
if (frs->find(pp.first) != frs->end()) {
|
||||
auto frm = (*frs)[pp.first];
|
||||
auto data = frm->get(pp.second);
|
||||
if (data != NULL) {
|
||||
fr.setImage(kj::arrayPtr(data, frm->getRGBSize()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto words = capnp::messageToFlatArray(msg);
|
||||
auto bytes = words.asBytes();
|
||||
|
||||
// TODO: Can PubSocket take a const char?
|
||||
(*it)->send((char*)bytes.begin(), bytes.size());
|
||||
}
|
||||
++eit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QThread>
|
||||
#include <QReadWriteLock>
|
||||
#include "messaging.hpp"
|
||||
#include "FileReader.hpp"
|
||||
#include "FrameReader.hpp"
|
||||
|
||||
class Unlogger : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Unlogger(Events *events_, QReadWriteLock* events_lock_, QMap<int, FrameReader*> *frs_, int seek);
|
||||
uint64_t getCurrentTime() { return tc; }
|
||||
void setSeekRequest(uint64_t seek_request_) { seek_request = seek_request_; }
|
||||
void setPause(bool pause) { paused = pause; }
|
||||
void togglePause() { paused = !paused; }
|
||||
QMap<int, QPair<int, int> > eidx;
|
||||
public slots:
|
||||
void process();
|
||||
signals:
|
||||
void elapsed();
|
||||
void finished();
|
||||
private:
|
||||
Events *events;
|
||||
QReadWriteLock *events_lock;
|
||||
QMap<int, FrameReader*> *frs;
|
||||
QMap<int, PubSocket*> socks;
|
||||
Context *ctx;
|
||||
uint64_t tc = 0;
|
||||
uint64_t seek_request = 0;
|
||||
bool paused = false;
|
||||
};
|
||||
|
||||
@@ -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,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:445cf96b9a1dea87579a730bc0d3b0f7d32fe15eb0e677d406abf7cf49ab75e1
|
||||
size 463
|
||||
Reference in New Issue
Block a user