replay: remove Qt dependency from Segment and Timeline (#33847)
remove Segment, Timeline dependency on Qtpull/33867/head
parent
db98ba88ab
commit
24a32c3dec
15 changed files with 281 additions and 228 deletions
@ -0,0 +1,109 @@ |
||||
#include "tools/replay/timeline.h" |
||||
|
||||
#include <array> |
||||
|
||||
#include "cereal/gen/cpp/log.capnp.h" |
||||
|
||||
Timeline::~Timeline() { |
||||
should_exit_.store(true); |
||||
if (thread_.joinable()) { |
||||
thread_.join(); |
||||
} |
||||
} |
||||
|
||||
void Timeline::initialize(const Route &route, uint64_t route_start_ts, bool local_cache, |
||||
std::function<void(std::shared_ptr<LogReader>)> callback) { |
||||
thread_ = std::thread(&Timeline::buildTimeline, this, route, route_start_ts, local_cache, callback); |
||||
} |
||||
|
||||
std::optional<uint64_t> Timeline::find(double cur_ts, FindFlag flag) const { |
||||
for (const auto &entry : *get()) { |
||||
if (entry.type == TimelineType::Engaged) { |
||||
if (flag == FindFlag::nextEngagement && entry.start_time > cur_ts) { |
||||
return entry.start_time; |
||||
} else if (flag == FindFlag::nextDisEngagement && entry.end_time > cur_ts) { |
||||
return entry.end_time; |
||||
} |
||||
} else if (entry.start_time > cur_ts) { |
||||
if ((flag == FindFlag::nextUserFlag && entry.type == TimelineType::UserFlag) || |
||||
(flag == FindFlag::nextInfo && entry.type == TimelineType::AlertInfo) || |
||||
(flag == FindFlag::nextWarning && entry.type == TimelineType::AlertWarning) || |
||||
(flag == FindFlag::nextCritical && entry.type == TimelineType::AlertCritical)) { |
||||
return entry.start_time; |
||||
} |
||||
} |
||||
} |
||||
return std::nullopt; |
||||
} |
||||
|
||||
std::optional<Timeline::Entry> Timeline::findAlertAtTime(double target_time) const { |
||||
for (const auto &entry : *get()) { |
||||
if (entry.start_time > target_time) break; |
||||
if (entry.end_time >= target_time && entry.type >= TimelineType::AlertInfo) { |
||||
return entry; |
||||
} |
||||
} |
||||
return std::nullopt; |
||||
} |
||||
|
||||
void Timeline::buildTimeline(const Route &route, uint64_t route_start_ts, bool local_cache, |
||||
std::function<void(std::shared_ptr<LogReader>)> callback) { |
||||
std::optional<size_t> current_engaged_idx, current_alert_idx; |
||||
|
||||
for (const auto &segment : route.segments()) { |
||||
if (should_exit_) break; |
||||
|
||||
auto log = std::make_shared<LogReader>(); |
||||
if (!log->load(segment.second.qlog, &should_exit_, local_cache, 0, 3) || log->events.empty()) { |
||||
continue; // Skip if log loading fails or no events
|
||||
} |
||||
|
||||
for (const Event &e : log->events) { |
||||
double seconds = (e.mono_time - route_start_ts) / 1e9; |
||||
if (e.which == cereal::Event::Which::SELFDRIVE_STATE) { |
||||
capnp::FlatArrayMessageReader reader(e.data); |
||||
auto cs = reader.getRoot<cereal::Event>().getSelfdriveState(); |
||||
updateEngagementStatus(cs, current_engaged_idx, seconds); |
||||
updateAlertStatus(cs, current_alert_idx, seconds); |
||||
} else if (e.which == cereal::Event::Which::USER_FLAG) { |
||||
staging_entries_.emplace_back(Entry{seconds, seconds, TimelineType::UserFlag}); |
||||
} |
||||
} |
||||
|
||||
callback(log); // Notify the callback once the log is processed
|
||||
|
||||
// Sort and finalize the timeline entries
|
||||
std::sort(staging_entries_.begin(), staging_entries_.end(), [](auto &a, auto &b) { return a.start_time < b.start_time; }); |
||||
timeline_entries_ = std::make_shared<std::vector<Entry>>(staging_entries_); |
||||
} |
||||
} |
||||
|
||||
void Timeline::updateEngagementStatus(const cereal::SelfdriveState::Reader &cs, std::optional<size_t> &idx, double seconds) { |
||||
if (idx) staging_entries_[*idx].end_time = seconds; |
||||
if (cs.getEnabled()) { |
||||
if (!idx) { |
||||
idx = staging_entries_.size(); |
||||
staging_entries_.emplace_back(Entry{seconds, seconds, TimelineType::Engaged}); |
||||
} |
||||
} else { |
||||
idx.reset(); |
||||
} |
||||
} |
||||
|
||||
void Timeline::updateAlertStatus(const cereal::SelfdriveState::Reader &cs, std::optional<size_t> &idx, double seconds) { |
||||
static auto alert_types = std::array{TimelineType::AlertInfo, TimelineType::AlertWarning, TimelineType::AlertCritical}; |
||||
|
||||
Entry *entry = idx ? &staging_entries_[*idx] : nullptr; |
||||
if (entry) entry->end_time = seconds; |
||||
if (cs.getAlertSize() != cereal::SelfdriveState::AlertSize::NONE) { |
||||
auto type = alert_types[(int)cs.getAlertStatus()]; |
||||
std::string text1 = cs.getAlertText1().cStr(); |
||||
std::string text2 = cs.getAlertText2().cStr(); |
||||
if (!entry || entry->type != type || entry->text1 != text1 || entry->text2 != text2) { |
||||
idx = staging_entries_.size(); |
||||
staging_entries_.emplace_back(Entry{seconds, seconds, type, text1, text2}); // Start a new entry
|
||||
} |
||||
} else { |
||||
idx.reset(); |
||||
} |
||||
} |
@ -0,0 +1,46 @@ |
||||
#pragma once |
||||
|
||||
#include <atomic> |
||||
#include <optional> |
||||
#include <thread> |
||||
#include <vector> |
||||
|
||||
#include "tools/replay/route.h" |
||||
|
||||
enum class TimelineType { None, Engaged, AlertInfo, AlertWarning, AlertCritical, UserFlag }; |
||||
enum class FindFlag { nextEngagement, nextDisEngagement, nextUserFlag, nextInfo, nextWarning, nextCritical }; |
||||
|
||||
class Timeline { |
||||
public: |
||||
struct Entry { |
||||
double start_time; |
||||
double end_time; |
||||
TimelineType type; |
||||
std::string text1; |
||||
std::string text2; |
||||
}; |
||||
|
||||
Timeline() : timeline_entries_(std::make_shared<std::vector<Entry>>()) {} |
||||
~Timeline(); |
||||
|
||||
void initialize(const Route &route, uint64_t route_start_ts, bool local_cache, |
||||
std::function<void(std::shared_ptr<LogReader>)> callback); |
||||
std::optional<uint64_t> find(double cur_ts, FindFlag flag) const; |
||||
std::optional<Entry> findAlertAtTime(double target_time) const; |
||||
const std::shared_ptr<std::vector<Entry>> get() const { return timeline_entries_; } |
||||
|
||||
private: |
||||
void buildTimeline(const Route &route, uint64_t route_start_ts, bool local_cache, |
||||
std::function<void(std::shared_ptr<LogReader>)> callback); |
||||
void updateEngagementStatus(const cereal::SelfdriveState::Reader &cs, std::optional<size_t> &idx, double seconds); |
||||
void updateAlertStatus(const cereal::SelfdriveState::Reader &cs, std::optional<size_t> &idx, double seconds); |
||||
|
||||
std::thread thread_; |
||||
std::atomic<bool> should_exit_ = false; |
||||
|
||||
// Temporarily holds entries before they are sorted and finalized
|
||||
std::vector<Entry> staging_entries_; |
||||
|
||||
// Final sorted timeline entries
|
||||
std::shared_ptr<std::vector<Entry>> timeline_entries_; |
||||
}; |
Loading…
Reference in new issue