replay: remove Qt dependency from Segment and Timeline (#33847)
	
		
	
				
					
				
			remove Segment, Timeline dependency on Qtfca-giorgio
							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