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