diff --git a/tools/replay/consoleui.cc b/tools/replay/consoleui.cc index 5d2cf409d9..42c7238f7a 100644 --- a/tools/replay/consoleui.cc +++ b/tools/replay/consoleui.cc @@ -7,6 +7,7 @@ #include +#include "common/ratekeeper.h" #include "common/util.h" #include "common/version.h" @@ -57,7 +58,7 @@ void add_str(WINDOW *w, const char *str, Color color = Color::Default, bool bold } // namespace -ConsoleUI::ConsoleUI(Replay *replay, QObject *parent) : replay(replay), sm({"carState", "liveParameters"}), QObject(parent) { +ConsoleUI::ConsoleUI(Replay *replay) : replay(replay), sm({"carState", "liveParameters"}) { // Initialize curses initscr(); clear(); @@ -80,24 +81,16 @@ ConsoleUI::ConsoleUI(Replay *replay, QObject *parent) : replay(replay), sm({"car initWindows(); - qRegisterMetaType("uint64_t"); - qRegisterMetaType("ReplyMsgType"); installMessageHandler([this](ReplyMsgType type, const std::string msg) { - emit logMessageSignal(type, QString::fromStdString(msg)); + std::scoped_lock lock(mutex); + logs.emplace_back(type, msg); }); installDownloadProgressHandler([this](uint64_t cur, uint64_t total, bool success) { - emit updateProgressBarSignal(cur, total, success); + std::scoped_lock lock(mutex); + progress_cur = cur; + progress_total = total; + download_success = success; }); - - QObject::connect(replay, &Replay::streamStarted, this, &ConsoleUI::updateSummary); - QObject::connect(¬ifier, SIGNAL(activated(int)), SLOT(readyRead())); - QObject::connect(this, &ConsoleUI::updateProgressBarSignal, this, &ConsoleUI::updateProgressBar); - QObject::connect(this, &ConsoleUI::logMessageSignal, this, &ConsoleUI::logMessage); - - sm_timer.callOnTimeout(this, &ConsoleUI::updateStatus); - sm_timer.start(100); - getch_timer.start(1000, this); - readyRead(); } ConsoleUI::~ConsoleUI() { @@ -136,9 +129,7 @@ void ConsoleUI::initWindows() { } } -void ConsoleUI::timerEvent(QTimerEvent *ev) { - if (ev->timerId() != getch_timer.timerId()) return; - +void ConsoleUI::updateSize() { if (is_term_resized(max_height, max_width)) { for (auto win : w) { if (win) delwin(win); @@ -149,7 +140,6 @@ void ConsoleUI::timerEvent(QTimerEvent *ev) { initWindows(); rWarning("resize term %dx%d", max_height, max_width); } - updateTimeline(); } void ConsoleUI::updateStatus() { @@ -169,12 +159,6 @@ void ConsoleUI::updateStatus() { sm.update(0); - if (status != Status::Paused) { - auto events = replay->events(); - uint64_t current_mono_time = replay->routeStartNanos() + replay->currentSeconds() * 1e9; - bool playing = !events->empty() && events->back().mono_time > current_mono_time; - status = playing ? Status::Playing : Status::Waiting; - } auto [status_str, status_color] = status_text[status]; write_item(0, 0, "STATUS: ", status_str, " ", false, status_color); std::string current_segment = " - " + std::to_string((int)(replay->currentSeconds() / 60)); @@ -218,7 +202,7 @@ void ConsoleUI::displayTimelineDesc() { } } -void ConsoleUI::logMessage(ReplyMsgType type, const QString &msg) { +void ConsoleUI::logMessage(ReplyMsgType type, const std::string &msg) { if (auto win = w[Win::Log]) { Color color = Color::Default; if (type == ReplyMsgType::Debug) { @@ -228,19 +212,19 @@ void ConsoleUI::logMessage(ReplyMsgType type, const QString &msg) { } else if (type == ReplyMsgType::Critical) { color = Color::Red; } - add_str(win, qPrintable(msg + "\n"), color); + add_str(win, (msg + "\n").c_str(), color); wrefresh(win); } } -void ConsoleUI::updateProgressBar(uint64_t cur, uint64_t total, bool success) { +void ConsoleUI::updateProgressBar() { werase(w[Win::DownloadBar]); - if (success && cur < total) { + if (download_success && progress_cur < progress_total) { const int width = 35; - const float progress = cur / (double)total; + const float progress = progress_cur / (double)progress_total; const int pos = width * progress; wprintw(w[Win::DownloadBar], "Downloading [%s>%s] %d%% %s", std::string(pos, '=').c_str(), - std::string(width - pos, ' ').c_str(), int(progress * 100.0), formattedDataSize(total).c_str()); + std::string(width - pos, ' ').c_str(), int(progress * 100.0), formattedDataSize(progress_total).c_str()); } wrefresh(w[Win::DownloadBar]); } @@ -288,16 +272,9 @@ void ConsoleUI::updateTimeline() { wrefresh(win); } -void ConsoleUI::readyRead() { - int c; - while ((c = getch()) != ERR) { - handleKey(c); - } -} - void ConsoleUI::pauseReplay(bool pause) { replay->pause(pause); - status = pause ? Status::Paused : Status::Waiting; + status = pause ? Status::Paused : Status::Playing; } void ConsoleUI::handleKey(char c) { @@ -305,7 +282,6 @@ void ConsoleUI::handleKey(char c) { // pause the replay and blocking getchar() pauseReplay(true); updateStatus(); - getch_timer.stop(); curs_set(true); nodelay(stdscr, false); @@ -330,7 +306,6 @@ void ConsoleUI::handleKey(char c) { nodelay(stdscr, true); curs_set(false); refresh(); - getch_timer.start(1000, this); } else if (c == '+' || c == '=') { auto it = std::upper_bound(speed_array.begin(), speed_array.end(), replay->getSpeed()); @@ -367,7 +342,37 @@ void ConsoleUI::handleKey(char c) { replay->seekTo(-10, true); } else if (c == ' ') { pauseReplay(!replay->isPaused()); - } else if (c == 'q' || c == 'Q') { - qApp->exit(); } } + +int ConsoleUI::exec() { + RateKeeper rk("Replay", 20); + while (true) { + int c = getch(); + if (c == 'q' || c == 'Q') { + break; + } + handleKey(c); + + if (rk.frame() % 25) { + updateSize(); + updateSummary(); + } + + updateTimeline(); + updateStatus(); + + { + std::scoped_lock lock(mutex); + updateProgressBar(); + for (auto &[type, msg] : logs) { + logMessage(type, msg); + } + logs.clear(); + } + + qApp->processEvents(); + rk.keepTime(); + } + return 0; +} diff --git a/tools/replay/consoleui.h b/tools/replay/consoleui.h index 6ed44bc623..3d4abeb458 100644 --- a/tools/replay/consoleui.h +++ b/tools/replay/consoleui.h @@ -1,21 +1,17 @@ #pragma once #include -#include -#include -#include -#include -#include +#include +#include #include "tools/replay/replay.h" #include -class ConsoleUI : public QObject { - Q_OBJECT - +class ConsoleUI { public: - ConsoleUI(Replay *replay, QObject *parent = 0); + ConsoleUI(Replay *replay); ~ConsoleUI(); + int exec(); inline static const std::array speed_array = {0.2f, 0.5f, 1.0f, 2.0f, 3.0f}; private: @@ -27,25 +23,21 @@ private: void updateSummary(); void updateStatus(); void pauseReplay(bool pause); + void updateSize(); + void updateProgressBar(); + void logMessage(ReplyMsgType type, const std::string &msg); - enum Status { Waiting, Playing, Paused }; + enum Status { Playing, Paused }; enum Win { Title, Stats, Log, LogBorder, DownloadBar, Timeline, TimelineDesc, Help, CarState, Max}; std::array w{}; SubMaster sm; Replay *replay; - QBasicTimer getch_timer; - QTimer sm_timer; - QSocketNotifier notifier{0, QSocketNotifier::Read, this}; int max_width, max_height; - Status status = Status::Waiting; - -signals: - void updateProgressBarSignal(uint64_t cur, uint64_t total, bool success); - void logMessageSignal(ReplyMsgType type, const QString &msg); + Status status = Status::Playing; -private slots: - void readyRead(); - void timerEvent(QTimerEvent *ev); - void updateProgressBar(uint64_t cur, uint64_t total, bool success); - void logMessage(ReplyMsgType type, const QString &msg); + std::mutex mutex; + std::vector> logs; + uint64_t progress_cur = 0; + uint64_t progress_total = 0; + bool download_success = false; }; diff --git a/tools/replay/main.cc b/tools/replay/main.cc index 4e11ddb583..b880e99e23 100644 --- a/tools/replay/main.cc +++ b/tools/replay/main.cc @@ -151,5 +151,5 @@ int main(int argc, char *argv[]) { ConsoleUI console_ui(replay); replay->start(config.start_seconds); - return app.exec(); + return console_ui.exec(); } diff --git a/tools/replay/replay.h b/tools/replay/replay.h index 8d2e07ba5b..e828b369aa 100644 --- a/tools/replay/replay.h +++ b/tools/replay/replay.h @@ -43,6 +43,7 @@ enum class FindFlag { enum class TimelineType { None, Engaged, AlertInfo, AlertWarning, AlertCritical, UserFlag }; typedef bool (*replayEventFilter)(const Event *, void *); +typedef std::map> SegmentMap; Q_DECLARE_METATYPE(std::shared_ptr); class Replay : public QObject { @@ -82,8 +83,7 @@ public: inline double maxSeconds() const { return max_seconds_; } inline void setSpeed(float speed) { speed_ = speed; } inline float getSpeed() const { return speed_; } - inline const std::vector *events() const { return &events_; } - inline const std::map> &segments() const { return segments_; } + inline const SegmentMap &segments() const { return segments_; } inline const std::string &carFingerprint() const { return car_fingerprint_; } inline const std::vector> getTimeline() { std::lock_guard lk(timeline_lock); @@ -102,7 +102,6 @@ protected slots: void segmentLoadFinished(bool success); protected: - typedef std::map> SegmentMap; std::optional find(FindFlag flag); void pauseStreamThread(); void startStream(const Segment *cur_segment);