Add ability to get source aos::Node* from a Channel*

This makes it a lot easier to index by Node given a channel.  And this
should be shared code anyways, not in an application.

Change-Id: Ia176e58f91a58a718b3eca8edd7ceb3bd36e441d
Signed-off-by: Austin Schuh <austin.schuh@bluerivertech.com>
Signed-off-by: James Kuszmaul <james.kuszmaul@bluerivertech.com>
diff --git a/aos/configuration.cc b/aos/configuration.cc
index 4de5332..3b50ab4 100644
--- a/aos/configuration.cc
+++ b/aos/configuration.cc
@@ -1636,6 +1636,13 @@
   return nullptr;
 }
 
+const Node *SourceNode(const Configuration *config, const Channel *channel) {
+  if (!MultiNode(config)) {
+    return nullptr;
+  }
+  return GetNode(config, channel->source_node()->string_view());
+}
+
 std::vector<size_t> SourceNodeIndex(const Configuration *config) {
   CHECK(config->has_channels());
   std::vector<size_t> result;
diff --git a/aos/configuration.h b/aos/configuration.h
index 7ef2e6e..80c0238 100644
--- a/aos/configuration.h
+++ b/aos/configuration.h
@@ -198,6 +198,9 @@
 // Returns the source node index for each channel in a config.
 std::vector<size_t> SourceNodeIndex(const Configuration *config);
 
+// Returns the source node for a channel.
+const Node *SourceNode(const Configuration *config, const Channel *channel);
+
 // Returns the all nodes that are logging timestamps on our node.
 std::vector<const Node *> TimestampNodes(const Configuration *config,
                                          const Node *my_node);
diff --git a/aos/configuration_test.cc b/aos/configuration_test.cc
index eb08d78..0ce6772 100644
--- a/aos/configuration_test.cc
+++ b/aos/configuration_test.cc
@@ -1022,6 +1022,38 @@
   EXPECT_THAT(result, ::testing::ElementsAreArray({0, 1, 0, 0}));
 }
 
+// Tests that SourceNode reasonably handles both single and multi-node configs.
+TEST_F(ConfigurationTest, SourceNode) {
+  {
+    FlatbufferDetachedBuffer<Configuration> config_single_node =
+        ReadConfig(ArtifactPath("aos/testdata/config1.json"));
+    const Node *result =
+        SourceNode(&config_single_node.message(),
+                   config_single_node.message().channels()->Get(0));
+    EXPECT_EQ(result, nullptr);
+  }
+
+  {
+    FlatbufferDetachedBuffer<Configuration> config_multi_node =
+        ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
+    size_t pi1_channels = 0;
+    size_t pi2_channels = 0;
+    for (const aos::Channel *channel :
+         *config_multi_node.message().channels()) {
+      const Node *result = SourceNode(&config_multi_node.message(), channel);
+      if (channel->source_node()->string_view() == "pi1") {
+        ++pi1_channels;
+        EXPECT_EQ(result, config_multi_node.message().nodes()->Get(0));
+      } else {
+        ++pi2_channels;
+        EXPECT_EQ(result, config_multi_node.message().nodes()->Get(1));
+      }
+    }
+    EXPECT_GT(pi1_channels, 0u);
+    EXPECT_GT(pi2_channels, 0u);
+  }
+}
+
 // Tests that we reject invalid logging configurations.
 TEST_F(ConfigurationDeathTest, InvalidLoggerConfig) {
   EXPECT_DEATH(