Create AOS MCAP logger for testing Foxglove

Doesn't add indexing yet, so still limited in size.

https://github.com/foxglove/studio/issues/2909 tracks support for
flatbuffers in foxglove studio

References: PRO-13587
Change-Id: I7c8c15c765395ade979eb8a011cfdae65451b526
Signed-off-by: James Kuszmaul <james.kuszmaul@bluerivertech.com>
diff --git a/aos/util/mcap_logger.h b/aos/util/mcap_logger.h
new file mode 100644
index 0000000..15d11db
--- /dev/null
+++ b/aos/util/mcap_logger.h
@@ -0,0 +1,67 @@
+#ifndef AOS_UTIL_MCAP_LOGGER_H_
+#define AOS_UTIL_MCAP_LOGGER_H_
+
+#include "aos/configuration_generated.h"
+#include "aos/events/event_loop.h"
+#include "aos/fast_string_builder.h"
+#include "aos/flatbuffer_utils.h"
+#include "single_include/nlohmann/json.hpp"
+
+namespace aos {
+
+// Produces a JSON Schema (https://json-schema.org/) for a given flatbuffer
+// type. If recursion_level is set, will include a $schema attribute indicating
+// the schema definition being used (this is used to allow for recursion).
+//
+// Note that this is pretty bare-bones, so, e.g., we don't distinguish between
+// structs and tables when generating the JSON schema, so we don't bother to
+// mark struct fields as required.
+enum class JsonSchemaRecursion {
+  kTopLevel,
+  kNested,
+};
+nlohmann::json JsonSchemaForFlatbuffer(
+    const FlatbufferType &type,
+    JsonSchemaRecursion recursion_level = JsonSchemaRecursion::kTopLevel);
+
+// Generates an MCAP file, per the specification at
+// https://github.com/foxglove/mcap/tree/main/docs/specification
+class McapLogger {
+ public:
+  McapLogger(EventLoop *event_loop, const std::string &output_path);
+  ~McapLogger();
+
+ private:
+  enum class OpCode {
+    kHeader = 0x01,
+    kFooter = 0x02,
+    kSchema = 0x03,
+    kChannel = 0x04,
+    kMessage = 0x05,
+    kDataEnd = 0x0F,
+  };
+  // Helpers to write each type of relevant record.
+  void WriteMagic();
+  void WriteHeader();
+  void WriteFooter();
+  void WriteDataEnd();
+  void WriteSchema(const uint16_t id, const aos::Channel *channel);
+  void WriteChannel(const uint16_t id, const uint16_t schema_id,
+                    const aos::Channel *channel);
+  void WriteMessage(uint16_t channel_id, const Channel *channel,
+                    const Context &context);
+  void WriteConfig();
+
+  // Writes an MCAP record to the output file.
+  void WriteRecord(OpCode op, std::string_view record);
+  // Adds an MCAP-spec string/fixed-size integer to a buffer.
+  static void AppendString(FastStringBuilder *builder, std::string_view string);
+  static void AppendInt16(FastStringBuilder *builder, uint16_t val);
+  static void AppendInt32(FastStringBuilder *builder, uint32_t val);
+  static void AppendInt64(FastStringBuilder *builder, uint64_t val);
+
+  std::ofstream output_;
+  FastStringBuilder string_builder_;
+};
+}  // namespace aos
+#endif  // AOS_UTIL_MCAP_LOGGER_H_