blob: c7fa2a3aa8657f33165501242be52d4b5695f586 [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 Schuhcb108412019-10-13 16:09:54 -070016#include "aos/unique_malloc_ptr.h"
17#include "aos/util/file.h"
18#include "glog/logging.h"
John Park7bca9812019-10-14 21:23:45 -070019#include "absl/base/call_once.h"
Brian Silverman66f079a2013-08-26 16:24:30 -070020
21namespace aos {
Austin Schuhcb108412019-10-13 16:09:54 -070022
Austin Schuh40485ed2019-10-26 21:51:44 -070023// Define the compare and equal operators for Channel and Application so we can
Austin Schuhcb108412019-10-13 16:09:54 -070024// insert them in the btree below.
Austin Schuh40485ed2019-10-26 21:51:44 -070025bool operator<(const FlatbufferDetachedBuffer<Channel> &lhs,
26 const FlatbufferDetachedBuffer<Channel> &rhs) {
Austin Schuhcb108412019-10-13 16:09:54 -070027 int name_compare = lhs.message().name()->string_view().compare(
28 rhs.message().name()->string_view());
29 if (name_compare == 0) {
30 return lhs.message().type()->string_view() <
31 rhs.message().type()->string_view();
32 } else if (name_compare < 0) {
33 return true;
34 } else {
35 return false;
36 }
37}
38
Austin Schuh40485ed2019-10-26 21:51:44 -070039bool operator==(const FlatbufferDetachedBuffer<Channel> &lhs,
40 const FlatbufferDetachedBuffer<Channel> &rhs) {
Austin Schuhcb108412019-10-13 16:09:54 -070041 return lhs.message().name()->string_view() ==
42 rhs.message().name()->string_view() &&
43 lhs.message().type()->string_view() ==
44 rhs.message().type()->string_view();
45}
46
Austin Schuh40485ed2019-10-26 21:51:44 -070047bool operator==(const FlatbufferDetachedBuffer<Application> &lhs,
48 const FlatbufferDetachedBuffer<Application> &rhs) {
Austin Schuhcb108412019-10-13 16:09:54 -070049 return lhs.message().name()->string_view() ==
50 rhs.message().name()->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
Brian Silverman66f079a2013-08-26 16:24:30 -070059namespace configuration {
60namespace {
61
Austin Schuh629821e2014-02-10 21:18:27 -080062// TODO(brians): This shouldn't be necesary for running tests. Provide a way to
63// set the IP address when running tests from the test.
Brian Silverman14fd0fb2014-01-14 21:42:01 -080064const char *const kLinuxNetInterface = "eth0";
John Park7bca9812019-10-14 21:23:45 -070065
66void DoGetOwnIPAddress(in_addr *retu) {
Brian Silverman01be0002014-05-10 15:44:38 -070067 static const char *kOverrideVariable = "FRC971_IP_OVERRIDE";
68 const char *override_ip = getenv(kOverrideVariable);
69 if (override_ip != NULL) {
Austin Schuhcb108412019-10-13 16:09:54 -070070 LOG(INFO) << "Override IP is " << override_ip;
John Park7bca9812019-10-14 21:23:45 -070071 if (inet_aton(override_ip, retu) != 0) {
72 return;
joe3779d0c2014-02-15 19:41:22 -080073 } else {
Austin Schuhcb108412019-10-13 16:09:54 -070074 LOG(WARNING) << "error parsing " << kOverrideVariable << " value '"
75 << override_ip << "'";
joe3779d0c2014-02-15 19:41:22 -080076 }
Brian Silverman01be0002014-05-10 15:44:38 -070077 } else {
Austin Schuhcb108412019-10-13 16:09:54 -070078 LOG(INFO) << "Couldn't get environmental variable.";
Brian Silverman01be0002014-05-10 15:44:38 -070079 }
joe3779d0c2014-02-15 19:41:22 -080080
Brian Silverman66f079a2013-08-26 16:24:30 -070081 ifaddrs *addrs;
82 if (getifaddrs(&addrs) != 0) {
Austin Schuhcb108412019-10-13 16:09:54 -070083 PLOG(FATAL) << "getifaddrs(" << &addrs << ") failed";
Brian Silverman66f079a2013-08-26 16:24:30 -070084 }
85 // Smart pointers don't work very well for iterating through a linked list,
86 // but it does do a very nice job of making sure that addrs gets freed.
87 unique_c_ptr<ifaddrs, freeifaddrs> addrs_deleter(addrs);
88
Austin Schuh629821e2014-02-10 21:18:27 -080089 for (; addrs != nullptr; addrs = addrs->ifa_next) {
Brian Silverman6da04272014-05-18 18:47:48 -070090 // ifa_addr tends to be nullptr on CAN interfaces.
Austin Schuh629821e2014-02-10 21:18:27 -080091 if (addrs->ifa_addr != nullptr && addrs->ifa_addr->sa_family == AF_INET) {
Brian Silverman14fd0fb2014-01-14 21:42:01 -080092 if (strcmp(kLinuxNetInterface, addrs->ifa_name) == 0) {
John Park7bca9812019-10-14 21:23:45 -070093 *retu = reinterpret_cast<sockaddr_in *>(__builtin_assume_aligned(
Brian Silverman63cf2412013-11-17 05:44:36 -080094 addrs->ifa_addr, alignof(sockaddr_in)))->sin_addr;
John Park7bca9812019-10-14 21:23:45 -070095 return;
Brian Silverman66f079a2013-08-26 16:24:30 -070096 }
97 }
98 }
Austin Schuhcb108412019-10-13 16:09:54 -070099 LOG(FATAL) << "couldn't find an AF_INET interface named \""
100 << kLinuxNetInterface << "\"";
Brian Silverman66f079a2013-08-26 16:24:30 -0700101}
102
John Park7bca9812019-10-14 21:23:45 -0700103void DoGetRootDirectory(char** retu) {
Brian Silverman66f079a2013-08-26 16:24:30 -0700104 ssize_t size = 0;
John Park7bca9812019-10-14 21:23:45 -0700105 *retu = NULL;
Brian Silverman66f079a2013-08-26 16:24:30 -0700106 while (true) {
Brian Silverman66f079a2013-08-26 16:24:30 -0700107 size += 256;
John Park7bca9812019-10-14 21:23:45 -0700108 if (*retu != nullptr) delete *retu;
109 *retu = new char[size];
Brian Silverman66f079a2013-08-26 16:24:30 -0700110
John Park7bca9812019-10-14 21:23:45 -0700111 ssize_t ret = readlink("/proc/self/exe", *retu, size);
Brian Silverman66f079a2013-08-26 16:24:30 -0700112 if (ret < 0) {
113 if (ret != -1) {
Austin Schuhcb108412019-10-13 16:09:54 -0700114 LOG(WARNING) << "it returned " << ret << ", not -1";
Brian Silverman66f079a2013-08-26 16:24:30 -0700115 }
John Park7bca9812019-10-14 21:23:45 -0700116
117 PLOG(FATAL) << "readlink(\"/proc/self/exe\", " << *retu << ", " << size
Austin Schuhcb108412019-10-13 16:09:54 -0700118 << ") failed";
Brian Silverman66f079a2013-08-26 16:24:30 -0700119 }
120 if (ret < size) {
John Park7bca9812019-10-14 21:23:45 -0700121 void *last_slash = memrchr(*retu, '/', ret);
Brian Silverman66f079a2013-08-26 16:24:30 -0700122 if (last_slash == NULL) {
John Park7bca9812019-10-14 21:23:45 -0700123 *retu[ret] = '\0';
124 LOG(FATAL) << "couldn't find a '/' in \"" << *retu << "\"";
Brian Silverman66f079a2013-08-26 16:24:30 -0700125 }
126 *static_cast<char *>(last_slash) = '\0';
John Park7bca9812019-10-14 21:23:45 -0700127 LOG(INFO) << "got a root dir of \"" << *retu << "\"";
128 return;
Brian Silverman66f079a2013-08-26 16:24:30 -0700129 }
130 }
131}
132
John Park7bca9812019-10-14 21:23:45 -0700133void DoGetLoggingDirectory(char** retu) {
Brian Silverman66f079a2013-08-26 16:24:30 -0700134 static const char kSuffix[] = "/../../tmp/robot_logs";
135 const char *root = GetRootDirectory();
John Park7bca9812019-10-14 21:23:45 -0700136 *retu = new char[strlen(root) + sizeof(kSuffix)];
137 strcpy(*retu, root);
138 strcat(*retu, kSuffix);
Brian Silverman66f079a2013-08-26 16:24:30 -0700139}
140
Austin Schuhcb108412019-10-13 16:09:54 -0700141// Extracts the folder part of a path. Returns ./ if there is no path.
James Kuszmaul3ae42262019-11-08 12:33:41 -0800142std::string_view ExtractFolder(
143 const std::string_view filename) {
Austin Schuhcb108412019-10-13 16:09:54 -0700144 auto last_slash_pos = filename.find_last_of("/\\");
145
James Kuszmaul3ae42262019-11-08 12:33:41 -0800146 return last_slash_pos == std::string_view::npos
147 ? std::string_view("./")
Austin Schuhcb108412019-10-13 16:09:54 -0700148 : filename.substr(0, last_slash_pos + 1);
149}
150
Austin Schuh40485ed2019-10-26 21:51:44 -0700151FlatbufferDetachedBuffer<Configuration> ReadConfig(
James Kuszmaul3ae42262019-11-08 12:33:41 -0800152 const std::string_view path, absl::btree_set<std::string> *visited_paths) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700153 flatbuffers::DetachedBuffer buffer = JsonToFlatbuffer(
154 util::ReadFileToStringOrDie(path), ConfigurationTypeTable());
155
156 CHECK_GT(buffer.size(), 0u) << ": Failed to parse JSON file";
157
158 FlatbufferDetachedBuffer<Configuration> config(std::move(buffer));
Austin Schuhcb108412019-10-13 16:09:54 -0700159 // Depth first. Take the following example:
160 //
161 // config1.json:
162 // {
Austin Schuh40485ed2019-10-26 21:51:44 -0700163 // "channels": [
Austin Schuhcb108412019-10-13 16:09:54 -0700164 // {
165 // "name": "/foo",
166 // "type": ".aos.bar",
167 // "max_size": 5
168 // }
169 // ],
170 // "imports": [
171 // "config2.json",
172 // ]
173 // }
174 //
175 // config2.json:
176 // {
Austin Schuh40485ed2019-10-26 21:51:44 -0700177 // "channels": [
Austin Schuhcb108412019-10-13 16:09:54 -0700178 // {
179 // "name": "/foo",
180 // "type": ".aos.bar",
181 // "max_size": 7
182 // }
183 // ],
184 // }
185 //
186 // We want the main config (config1.json) to be able to override the imported
187 // config. That means that it needs to be merged into the imported configs,
188 // not the other way around.
189
190 // Track that we have seen this file before recursing.
191 visited_paths->insert(::std::string(path));
192
193 if (config.message().has_imports()) {
194 // Capture the imports.
195 const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *v =
196 config.message().imports();
197
198 // And then wipe them. This gets GCed when we merge later.
199 config.mutable_message()->clear_imports();
200
201 // Start with an empty configuration to merge into.
Austin Schuh40485ed2019-10-26 21:51:44 -0700202 FlatbufferDetachedBuffer<Configuration> merged_config =
203 FlatbufferDetachedBuffer<Configuration>::Empty();
Austin Schuhcb108412019-10-13 16:09:54 -0700204
205 const ::std::string folder(ExtractFolder(path));
206
207 for (const flatbuffers::String *str : *v) {
208 const ::std::string included_config = folder + str->c_str();
209 // Abort on any paths we have already seen.
210 CHECK(visited_paths->find(included_config) == visited_paths->end())
211 << ": Found duplicate file " << included_config << " while reading "
212 << path;
213
214 // And them merge everything in.
215 merged_config = MergeFlatBuffers(
216 merged_config, ReadConfig(included_config, visited_paths));
217 }
218
219 // Finally, merge this file in.
220 config = MergeFlatBuffers(merged_config, config);
221 }
222 return config;
223}
224
Alex Perrycb7da4b2019-08-28 19:35:56 -0700225// Compares (c < p) a channel, and a name, type tuple.
226bool CompareChannels(const Channel *c,
James Kuszmaul3ae42262019-11-08 12:33:41 -0800227 ::std::pair<std::string_view, std::string_view> p) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700228 int name_compare = c->name()->string_view().compare(p.first);
229 if (name_compare == 0) {
230 return c->type()->string_view() < p.second;
231 } else if (name_compare < 0) {
232 return true;
233 } else {
234 return false;
235 }
236};
237
238// Compares for equality (c == p) a channel, and a name, type tuple.
239bool EqualsChannels(const Channel *c,
James Kuszmaul3ae42262019-11-08 12:33:41 -0800240 ::std::pair<std::string_view, std::string_view> p) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700241 return c->name()->string_view() == p.first &&
242 c->type()->string_view() == p.second;
243}
244
245// Compares (c < p) an application, and a name;
James Kuszmaul3ae42262019-11-08 12:33:41 -0800246bool CompareApplications(const Application *a, std::string_view name) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700247 return a->name()->string_view() < name;
248};
249
250// Compares for equality (c == p) an application, and a name;
James Kuszmaul3ae42262019-11-08 12:33:41 -0800251bool EqualsApplications(const Application *a, std::string_view name) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700252 return a->name()->string_view() == name;
253}
254
255// Maps name for the provided maps. Modifies name.
256void HandleMaps(const flatbuffers::Vector<flatbuffers::Offset<aos::Map>> *maps,
James Kuszmaul3ae42262019-11-08 12:33:41 -0800257 std::string_view *name) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700258 // For the same reason we merge configs in reverse order, we want to process
259 // maps in reverse order. That lets the outer config overwrite channels from
260 // the inner configs.
261 for (auto i = maps->rbegin(); i != maps->rend(); ++i) {
262 if (i->has_match() && i->match()->has_name() && i->has_rename() &&
263 i->rename()->has_name() && i->match()->name()->string_view() == *name) {
264 VLOG(1) << "Renamed \"" << *name << "\" to \""
265 << i->rename()->name()->string_view() << "\"";
266 *name = i->rename()->name()->string_view();
267 }
268 }
269}
270
271} // namespace
272
Austin Schuh40485ed2019-10-26 21:51:44 -0700273FlatbufferDetachedBuffer<Configuration> MergeConfiguration(
Austin Schuhcb108412019-10-13 16:09:54 -0700274 const Flatbuffer<Configuration> &config) {
Austin Schuh40485ed2019-10-26 21:51:44 -0700275 // Store all the channels in a sorted set. This lets us track channels we
Austin Schuhcb108412019-10-13 16:09:54 -0700276 // have seen before and merge the updates in.
Austin Schuh40485ed2019-10-26 21:51:44 -0700277 absl::btree_set<FlatbufferDetachedBuffer<Channel>> channels;
Austin Schuhcb108412019-10-13 16:09:54 -0700278
Austin Schuh40485ed2019-10-26 21:51:44 -0700279 if (config.message().has_channels()) {
280 for (const Channel *c : *config.message().channels()) {
Austin Schuhcb108412019-10-13 16:09:54 -0700281 // Ignore malformed entries.
Austin Schuh40485ed2019-10-26 21:51:44 -0700282 if (!c->has_name()) {
Austin Schuhcb108412019-10-13 16:09:54 -0700283 continue;
284 }
Austin Schuh40485ed2019-10-26 21:51:44 -0700285 if (!c->has_type()) {
Austin Schuhcb108412019-10-13 16:09:54 -0700286 continue;
287 }
288
Austin Schuh40485ed2019-10-26 21:51:44 -0700289 // Attempt to insert the channel.
290 auto result = channels.insert(CopyFlatBuffer(c));
Austin Schuhcb108412019-10-13 16:09:54 -0700291 if (!result.second) {
292 // Already there, so merge the new table into the original.
Austin Schuh40485ed2019-10-26 21:51:44 -0700293 *result.first = MergeFlatBuffers(*result.first, CopyFlatBuffer(c));
Austin Schuhcb108412019-10-13 16:09:54 -0700294 }
295 }
296 }
297
298 // Now repeat this for the application list.
Austin Schuh40485ed2019-10-26 21:51:44 -0700299 absl::btree_set<FlatbufferDetachedBuffer<Application>> applications;
Austin Schuhcb108412019-10-13 16:09:54 -0700300 if (config.message().has_applications()) {
301 for (const Application *a : *config.message().applications()) {
302 if (!a->has_name()) {
303 continue;
304 }
305
306 auto result = applications.insert(CopyFlatBuffer(a));
307 if (!result.second) {
308 *result.first = MergeFlatBuffers(*result.first, CopyFlatBuffer(a));
309 }
310 }
311 }
312
313 flatbuffers::FlatBufferBuilder fbb;
314 fbb.ForceDefaults(1);
315
316 // Start by building the vectors. They need to come before the final table.
Austin Schuh40485ed2019-10-26 21:51:44 -0700317 // Channels
318 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Channel>>>
319 channels_offset;
Austin Schuhcb108412019-10-13 16:09:54 -0700320 {
Austin Schuh40485ed2019-10-26 21:51:44 -0700321 ::std::vector<flatbuffers::Offset<Channel>> channel_offsets;
322 for (const FlatbufferDetachedBuffer<Channel> &c : channels) {
323 channel_offsets.emplace_back(
324 CopyFlatBuffer<Channel>(&c.message(), &fbb));
Austin Schuhcb108412019-10-13 16:09:54 -0700325 }
Austin Schuh40485ed2019-10-26 21:51:44 -0700326 channels_offset = fbb.CreateVector(channel_offsets);
Austin Schuhcb108412019-10-13 16:09:54 -0700327 }
328
329 // Applications
330 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Application>>>
331 applications_offset;
332 {
333 ::std::vector<flatbuffers::Offset<Application>> applications_offsets;
Austin Schuh40485ed2019-10-26 21:51:44 -0700334 for (const FlatbufferDetachedBuffer<Application> &a : applications) {
Austin Schuhcb108412019-10-13 16:09:54 -0700335 applications_offsets.emplace_back(
336 CopyFlatBuffer<Application>(&a.message(), &fbb));
337 }
338 applications_offset = fbb.CreateVector(applications_offsets);
339 }
340
341 // Just copy the maps
342 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Map>>>
343 maps_offset;
344 {
345 ::std::vector<flatbuffers::Offset<Map>> map_offsets;
346 if (config.message().has_maps()) {
347 for (const Map *m : *config.message().maps()) {
348 map_offsets.emplace_back(CopyFlatBuffer<Map>(m, &fbb));
349 }
350 maps_offset = fbb.CreateVector(map_offsets);
351 }
352 }
353
354 // And then build a Configuration with them all.
355 ConfigurationBuilder configuration_builder(fbb);
Austin Schuh40485ed2019-10-26 21:51:44 -0700356 configuration_builder.add_channels(channels_offset);
Austin Schuhcb108412019-10-13 16:09:54 -0700357 if (config.message().has_maps()) {
358 configuration_builder.add_maps(maps_offset);
359 }
360 configuration_builder.add_applications(applications_offset);
361
362 fbb.Finish(configuration_builder.Finish());
363 return fbb.Release();
364}
365
Brian Silverman66f079a2013-08-26 16:24:30 -0700366const char *GetRootDirectory() {
John Park7bca9812019-10-14 21:23:45 -0700367 static char* root_dir;// return value
368 static absl::once_flag once_;
369 absl::call_once(once_, DoGetRootDirectory, &root_dir);
370 return root_dir;
Brian Silverman66f079a2013-08-26 16:24:30 -0700371}
372
373const char *GetLoggingDirectory() {
John Park7bca9812019-10-14 21:23:45 -0700374 static char* retu;// return value
375 static absl::once_flag once_;
376 absl::call_once(once_, DoGetLoggingDirectory, &retu);
377 return retu;
Brian Silverman66f079a2013-08-26 16:24:30 -0700378}
379
380const in_addr &GetOwnIPAddress() {
John Park7bca9812019-10-14 21:23:45 -0700381 static in_addr retu;// return value
382 static absl::once_flag once_;
383 absl::call_once(once_, DoGetOwnIPAddress, &retu);
384 return retu;
Brian Silverman66f079a2013-08-26 16:24:30 -0700385}
386
Austin Schuh40485ed2019-10-26 21:51:44 -0700387FlatbufferDetachedBuffer<Configuration> ReadConfig(
James Kuszmaul3ae42262019-11-08 12:33:41 -0800388 const std::string_view path) {
Austin Schuhcb108412019-10-13 16:09:54 -0700389 // We only want to read a file once. So track the visited files in a set.
390 absl::btree_set<std::string> visited_paths;
391 return MergeConfiguration(ReadConfig(path, &visited_paths));
392}
393
James Kuszmaul3ae42262019-11-08 12:33:41 -0800394const Channel *GetChannel(const Configuration *config, std::string_view name,
395 std::string_view type,
396 std::string_view application_name) {
Austin Schuhcb108412019-10-13 16:09:54 -0700397 VLOG(1) << "Looking up { \"name\": \"" << name << "\", \"type\": \"" << type
398 << "\" }";
399
400 // First handle application specific maps. Only do this if we have a matching
401 // application name, and it has maps.
Austin Schuh40485ed2019-10-26 21:51:44 -0700402 if (config->has_applications()) {
403 auto application_iterator = std::lower_bound(
404 config->applications()->cbegin(), config->applications()->cend(),
405 application_name, CompareApplications);
406 if (application_iterator != config->applications()->cend() &&
Austin Schuhcb108412019-10-13 16:09:54 -0700407 EqualsApplications(*application_iterator, application_name)) {
408 if (application_iterator->has_maps()) {
409 HandleMaps(application_iterator->maps(), &name);
410 }
411 }
412 }
413
414 // Now do global maps.
Austin Schuh40485ed2019-10-26 21:51:44 -0700415 if (config->has_maps()) {
416 HandleMaps(config->maps(), &name);
Austin Schuhcb108412019-10-13 16:09:54 -0700417 }
418
Alex Perrycb7da4b2019-08-28 19:35:56 -0700419 VLOG(1) << "Acutally looking up { \"name\": \"" << name << "\", \"type\": \""
420 << type << "\" }";
421
Austin Schuh40485ed2019-10-26 21:51:44 -0700422 // Then look for the channel.
423 auto channel_iterator =
424 std::lower_bound(config->channels()->cbegin(),
425 config->channels()->cend(),
426 std::make_pair(name, type), CompareChannels);
Austin Schuhcb108412019-10-13 16:09:54 -0700427
428 // Make sure we actually found it, and it matches.
Austin Schuh40485ed2019-10-26 21:51:44 -0700429 if (channel_iterator != config->channels()->cend() &&
430 EqualsChannels(*channel_iterator, std::make_pair(name, type))) {
431 VLOG(1) << "Found: " << FlatbufferToJson(*channel_iterator);
432 return *channel_iterator;
Austin Schuhcb108412019-10-13 16:09:54 -0700433 } else {
434 VLOG(1) << "No match for { \"name\": \"" << name << "\", \"type\": \""
435 << type << "\" }";
436 return nullptr;
437 }
438}
439
Alex Perrycb7da4b2019-08-28 19:35:56 -0700440FlatbufferDetachedBuffer<Configuration> MergeConfiguration(
441 const Flatbuffer<Configuration> &config,
442 const std::vector<aos::FlatbufferString<reflection::Schema>> &schemas) {
443 flatbuffers::FlatBufferBuilder fbb;
444 fbb.ForceDefaults(1);
445
446 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Channel>>>
447 channels_offset;
448 if (config.message().has_channels()) {
449 std::vector<flatbuffers::Offset<Channel>> channel_offsets;
450 for (const Channel *c : *config.message().channels()) {
451 flatbuffers::FlatBufferBuilder channel_fbb;
452 channel_fbb.ForceDefaults(1);
453
454 // Search for a schema with a matching type.
455 const aos::FlatbufferString<reflection::Schema> *found_schema = nullptr;
456 for (const aos::FlatbufferString<reflection::Schema> &schema: schemas) {
457 if (schema.message().root_table() != nullptr) {
458 if (schema.message().root_table()->name()->string_view() ==
459 c->type()->string_view()) {
460 found_schema = &schema;
461 }
462 }
463 }
464
465 CHECK(found_schema != nullptr)
466 << ": Failed to find schema for " << FlatbufferToJson(c);
467
468 // The following is wasteful, but works.
469 //
470 // Copy it into a Channel object by creating an object with only the
471 // schema populated and merge that into the current channel.
472 flatbuffers::Offset<reflection::Schema> schema_offset =
473 CopyFlatBuffer<reflection::Schema>(&found_schema->message(),
474 &channel_fbb);
475 Channel::Builder channel_builder(channel_fbb);
476 channel_builder.add_schema(schema_offset);
477 channel_fbb.Finish(channel_builder.Finish());
478 FlatbufferDetachedBuffer<Channel> channel_schema_flatbuffer(
479 channel_fbb.Release());
480
481 FlatbufferDetachedBuffer<Channel> merged_channel(
482 MergeFlatBuffers(channel_schema_flatbuffer, CopyFlatBuffer(c)));
483
484 channel_offsets.emplace_back(
485 CopyFlatBuffer<Channel>(&merged_channel.message(), &fbb));
486 }
487 channels_offset = fbb.CreateVector(channel_offsets);
488 }
489
490 // Copy the applications and maps unmodified.
491 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Application>>>
492 applications_offset;
493 {
494 ::std::vector<flatbuffers::Offset<Application>> applications_offsets;
495 if (config.message().has_applications()) {
496 for (const Application *a : *config.message().applications()) {
497 applications_offsets.emplace_back(CopyFlatBuffer<Application>(a, &fbb));
498 }
499 }
500 applications_offset = fbb.CreateVector(applications_offsets);
501 }
502
503 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Map>>>
504 maps_offset;
505 {
506 ::std::vector<flatbuffers::Offset<Map>> map_offsets;
507 if (config.message().has_maps()) {
508 for (const Map *m : *config.message().maps()) {
509 map_offsets.emplace_back(CopyFlatBuffer<Map>(m, &fbb));
510 }
511 maps_offset = fbb.CreateVector(map_offsets);
512 }
513 }
514
515 // Now insert eerything else in unmodified.
516 ConfigurationBuilder configuration_builder(fbb);
517 if (config.message().has_channels()) {
518 configuration_builder.add_channels(channels_offset);
519 }
520 if (config.message().has_maps()) {
521 configuration_builder.add_maps(maps_offset);
522 }
523 if (config.message().has_applications()) {
524 configuration_builder.add_applications(applications_offset);
525 }
526
527 fbb.Finish(configuration_builder.Finish());
528 return fbb.Release();
529}
530
Brian Silverman66f079a2013-08-26 16:24:30 -0700531} // namespace configuration
532} // namespace aos