Allow using shortened channel names in foxglove_websocket

This makes the websocket server's defaults consistent with log_to_mcap.
By using shortened channel names, we make ti easier to reuse layouts
across nodes (e.g., this way, the /piX prefix will be removed for
the channels available on the current pi being debugged).

Change-Id: Ief1f0c5a8932425017b4718ec9f9d2bae1de2c72
Signed-off-by: James Kuszmaul <jabukuszmaul@gmail.com>
diff --git a/aos/util/foxglove_websocket.cc b/aos/util/foxglove_websocket.cc
index 715ba13..77dca20 100644
--- a/aos/util/foxglove_websocket.cc
+++ b/aos/util/foxglove_websocket.cc
@@ -11,6 +11,10 @@
             "with a read_method of PIN (see aos/configuration.fbs; PIN is an "
             "enum value). Having this enabled will cause foxglove to  consume "
             "extra shared memory resources.");
+DEFINE_bool(
+    canonical_channel_names, false,
+    "If set, use full channel names; by default, will shorten names to be the "
+    "shortest possible version of the name (e.g., /aos instead of /pi/aos).");
 
 int main(int argc, char *argv[]) {
   gflags::SetUsageMessage(
@@ -53,7 +57,10 @@
           : aos::FoxgloveWebsocketServer::Serialization::kJson,
       FLAGS_fetch_pinned_channels
           ? aos::FoxgloveWebsocketServer::FetchPinnedChannels::kYes
-          : aos::FoxgloveWebsocketServer::FetchPinnedChannels::kNo);
+          : aos::FoxgloveWebsocketServer::FetchPinnedChannels::kNo,
+      FLAGS_canonical_channel_names
+          ? aos::FoxgloveWebsocketServer::CanonicalChannelNames::kCanonical
+          : aos::FoxgloveWebsocketServer::CanonicalChannelNames::kShortened);
 
   event_loop.Run();
 }
diff --git a/aos/util/foxglove_websocket_lib.cc b/aos/util/foxglove_websocket_lib.cc
index 1cc3a8a..06c551e 100644
--- a/aos/util/foxglove_websocket_lib.cc
+++ b/aos/util/foxglove_websocket_lib.cc
@@ -1,8 +1,8 @@
 #include "aos/util/foxglove_websocket_lib.h"
 
-#include "aos/util/mcap_logger.h"
-#include "aos/flatbuffer_merge.h"
 #include "absl/strings/escaping.h"
+#include "aos/flatbuffer_merge.h"
+#include "aos/util/mcap_logger.h"
 #include "gflags/gflags.h"
 
 DEFINE_uint32(sorting_buffer_ms, 100,
@@ -17,10 +17,12 @@
 namespace aos {
 FoxgloveWebsocketServer::FoxgloveWebsocketServer(
     aos::EventLoop *event_loop, uint32_t port, Serialization serialization,
-    FetchPinnedChannels fetch_pinned_channels)
+    FetchPinnedChannels fetch_pinned_channels,
+    CanonicalChannelNames canonical_channels)
     : event_loop_(event_loop),
       serialization_(serialization),
       fetch_pinned_channels_(fetch_pinned_channels),
+      canonical_channels_(canonical_channels),
       server_(port, "aos_foxglove") {
   for (const aos::Channel *channel :
        *event_loop_->configuration()->channels()) {
@@ -30,18 +32,28 @@
         (!is_pinned || fetch_pinned_channels_ == FetchPinnedChannels::kYes)) {
       const FlatbufferDetachedBuffer<reflection::Schema> schema =
           RecursiveCopyFlatBuffer(channel->schema());
+      const std::string shortest_name =
+          ShortenedChannelName(event_loop_->configuration(), channel,
+                               event_loop_->name(), event_loop_->node());
+      std::string name_to_send;
+      switch (canonical_channels_) {
+        case CanonicalChannelNames::kCanonical:
+          name_to_send = channel->name()->string_view();
+          break;
+        case CanonicalChannelNames::kShortened:
+          name_to_send = shortest_name;
+          break;
+      }
       const ChannelId id =
           (serialization_ == Serialization::kJson)
               ? server_.addChannel(foxglove::websocket::ChannelWithoutId{
-                    .topic =
-                        channel->name()->str() + " " + channel->type()->str(),
+                    .topic = name_to_send + " " + channel->type()->str(),
                     .encoding = "json",
                     .schemaName = channel->type()->str(),
                     .schema =
                         JsonSchemaForFlatbuffer({channel->schema()}).dump()})
               : server_.addChannel(foxglove::websocket::ChannelWithoutId{
-                    .topic =
-                        channel->name()->str() + " " + channel->type()->str(),
+                    .topic = name_to_send + " " + channel->type()->str(),
                     .encoding = "flatbuffer",
                     .schemaName = channel->type()->str(),
                     .schema = absl::Base64Escape(
diff --git a/aos/util/foxglove_websocket_lib.h b/aos/util/foxglove_websocket_lib.h
index 9be2f61..7c326c2 100644
--- a/aos/util/foxglove_websocket_lib.h
+++ b/aos/util/foxglove_websocket_lib.h
@@ -24,9 +24,20 @@
     kYes,
     kNo,
   };
+  // Whether to attempt to shorten channel names.
+  enum class CanonicalChannelNames {
+    // Just use the full, unambiguous, channel names.
+    kCanonical,
+    // Use GetChannelAliases() to determine the shortest possible name for the
+    // channel for the current node, and use that in the MCAP file. This makes
+    // it so that the channels in the resulting file are more likely to match
+    // the channel names that are used in "real" applications.
+    kShortened,
+  };
   FoxgloveWebsocketServer(aos::EventLoop *event_loop, uint32_t port,
                           Serialization serialization,
-                          FetchPinnedChannels fetch_pinned_channels);
+                          FetchPinnedChannels fetch_pinned_channels,
+                          CanonicalChannelNames canonical_channels);
   ~FoxgloveWebsocketServer();
 
  private:
@@ -47,6 +58,7 @@
   aos::EventLoop *event_loop_;
   const Serialization serialization_;
   const FetchPinnedChannels fetch_pinned_channels_;
+  const CanonicalChannelNames canonical_channels_;
   foxglove::websocket::Server server_;
   // A map of fetchers for every single channel that could be subscribed to.
   std::map<ChannelId, FetcherState> fetchers_;
diff --git a/aos/util/mcap_logger.cc b/aos/util/mcap_logger.cc
index 40e55f0..111d784 100644
--- a/aos/util/mcap_logger.cc
+++ b/aos/util/mcap_logger.cc
@@ -84,6 +84,21 @@
   return schema;
 }
 
+std::string ShortenedChannelName(const aos::Configuration *config,
+                                 const aos::Channel *channel,
+                                 std::string_view application_name,
+                                 const aos::Node *node) {
+  std::set<std::string> names =
+      configuration::GetChannelAliases(config, channel, application_name, node);
+  std::string_view shortest_name;
+  for (const std::string &name : names) {
+    if (shortest_name.empty() || name.size() < shortest_name.size()) {
+      shortest_name = name;
+    }
+  }
+  return std::string(shortest_name);
+}
+
 namespace {
 std::string_view CompressionName(McapLogger::Compression compression) {
   switch (compression) {
@@ -354,15 +369,9 @@
                                   channel->type()->string_view());
         break;
       case CanonicalChannelNames::kShortened: {
-        std::set<std::string> names = configuration::GetChannelAliases(
-            event_loop_->configuration(), channel, event_loop_->name(),
-            event_loop_->node());
-        std::string_view shortest_name;
-        for (const std::string &name : names) {
-          if (shortest_name.empty() || name.size() < shortest_name.size()) {
-            shortest_name = name;
-          }
-        }
+        const std::string shortest_name =
+            ShortenedChannelName(event_loop_->configuration(), channel,
+                                 event_loop_->name(), event_loop_->node());
         if (shortest_name != channel->name()->string_view()) {
           VLOG(1) << "Shortening " << channel->name()->string_view() << " "
                   << channel->type()->string_view() << " to " << shortest_name;
diff --git a/aos/util/mcap_logger.h b/aos/util/mcap_logger.h
index c3fdda1..50e9300 100644
--- a/aos/util/mcap_logger.h
+++ b/aos/util/mcap_logger.h
@@ -24,6 +24,13 @@
     const FlatbufferType &type,
     JsonSchemaRecursion recursion_level = JsonSchemaRecursion::kTopLevel);
 
+// Returns the shortest possible alias for the specified channel on the
+// specified node/application.
+std::string ShortenedChannelName(const aos::Configuration *config,
+                                 const aos::Channel *channel,
+                                 std::string_view application_name,
+                                 const aos::Node *node);
+
 // Generates an MCAP file, per the specification at
 // https://github.com/foxglove/mcap/tree/main/docs/specification
 // This currently generates an uncompressed logfile with full message indexing
diff --git a/aos/util/mcap_logger_test.cc b/aos/util/mcap_logger_test.cc
index 8bc3419..c6febf9 100644
--- a/aos/util/mcap_logger_test.cc
+++ b/aos/util/mcap_logger_test.cc
@@ -10,7 +10,8 @@
 // will require writing an MCAP reader (or importing an existing one).
 
 // Confirm that the schema for the reflection.Schema table itself hasn't
-// changed. reflection.Schema should be a very stable type, so this should need
+// changed. reflection.Schema should be a very stable type, so this should
+// need
 // updating except when we change the JSON schema generation itself.
 TEST(JsonSchemaTest, ReflectionSchema) {
   std::string schema_json =