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.
		
		
		
		
		
			
		
			
				
					
					
						
							261 lines
						
					
					
						
							6.7 KiB
						
					
					
				
			
		
		
	
	
							261 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 != 0){
 | |
|     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 != 0)
 | |
|       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 != 0)
 | |
|       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();
 | |
|   uint64_t t1 = (events.end()-1).key();
 | |
| 
 | |
|   //p.drawRect(0, 0, 600, 100);
 | |
| 
 | |
|   // TODO: we really don't have to redraw this every time, only on updates to events
 | |
|   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::CONTROLS_STATE) {
 | |
|         auto controlsState = e.getControlsState();
 | |
|         uint64_t t = (e.getLogMonoTime()-t0);
 | |
|         float vEgo = controlsState.getVEgo();
 | |
|         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);
 | |
|   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();
 | |
| }
 | |
| 
 | |
| 
 |