blob: bcb67bac48b14c29c28cb3eae8b8b76c07159592 [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>
10
Austin Schuhcb108412019-10-13 16:09:54 -070011#include "absl/container/btree_set.h"
12#include "absl/strings/string_view.h"
13#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.
142absl::string_view ExtractFolder(const absl::string_view filename) {
143 auto last_slash_pos = filename.find_last_of("/\\");
144
145 return last_slash_pos == absl::string_view::npos
146 ? absl::string_view("./")
147 : filename.substr(0, last_slash_pos + 1);
148}
149
Austin Schuh40485ed2019-10-26 21:51:44 -0700150FlatbufferDetachedBuffer<Configuration> ReadConfig(
Austin Schuhcb108412019-10-13 16:09:54 -0700151 const absl::string_view path, absl::btree_set<std::string> *visited_paths) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700152 flatbuffers::DetachedBuffer buffer = JsonToFlatbuffer(
153 util::ReadFileToStringOrDie(path), ConfigurationTypeTable());
154
155 CHECK_GT(buffer.size(), 0u) << ": Failed to parse JSON file";
156
157 FlatbufferDetachedBuffer<Configuration> config(std::move(buffer));
Austin Schuhcb108412019-10-13 16:09:54 -0700158 // Depth first. Take the following example:
159 //
160 // config1.json:
161 // {
Austin Schuh40485ed2019-10-26 21:51:44 -0700162 // "channels": [
Austin Schuhcb108412019-10-13 16:09:54 -0700163 // {
164 // "name": "/foo",
165 // "type": ".aos.bar",
166 // "max_size": 5
167 // }
168 // ],
169 // "imports": [
170 // "config2.json",
171 // ]
172 // }
173 //
174 // config2.json:
175 // {
Austin Schuh40485ed2019-10-26 21:51:44 -0700176 // "channels": [
Austin Schuhcb108412019-10-13 16:09:54 -0700177 // {
178 // "name": "/foo",
179 // "type": ".aos.bar",
180 // "max_size": 7
181 // }
182 // ],
183 // }
184 //
185 // We want the main config (config1.json) to be able to override the imported
186 // config. That means that it needs to be merged into the imported configs,
187 // not the other way around.
188
189 // Track that we have seen this file before recursing.
190 visited_paths->insert(::std::string(path));
191
192 if (config.message().has_imports()) {
193 // Capture the imports.
194 const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *v =
195 config.message().imports();
196
197 // And then wipe them. This gets GCed when we merge later.
198 config.mutable_message()->clear_imports();
199
200 // Start with an empty configuration to merge into.
Austin Schuh40485ed2019-10-26 21:51:44 -0700201 FlatbufferDetachedBuffer<Configuration> merged_config =
202 FlatbufferDetachedBuffer<Configuration>::Empty();
Austin Schuhcb108412019-10-13 16:09:54 -0700203
204 const ::std::string folder(ExtractFolder(path));
205
206 for (const flatbuffers::String *str : *v) {
207 const ::std::string included_config = folder + str->c_str();
208 // Abort on any paths we have already seen.
209 CHECK(visited_paths->find(included_config) == visited_paths->end())
210 << ": Found duplicate file " << included_config << " while reading "
211 << path;
212
213 // And them merge everything in.
214 merged_config = MergeFlatBuffers(
215 merged_config, ReadConfig(included_config, visited_paths));
216 }
217
218 // Finally, merge this file in.
219 config = MergeFlatBuffers(merged_config, config);
220 }
221 return config;
222}
223
Alex Perrycb7da4b2019-08-28 19:35:56 -0700224// Compares (c < p) a channel, and a name, type tuple.
225bool CompareChannels(const Channel *c,
226 ::std::pair<absl::string_view, absl::string_view> p) {
227 int name_compare = c->name()->string_view().compare(p.first);
228 if (name_compare == 0) {
229 return c->type()->string_view() < p.second;
230 } else if (name_compare < 0) {
231 return true;
232 } else {
233 return false;
234 }
235};
236
237// Compares for equality (c == p) a channel, and a name, type tuple.
238bool EqualsChannels(const Channel *c,
239 ::std::pair<absl::string_view, absl::string_view> p) {
240 return c->name()->string_view() == p.first &&
241 c->type()->string_view() == p.second;
242}
243
244// Compares (c < p) an application, and a name;
245bool CompareApplications(const Application *a, absl::string_view name) {
246 return a->name()->string_view() < name;
247};
248
249// Compares for equality (c == p) an application, and a name;
250bool EqualsApplications(const Application *a, absl::string_view name) {
251 return a->name()->string_view() == name;
252}
253
254// Maps name for the provided maps. Modifies name.
255void HandleMaps(const flatbuffers::Vector<flatbuffers::Offset<aos::Map>> *maps,
256 absl::string_view *name) {
257 // For the same reason we merge configs in reverse order, we want to process
258 // maps in reverse order. That lets the outer config overwrite channels from
259 // the inner configs.
260 for (auto i = maps->rbegin(); i != maps->rend(); ++i) {
261 if (i->has_match() && i->match()->has_name() && i->has_rename() &&
262 i->rename()->has_name() && i->match()->name()->string_view() == *name) {
263 VLOG(1) << "Renamed \"" << *name << "\" to \""
264 << i->rename()->name()->string_view() << "\"";
265 *name = i->rename()->name()->string_view();
266 }
267 }
268}
269
270} // namespace
271
Austin Schuh40485ed2019-10-26 21:51:44 -0700272FlatbufferDetachedBuffer<Configuration> MergeConfiguration(
Austin Schuhcb108412019-10-13 16:09:54 -0700273 const Flatbuffer<Configuration> &config) {
Austin Schuh40485ed2019-10-26 21:51:44 -0700274 // Store all the channels in a sorted set. This lets us track channels we
Austin Schuhcb108412019-10-13 16:09:54 -0700275 // have seen before and merge the updates in.
Austin Schuh40485ed2019-10-26 21:51:44 -0700276 absl::btree_set<FlatbufferDetachedBuffer<Channel>> channels;
Austin Schuhcb108412019-10-13 16:09:54 -0700277
Austin Schuh40485ed2019-10-26 21:51:44 -0700278 if (config.message().has_channels()) {
279 for (const Channel *c : *config.message().channels()) {
Austin Schuhcb108412019-10-13 16:09:54 -0700280 // Ignore malformed entries.
Austin Schuh40485ed2019-10-26 21:51:44 -0700281 if (!c->has_name()) {
Austin Schuhcb108412019-10-13 16:09:54 -0700282 continue;
283 }
Austin Schuh40485ed2019-10-26 21:51:44 -0700284 if (!c->has_type()) {
Austin Schuhcb108412019-10-13 16:09:54 -0700285 continue;
286 }
287
Austin Schuh40485ed2019-10-26 21:51:44 -0700288 // Attempt to insert the channel.
289 auto result = channels.insert(CopyFlatBuffer(c));
Austin Schuhcb108412019-10-13 16:09:54 -0700290 if (!result.second) {
291 // Already there, so merge the new table into the original.
Austin Schuh40485ed2019-10-26 21:51:44 -0700292 *result.first = MergeFlatBuffers(*result.first, CopyFlatBuffer(c));
Austin Schuhcb108412019-10-13 16:09:54 -0700293 }
294 }
295 }
296
297 // Now repeat this for the application list.
Austin Schuh40485ed2019-10-26 21:51:44 -0700298 absl::btree_set<FlatbufferDetachedBuffer<Application>> applications;
Austin Schuhcb108412019-10-13 16:09:54 -0700299 if (config.message().has_applications()) {
300 for (const Application *a : *config.message().applications()) {
301 if (!a->has_name()) {
302 continue;
303 }
304
305 auto result = applications.insert(CopyFlatBuffer(a));
306 if (!result.second) {
307 *result.first = MergeFlatBuffers(*result.first, CopyFlatBuffer(a));
308 }
309 }
310 }
311
312 flatbuffers::FlatBufferBuilder fbb;
313 fbb.ForceDefaults(1);
314
315 // Start by building the vectors. They need to come before the final table.
Austin Schuh40485ed2019-10-26 21:51:44 -0700316 // Channels
317 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Channel>>>
318 channels_offset;
Austin Schuhcb108412019-10-13 16:09:54 -0700319 {
Austin Schuh40485ed2019-10-26 21:51:44 -0700320 ::std::vector<flatbuffers::Offset<Channel>> channel_offsets;
321 for (const FlatbufferDetachedBuffer<Channel> &c : channels) {
322 channel_offsets.emplace_back(
323 CopyFlatBuffer<Channel>(&c.message(), &fbb));
Austin Schuhcb108412019-10-13 16:09:54 -0700324 }
Austin Schuh40485ed2019-10-26 21:51:44 -0700325 channels_offset = fbb.CreateVector(channel_offsets);
Austin Schuhcb108412019-10-13 16:09:54 -0700326 }
327
328 // Applications
329 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Application>>>
330 applications_offset;
331 {
332 ::std::vector<flatbuffers::Offset<Application>> applications_offsets;
Austin Schuh40485ed2019-10-26 21:51:44 -0700333 for (const FlatbufferDetachedBuffer<Application> &a : applications) {
Austin Schuhcb108412019-10-13 16:09:54 -0700334 applications_offsets.emplace_back(
335 CopyFlatBuffer<Application>(&a.message(), &fbb));
336 }
337 applications_offset = fbb.CreateVector(applications_offsets);
338 }
339
340 // Just copy the maps
341 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Map>>>
342 maps_offset;
343 {
344 ::std::vector<flatbuffers::Offset<Map>> map_offsets;
345 if (config.message().has_maps()) {
346 for (const Map *m : *config.message().maps()) {
347 map_offsets.emplace_back(CopyFlatBuffer<Map>(m, &fbb));
348 }
349 maps_offset = fbb.CreateVector(map_offsets);
350 }
351 }
352
353 // And then build a Configuration with them all.
354 ConfigurationBuilder configuration_builder(fbb);
Austin Schuh40485ed2019-10-26 21:51:44 -0700355 configuration_builder.add_channels(channels_offset);
Austin Schuhcb108412019-10-13 16:09:54 -0700356 if (config.message().has_maps()) {
357 configuration_builder.add_maps(maps_offset);
358 }
359 configuration_builder.add_applications(applications_offset);
360
361 fbb.Finish(configuration_builder.Finish());
362 return fbb.Release();
363}
364
Brian Silverman66f079a2013-08-26 16:24:30 -0700365const char *GetRootDirectory() {
John Park7bca9812019-10-14 21:23:45 -0700366 static char* root_dir;// return value
367 static absl::once_flag once_;
368 absl::call_once(once_, DoGetRootDirectory, &root_dir);
369 return root_dir;
Brian Silverman66f079a2013-08-26 16:24:30 -0700370}
371
372const char *GetLoggingDirectory() {
John Park7bca9812019-10-14 21:23:45 -0700373 static char* retu;// return value
374 static absl::once_flag once_;
375 absl::call_once(once_, DoGetLoggingDirectory, &retu);
376 return retu;
Brian Silverman66f079a2013-08-26 16:24:30 -0700377}
378
379const in_addr &GetOwnIPAddress() {
John Park7bca9812019-10-14 21:23:45 -0700380 static in_addr retu;// return value
381 static absl::once_flag once_;
382 absl::call_once(once_, DoGetOwnIPAddress, &retu);
383 return retu;
Brian Silverman66f079a2013-08-26 16:24:30 -0700384}
385
Austin Schuh40485ed2019-10-26 21:51:44 -0700386FlatbufferDetachedBuffer<Configuration> ReadConfig(
387 const absl::string_view path) {
Austin Schuhcb108412019-10-13 16:09:54 -0700388 // We only want to read a file once. So track the visited files in a set.
389 absl::btree_set<std::string> visited_paths;
390 return MergeConfiguration(ReadConfig(path, &visited_paths));
391}
392
Austin Schuh40485ed2019-10-26 21:51:44 -0700393const Channel *GetChannel(const Configuration *config, absl::string_view name,
394 absl::string_view type,
395 absl::string_view application_name) {
Austin Schuhcb108412019-10-13 16:09:54 -0700396 VLOG(1) << "Looking up { \"name\": \"" << name << "\", \"type\": \"" << type
397 << "\" }";
398
399 // First handle application specific maps. Only do this if we have a matching
400 // application name, and it has maps.
Austin Schuh40485ed2019-10-26 21:51:44 -0700401 if (config->has_applications()) {
402 auto application_iterator = std::lower_bound(
403 config->applications()->cbegin(), config->applications()->cend(),
404 application_name, CompareApplications);
405 if (application_iterator != config->applications()->cend() &&
Austin Schuhcb108412019-10-13 16:09:54 -0700406 EqualsApplications(*application_iterator, application_name)) {
407 if (application_iterator->has_maps()) {
408 HandleMaps(application_iterator->maps(), &name);
409 }
410 }
411 }
412
413 // Now do global maps.
Austin Schuh40485ed2019-10-26 21:51:44 -0700414 if (config->has_maps()) {
415 HandleMaps(config->maps(), &name);
Austin Schuhcb108412019-10-13 16:09:54 -0700416 }
417
Alex Perrycb7da4b2019-08-28 19:35:56 -0700418 VLOG(1) << "Acutally looking up { \"name\": \"" << name << "\", \"type\": \""
419 << type << "\" }";
420
Austin Schuh40485ed2019-10-26 21:51:44 -0700421 // Then look for the channel.
422 auto channel_iterator =
423 std::lower_bound(config->channels()->cbegin(),
424 config->channels()->cend(),
425 std::make_pair(name, type), CompareChannels);
Austin Schuhcb108412019-10-13 16:09:54 -0700426
427 // Make sure we actually found it, and it matches.
Austin Schuh40485ed2019-10-26 21:51:44 -0700428 if (channel_iterator != config->channels()->cend() &&
429 EqualsChannels(*channel_iterator, std::make_pair(name, type))) {
430 VLOG(1) << "Found: " << FlatbufferToJson(*channel_iterator);
431 return *channel_iterator;
Austin Schuhcb108412019-10-13 16:09:54 -0700432 } else {
433 VLOG(1) << "No match for { \"name\": \"" << name << "\", \"type\": \""
434 << type << "\" }";
435 return nullptr;
436 }
437}
438
Alex Perrycb7da4b2019-08-28 19:35:56 -0700439FlatbufferDetachedBuffer<Configuration> MergeConfiguration(
440 const Flatbuffer<Configuration> &config,
441 const std::vector<aos::FlatbufferString<reflection::Schema>> &schemas) {
442 flatbuffers::FlatBufferBuilder fbb;
443 fbb.ForceDefaults(1);
444
445 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Channel>>>
446 channels_offset;
447 if (config.message().has_channels()) {
448 std::vector<flatbuffers::Offset<Channel>> channel_offsets;
449 for (const Channel *c : *config.message().channels()) {
450 flatbuffers::FlatBufferBuilder channel_fbb;
451 channel_fbb.ForceDefaults(1);
452
453 // Search for a schema with a matching type.
454 const aos::FlatbufferString<reflection::Schema> *found_schema = nullptr;
455 for (const aos::FlatbufferString<reflection::Schema> &schema: schemas) {
456 if (schema.message().root_table() != nullptr) {
457 if (schema.message().root_table()->name()->string_view() ==
458 c->type()->string_view()) {
459 found_schema = &schema;
460 }
461 }
462 }
463
464 CHECK(found_schema != nullptr)
465 << ": Failed to find schema for " << FlatbufferToJson(c);
466
467 // The following is wasteful, but works.
468 //
469 // Copy it into a Channel object by creating an object with only the
470 // schema populated and merge that into the current channel.
471 flatbuffers::Offset<reflection::Schema> schema_offset =
472 CopyFlatBuffer<reflection::Schema>(&found_schema->message(),
473 &channel_fbb);
474 Channel::Builder channel_builder(channel_fbb);
475 channel_builder.add_schema(schema_offset);
476 channel_fbb.Finish(channel_builder.Finish());
477 FlatbufferDetachedBuffer<Channel> channel_schema_flatbuffer(
478 channel_fbb.Release());
479
480 FlatbufferDetachedBuffer<Channel> merged_channel(
481 MergeFlatBuffers(channel_schema_flatbuffer, CopyFlatBuffer(c)));
482
483 channel_offsets.emplace_back(
484 CopyFlatBuffer<Channel>(&merged_channel.message(), &fbb));
485 }
486 channels_offset = fbb.CreateVector(channel_offsets);
487 }
488
489 // Copy the applications and maps unmodified.
490 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Application>>>
491 applications_offset;
492 {
493 ::std::vector<flatbuffers::Offset<Application>> applications_offsets;
494 if (config.message().has_applications()) {
495 for (const Application *a : *config.message().applications()) {
496 applications_offsets.emplace_back(CopyFlatBuffer<Application>(a, &fbb));
497 }
498 }
499 applications_offset = fbb.CreateVector(applications_offsets);
500 }
501
502 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Map>>>
503 maps_offset;
504 {
505 ::std::vector<flatbuffers::Offset<Map>> map_offsets;
506 if (config.message().has_maps()) {
507 for (const Map *m : *config.message().maps()) {
508 map_offsets.emplace_back(CopyFlatBuffer<Map>(m, &fbb));
509 }
510 maps_offset = fbb.CreateVector(map_offsets);
511 }
512 }
513
514 // Now insert eerything else in unmodified.
515 ConfigurationBuilder configuration_builder(fbb);
516 if (config.message().has_channels()) {
517 configuration_builder.add_channels(channels_offset);
518 }
519 if (config.message().has_maps()) {
520 configuration_builder.add_maps(maps_offset);
521 }
522 if (config.message().has_applications()) {
523 configuration_builder.add_applications(applications_offset);
524 }
525
526 fbb.Finish(configuration_builder.Finish());
527 return fbb.Release();
528}
529
Brian Silverman66f079a2013-08-26 16:24:30 -0700530} // namespace configuration
531} // namespace aos