replay: eliminate qt dependency (#34102)
refactor to remove qt dependency and module Replay classespull/34106/head
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