replay: eliminate qt dependency (#34102)
	
		
	
				
					
				
			refactor to remove qt dependency and module Replay classesxavvypls-test
							parent
							
								
									a58853e70e
								
							
						
					
					
						commit
						3c765a1f45
					
				
				 18 changed files with 404 additions and 436 deletions
			
			
		@ -0,0 +1,133 @@ | 
				
			||||
#include "tools/replay/seg_mgr.h" | 
				
			||||
 | 
				
			||||
SegmentManager::~SegmentManager() { | 
				
			||||
  { | 
				
			||||
    std::unique_lock lock(mutex_); | 
				
			||||
    exit_ = true; | 
				
			||||
    onSegmentMergedCallback_ = nullptr; | 
				
			||||
  } | 
				
			||||
  cv_.notify_one(); | 
				
			||||
  if (thread_.joinable()) thread_.join(); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
bool SegmentManager::load() { | 
				
			||||
  if (!route_.load()) { | 
				
			||||
    rError("failed to load route: %s", route_.name().c_str()); | 
				
			||||
    return false; | 
				
			||||
  } | 
				
			||||
 | 
				
			||||
  for (const auto &[n, file] : route_.segments()) { | 
				
			||||
    if (!file.rlog.empty() || !file.qlog.empty()) { | 
				
			||||
      segments_.insert({n, nullptr}); | 
				
			||||
    } | 
				
			||||
  } | 
				
			||||
 | 
				
			||||
  if (segments_.empty()) { | 
				
			||||
    rInfo("no valid segments in route: %s", route_.name().c_str()); | 
				
			||||
    return false; | 
				
			||||
  } | 
				
			||||
 | 
				
			||||
  rInfo("loaded route %s with %zu valid segments", route_.name().c_str(), segments_.size()); | 
				
			||||
  thread_ = std::thread(&SegmentManager::manageSegmentCache, this); | 
				
			||||
  return true; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void SegmentManager::setCurrentSegment(int seg_num) { | 
				
			||||
  { | 
				
			||||
    std::unique_lock lock(mutex_); | 
				
			||||
    cur_seg_num_ = seg_num; | 
				
			||||
    needs_update_ = true; | 
				
			||||
  } | 
				
			||||
  cv_.notify_one(); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void SegmentManager::manageSegmentCache() { | 
				
			||||
  while (true) { | 
				
			||||
    std::unique_lock lock(mutex_); | 
				
			||||
    cv_.wait(lock, [this]() { return exit_ || needs_update_; }); | 
				
			||||
    if (exit_) break; | 
				
			||||
 | 
				
			||||
    needs_update_ = false; | 
				
			||||
    auto cur = segments_.lower_bound(cur_seg_num_); | 
				
			||||
    if (cur == segments_.end()) continue; | 
				
			||||
 | 
				
			||||
    // Calculate the range of segments to load
 | 
				
			||||
    auto begin = std::prev(cur, std::min<int>(segment_cache_limit_ / 2, std::distance(segments_.begin(), cur))); | 
				
			||||
    auto end = std::next(begin, std::min<int>(segment_cache_limit_, std::distance(begin, segments_.end()))); | 
				
			||||
    begin = std::prev(end, std::min<int>(segment_cache_limit_, std::distance(segments_.begin(), end))); | 
				
			||||
 | 
				
			||||
    loadSegmentsInRange(begin, cur, end); | 
				
			||||
    bool merged = mergeSegments(begin, end); | 
				
			||||
 | 
				
			||||
    // Free segments outside the current range
 | 
				
			||||
    std::for_each(segments_.begin(), begin, [](auto &segment) { segment.second.reset(); }); | 
				
			||||
    std::for_each(end, segments_.end(), [](auto &segment) { segment.second.reset(); }); | 
				
			||||
 | 
				
			||||
    lock.unlock(); | 
				
			||||
 | 
				
			||||
    if (merged && onSegmentMergedCallback_) { | 
				
			||||
      onSegmentMergedCallback_();  // Notify listener that segments have been merged
 | 
				
			||||
    } | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
bool SegmentManager::mergeSegments(const SegmentMap::iterator &begin, const SegmentMap::iterator &end) { | 
				
			||||
  std::set<int> segments_to_merge; | 
				
			||||
  size_t total_event_count = 0; | 
				
			||||
  for (auto it = begin; it != end; ++it) { | 
				
			||||
    const auto &segment = it->second; | 
				
			||||
    if (segment && segment->getState() == Segment::LoadState::Loaded) { | 
				
			||||
      segments_to_merge.insert(segment->seg_num); | 
				
			||||
      total_event_count += segment->log->events.size(); | 
				
			||||
    } | 
				
			||||
  } | 
				
			||||
 | 
				
			||||
  if (segments_to_merge == merged_segments_) return false; | 
				
			||||
 | 
				
			||||
  auto merged_event_data = std::make_shared<EventData>(); | 
				
			||||
  auto &merged_events = merged_event_data->events; | 
				
			||||
  merged_events.reserve(total_event_count); | 
				
			||||
 | 
				
			||||
  rDebug("merging segments: %s", join(segments_to_merge, ", ").c_str()); | 
				
			||||
  for (int n : segments_to_merge) { | 
				
			||||
    const auto &events = segments_.at(n)->log->events; | 
				
			||||
    if (events.empty()) continue; | 
				
			||||
 | 
				
			||||
    // Skip INIT_DATA if present
 | 
				
			||||
    auto events_begin = (events.front().which == cereal::Event::Which::INIT_DATA) ? std::next(events.begin()) : events.begin(); | 
				
			||||
 | 
				
			||||
    size_t previous_size = merged_events.size(); | 
				
			||||
    merged_events.insert(merged_events.end(), events_begin, events.end()); | 
				
			||||
    std::inplace_merge(merged_events.begin(), merged_events.begin() + previous_size, merged_events.end()); | 
				
			||||
 | 
				
			||||
    merged_event_data->segments[n] = segments_.at(n); | 
				
			||||
  } | 
				
			||||
 | 
				
			||||
  event_data_ = merged_event_data; | 
				
			||||
  merged_segments_ = segments_to_merge; | 
				
			||||
 | 
				
			||||
  return true; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void SegmentManager::loadSegmentsInRange(SegmentMap::iterator begin, SegmentMap::iterator cur, SegmentMap::iterator end) { | 
				
			||||
  auto tryLoadSegment = [this](auto first, auto last) { | 
				
			||||
    for (auto it = first; it != last; ++it) { | 
				
			||||
      auto &segment_ptr = it->second; | 
				
			||||
      if (!segment_ptr) { | 
				
			||||
        segment_ptr = std::make_shared<Segment>( | 
				
			||||
            it->first, route_.at(it->first), flags_, filters_, | 
				
			||||
            [this](int seg_num, bool success) { setCurrentSegment(cur_seg_num_); }); | 
				
			||||
      } | 
				
			||||
 | 
				
			||||
      if (segment_ptr->getState() == Segment::LoadState::Loading) { | 
				
			||||
        return true;  // Segment is still loading
 | 
				
			||||
      } | 
				
			||||
    } | 
				
			||||
    return false;  // No segments need loading
 | 
				
			||||
  }; | 
				
			||||
 | 
				
			||||
  // Try forward loading, then reverse if necessary
 | 
				
			||||
  if (!tryLoadSegment(cur, end)) { | 
				
			||||
    tryLoadSegment(std::make_reverse_iterator(cur), std::make_reverse_iterator(begin)); | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,56 @@ | 
				
			||||
#pragma once | 
				
			||||
 | 
				
			||||
#include <condition_variable> | 
				
			||||
#include <map> | 
				
			||||
#include <mutex> | 
				
			||||
#include <set> | 
				
			||||
#include <vector> | 
				
			||||
 | 
				
			||||
#include "tools/replay/route.h" | 
				
			||||
 | 
				
			||||
constexpr int MIN_SEGMENTS_CACHE = 5; | 
				
			||||
 | 
				
			||||
using SegmentMap = std::map<int, std::shared_ptr<Segment>>; | 
				
			||||
 | 
				
			||||
class SegmentManager { | 
				
			||||
public: | 
				
			||||
  struct EventData { | 
				
			||||
    std::vector<Event> events;  //  Events extracted from the segments
 | 
				
			||||
    SegmentMap segments;        // Associated segments that contributed to these events
 | 
				
			||||
    bool isSegmentLoaded(int n) const { return segments.find(n) != segments.end(); } | 
				
			||||
  }; | 
				
			||||
 | 
				
			||||
  SegmentManager(const std::string &route_name, uint32_t flags, const std::string &data_dir = "") | 
				
			||||
      : flags_(flags), route_(route_name, data_dir) {}; | 
				
			||||
  ~SegmentManager(); | 
				
			||||
 | 
				
			||||
  bool load(); | 
				
			||||
  void setCurrentSegment(int seg_num); | 
				
			||||
  void setCallback(const std::function<void()> &callback) { onSegmentMergedCallback_ = callback; } | 
				
			||||
  void setFilters(const std::vector<bool> &filters) { filters_ = filters; } | 
				
			||||
  const std::shared_ptr<EventData> getEventData() const { return event_data_; } | 
				
			||||
  bool hasSegment(int n) const { return segments_.find(n) != segments_.end(); } | 
				
			||||
 | 
				
			||||
  Route route_; | 
				
			||||
  int segment_cache_limit_ = MIN_SEGMENTS_CACHE; | 
				
			||||
 | 
				
			||||
private: | 
				
			||||
  void manageSegmentCache(); | 
				
			||||
  void loadSegmentsInRange(SegmentMap::iterator begin, SegmentMap::iterator cur, SegmentMap::iterator end); | 
				
			||||
  bool mergeSegments(const SegmentMap::iterator &begin, const SegmentMap::iterator &end); | 
				
			||||
 | 
				
			||||
  std::vector<bool> filters_; | 
				
			||||
  uint32_t flags_; | 
				
			||||
 | 
				
			||||
  std::mutex mutex_; | 
				
			||||
  std::condition_variable cv_; | 
				
			||||
  std::thread thread_; | 
				
			||||
  std::atomic<int> cur_seg_num_ = -1; | 
				
			||||
  bool needs_update_ = false; | 
				
			||||
  bool exit_ = false; | 
				
			||||
 | 
				
			||||
  SegmentMap segments_; | 
				
			||||
  std::shared_ptr<EventData> event_data_; | 
				
			||||
  std::function<void()> onSegmentMergedCallback_ = nullptr; | 
				
			||||
  std::set<int> merged_segments_; | 
				
			||||
}; | 
				
			||||
@ -1,10 +0,0 @@ | 
				
			||||
#define CATCH_CONFIG_RUNNER | 
				
			||||
#include "catch2/catch.hpp" | 
				
			||||
#include <QCoreApplication> | 
				
			||||
 | 
				
			||||
int main(int argc, char **argv) { | 
				
			||||
  // unit tests for Qt
 | 
				
			||||
  QCoreApplication app(argc, argv); | 
				
			||||
  const int res = Catch::Session().run(argc, argv); | 
				
			||||
  return (res < 0xff ? res : 0xff); | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue