blob: 65c218e96b08eb01bed22269836ccb25994c8eaa [file] [log] [blame]
John Park398c74a2018-10-20 21:17:39 -07001#include "aos/configuration.h"
Brian Silverman66f079a2013-08-26 16:24:30 -07002
3#include <string.h>
Brian Silverman66f079a2013-08-26 16:24:30 -07004#include <stdlib.h>
5#include <sys/types.h>
6#include <netinet/in.h>
7#include <arpa/inet.h>
8#include <ifaddrs.h>
9#include <unistd.h>
James Kuszmaul3ae42262019-11-08 12:33:41 -080010#include <string_view>
Brian Silverman66f079a2013-08-26 16:24:30 -070011
Austin Schuhcb108412019-10-13 16:09:54 -070012#include "absl/container/btree_set.h"
Austin Schuhcb108412019-10-13 16:09:54 -070013#include "aos/configuration_generated.h"
14#include "aos/flatbuffer_merge.h"
15#include "aos/json_to_flatbuffer.h"
Austin Schuh217a9782019-12-21 23:02:50 -080016#include "aos/network/team_number.h"
Austin Schuhcb108412019-10-13 16:09:54 -070017#include "aos/unique_malloc_ptr.h"
18#include "aos/util/file.h"
Austin Schuh217a9782019-12-21 23:02:50 -080019#include "gflags/gflags.h"
Austin Schuhcb108412019-10-13 16:09:54 -070020#include "glog/logging.h"
Austin Schuh217a9782019-12-21 23:02:50 -080021
22DEFINE_string(
23 override_hostname, "",
24 "If set, this forces the hostname of this node to be the provided "
25 "hostname.");
Brian Silverman66f079a2013-08-26 16:24:30 -070026
27namespace aos {
Austin Schuhcb108412019-10-13 16:09:54 -070028
Austin Schuh40485ed2019-10-26 21:51:44 -070029// Define the compare and equal operators for Channel and Application so we can
Austin Schuhcb108412019-10-13 16:09:54 -070030// insert them in the btree below.
Austin Schuh40485ed2019-10-26 21:51:44 -070031bool operator<(const FlatbufferDetachedBuffer<Channel> &lhs,
32 const FlatbufferDetachedBuffer<Channel> &rhs) {
Austin Schuhcb108412019-10-13 16:09:54 -070033 int name_compare = lhs.message().name()->string_view().compare(
34 rhs.message().name()->string_view());
35 if (name_compare == 0) {
36 return lhs.message().type()->string_view() <
37 rhs.message().type()->string_view();
38 } else if (name_compare < 0) {
39 return true;
40 } else {
41 return false;
42 }
43}
44
Austin Schuh40485ed2019-10-26 21:51:44 -070045bool operator==(const FlatbufferDetachedBuffer<Channel> &lhs,
46 const FlatbufferDetachedBuffer<Channel> &rhs) {
Austin Schuhcb108412019-10-13 16:09:54 -070047 return lhs.message().name()->string_view() ==
48 rhs.message().name()->string_view() &&
49 lhs.message().type()->string_view() ==
50 rhs.message().type()->string_view();
51}
52
Austin Schuh40485ed2019-10-26 21:51:44 -070053bool operator==(const FlatbufferDetachedBuffer<Application> &lhs,
54 const FlatbufferDetachedBuffer<Application> &rhs) {
Austin Schuhcb108412019-10-13 16:09:54 -070055 return lhs.message().name()->string_view() ==
56 rhs.message().name()->string_view();
57}
58
Austin Schuh40485ed2019-10-26 21:51:44 -070059bool operator<(const FlatbufferDetachedBuffer<Application> &lhs,
60 const FlatbufferDetachedBuffer<Application> &rhs) {
Austin Schuhcb108412019-10-13 16:09:54 -070061 return lhs.message().name()->string_view() <
62 rhs.message().name()->string_view();
63}
64
Austin Schuh217a9782019-12-21 23:02:50 -080065bool operator==(const FlatbufferDetachedBuffer<Node> &lhs,
66 const FlatbufferDetachedBuffer<Node> &rhs) {
67 return lhs.message().name()->string_view() ==
68 rhs.message().name()->string_view();
69}
70
71bool operator<(const FlatbufferDetachedBuffer<Node> &lhs,
72 const FlatbufferDetachedBuffer<Node> &rhs) {
73 return lhs.message().name()->string_view() <
74 rhs.message().name()->string_view();
75}
76
Brian Silverman66f079a2013-08-26 16:24:30 -070077namespace configuration {
78namespace {
79
Austin Schuhcb108412019-10-13 16:09:54 -070080// Extracts the folder part of a path. Returns ./ if there is no path.
James Kuszmaul3ae42262019-11-08 12:33:41 -080081std::string_view ExtractFolder(
82 const std::string_view filename) {
Austin Schuhcb108412019-10-13 16:09:54 -070083 auto last_slash_pos = filename.find_last_of("/\\");
84
James Kuszmaul3ae42262019-11-08 12:33:41 -080085 return last_slash_pos == std::string_view::npos
86 ? std::string_view("./")
Austin Schuhcb108412019-10-13 16:09:54 -070087 : filename.substr(0, last_slash_pos + 1);
88}
89
Austin Schuh40485ed2019-10-26 21:51:44 -070090FlatbufferDetachedBuffer<Configuration> ReadConfig(
James Kuszmaul3ae42262019-11-08 12:33:41 -080091 const std::string_view path, absl::btree_set<std::string> *visited_paths) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070092 flatbuffers::DetachedBuffer buffer = JsonToFlatbuffer(
93 util::ReadFileToStringOrDie(path), ConfigurationTypeTable());
94
95 CHECK_GT(buffer.size(), 0u) << ": Failed to parse JSON file";
96
97 FlatbufferDetachedBuffer<Configuration> config(std::move(buffer));
Austin Schuhcb108412019-10-13 16:09:54 -070098 // Depth first. Take the following example:
99 //
100 // config1.json:
101 // {
Austin Schuh40485ed2019-10-26 21:51:44 -0700102 // "channels": [
Austin Schuhcb108412019-10-13 16:09:54 -0700103 // {
104 // "name": "/foo",
105 // "type": ".aos.bar",
106 // "max_size": 5
107 // }
108 // ],
109 // "imports": [
110 // "config2.json",
111 // ]
112 // }
113 //
114 // config2.json:
115 // {
Austin Schuh40485ed2019-10-26 21:51:44 -0700116 // "channels": [
Austin Schuhcb108412019-10-13 16:09:54 -0700117 // {
118 // "name": "/foo",
119 // "type": ".aos.bar",
120 // "max_size": 7
121 // }
122 // ],
123 // }
124 //
125 // We want the main config (config1.json) to be able to override the imported
126 // config. That means that it needs to be merged into the imported configs,
127 // not the other way around.
128
129 // Track that we have seen this file before recursing.
130 visited_paths->insert(::std::string(path));
131
132 if (config.message().has_imports()) {
133 // Capture the imports.
134 const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *v =
135 config.message().imports();
136
137 // And then wipe them. This gets GCed when we merge later.
138 config.mutable_message()->clear_imports();
139
140 // Start with an empty configuration to merge into.
Austin Schuh40485ed2019-10-26 21:51:44 -0700141 FlatbufferDetachedBuffer<Configuration> merged_config =
142 FlatbufferDetachedBuffer<Configuration>::Empty();
Austin Schuhcb108412019-10-13 16:09:54 -0700143
144 const ::std::string folder(ExtractFolder(path));
145
146 for (const flatbuffers::String *str : *v) {
147 const ::std::string included_config = folder + str->c_str();
148 // Abort on any paths we have already seen.
149 CHECK(visited_paths->find(included_config) == visited_paths->end())
150 << ": Found duplicate file " << included_config << " while reading "
151 << path;
152
153 // And them merge everything in.
154 merged_config = MergeFlatBuffers(
155 merged_config, ReadConfig(included_config, visited_paths));
156 }
157
158 // Finally, merge this file in.
159 config = MergeFlatBuffers(merged_config, config);
160 }
161 return config;
162}
163
Alex Perrycb7da4b2019-08-28 19:35:56 -0700164// Compares (c < p) a channel, and a name, type tuple.
165bool CompareChannels(const Channel *c,
James Kuszmaul3ae42262019-11-08 12:33:41 -0800166 ::std::pair<std::string_view, std::string_view> p) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700167 int name_compare = c->name()->string_view().compare(p.first);
168 if (name_compare == 0) {
169 return c->type()->string_view() < p.second;
170 } else if (name_compare < 0) {
171 return true;
172 } else {
173 return false;
174 }
175};
176
177// Compares for equality (c == p) a channel, and a name, type tuple.
178bool EqualsChannels(const Channel *c,
James Kuszmaul3ae42262019-11-08 12:33:41 -0800179 ::std::pair<std::string_view, std::string_view> p) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700180 return c->name()->string_view() == p.first &&
181 c->type()->string_view() == p.second;
182}
183
184// Compares (c < p) an application, and a name;
James Kuszmaul3ae42262019-11-08 12:33:41 -0800185bool CompareApplications(const Application *a, std::string_view name) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700186 return a->name()->string_view() < name;
187};
188
189// Compares for equality (c == p) an application, and a name;
James Kuszmaul3ae42262019-11-08 12:33:41 -0800190bool EqualsApplications(const Application *a, std::string_view name) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700191 return a->name()->string_view() == name;
192}
193
194// Maps name for the provided maps. Modifies name.
195void HandleMaps(const flatbuffers::Vector<flatbuffers::Offset<aos::Map>> *maps,
Austin Schuhbca6cf02019-12-22 17:28:34 -0800196 std::string_view *name, std::string_view type,
197 const Node *node) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700198 // For the same reason we merge configs in reverse order, we want to process
199 // maps in reverse order. That lets the outer config overwrite channels from
200 // the inner configs.
201 for (auto i = maps->rbegin(); i != maps->rend(); ++i) {
Austin Schuhbca6cf02019-12-22 17:28:34 -0800202 if (!i->has_match() || !i->match()->has_name()) {
203 continue;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700204 }
Austin Schuhbca6cf02019-12-22 17:28:34 -0800205 if (!i->has_rename() || !i->rename()->has_name()) {
206 continue;
207 }
208
209 // Handle normal maps (now that we know that match and rename are filled
210 // out).
211 if (i->match()->name()->string_view() != *name) {
212 continue;
213 }
214
215 // Handle type specific maps.
216 if (i->match()->has_type() && i->match()->type()->string_view() != type) {
217 continue;
218 }
219
220 if (node != nullptr && i->match()->has_source_node() &&
221 i->match()->source_node()->string_view() !=
222 node->name()->string_view()) {
223 continue;
224 }
225
226 VLOG(1) << "Renamed \"" << *name << "\" to \""
227 << i->rename()->name()->string_view() << "\"";
228 *name = i->rename()->name()->string_view();
Alex Perrycb7da4b2019-08-28 19:35:56 -0700229 }
230}
231
232} // namespace
233
Austin Schuh40485ed2019-10-26 21:51:44 -0700234FlatbufferDetachedBuffer<Configuration> MergeConfiguration(
Austin Schuhcb108412019-10-13 16:09:54 -0700235 const Flatbuffer<Configuration> &config) {
Austin Schuh40485ed2019-10-26 21:51:44 -0700236 // Store all the channels in a sorted set. This lets us track channels we
Austin Schuhcb108412019-10-13 16:09:54 -0700237 // have seen before and merge the updates in.
Austin Schuh40485ed2019-10-26 21:51:44 -0700238 absl::btree_set<FlatbufferDetachedBuffer<Channel>> channels;
Austin Schuhcb108412019-10-13 16:09:54 -0700239
Austin Schuh40485ed2019-10-26 21:51:44 -0700240 if (config.message().has_channels()) {
241 for (const Channel *c : *config.message().channels()) {
Austin Schuhcb108412019-10-13 16:09:54 -0700242 // Ignore malformed entries.
Austin Schuh40485ed2019-10-26 21:51:44 -0700243 if (!c->has_name()) {
Austin Schuhcb108412019-10-13 16:09:54 -0700244 continue;
245 }
Austin Schuh40485ed2019-10-26 21:51:44 -0700246 if (!c->has_type()) {
Austin Schuhcb108412019-10-13 16:09:54 -0700247 continue;
248 }
249
Austin Schuh40485ed2019-10-26 21:51:44 -0700250 // Attempt to insert the channel.
251 auto result = channels.insert(CopyFlatBuffer(c));
Austin Schuhcb108412019-10-13 16:09:54 -0700252 if (!result.second) {
253 // Already there, so merge the new table into the original.
Austin Schuh40485ed2019-10-26 21:51:44 -0700254 *result.first = MergeFlatBuffers(*result.first, CopyFlatBuffer(c));
Austin Schuhcb108412019-10-13 16:09:54 -0700255 }
256 }
257 }
258
259 // Now repeat this for the application list.
Austin Schuh40485ed2019-10-26 21:51:44 -0700260 absl::btree_set<FlatbufferDetachedBuffer<Application>> applications;
Austin Schuhcb108412019-10-13 16:09:54 -0700261 if (config.message().has_applications()) {
262 for (const Application *a : *config.message().applications()) {
263 if (!a->has_name()) {
264 continue;
265 }
266
267 auto result = applications.insert(CopyFlatBuffer(a));
268 if (!result.second) {
269 *result.first = MergeFlatBuffers(*result.first, CopyFlatBuffer(a));
270 }
271 }
272 }
273
Austin Schuh217a9782019-12-21 23:02:50 -0800274 // Now repeat this for the node list.
275 absl::btree_set<FlatbufferDetachedBuffer<Node>> nodes;
276 if (config.message().has_nodes()) {
277 for (const Node *n : *config.message().nodes()) {
278 if (!n->has_name()) {
279 continue;
280 }
281
282 auto result = nodes.insert(CopyFlatBuffer(n));
283 if (!result.second) {
284 *result.first = MergeFlatBuffers(*result.first, CopyFlatBuffer(n));
285 }
286 }
287 }
288
Austin Schuhcb108412019-10-13 16:09:54 -0700289 flatbuffers::FlatBufferBuilder fbb;
290 fbb.ForceDefaults(1);
291
292 // Start by building the vectors. They need to come before the final table.
Austin Schuh40485ed2019-10-26 21:51:44 -0700293 // Channels
294 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Channel>>>
295 channels_offset;
Austin Schuhcb108412019-10-13 16:09:54 -0700296 {
Austin Schuh40485ed2019-10-26 21:51:44 -0700297 ::std::vector<flatbuffers::Offset<Channel>> channel_offsets;
298 for (const FlatbufferDetachedBuffer<Channel> &c : channels) {
299 channel_offsets.emplace_back(
300 CopyFlatBuffer<Channel>(&c.message(), &fbb));
Austin Schuhcb108412019-10-13 16:09:54 -0700301 }
Austin Schuh40485ed2019-10-26 21:51:44 -0700302 channels_offset = fbb.CreateVector(channel_offsets);
Austin Schuhcb108412019-10-13 16:09:54 -0700303 }
304
305 // Applications
306 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Application>>>
307 applications_offset;
308 {
309 ::std::vector<flatbuffers::Offset<Application>> applications_offsets;
Austin Schuh40485ed2019-10-26 21:51:44 -0700310 for (const FlatbufferDetachedBuffer<Application> &a : applications) {
Austin Schuhcb108412019-10-13 16:09:54 -0700311 applications_offsets.emplace_back(
312 CopyFlatBuffer<Application>(&a.message(), &fbb));
313 }
314 applications_offset = fbb.CreateVector(applications_offsets);
315 }
316
317 // Just copy the maps
318 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Map>>>
319 maps_offset;
320 {
321 ::std::vector<flatbuffers::Offset<Map>> map_offsets;
322 if (config.message().has_maps()) {
323 for (const Map *m : *config.message().maps()) {
324 map_offsets.emplace_back(CopyFlatBuffer<Map>(m, &fbb));
325 }
326 maps_offset = fbb.CreateVector(map_offsets);
327 }
328 }
329
Austin Schuh217a9782019-12-21 23:02:50 -0800330 // Nodes
331 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Node>>>
332 nodes_offset;
333 {
334 ::std::vector<flatbuffers::Offset<Node>> node_offsets;
335 for (const FlatbufferDetachedBuffer<Node> &n : nodes) {
336 node_offsets.emplace_back(CopyFlatBuffer<Node>(&n.message(), &fbb));
337 }
338 nodes_offset = fbb.CreateVector(node_offsets);
339 }
340
Austin Schuhcb108412019-10-13 16:09:54 -0700341 // And then build a Configuration with them all.
342 ConfigurationBuilder configuration_builder(fbb);
Austin Schuh40485ed2019-10-26 21:51:44 -0700343 configuration_builder.add_channels(channels_offset);
Austin Schuhcb108412019-10-13 16:09:54 -0700344 if (config.message().has_maps()) {
345 configuration_builder.add_maps(maps_offset);
346 }
Austin Schuh217a9782019-12-21 23:02:50 -0800347 if (config.message().has_applications()) {
348 configuration_builder.add_applications(applications_offset);
349 }
350 if (config.message().has_nodes()) {
351 configuration_builder.add_nodes(nodes_offset);
352 }
Austin Schuhcb108412019-10-13 16:09:54 -0700353
354 fbb.Finish(configuration_builder.Finish());
Austin Schuh217a9782019-12-21 23:02:50 -0800355
356 // Now, validate that if there is a node list, every channel has a source
357 // node.
358 FlatbufferDetachedBuffer<Configuration> result(fbb.Release());
359
360 // Check that if there is a node list, all the source nodes are filled out and
361 // valid, and all the destination nodes are valid (and not the source). This
362 // is a basic consistency check.
363 if (result.message().has_nodes()) {
364 for (const Channel *c : *config.message().channels()) {
365 CHECK(c->has_source_node()) << ": Channel " << FlatbufferToJson(c)
366 << " is missing \"source_node\"";
367 CHECK(GetNode(&result.message(), c->source_node()->string_view()) !=
368 nullptr)
369 << ": Channel " << FlatbufferToJson(c)
370 << " has an unknown \"source_node\"";
371
372 if (c->has_destination_nodes()) {
Austin Schuh719946b2019-12-28 14:51:01 -0800373 for (const Connection *connection : *c->destination_nodes()) {
374 CHECK(connection->has_name());
375 CHECK(GetNode(&result.message(), connection->name()->string_view()) !=
376 nullptr)
Austin Schuh217a9782019-12-21 23:02:50 -0800377 << ": Channel " << FlatbufferToJson(c)
Austin Schuh719946b2019-12-28 14:51:01 -0800378 << " has an unknown \"destination_nodes\" "
379 << connection->name()->string_view();
Austin Schuh217a9782019-12-21 23:02:50 -0800380
Austin Schuh719946b2019-12-28 14:51:01 -0800381 switch (connection->timestamp_logger()) {
382 case LoggerConfig::LOCAL_LOGGER:
383 case LoggerConfig::NOT_LOGGED:
384 CHECK(!connection->has_timestamp_logger_node());
385 break;
386 case LoggerConfig::REMOTE_LOGGER:
387 case LoggerConfig::LOCAL_AND_REMOTE_LOGGER:
388 CHECK(connection->has_timestamp_logger_node());
389 CHECK(
390 GetNode(&result.message(),
391 connection->timestamp_logger_node()->string_view()) !=
392 nullptr)
393 << ": Channel " << FlatbufferToJson(c)
394 << " has an unknown \"timestamp_logger_node\""
395 << connection->name()->string_view();
396 break;
397 }
398
399 CHECK_NE(connection->name()->string_view(),
400 c->source_node()->string_view())
Austin Schuh217a9782019-12-21 23:02:50 -0800401 << ": Channel " << FlatbufferToJson(c)
402 << " is forwarding data to itself";
403 }
404 }
405 }
406 }
407
408 return result;
Austin Schuhcb108412019-10-13 16:09:54 -0700409}
410
Austin Schuh40485ed2019-10-26 21:51:44 -0700411FlatbufferDetachedBuffer<Configuration> ReadConfig(
James Kuszmaul3ae42262019-11-08 12:33:41 -0800412 const std::string_view path) {
Austin Schuhcb108412019-10-13 16:09:54 -0700413 // We only want to read a file once. So track the visited files in a set.
414 absl::btree_set<std::string> visited_paths;
415 return MergeConfiguration(ReadConfig(path, &visited_paths));
416}
417
James Kuszmaul3ae42262019-11-08 12:33:41 -0800418const Channel *GetChannel(const Configuration *config, std::string_view name,
419 std::string_view type,
Austin Schuhbca6cf02019-12-22 17:28:34 -0800420 std::string_view application_name, const Node *node) {
421 const std::string_view original_name = name;
Austin Schuhcb108412019-10-13 16:09:54 -0700422 VLOG(1) << "Looking up { \"name\": \"" << name << "\", \"type\": \"" << type
423 << "\" }";
424
425 // First handle application specific maps. Only do this if we have a matching
426 // application name, and it has maps.
Austin Schuh40485ed2019-10-26 21:51:44 -0700427 if (config->has_applications()) {
428 auto application_iterator = std::lower_bound(
429 config->applications()->cbegin(), config->applications()->cend(),
430 application_name, CompareApplications);
431 if (application_iterator != config->applications()->cend() &&
Austin Schuhcb108412019-10-13 16:09:54 -0700432 EqualsApplications(*application_iterator, application_name)) {
433 if (application_iterator->has_maps()) {
Austin Schuhbca6cf02019-12-22 17:28:34 -0800434 HandleMaps(application_iterator->maps(), &name, type, node);
Austin Schuhcb108412019-10-13 16:09:54 -0700435 }
436 }
437 }
438
439 // Now do global maps.
Austin Schuh40485ed2019-10-26 21:51:44 -0700440 if (config->has_maps()) {
Austin Schuhbca6cf02019-12-22 17:28:34 -0800441 HandleMaps(config->maps(), &name, type, node);
Austin Schuhcb108412019-10-13 16:09:54 -0700442 }
443
Austin Schuhbca6cf02019-12-22 17:28:34 -0800444 if (original_name != name) {
445 VLOG(1) << "Remapped to { \"name\": \"" << name << "\", \"type\": \""
446 << type << "\" }";
447 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700448
Austin Schuh40485ed2019-10-26 21:51:44 -0700449 // Then look for the channel.
450 auto channel_iterator =
451 std::lower_bound(config->channels()->cbegin(),
452 config->channels()->cend(),
453 std::make_pair(name, type), CompareChannels);
Austin Schuhcb108412019-10-13 16:09:54 -0700454
455 // Make sure we actually found it, and it matches.
Austin Schuh40485ed2019-10-26 21:51:44 -0700456 if (channel_iterator != config->channels()->cend() &&
457 EqualsChannels(*channel_iterator, std::make_pair(name, type))) {
Austin Schuhbca6cf02019-12-22 17:28:34 -0800458 if (VLOG_IS_ON(2)) {
459 VLOG(2) << "Found: " << FlatbufferToJson(*channel_iterator);
460 } else if (VLOG_IS_ON(1)) {
461 VLOG(1) << "Found: " << CleanedChannelToString(*channel_iterator);
462 }
Austin Schuh40485ed2019-10-26 21:51:44 -0700463 return *channel_iterator;
Austin Schuhcb108412019-10-13 16:09:54 -0700464 } else {
465 VLOG(1) << "No match for { \"name\": \"" << name << "\", \"type\": \""
466 << type << "\" }";
467 return nullptr;
468 }
469}
470
Austin Schuhbca6cf02019-12-22 17:28:34 -0800471std::string CleanedChannelToString(const Channel *channel) {
472 FlatbufferDetachedBuffer<Channel> cleaned_channel = CopyFlatBuffer(channel);
473 cleaned_channel.mutable_message()->clear_schema();
474 return FlatbufferToJson(cleaned_channel);
475}
476
Alex Perrycb7da4b2019-08-28 19:35:56 -0700477FlatbufferDetachedBuffer<Configuration> MergeConfiguration(
478 const Flatbuffer<Configuration> &config,
479 const std::vector<aos::FlatbufferString<reflection::Schema>> &schemas) {
480 flatbuffers::FlatBufferBuilder fbb;
481 fbb.ForceDefaults(1);
482
483 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Channel>>>
484 channels_offset;
485 if (config.message().has_channels()) {
486 std::vector<flatbuffers::Offset<Channel>> channel_offsets;
487 for (const Channel *c : *config.message().channels()) {
488 flatbuffers::FlatBufferBuilder channel_fbb;
489 channel_fbb.ForceDefaults(1);
490
491 // Search for a schema with a matching type.
492 const aos::FlatbufferString<reflection::Schema> *found_schema = nullptr;
493 for (const aos::FlatbufferString<reflection::Schema> &schema: schemas) {
494 if (schema.message().root_table() != nullptr) {
495 if (schema.message().root_table()->name()->string_view() ==
496 c->type()->string_view()) {
497 found_schema = &schema;
498 }
499 }
500 }
501
502 CHECK(found_schema != nullptr)
503 << ": Failed to find schema for " << FlatbufferToJson(c);
504
505 // The following is wasteful, but works.
506 //
507 // Copy it into a Channel object by creating an object with only the
508 // schema populated and merge that into the current channel.
509 flatbuffers::Offset<reflection::Schema> schema_offset =
510 CopyFlatBuffer<reflection::Schema>(&found_schema->message(),
511 &channel_fbb);
512 Channel::Builder channel_builder(channel_fbb);
513 channel_builder.add_schema(schema_offset);
514 channel_fbb.Finish(channel_builder.Finish());
515 FlatbufferDetachedBuffer<Channel> channel_schema_flatbuffer(
516 channel_fbb.Release());
517
518 FlatbufferDetachedBuffer<Channel> merged_channel(
519 MergeFlatBuffers(channel_schema_flatbuffer, CopyFlatBuffer(c)));
520
521 channel_offsets.emplace_back(
522 CopyFlatBuffer<Channel>(&merged_channel.message(), &fbb));
523 }
524 channels_offset = fbb.CreateVector(channel_offsets);
525 }
526
527 // Copy the applications and maps unmodified.
528 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Application>>>
529 applications_offset;
530 {
531 ::std::vector<flatbuffers::Offset<Application>> applications_offsets;
532 if (config.message().has_applications()) {
533 for (const Application *a : *config.message().applications()) {
534 applications_offsets.emplace_back(CopyFlatBuffer<Application>(a, &fbb));
535 }
536 }
537 applications_offset = fbb.CreateVector(applications_offsets);
538 }
539
540 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Map>>>
541 maps_offset;
542 {
543 ::std::vector<flatbuffers::Offset<Map>> map_offsets;
544 if (config.message().has_maps()) {
545 for (const Map *m : *config.message().maps()) {
546 map_offsets.emplace_back(CopyFlatBuffer<Map>(m, &fbb));
547 }
548 maps_offset = fbb.CreateVector(map_offsets);
549 }
550 }
551
Austin Schuh217a9782019-12-21 23:02:50 -0800552 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Node>>>
553 nodes_offset;
554 {
555 ::std::vector<flatbuffers::Offset<Node>> node_offsets;
556 if (config.message().has_nodes()) {
557 for (const Node *n : *config.message().nodes()) {
558 node_offsets.emplace_back(CopyFlatBuffer<Node>(n, &fbb));
559 }
560 nodes_offset = fbb.CreateVector(node_offsets);
561 }
562 }
563
564 // Now insert everything else in unmodified.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700565 ConfigurationBuilder configuration_builder(fbb);
566 if (config.message().has_channels()) {
567 configuration_builder.add_channels(channels_offset);
568 }
569 if (config.message().has_maps()) {
570 configuration_builder.add_maps(maps_offset);
571 }
572 if (config.message().has_applications()) {
573 configuration_builder.add_applications(applications_offset);
574 }
Austin Schuh217a9782019-12-21 23:02:50 -0800575 if (config.message().has_nodes()) {
576 configuration_builder.add_nodes(nodes_offset);
577 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700578
579 fbb.Finish(configuration_builder.Finish());
580 return fbb.Release();
581}
582
Austin Schuh217a9782019-12-21 23:02:50 -0800583const Node *GetNodeFromHostname(const Configuration *config,
584 std::string_view hostname) {
585 for (const Node *node : *config->nodes()) {
586 if (node->hostname()->string_view() == hostname) {
587 return node;
588 }
589 }
590 return nullptr;
591}
592const Node *GetMyNode(const Configuration *config) {
593 const std::string hostname = (FLAGS_override_hostname.size() > 0)
594 ? FLAGS_override_hostname
595 : network::GetHostname();
596 const Node *node = GetNodeFromHostname(config, hostname);
597 if (node != nullptr) return node;
598
599 LOG(FATAL) << "Unknown node for host: " << hostname
600 << ". Consider using --override_hostname if hostname detection "
601 "is wrong.";
602 return nullptr;
603}
604
605const Node *GetNode(const Configuration *config, std::string_view name) {
606 for (const Node *node : *config->nodes()) {
607 if (node->name()->string_view() == name) {
608 return node;
609 }
610 }
611 return nullptr;
612}
613
614bool ChannelIsSendableOnNode(const Channel *channel, const Node *node) {
Austin Schuhca4828c2019-12-28 14:21:35 -0800615 if (node == nullptr) {
616 return true;
617 }
Austin Schuh217a9782019-12-21 23:02:50 -0800618 return (channel->source_node()->string_view() == node->name()->string_view());
619}
620
621bool ChannelIsReadableOnNode(const Channel *channel, const Node *node) {
Austin Schuhca4828c2019-12-28 14:21:35 -0800622 if (node == nullptr) {
623 return true;
624 }
625
Austin Schuh217a9782019-12-21 23:02:50 -0800626 if (channel->source_node()->string_view() == node->name()->string_view()) {
627 return true;
628 }
629
630 if (!channel->has_destination_nodes()) {
631 return false;
632 }
633
Austin Schuh719946b2019-12-28 14:51:01 -0800634 for (const Connection *connection : *channel->destination_nodes()) {
635 CHECK(connection->has_name());
636 if (connection->name()->string_view() == node->name()->string_view()) {
Austin Schuh217a9782019-12-21 23:02:50 -0800637 return true;
638 }
639 }
640
641 return false;
642}
643
Austin Schuh719946b2019-12-28 14:51:01 -0800644bool ChannelMessageIsLoggedOnNode(const Channel *channel, const Node *node) {
645 switch(channel->logger()) {
646 case LoggerConfig::LOCAL_LOGGER:
647 if (node == nullptr) {
648 // Single node world. If there is a local logger, then we want to use
649 // it.
650 return true;
651 }
652 return channel->source_node()->string_view() ==
653 node->name()->string_view();
654 case LoggerConfig::REMOTE_LOGGER:
655 CHECK(channel->has_logger_node());
656
657 return channel->logger_node()->string_view() ==
658 CHECK_NOTNULL(node)->name()->string_view();
659 case LoggerConfig::LOCAL_AND_REMOTE_LOGGER:
660 CHECK(channel->has_logger_node());
661
662 if (channel->source_node()->string_view() ==
663 CHECK_NOTNULL(node)->name()->string_view()) {
664 return true;
665 }
666 if (channel->logger_node()->string_view() == node->name()->string_view()) {
667 return true;
668 }
669
670 return false;
671 case LoggerConfig::NOT_LOGGED:
672 return false;
673 }
674
675 LOG(FATAL) << "Unknown logger config " << static_cast<int>(channel->logger());
676}
677
678const Connection *ConnectionToNode(const Channel *channel, const Node *node) {
679 if (!channel->has_destination_nodes()) {
680 return nullptr;
681 }
682 for (const Connection *connection : *channel->destination_nodes()) {
683 if (connection->name()->string_view() == node->name()->string_view()) {
684 return connection;
685 }
686 }
687 return nullptr;
688}
689
690bool ConnectionDeliveryTimeIsLoggedOnNode(const Channel *channel,
691 const Node *node,
692 const Node *logger_node) {
693 const Connection *connection = ConnectionToNode(channel, node);
694 if (connection == nullptr) {
695 return false;
696 }
697 return ConnectionDeliveryTimeIsLoggedOnNode(connection, logger_node);
698}
699
700bool ConnectionDeliveryTimeIsLoggedOnNode(const Connection *connection,
701 const Node *node) {
702 switch (connection->timestamp_logger()) {
703 case LoggerConfig::LOCAL_AND_REMOTE_LOGGER:
704 CHECK(connection->has_timestamp_logger_node());
705 if (connection->name()->string_view() == node->name()->string_view()) {
706 return true;
707 }
708
709 if (connection->timestamp_logger_node()->string_view() ==
710 node->name()->string_view()) {
711 return true;
712 }
713
714 return false;
715 case LoggerConfig::LOCAL_LOGGER:
716 return connection->name()->string_view() == node->name()->string_view();
717 case LoggerConfig::REMOTE_LOGGER:
718 CHECK(connection->has_timestamp_logger_node());
719
720 return connection->timestamp_logger_node()->string_view() ==
721 node->name()->string_view();
722 case LoggerConfig::NOT_LOGGED:
723 return false;
724 }
725
726 LOG(FATAL) << "Unknown logger config "
727 << static_cast<int>(connection->timestamp_logger());
728}
729
Brian Silverman66f079a2013-08-26 16:24:30 -0700730} // namespace configuration
731} // namespace aos