Support replaying the realtime clock from logs

This adds the concept of the realtime offset to the event scheduler.

Side note: the event scheduler is going to have to get significantly
more complicated when multi-node log files show up.

Change-Id: Ia6f891c77b8c3badcea930cdfa0e236acbff7801
diff --git a/aos/events/event_scheduler.h b/aos/events/event_scheduler.h
index 2fcf90b..432f4ad 100644
--- a/aos/events/event_scheduler.h
+++ b/aos/events/event_scheduler.h
@@ -45,15 +45,23 @@
   bool is_running() const { return is_running_; }
 
   monotonic_clock::time_point monotonic_now() const { return now_; }
+
   realtime_clock::time_point realtime_now() const {
-    // TODO(austin): Make this all configurable...
-    return realtime_clock::epoch() + now_.time_since_epoch() +
-           std::chrono::seconds(1000000);
+    return realtime_clock::time_point(monotonic_now().time_since_epoch() +
+                                      realtime_offset_);
+  }
+
+  // Sets realtime clock to realtime_now for a given monotonic clock.
+  void SetRealtimeOffset(monotonic_clock::time_point monotonic_now,
+                         realtime_clock::time_point realtime_now) {
+    realtime_offset_ =
+        realtime_now.time_since_epoch() - monotonic_now.time_since_epoch();
   }
 
  private:
   // Current execution time.
   monotonic_clock::time_point now_ = monotonic_clock::epoch();
+  std::chrono::nanoseconds realtime_offset_ = std::chrono::seconds(0);
 
   std::vector<std::function<void()>> on_run_;
 
diff --git a/aos/events/logging/BUILD b/aos/events/logging/BUILD
index 3df5592..94e25c2 100644
--- a/aos/events/logging/BUILD
+++ b/aos/events/logging/BUILD
@@ -14,10 +14,12 @@
     name = "logger",
     srcs = ["logger.cc"],
     hdrs = ["logger.h"],
+    visibility = ["//visibility:public"],
     deps = [
         ":logger_fbs",
         "//aos:flatbuffer_merge",
         "//aos/events:event_loop",
+        "//aos/events:simulated_event_loop",
         "//aos/network:team_number",
         "//aos/time",
         "@com_github_google_flatbuffers//:flatbuffers",
diff --git a/aos/events/logging/log_cat.cc b/aos/events/logging/log_cat.cc
index 9c75076..a53d337 100644
--- a/aos/events/logging/log_cat.cc
+++ b/aos/events/logging/log_cat.cc
@@ -29,12 +29,7 @@
 
   aos::logger::LogReader reader(FLAGS_logfile);
   aos::SimulatedEventLoopFactory log_reader_factory(reader.configuration());
-  std::unique_ptr<aos::EventLoop> reader_event_loop =
-      log_reader_factory.MakeEventLoop("log_reader");
-  reader.Register(reader_event_loop.get());
-  // We don't run timing reports when trying to print out logged data, because
-  // otherwise we would end up printing out the timing reports themselves...
-  reader_event_loop->SkipTimingReport();
+  reader.Register(&log_reader_factory);
 
   std::unique_ptr<aos::EventLoop> printer_event_loop =
       log_reader_factory.MakeEventLoop("printer");
@@ -58,11 +53,10 @@
             // unnecessary cruft from glog and to allow the user to readily
             // redirect just the logged output independent of any debugging
             // information on stderr.
-            // TODO(james): Also print out realtime time once we support
-            // replaying it.
-            std::cout << channel->name()->c_str() << ' '
-                      << channel->type()->c_str() << " at "
-                      << context.monotonic_event_time << ": "
+            std::cout << context.realtime_event_time << " ("
+                      << context.monotonic_event_time << ") "
+                      << channel->name()->c_str() << ' '
+                      << channel->type()->c_str() << ": "
                       << aos::FlatbufferToJson(
                              channel->schema(),
                              static_cast<const uint8_t *>(message))
diff --git a/aos/events/logging/logger.cc b/aos/events/logging/logger.cc
index e38c476..e35fe5b 100644
--- a/aos/events/logging/logger.cc
+++ b/aos/events/logging/logger.cc
@@ -390,6 +390,19 @@
           ->realtime_start_time()));
 }
 
+void LogReader::Register(SimulatedEventLoopFactory *event_loop_factory) {
+  event_loop_unique_ptr_ = event_loop_factory->MakeEventLoop("log_reader");
+  event_loop_factory_ = event_loop_factory;
+  // We don't run timing reports when trying to print out logged data, because
+  // otherwise we would end up printing out the timing reports themselves...
+  // This is only really relevant when we are replaying into a simulation.
+  event_loop_unique_ptr_->SkipTimingReport();
+
+  Register(event_loop_unique_ptr_.get());
+  event_loop_factory_->RunFor(monotonic_start_time() -
+                              event_loop_factory_->monotonic_now());
+}
+
 void LogReader::Register(EventLoop *event_loop) {
   event_loop_ = event_loop;
 
@@ -422,7 +435,17 @@
     FlatbufferVector<MessageHeader> front = std::move(channel.front());
 
     CHECK(front.message().data() != nullptr);
+
     if (oldest_channel_index.first > monotonic_start_time()) {
+      // If we have access to the factory, use it to fix the realtime time.
+      if (event_loop_factory_ != nullptr) {
+        event_loop_factory_->SetRealtimeOffset(
+            monotonic_clock::time_point(
+                chrono::nanoseconds(front.message().monotonic_sent_time())),
+            realtime_clock::time_point(
+                chrono::nanoseconds(front.message().realtime_sent_time())));
+      }
+
       channel.raw_sender->Send(front.message().data()->Data(),
                                front.message().data()->size());
     } else {
@@ -463,6 +486,10 @@
   for (size_t i = 0; i < channels_.size(); ++i) {
     channels_[i].raw_sender.reset();
   }
+
+  event_loop_factory_ = nullptr;
+  event_loop_unique_ptr_.reset();
+  event_loop_ = nullptr;
 }
 
 void LogReader::EmplaceDataBack(FlatbufferVector<MessageHeader> &&new_data) {
diff --git a/aos/events/logging/logger.h b/aos/events/logging/logger.h
index db23767..497dabb 100644
--- a/aos/events/logging/logger.h
+++ b/aos/events/logging/logger.h
@@ -8,6 +8,7 @@
 #include "absl/types/span.h"
 #include "aos/events/event_loop.h"
 #include "aos/events/logging/logger_generated.h"
+#include "aos/events/simulated_event_loop.h"
 #include "aos/time/time.h"
 #include "flatbuffers/flatbuffers.h"
 
@@ -96,6 +97,9 @@
   // Registers the timer and senders used to resend the messages from the log
   // file.
   void Register(EventLoop *event_loop);
+  // Registers everything, but also updates the real time time in sync.  Runs
+  // until the log file starts.
+  void Register(SimulatedEventLoopFactory *factory);
   // Unregisters the senders.
   void Deregister();
 
@@ -186,7 +190,9 @@
   // File descriptor for the log file.
   int fd_ = -1;
 
-  EventLoop *event_loop_;
+  SimulatedEventLoopFactory *event_loop_factory_ = nullptr;
+  std::unique_ptr<EventLoop> event_loop_unique_ptr_;
+  EventLoop *event_loop_ = nullptr;
   TimerHandler *timer_handler_;
 
   // Vector to read into.  This uses an allocator which doesn't zero initialize
diff --git a/aos/events/logging/logger_test.cc b/aos/events/logging/logger_test.cc
index c627101..9aae936 100644
--- a/aos/events/logging/logger_test.cc
+++ b/aos/events/logging/logger_test.cc
@@ -63,22 +63,11 @@
 
   LOG(INFO) << "Config " << FlatbufferToJson(reader.configuration());
 
-  // TODO(austin): Figure out what the API needs to look like.  How do we replay
-  // the data that was fetched before it all starts?  How do we set the starting
-  // time from the log file?  Probably need to let the reader do more if it
-  // knows about the factory.
   SimulatedEventLoopFactory log_reader_factory(reader.configuration());
-  std::unique_ptr<EventLoop> reader_event_loop =
-      log_reader_factory.MakeEventLoop("log_reader");
 
-  reader.Register(reader_event_loop.get());
+  // This sends out the fetched messages and advances time to the start of the log file.
+  reader.Register(&log_reader_factory);
 
-  // Capture monotonic start time in OnRun and offset from there?  Let the user
-  // configure the factory if they want time to match?
-  /*
-  log_reader_factory.InitializeTime(log_reader.monotonic_start_time(),
-                                    log_reader.realtime_start_time());
-                                    */
   std::unique_ptr<EventLoop> test_event_loop =
       log_reader_factory.MakeEventLoop("log_reader");
 
diff --git a/aos/events/simulated_event_loop.h b/aos/events/simulated_event_loop.h
index 740a4c2..fee3ef0 100644
--- a/aos/events/simulated_event_loop.h
+++ b/aos/events/simulated_event_loop.h
@@ -81,6 +81,12 @@
     return scheduler_.realtime_now();
   }
 
+  // Sets realtime clock to realtime_now for a given monotonic clock.
+  void SetRealtimeOffset(monotonic_clock::time_point monotonic_now,
+                         realtime_clock::time_point realtime_now) {
+    scheduler_.SetRealtimeOffset(monotonic_now, realtime_now);
+  }
+
  private:
   const Configuration *const configuration_;
   EventScheduler scheduler_;