| 
						
						
							
								
							
						
						
					 | 
					 | 
					@ -9,35 +9,23 @@ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include "common/timing.h" | 
					 | 
					 | 
					 | 
					#include "common/timing.h" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include "tools/replay/util.h" | 
					 | 
					 | 
					 | 
					#include "tools/replay/util.h" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					Replay::Replay(QString route, QStringList allow, QStringList block, QStringList base_blacklist, SubMaster *sm_, uint32_t flags, QString data_dir, QObject *parent) | 
					 | 
					 | 
					 | 
					Replay::Replay(QString route, QStringList allow, QStringList block, SubMaster *sm_, | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    : sm(sm_), flags_(flags), QObject(parent) { | 
					 | 
					 | 
					 | 
					               uint32_t flags, QString data_dir, QObject *parent) : sm(sm_), flags_(flags), QObject(parent) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  std::vector<const char *> s; | 
					 | 
					 | 
					 | 
					  if (!(flags_ & REPLAY_FLAG_ALL_SERVICES)) { | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    block << "uiDebug" << "userFlag"; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  auto event_struct = capnp::Schema::from<cereal::Event>().asStruct(); | 
					 | 
					 | 
					 | 
					  auto event_struct = capnp::Schema::from<cereal::Event>().asStruct(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  sockets_.resize(event_struct.getUnionFields().size()); | 
					 | 
					 | 
					 | 
					  sockets_.resize(event_struct.getUnionFields().size()); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  for (const auto &it : services) { | 
					 | 
					 | 
					 | 
					  for (const auto &[name, _] : services) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    auto name = it.second.name.c_str(); | 
					 | 
					 | 
					 | 
					    if (!block.contains(name.c_str()) && (allow.empty() || allow.contains(name.c_str()))) { | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      uint16_t which = event_struct.getFieldByName(name).getProto().getDiscriminantValue(); | 
					 | 
					 | 
					 | 
					      uint16_t which = event_struct.getFieldByName(name).getProto().getDiscriminantValue(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if ((which == cereal::Event::Which::UI_DEBUG || which == cereal::Event::Which::USER_FLAG) && | 
					 | 
					 | 
					 | 
					      sockets_[which] = name.c_str(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        !(flags & REPLAY_FLAG_ALL_SERVICES) && | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        !allow.contains(name)) { | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      continue; | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if ((allow.empty() || allow.contains(name)) && !block.contains(name)) { | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      sockets_[which] = name; | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (!allow.empty() || !block.empty()) { | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        allow_list.insert((cereal::Event::Which)which); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      s.push_back(name); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  if (!allow_list.empty()) { | 
					 | 
					 | 
					 | 
					  std::vector<const char *> s; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    // the following events are needed for replay to work properly.
 | 
					 | 
					 | 
					 | 
					  std::copy_if(sockets_.begin(), sockets_.end(), std::back_inserter(s), | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    allow_list.insert(cereal::Event::Which::INIT_DATA); | 
					 | 
					 | 
					 | 
					               [](const char *name) { return name != nullptr; }); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    allow_list.insert(cereal::Event::Which::CAR_PARAMS); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  qDebug() << "services " << s; | 
					 | 
					 | 
					 | 
					  qDebug() << "services " << s; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  qDebug() << "loading route " << route; | 
					 | 
					 | 
					 | 
					  qDebug() << "loading route " << route; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -150,7 +138,7 @@ void Replay::buildTimeline() { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  const auto &route_segments = route_->segments(); | 
					 | 
					 | 
					 | 
					  const auto &route_segments = route_->segments(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  for (auto it = route_segments.cbegin(); it != route_segments.cend() && !exit_; ++it) { | 
					 | 
					 | 
					 | 
					  for (auto it = route_segments.cbegin(); it != route_segments.cend() && !exit_; ++it) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    std::shared_ptr<LogReader> log(new LogReader()); | 
					 | 
					 | 
					 | 
					    std::shared_ptr<LogReader> log(new LogReader()); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (!log->load(it->second.qlog.toStdString(), &exit_, {}, !hasFlag(REPLAY_FLAG_NO_FILE_CACHE), 0, 3)) continue; | 
					 | 
					 | 
					 | 
					    if (!log->load(it->second.qlog.toStdString(), &exit_, !hasFlag(REPLAY_FLAG_NO_FILE_CACHE), 0, 3)) continue; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    for (const Event *e : log->events) { | 
					 | 
					 | 
					 | 
					    for (const Event *e : log->events) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (e->which == cereal::Event::Which::CONTROLS_STATE) { | 
					 | 
					 | 
					 | 
					      if (e->which == cereal::Event::Which::CONTROLS_STATE) { | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -233,30 +221,17 @@ void Replay::segmentLoadFinished(bool success) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					} | 
					 | 
					 | 
					 | 
					} | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					void Replay::queueSegment() { | 
					 | 
					 | 
					 | 
					void Replay::queueSegment() { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  if (segments_.empty()) return; | 
					 | 
					 | 
					 | 
					  auto cur = segments_.lower_bound(current_segment_.load()); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					  if (cur == segments_.end()) return; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  SegmentMap::iterator begin, cur; | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  begin = cur = segments_.lower_bound(std::min(current_segment_.load(), segments_.rbegin()->first)); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  int distance = std::max<int>(std::ceil(segment_cache_limit / 2.0) - 1, segment_cache_limit - std::distance(cur, segments_.end())); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  for (int i = 0; begin != segments_.begin() && i < distance; ++i) { | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    --begin; | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  auto end = begin; | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  for (int i = 0; end != segments_.end() && i < segment_cache_limit; ++i) { | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    ++end; | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					  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, segments_.size())); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  // load one segment at a time
 | 
					 | 
					 | 
					 | 
					  // load one segment at a time
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  for (auto it = cur; it != end; ++it) { | 
					 | 
					 | 
					 | 
					  auto it = std::find_if(cur, end, [](auto &it) { return !it.second || !it.second->isLoaded(); }); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    auto &[n, seg] = *it; | 
					 | 
					 | 
					 | 
					  if (it != end && !it->second) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if ((seg && !seg->isLoaded()) || !seg) { | 
					 | 
					 | 
					 | 
					    rDebug("loading segment %d...", it->first); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (!seg) { | 
					 | 
					 | 
					 | 
					    it->second = std::make_unique<Segment>(it->first, route_->at(it->first), flags_); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        rDebug("loading segment %d...", n); | 
					 | 
					 | 
					 | 
					    QObject::connect(it->second.get(), &Segment::loadFinished, this, &Replay::segmentLoadFinished); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        seg = std::make_unique<Segment>(n, route_->at(n), flags_, allow_list); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        QObject::connect(seg.get(), &Segment::loadFinished, this, &Replay::segmentLoadFinished); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      break; | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  mergeSegments(begin, end); | 
					 | 
					 | 
					 | 
					  mergeSegments(begin, end); | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -293,13 +268,11 @@ void Replay::mergeSegments(const SegmentMap::iterator &begin, const SegmentMap:: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    new_events_->clear(); | 
					 | 
					 | 
					 | 
					    new_events_->clear(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    new_events_->reserve(new_events_size); | 
					 | 
					 | 
					 | 
					    new_events_->reserve(new_events_size); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    for (int n : segments_need_merge) { | 
					 | 
					 | 
					 | 
					    for (int n : segments_need_merge) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      const auto &e = segments_[n]->log->events; | 
					 | 
					 | 
					 | 
					      size_t size = new_events_->size(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (e.size() > 0) { | 
					 | 
					 | 
					 | 
					      const auto &events = segments_[n]->log->events; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        auto insert_from = e.begin(); | 
					 | 
					 | 
					 | 
					      std::copy_if(events.begin(), events.end(), std::back_inserter(*new_events_), | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        if (new_events_->size() > 0 && (*insert_from)->which == cereal::Event::Which::INIT_DATA) ++insert_from; | 
					 | 
					 | 
					 | 
					                   [this](auto e) { return e->which < sockets_.size() && sockets_[e->which] != nullptr; }); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        auto middle = new_events_->insert(new_events_->end(), insert_from, e.end()); | 
					 | 
					 | 
					 | 
					      std::inplace_merge(new_events_->begin(), new_events_->begin() + size, new_events_->end(), Event::lessThan()); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        std::inplace_merge(new_events_->begin(), middle, new_events_->end(), Event::lessThan()); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (stream_thread_) { | 
					 | 
					 | 
					 | 
					    if (stream_thread_) { | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -414,7 +387,7 @@ void Replay::stream() { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      cur_mono_time_ = evt->mono_time; | 
					 | 
					 | 
					 | 
					      cur_mono_time_ = evt->mono_time; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      setCurrentSegment(toSeconds(cur_mono_time_) / 60); | 
					 | 
					 | 
					 | 
					      setCurrentSegment(toSeconds(cur_mono_time_) / 60); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (cur_which < sockets_.size() && sockets_[cur_which] != nullptr) { | 
					 | 
					 | 
					 | 
					      if (sockets_[cur_which] != nullptr) { | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        // keep time
 | 
					 | 
					 | 
					 | 
					        // keep time
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        long etime = (cur_mono_time_ - evt_start_ts) / speed_; | 
					 | 
					 | 
					 | 
					        long etime = (cur_mono_time_ - evt_start_ts) / speed_; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        long rtime = nanos_since_boot() - loop_start_ts; | 
					 | 
					 | 
					 | 
					        long rtime = nanos_since_boot() - loop_start_ts; | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
					 | 
					
  |