Merge "Add a Primal-dual interior-point method solver"
diff --git a/aos/events/event_loop_param_test.cc b/aos/events/event_loop_param_test.cc
index d3d21fd..2076467 100644
--- a/aos/events/event_loop_param_test.cc
+++ b/aos/events/event_loop_param_test.cc
@@ -3254,12 +3254,12 @@
auto sender = event_loop->MakeSender<TestMessage>("/test");
- // We are sending messages at 1 kHz, so we will be sending too fast after
- // queue_size (1600) ms. After this, keep sending messages, and exactly a
- // channel storage duration (2s) after we send the first message we should
- // be able to successfully send a message.
+ // We are sending bunches of messages at 100 Hz, so we will be sending too
+ // fast after queue_size (800) ms. After this, keep sending messages, and
+ // exactly a channel storage duration (2s) after we send the first message we
+ // should be able to successfully send a message.
- const monotonic_clock::duration kInterval = std::chrono::milliseconds(1);
+ const std::chrono::milliseconds kInterval = std::chrono::milliseconds(10);
const monotonic_clock::duration channel_storage_duration =
std::chrono::nanoseconds(
event_loop->configuration()->channel_storage_duration());
@@ -3270,33 +3270,38 @@
auto start = monotonic_clock::min_time;
event_loop->AddPhasedLoop(
- [&](int) {
- const auto actual_err = SendTestMessage(sender);
- const bool done_waiting = (start != monotonic_clock::min_time &&
- sender.monotonic_sent_time() >=
- (start + channel_storage_duration));
- const auto expected_err =
- (msgs_sent < queue_size || done_waiting
- ? RawSender::Error::kOk
- : RawSender::Error::kMessagesSentTooFast);
+ [&](int elapsed_cycles) {
+ // The queue is setup for 800 messages/sec. We want to fill that up at
+ // a rate of 2000 messages/sec so we make sure we fill it up.
+ for (int i = 0; i < 2 * kInterval.count() * elapsed_cycles; ++i) {
+ const auto actual_err = SendTestMessage(sender);
+ const bool done_waiting = (start != monotonic_clock::min_time &&
+ sender.monotonic_sent_time() >=
+ (start + channel_storage_duration));
+ const auto expected_err =
+ (msgs_sent < queue_size || done_waiting
+ ? RawSender::Error::kOk
+ : RawSender::Error::kMessagesSentTooFast);
- if (start == monotonic_clock::min_time) {
- start = sender.monotonic_sent_time();
- }
+ if (start == monotonic_clock::min_time) {
+ start = sender.monotonic_sent_time();
+ }
- ASSERT_EQ(actual_err, expected_err);
- counter.Count(actual_err);
- msgs_sent++;
+ ASSERT_EQ(actual_err, expected_err);
+ counter.Count(actual_err);
+ msgs_sent++;
- EXPECT_EQ(counter.failures(),
- msgs_sent <= queue_size
- ? 0
- : (msgs_sent - queue_size) -
- (actual_err == RawSender::Error::kOk ? 1 : 0));
- EXPECT_EQ(counter.just_failed(), actual_err != RawSender::Error::kOk);
+ EXPECT_EQ(counter.failures(),
+ msgs_sent <= queue_size
+ ? 0
+ : (msgs_sent - queue_size) -
+ (actual_err == RawSender::Error::kOk ? 1 : 0));
+ EXPECT_EQ(counter.just_failed(), actual_err != RawSender::Error::kOk);
- if (done_waiting) {
- Exit();
+ if (done_waiting) {
+ Exit();
+ return;
+ }
}
},
kInterval);
diff --git a/aos/network/message_bridge_test_common.json b/aos/network/message_bridge_test_common.json
index dad1675..9bb0863 100644
--- a/aos/network/message_bridge_test_common.json
+++ b/aos/network/message_bridge_test_common.json
@@ -75,28 +75,33 @@
{
"name": "/pi1/aos/remote_timestamps/pi2/pi1/aos/aos-message_bridge-Timestamp",
"type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
"source_node": "pi1",
"frequency": 15
},
{
"name": "/pi2/aos/remote_timestamps/pi1/pi2/aos/aos-message_bridge-Timestamp",
"type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
"source_node": "pi2",
"frequency": 15
},
{
"name": "/pi1/aos/remote_timestamps/pi2/test/aos-examples-Ping",
"type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
"source_node": "pi1"
},
{
"name": "/pi2/aos/remote_timestamps/pi1/test/aos-examples-Pong",
"type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
"source_node": "pi2"
},
{
"name": "/pi1/aos/remote_timestamps/pi2/unreliable/aos-examples-Ping",
"type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
"source_node": "pi1"
},
{
diff --git a/aos/realtime.cc b/aos/realtime.cc
index 2f299e6..d08dd91 100644
--- a/aos/realtime.cc
+++ b/aos/realtime.cc
@@ -20,7 +20,7 @@
#include "glog/raw_logging.h"
DEFINE_bool(
- die_on_malloc, false,
+ die_on_malloc, true,
"If true, die when the application allocates memory in a RT section.");
DEFINE_bool(skip_realtime_scheduler, false,
"If true, skip changing the scheduler. Pretend that we changed "
diff --git a/aos/util/BUILD b/aos/util/BUILD
index 2f10a70..8ce96f0 100644
--- a/aos/util/BUILD
+++ b/aos/util/BUILD
@@ -1,10 +1,10 @@
load("@com_github_google_flatbuffers//:build_defs.bzl", "flatbuffer_cc_library")
load("//aos:flatbuffers.bzl", "cc_static_flatbuffer")
-load("config_validator_macro.bzl", "config_validator_rule")
+load("config_validator_macro.bzl", "config_validator_test")
package(default_visibility = ["//visibility:public"])
-config_validator_rule(
+config_validator_test(
name = "config_validator_test",
config = "//aos/events:pingpong_config",
)
@@ -499,17 +499,14 @@
],
)
-cc_binary(
+cc_library(
name = "config_validator",
testonly = True,
srcs = ["config_validator.cc"],
target_compatible_with = ["@platforms//os:linux"],
deps = [
- "//aos:init",
+ ":config_validator_lib",
"//aos:json_to_flatbuffer",
- "//aos/events:simulated_event_loop",
- "//aos/events/logging:log_reader",
- "//aos/events/logging:log_writer",
"//aos/testing:googletest",
"@com_github_gflags_gflags//:gflags",
"@com_github_google_glog//:glog",
@@ -528,3 +525,59 @@
"@com_github_gflags_gflags//:gflags",
],
)
+
+cc_library(
+ name = "simulation_logger",
+ srcs = ["simulation_logger.cc"],
+ hdrs = ["simulation_logger.h"],
+ deps = [
+ "//aos/events:simulated_event_loop",
+ "//aos/events/logging:log_writer",
+ ],
+)
+
+flatbuffer_cc_library(
+ name = "config_validator_config_fbs",
+ srcs = ["config_validator_config.fbs"],
+ target_compatible_with = ["@platforms//os:linux"],
+ visibility = ["//visibility:public"],
+)
+
+cc_library(
+ name = "config_validator_lib",
+ testonly = True,
+ srcs = ["config_validator_lib.cc"],
+ hdrs = ["config_validator_lib.h"],
+ deps = [
+ ":config_validator_config_fbs",
+ ":simulation_logger",
+ "//aos/events:simulated_event_loop",
+ "//aos/events/logging:log_reader",
+ "//aos/events/logging:log_writer",
+ "//aos/network:timestamp_channel",
+ "//aos/testing:tmpdir",
+ "@com_github_google_glog//:glog",
+ "@com_google_googletest//:gtest",
+ ],
+)
+
+cc_test(
+ name = "config_validator_lib_test",
+ srcs = ["config_validator_lib_test.cc"],
+ data = [
+ "//aos/util/test_data:multinode_common_logger",
+ "//aos/util/test_data:multinode_extraneous_timestamp",
+ "//aos/util/test_data:multinode_invalid_timestamp_logger_list",
+ "//aos/util/test_data:multinode_no_logged_timestamps",
+ "//aos/util/test_data:multinode_no_statistics",
+ "//aos/util/test_data:multinode_timestamp_typo",
+ "//aos/util/test_data:valid_multinode_config",
+ "//aos/util/test_data:valid_singlenode_config",
+ ],
+ deps = [
+ ":config_validator_lib",
+ "//aos:json_to_flatbuffer",
+ "//aos/testing:googletest",
+ "//aos/testing:path",
+ ],
+)
diff --git a/aos/util/config_validator.cc b/aos/util/config_validator.cc
index d5bd6ba..df21ba0 100644
--- a/aos/util/config_validator.cc
+++ b/aos/util/config_validator.cc
@@ -1,16 +1,9 @@
-#include <chrono>
-
-#include "aos/configuration.h"
-#include "aos/events/logging/log_reader.h"
-#include "aos/events/logging/log_writer.h"
-#include "aos/events/simulated_event_loop.h"
-#include "aos/init.h"
#include "aos/json_to_flatbuffer.h"
-#include "aos/network/team_number.h"
-#include "gflags/gflags.h"
-#include "gtest/gtest.h"
+#include "aos/util/config_validator_lib.h"
DEFINE_string(config, "", "Name of the config file to replay using.");
+DEFINE_string(validation_config, "{}",
+ "JSON config to use to validate the config.");
/* This binary is used to validate that all of the
needed remote timestamps channels are in the config
to log the timestamps.
@@ -26,9 +19,11 @@
const aos::FlatbufferDetachedBuffer<aos::Configuration> config =
aos::configuration::ReadConfig(FLAGS_config);
- aos::SimulatedEventLoopFactory factory(&config.message());
-
- factory.RunFor(std::chrono::seconds(1));
+ const aos::FlatbufferDetachedBuffer<aos::util::ConfigValidatorConfig>
+ validator_config =
+ aos::JsonToFlatbuffer<aos::util::ConfigValidatorConfig>(
+ FLAGS_validation_config);
+ aos::util::ConfigIsValid(&config.message(), &validator_config.message());
}
// TODO(milind): add more tests, the above one doesn't
diff --git a/aos/util/config_validator_config.fbs b/aos/util/config_validator_config.fbs
new file mode 100644
index 0000000..cda84b2
--- /dev/null
+++ b/aos/util/config_validator_config.fbs
@@ -0,0 +1,55 @@
+namespace aos.util;
+
+// This file defines a schema for what to validate when we run the
+// config_validator against an AOS config.
+// The primary purpose of this config is to allow the user to specify what
+// sets of nodes they expect to be able to log on so that we can validate the
+// logging configurations. In the future this may also include flags to indicate
+// how aggressively to do certain checks.
+//
+// This flatbuffer should not exist in serialized form anywhere, and so is
+// safe to modify in non-backwards-compatible ways.
+
+// Species a set of nodes that you should be able to combine the logs from and
+// subsequently replay. E.g., this allows you to write a check that says
+// "If you combine logs from pi2 & pi4, you should be able to replay data from
+// nodes pi2, pi4, and pi6"; or
+// "When logs from all nodes are combined, you should be able to replay data
+// for all nodes;" or
+// "Each node should log all the data needed to replay its own data"
+// (this would require muliple LoggerNodeSetValidation's).
+//
+// Each LoggerNodeSetValidation table represents a single set of logging nodes
+// that should be able to replay data on some number of other nodes. An empty
+// list of loggers or replay_nodes indicates "all nodes." The above examples
+// could then be represented by, e.g.:
+// "pi2 & pi4 -> pi2, pi4, & pi6":
+// {"loggers": ["pi2", "pi4"], "replay_nodes": ["pi2", "pi4", "pi6"]}
+// "all -> all": {"logger": [], "replay_nodes": []}
+// "each node -> itself": [
+// {"logger": ["pi1"], "replay_nodes": ["pi1"]},
+// {"logger": ["pi2"], "replay_nodes": ["pi2"]},
+// {"logger": ["pi3"], "replay_nodes": ["pi3"]},
+// {"logger": ["pi4"], "replay_nodes": ["pi4"]}]
+table LoggerNodeSetValidation {
+ loggers:[string] (id: 0);
+ replay_nodes:[string] (id: 1);
+}
+
+// This table specifies which
+table LoggingConfigValidation {
+ // If true, all channels should be logged by some valid set of loggers.
+ // Essentially, this is checking that no channels are configured to be
+ // NOT_LOGGED except for remote timestamp channels.
+ all_channels_logged:bool = true (id: 0);
+ // A list of all the sets of logger nodes that we care about. Typically this
+ // should at least include an entry that says that "logs from all nodes should
+ // combine to allow you to replay all nodes."
+ logger_sets:[LoggerNodeSetValidation] (id: 1);
+}
+
+table ConfigValidatorConfig {
+ logging:LoggingConfigValidation (id: 0);
+}
+
+root_type ConfigValidatorConfig;
diff --git a/aos/util/config_validator_lib.cc b/aos/util/config_validator_lib.cc
new file mode 100644
index 0000000..0f90ed6
--- /dev/null
+++ b/aos/util/config_validator_lib.cc
@@ -0,0 +1,292 @@
+#include "aos/util/config_validator_lib.h"
+
+#include <chrono>
+
+#include "aos/events/logging/log_reader.h"
+#include "aos/events/logging/log_writer.h"
+#include "aos/events/simulated_event_loop.h"
+#include "aos/network/remote_message_generated.h"
+#include "aos/network/timestamp_channel.h"
+#include "aos/testing/tmpdir.h"
+#include "aos/util/simulation_logger.h"
+
+DECLARE_bool(validate_timestamp_logger_nodes);
+
+namespace aos::util {
+
+namespace {
+void RunSimulationAndExit(const aos::Configuration *config) {
+ aos::SimulatedEventLoopFactory factory(config);
+
+ factory.RunFor(std::chrono::seconds(1));
+
+ std::exit(EXIT_SUCCESS);
+}
+
+// Checks if either the node is in the specified list of node names or if the
+// list is empty (in which case it is treated as matching all nodes).
+bool NodeInList(
+ const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *list,
+ const aos::Node *node) {
+ if (list == nullptr || list->size() == 0) {
+ return true;
+ }
+ for (const flatbuffers::String *name : *list) {
+ if (name->string_view() == node->name()->string_view()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+void ConfigIsValid(const aos::Configuration *config,
+ const ConfigValidatorConfig *validation_config) {
+ ASSERT_TRUE(config->has_channels())
+ << "An AOS config must have channels. If you have a valid use-case for "
+ "channels with no channels, please write a design proposal.";
+
+ // First, we do some sanity checks--these are likely to indicate a malformed
+ // config, and so catching them early with a clear error message is likely to
+ // help.
+
+ // The set of all channels that are required by the channels that are
+ // configured--these are the remote timestamp channels that *must* be present,
+ // and ideally there are no other channels present.
+ std::set<const Channel *> required_timestamp_channels;
+ // The set of all channels that *look* like remote timestamp channels. This
+ // may include channels that are improperly configured and thus have typos &
+ // aren't actually going to do anything at runtime.
+ std::set<const Channel *> configured_timestamp_channels;
+ bool validation_failed = false;
+ for (size_t channel_index = 0; channel_index < config->channels()->size();
+ ++channel_index) {
+ const aos::Channel *channel = config->channels()->Get(channel_index);
+ ASSERT_TRUE(channel->has_name()) << "All AOS channels must have a name.";
+ ASSERT_TRUE(channel->has_type()) << "All AOS channels must have a type.";
+
+ const bool channel_looks_like_remote_message_channel =
+ channel->type()->string_view() ==
+ message_bridge::RemoteMessage::GetFullyQualifiedName();
+
+ const bool check_for_not_logged_channels =
+ !validation_config->has_logging() ||
+ validation_config->logging()->all_channels_logged();
+ const bool channel_is_not_logged =
+ channel->logger() == aos::LoggerConfig::NOT_LOGGED;
+ if (check_for_not_logged_channels) {
+ if (channel_looks_like_remote_message_channel != channel_is_not_logged) {
+ LOG(WARNING)
+ << "Channel " << configuration::StrippedChannelToString(channel)
+ << " is " << EnumNameLoggerConfig(channel->logger()) << " but "
+ << (channel_looks_like_remote_message_channel ? "is" : "is not")
+ << " a remote timestamp channel. This is almost certainly wrong.";
+ validation_failed = true;
+ }
+ }
+
+ if (channel_looks_like_remote_message_channel) {
+ configured_timestamp_channels.insert(channel);
+ } else {
+ if (channel->has_destination_nodes()) {
+ // TODO(james): Technically the timestamp finder should receive a
+ // non-empty application name. However, there are no known users that
+ // care at this moment.
+ message_bridge::ChannelTimestampFinder timestamp_finder(
+ config, "",
+ configuration::GetNode(config,
+ channel->source_node()->string_view()));
+ for (const Connection *connection : *channel->destination_nodes()) {
+ switch (connection->timestamp_logger()) {
+ case LoggerConfig::NOT_LOGGED:
+ case LoggerConfig::LOCAL_LOGGER:
+ if (connection->has_timestamp_logger_nodes()) {
+ LOG(WARNING)
+ << "Connections that are "
+ << EnumNameLoggerConfig(connection->timestamp_logger())
+ << " should not have remote timestamp logger nodes "
+ "populated. This is for the connection to "
+ << connection->name()->string_view() << " on "
+ << configuration::StrippedChannelToString(channel);
+ validation_failed = true;
+ }
+ break;
+ case LoggerConfig::REMOTE_LOGGER:
+ case LoggerConfig::LOCAL_AND_REMOTE_LOGGER:
+ if (!connection->has_timestamp_logger_nodes() ||
+ connection->timestamp_logger_nodes()->size() != 1 ||
+ connection->timestamp_logger_nodes()->Get(0)->string_view() !=
+ channel->source_node()->string_view()) {
+ LOG(WARNING)
+ << "Connections that are "
+ << EnumNameLoggerConfig(connection->timestamp_logger())
+ << " should have exactly 1 remote timestamp logger node "
+ "populated, and that node should be the source_node ("
+ << channel->source_node()->string_view()
+ << "). This is for the connection to "
+ << connection->name()->string_view() << " on "
+ << configuration::StrippedChannelToString(channel);
+ validation_failed = true;
+ }
+ // TODO(james): This will be overly noisy, as it ends up
+ // CHECK-failing.
+ required_timestamp_channels.insert(CHECK_NOTNULL(
+ timestamp_finder.ForChannel(channel, connection)));
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Check that all of the things that look like timestamp channels are indeed
+ // required.
+ // Note: Because ForChannel() will die if a required channel is not present,
+ // we do not do a separate check that all the required channels exist.
+ for (const auto &channel : configured_timestamp_channels) {
+ if (required_timestamp_channels.count(channel) == 0) {
+ LOG(WARNING) << "Timestamp channel "
+ << configuration::StrippedChannelToString(channel)
+ << " was specified in the config but is not used.";
+ validation_failed = true;
+ }
+ }
+
+ if (validation_failed) {
+ FAIL() << "Remote timestamp linting failed.";
+ return;
+ }
+
+ // Because the most common way for simulation to fail involves it dying, force
+ // it to fail in a slightly more controlled manner.
+ ASSERT_EXIT(RunSimulationAndExit(config),
+ ::testing::ExitedWithCode(EXIT_SUCCESS), "");
+
+ if (!validation_config->has_logging() || !configuration::MultiNode(config)) {
+ return;
+ }
+
+ // We will run all the logger configs in two modes:
+ // 1) We don't send any data on any non-infrastructure channels; this confirms
+ // that the logs are readable in the absence of any user applications being
+ // present.
+ // 2) We confirm that we can generate a good logfile that actually has data
+ // on every channel (some checks in the LogReader may not get hit if there
+ // is no data on a given channel).
+ const std::string log_path = aos::testing::TestTmpDir() + "/logs/";
+ for (const bool send_data_on_channels : {false, true}) {
+ SCOPED_TRACE(send_data_on_channels);
+ for (const LoggerNodeSetValidation *logger_set :
+ *validation_config->logging()->logger_sets()) {
+ SCOPED_TRACE(aos::FlatbufferToJson(logger_set));
+ aos::SimulatedEventLoopFactory factory(config);
+ std::vector<std::unique_ptr<LoggerState>> loggers;
+ if (logger_set->has_loggers() && logger_set->loggers()->size() > 0) {
+ std::vector<std::string> logger_nodes;
+ for (const auto &node : *logger_set->loggers()) {
+ logger_nodes.push_back(node->str());
+ }
+ loggers = MakeLoggersForNodes(&factory, logger_nodes, log_path);
+ } else {
+ loggers = MakeLoggersForAllNodes(&factory, log_path);
+ }
+
+ std::vector<std::unique_ptr<EventLoop>> test_loops;
+ std::map<std::string, std::vector<std::unique_ptr<RawSender>>>
+ test_senders;
+
+ if (send_data_on_channels) {
+ // Make a sender on every non-infrastructure channel on every node
+ // (including channels that may not be observable by the current logger
+ // set).
+ for (const aos::Node *node : configuration::GetNodes(config)) {
+ test_loops.emplace_back(factory.MakeEventLoop("", node));
+ for (const aos::Channel *channel : *config->channels()) {
+ // TODO(james): Make a more sophisticated check for "infrastructure"
+ // channels than just looking for a "/aos" in the channel--we don't
+ // accidentally want to spam nonsense data onto any timestamp
+ // channels, though.
+ if (configuration::ChannelIsSendableOnNode(channel, node) &&
+ channel->name()->str().find("/aos") == std::string::npos &&
+ channel->logger() != LoggerConfig::NOT_LOGGED) {
+ test_senders[node->name()->str()].emplace_back(
+ test_loops.back()->MakeRawSender(channel));
+ RawSender *sender =
+ test_senders[node->name()->str()].back().get();
+ test_loops.back()->OnRun([sender, channel]() {
+ flatbuffers::DetachedBuffer buffer =
+ JsonToFlatbuffer("{}", channel->schema());
+ sender->CheckOk(sender->Send(buffer.data(), buffer.size()));
+ });
+ }
+ }
+ }
+ }
+
+ factory.RunFor(std::chrono::seconds(2));
+
+ // Get all of the loggers to close before trying to read the logfiles.
+ loggers.clear();
+
+ // Confirm that we can read the log, and that if we put data in it that we
+ // can find data on all the nodes that the user cares about.
+ logger::LogReader reader(logger::SortParts(logger::FindLogs(log_path)));
+ SimulatedEventLoopFactory replay_factory(reader.configuration());
+ reader.RegisterWithoutStarting(&replay_factory);
+
+ // Find every channel we deliberately sent data on, and if it is for a
+ // node that we care about, confirm that we get it during replay.
+ std::vector<std::unique_ptr<EventLoop>> replay_loops;
+ std::vector<std::unique_ptr<RawFetcher>> fetchers;
+ for (const aos::Node *node :
+ configuration::GetNodes(replay_factory.configuration())) {
+ // If the user doesn't care about this node, don't check it.
+ if (!NodeInList(logger_set->replay_nodes(), node)) {
+ continue;
+ }
+ replay_loops.emplace_back(replay_factory.MakeEventLoop("", node));
+ for (const auto &sender : test_senders[node->name()->str()]) {
+ const aos::Channel *channel = configuration::GetChannel(
+ replay_factory.configuration(), sender->channel(), "", node);
+ fetchers.emplace_back(replay_loops.back()->MakeRawFetcher(channel));
+ }
+ }
+
+ std::vector<std::pair<const aos::Node *, std::unique_ptr<RawFetcher>>>
+ remote_fetchers;
+ for (const auto &fetcher : fetchers) {
+ for (auto &loop : replay_loops) {
+ const Connection *connection =
+ configuration::ConnectionToNode(fetcher->channel(), loop->node());
+ if (connection != nullptr) {
+ remote_fetchers.push_back(std::make_pair(
+ loop->node(), loop->MakeRawFetcher(fetcher->channel())));
+ }
+ }
+ }
+
+ replay_factory.Run();
+
+ for (auto &fetcher : fetchers) {
+ EXPECT_TRUE(fetcher->Fetch())
+ << "Failed to log or replay any data on "
+ << configuration::StrippedChannelToString(fetcher->channel());
+ }
+
+ for (auto &pair : remote_fetchers) {
+ EXPECT_TRUE(pair.second->Fetch())
+ << "Failed to log or replay any data on "
+ << configuration::StrippedChannelToString(pair.second->channel())
+ << " from remote node " << logger::MaybeNodeName(pair.first) << ".";
+ }
+
+ reader.Deregister();
+
+ // Clean up the logs.
+ UnlinkRecursive(log_path);
+ }
+ }
+}
+
+} // namespace aos::util
diff --git a/aos/util/config_validator_lib.h b/aos/util/config_validator_lib.h
new file mode 100644
index 0000000..61658e5
--- /dev/null
+++ b/aos/util/config_validator_lib.h
@@ -0,0 +1,12 @@
+#ifndef AOS_UTIL_CONFIG_VALIDATOR_H_
+#define AOS_UTIL_CONFIG_VALIDATOR_H_
+
+#include "aos/configuration.h"
+#include "aos/util/config_validator_config_generated.h"
+#include "gtest/gtest.h"
+namespace aos::util {
+
+void ConfigIsValid(const aos::Configuration *config,
+ const ConfigValidatorConfig *validation_config);
+} // namespace aos::util
+#endif // AOS_UTIL_CONFIG_VALIDATOR_H_
diff --git a/aos/util/config_validator_lib_test.cc b/aos/util/config_validator_lib_test.cc
new file mode 100644
index 0000000..c68695c
--- /dev/null
+++ b/aos/util/config_validator_lib_test.cc
@@ -0,0 +1,189 @@
+#include "aos/util/config_validator_lib.h"
+
+#include "aos/json_to_flatbuffer.h"
+#include "aos/testing/path.h"
+#include "gtest/gtest-spi.h"
+
+using aos::testing::ArtifactPath;
+namespace aos::util::testing {
+
+// Check that a reasonably normal config passes the config validator with a
+// reasonable set of checks turned on.
+TEST(ConfigValidatorTest, NoErrorOnValidConfigs) {
+ const FlatbufferDetachedBuffer<Configuration> config =
+ configuration::ReadConfig(
+ ArtifactPath("aos/util/test_data/valid_multinode_config.json"));
+ const FlatbufferDetachedBuffer<ConfigValidatorConfig> validator_config =
+ JsonToFlatbuffer<ConfigValidatorConfig>(R"json({"logging": {
+ "all_channels_logged": true,
+ "logger_sets": [
+ {
+ "loggers": [],
+ "replay_nodes": []
+ },
+ {
+ "loggers": ["pi1"],
+ "replay_nodes": ["pi1"]
+ },
+ {
+ "loggers": ["pi2"],
+ "replay_nodes": ["pi2"]
+ }
+ ]}})json");
+ ConfigIsValid(&config.message(), &validator_config.message());
+}
+
+// Check that a reasonably normal single-node config passes the config validator
+// with a reasonable set of checks turned on.
+TEST(ConfigValidatorTest, NoErrorOnValidSingleNodeConfig) {
+ const FlatbufferDetachedBuffer<Configuration> config =
+ configuration::ReadConfig(
+ ArtifactPath("aos/util/test_data/valid_singlenode_config.json"));
+ const FlatbufferDetachedBuffer<ConfigValidatorConfig> validator_config =
+ JsonToFlatbuffer<ConfigValidatorConfig>(R"json({"logging": {
+ "all_channels_logged": true,
+ "logger_sets": [
+ {
+ "loggers": [],
+ "replay_nodes": []
+ }
+ ]}})json");
+ ConfigIsValid(&config.message(), &validator_config.message());
+}
+
+// Checks that the validator fails if the message bridge statistics channels are
+// missing.
+TEST(ConfigValidatorTest, FailOnMissingStatisticsChannels) {
+ EXPECT_FATAL_FAILURE(
+ {
+ const FlatbufferDetachedBuffer<Configuration> config =
+ configuration::ReadConfig(ArtifactPath(
+ "aos/util/test_data/multinode_no_statistics.json"));
+ const FlatbufferDetachedBuffer<ConfigValidatorConfig> validator_config =
+ JsonToFlatbuffer<ConfigValidatorConfig>("{}");
+ ConfigIsValid(&config.message(), &validator_config.message());
+ },
+ "Statistics");
+}
+
+// Checks that the validator fails if a timestamp channel has a typo and so
+// doesn't exist.
+TEST(ConfigValidatorTest, FailOnTimestampTypo) {
+ EXPECT_DEATH(
+ {
+ const FlatbufferDetachedBuffer<Configuration> config =
+ configuration::ReadConfig(ArtifactPath(
+ "aos/util/test_data/multinode_timestamp_typo.json"));
+ const FlatbufferDetachedBuffer<ConfigValidatorConfig> validator_config =
+ JsonToFlatbuffer<ConfigValidatorConfig>("{}");
+ ConfigIsValid(&config.message(), &validator_config.message());
+ },
+ "not found in config");
+}
+
+// Checks that the validator fails if there is a RemoteMessage channel that is
+// *not* a timestamp channel (Since this is almost always a typo).
+TEST(ConfigValidatorTest, FailOnExtraneousTimestampChannel) {
+ EXPECT_FATAL_FAILURE(
+ {
+ const FlatbufferDetachedBuffer<Configuration> config =
+ configuration::ReadConfig(ArtifactPath(
+ "aos/util/test_data/multinode_extraneous_timestamp.json"));
+ const FlatbufferDetachedBuffer<ConfigValidatorConfig> validator_config =
+ JsonToFlatbuffer<ConfigValidatorConfig>("{}");
+ ConfigIsValid(&config.message(), &validator_config.message());
+ },
+ "linting failed");
+}
+
+// Checks that the validator fails on timestamp logger nodes that won't really
+// log the timestamps.
+TEST(ConfigValidatorTest, FailOnInvalidRemoteTimestampLogger) {
+ EXPECT_FATAL_FAILURE(
+ {
+ const FlatbufferDetachedBuffer<Configuration> config =
+ configuration::ReadConfig(
+ ArtifactPath("aos/util/test_data/"
+ "multinode_invalid_timestamp_logger_list.json"));
+ const FlatbufferDetachedBuffer<ConfigValidatorConfig> validator_config =
+ JsonToFlatbuffer<ConfigValidatorConfig>(R"json({"logging": {
+ "all_channels_logged": true,
+ "logger_sets": [
+ {
+ "loggers": [],
+ "replay_nodes": []
+ }
+ ]}})json");
+ ConfigIsValid(&config.message(), &validator_config.message());
+ },
+ "linting failed");
+}
+
+// Checks that if you attempt to log on pi2 but expect it to have data for pi1
+// then the test fails (at least, for a config which does not forward all the
+// channels between the nodes).
+TEST(ConfigValidatorTest, FailOnNormalInsufficientLogging) {
+ EXPECT_NONFATAL_FAILURE(
+ {
+ const FlatbufferDetachedBuffer<Configuration> config =
+ configuration::ReadConfig(
+ ArtifactPath("aos/util/test_data/valid_multinode_config.json"));
+ const FlatbufferDetachedBuffer<ConfigValidatorConfig> validator_config =
+ JsonToFlatbuffer<ConfigValidatorConfig>(R"json({"logging": {
+ "all_channels_logged": true,
+ "logger_sets": [
+ {
+ "loggers": ["pi2"],
+ "replay_nodes": ["pi1"]
+ }
+ ]}})json");
+ ConfigIsValid(&config.message(), &validator_config.message());
+ },
+ "Failed to log");
+}
+
+// Checks that if we have a node that is configured to log all the data from all
+// the nodes that the test passes.
+TEST(ConfigValidatorTest, PassCommonLoggerNode) {
+ const FlatbufferDetachedBuffer<Configuration> config =
+ configuration::ReadConfig(
+ ArtifactPath("aos/util/test_data/multinode_common_logger.json"));
+ const FlatbufferDetachedBuffer<ConfigValidatorConfig> validator_config =
+ JsonToFlatbuffer<ConfigValidatorConfig>(R"json({"logging": {
+ "all_channels_logged": true,
+ "logger_sets": [
+ {
+ "loggers": ["pi2"],
+ "replay_nodes": ["pi1"]
+ },
+ {
+ "loggers": [],
+ "replay_nodes": []
+ }
+ ]}})json");
+ ConfigIsValid(&config.message(), &validator_config.message());
+}
+
+// Sets up a config that will not actually log sufficient timestamp data to
+// support full replay, and ensures that we identify that.
+TEST(ConfigValidatorTest, FailOnInsufficientConfiguredTimestampData) {
+ EXPECT_NONFATAL_FAILURE(
+ {
+ const FlatbufferDetachedBuffer<Configuration> config =
+ configuration::ReadConfig(ArtifactPath(
+ "aos/util/test_data/multinode_no_logged_timestamps.json"));
+ const FlatbufferDetachedBuffer<ConfigValidatorConfig> validator_config =
+ JsonToFlatbuffer<ConfigValidatorConfig>(R"json({"logging": {
+ "all_channels_logged": true,
+ "logger_sets": [
+ {
+ "loggers": [],
+ "replay_nodes": []
+ }
+ ]}})json");
+ ConfigIsValid(&config.message(), &validator_config.message());
+ },
+ R"json(Failed to log or replay any data on { "name": "/test", "type": "aos.examples.Ping" } from remote node pi2)json");
+}
+
+} // namespace aos::util::testing
diff --git a/aos/util/config_validator_macro.bzl b/aos/util/config_validator_macro.bzl
index 453c5c2..cd05bab 100644
--- a/aos/util/config_validator_macro.bzl
+++ b/aos/util/config_validator_macro.bzl
@@ -1,20 +1,17 @@
-def config_validator_rule(name, config, extension = ".bfbs", visibility = None):
+def config_validator_test(name, config, logger_sets = [{}], check_for_not_logged_channels = False, extension = ".bfbs", visibility = None):
'''
Macro to take a config and pass it to the config validator to validate that it will work on a real system.
- Currently just checks that the system can startup, but will check that timestamp channels are properly logged in the future.
-
Args:
name: name that the config validator uses, e.g. "test_config",
config: config rule that needs to be validated, e.g. "//aos/events:pingpong_config",
'''
config_file = config + extension
- native.genrule(
+ config_json = json.encode({"logging": {"all_channels_logged": check_for_not_logged_channels, "logger_sets": logger_sets}})
+ native.cc_test(
name = name,
- outs = [name + ".txt"],
- cmd = "$(location //aos/util:config_validator) --config $(location %s) > $@" % config_file,
- srcs = [config_file],
- tools = ["//aos/util:config_validator"],
- testonly = True,
+ deps = ["//aos/util:config_validator"],
+ args = ["--config=$(location %s)" % config_file, "--validation_config='%s'" % config_json],
+ data = [config_file],
visibility = visibility,
)
diff --git a/aos/util/file_test.cc b/aos/util/file_test.cc
index d4382c4..ba03ea5 100644
--- a/aos/util/file_test.cc
+++ b/aos/util/file_test.cc
@@ -7,8 +7,6 @@
#include "aos/testing/tmpdir.h"
#include "gtest/gtest.h"
-DECLARE_bool(die_on_malloc);
-
namespace aos {
namespace util {
namespace testing {
@@ -52,9 +50,6 @@
FileReader reader(test_file);
- gflags::FlagSaver flag_saver;
- FLAGS_die_on_malloc = true;
- RegisterMallocHook();
aos::ScopedRealtime realtime;
{
std::array<char, 20> contents;
@@ -79,9 +74,6 @@
FileWriter writer(test_file);
- gflags::FlagSaver flag_saver;
- FLAGS_die_on_malloc = true;
- RegisterMallocHook();
FileWriter::WriteResult result;
{
aos::ScopedRealtime realtime;
@@ -104,9 +96,6 @@
// Mess up the file management by closing the file descriptor.
PCHECK(0 == close(writer.fd()));
- gflags::FlagSaver flag_saver;
- FLAGS_die_on_malloc = true;
- RegisterMallocHook();
FileWriter::WriteResult result;
{
aos::ScopedRealtime realtime;
diff --git a/aos/util/simulation_logger.cc b/aos/util/simulation_logger.cc
new file mode 100644
index 0000000..1f55ece
--- /dev/null
+++ b/aos/util/simulation_logger.cc
@@ -0,0 +1,40 @@
+#include "aos/util/simulation_logger.h"
+#include "aos/events/logging/logfile_utils.h"
+
+namespace aos::util {
+LoggerState::LoggerState(aos::SimulatedEventLoopFactory *factory,
+ const aos::Node *node, std::string_view output_folder)
+ : event_loop_(factory->MakeEventLoop("logger", node)),
+ namer_(std::make_unique<aos::logger::MultiNodeFilesLogNamer>(
+ absl::StrCat(output_folder, "/", logger::MaybeNodeName(node), "/"),
+ event_loop_.get())),
+ logger_(std::make_unique<aos::logger::Logger>(event_loop_.get())) {
+ event_loop_->SkipTimingReport();
+ event_loop_->SkipAosLog();
+ event_loop_->OnRun([this]() { logger_->StartLogging(std::move(namer_)); });
+}
+
+std::vector<std::unique_ptr<LoggerState>> MakeLoggersForNodes(
+ aos::SimulatedEventLoopFactory *factory,
+ const std::vector<std::string> &nodes_to_log,
+ std::string_view output_folder) {
+ std::vector<std::unique_ptr<LoggerState>> loggers;
+ for (const std::string &node : nodes_to_log) {
+ loggers.emplace_back(std::make_unique<LoggerState>(
+ factory, aos::configuration::GetNode(factory->configuration(), node),
+ output_folder));
+ }
+ return loggers;
+}
+
+std::vector<std::unique_ptr<LoggerState>> MakeLoggersForAllNodes(
+ aos::SimulatedEventLoopFactory *factory, std::string_view output_folder) {
+ std::vector<std::unique_ptr<LoggerState>> loggers;
+ for (const aos::Node *node : configuration::GetNodes(factory->configuration())) {
+ loggers.emplace_back(
+ std::make_unique<LoggerState>(factory, node, output_folder));
+ }
+ return loggers;
+}
+
+} // namespace aos::util
diff --git a/aos/util/simulation_logger.h b/aos/util/simulation_logger.h
new file mode 100644
index 0000000..f431b4c
--- /dev/null
+++ b/aos/util/simulation_logger.h
@@ -0,0 +1,33 @@
+#ifndef AOS_UTIL_SIMULATION_LOGGER_H_
+#define AOS_UTIL_SIMULATION_LOGGER_H_
+#include <string_view>
+
+#include "aos/events/logging/log_writer.h"
+#include "aos/events/simulated_event_loop.h"
+namespace aos::util {
+
+class LoggerState {
+ public:
+ LoggerState(aos::SimulatedEventLoopFactory *factory, const aos::Node *node,
+ std::string_view output_folder);
+
+ private:
+ std::unique_ptr<aos::EventLoop> event_loop_;
+ std::unique_ptr<aos::logger::LogNamer> namer_;
+ std::unique_ptr<aos::logger::Logger> logger_;
+};
+
+// Creates a logger for each of the specified nodes. This makes it so that you
+// can easily setup some number of loggers in simulation or log replay without
+// needing to redo all the boilerplate every time.
+std::vector<std::unique_ptr<LoggerState>> MakeLoggersForNodes(
+ aos::SimulatedEventLoopFactory *factory,
+ const std::vector<std::string> &nodes_to_log,
+ std::string_view output_folder);
+
+// Creates loggers for all of the nodes.
+std::vector<std::unique_ptr<LoggerState>> MakeLoggersForAllNodes(
+ aos::SimulatedEventLoopFactory *factory, std::string_view output_folder);
+
+} // namespace aos::util
+#endif // AOS_UTIL_SIMULATION_LOGGER_H_
diff --git a/aos/util/test_data/BUILD b/aos/util/test_data/BUILD
new file mode 100644
index 0000000..016dd07
--- /dev/null
+++ b/aos/util/test_data/BUILD
@@ -0,0 +1,28 @@
+load("//aos:config.bzl", "aos_config")
+
+[
+ aos_config(
+ name = name,
+ src = name + "_source.json",
+ flatbuffers = [
+ "//aos/network:remote_message_fbs",
+ "//aos/events:ping_fbs",
+ "//aos/network:message_bridge_client_fbs",
+ "//aos/network:message_bridge_server_fbs",
+ "//aos/network:timestamp_fbs",
+ ],
+ target_compatible_with = ["@platforms//os:linux"],
+ visibility = ["//visibility:public"],
+ deps = ["//aos/events:aos_config"],
+ )
+ for name in [
+ "valid_multinode_config",
+ "valid_singlenode_config",
+ "multinode_no_statistics",
+ "multinode_timestamp_typo",
+ "multinode_extraneous_timestamp",
+ "multinode_invalid_timestamp_logger_list",
+ "multinode_common_logger",
+ "multinode_no_logged_timestamps",
+ ]
+]
diff --git a/aos/util/test_data/multinode_common_logger_source.json b/aos/util/test_data/multinode_common_logger_source.json
new file mode 100644
index 0000000..81aa065
--- /dev/null
+++ b/aos/util/test_data/multinode_common_logger_source.json
@@ -0,0 +1,156 @@
+{
+ "channels": [
+ {
+ "name": "/pi1/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi1",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi2",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi1",
+ "frequency": 15,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi1"],
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi2",
+ "frequency": 15,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi1",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi2"],
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.ServerStatistics",
+ "source_node": "pi1",
+ "frequency": 2
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.ServerStatistics",
+ "source_node": "pi2",
+ "frequency": 2
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.ClientStatistics",
+ "source_node": "pi1",
+ "frequency": 15
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.ClientStatistics",
+ "source_node": "pi2",
+ "frequency": 15
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi2/pi1/aos/aos-message_bridge-Timestamp",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi1",
+ "frequency": 15
+ },
+ {
+ "name": "/pi2/aos/remote_timestamps/pi1/pi2/aos/aos-message_bridge-Timestamp",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi2",
+ "frequency": 15
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi2/test/aos-examples-Ping",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi1"
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi1",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi2",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/test",
+ "type": "aos.examples.Ping",
+ "source_node": "pi1",
+ "logger": "LOCAL_AND_REMOTE_LOGGER",
+ "logger_nodes": ["pi2"],
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1,
+ "timestamp_logger": "REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi1"]
+ }
+ ],
+ "max_size": 20480
+ }
+ ],
+ "maps": [
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi1"
+ },
+ "rename": {
+ "name": "/pi1/aos"
+ }
+ },
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi2"
+ },
+ "rename": {
+ "name": "/pi2/aos"
+ }
+ }
+ ],
+ "nodes": [
+ {
+ "name": "pi1"
+ },
+ {
+ "name": "pi2"
+ }
+ ]
+}
diff --git a/aos/util/test_data/multinode_extraneous_timestamp_source.json b/aos/util/test_data/multinode_extraneous_timestamp_source.json
new file mode 100644
index 0000000..2e889d0
--- /dev/null
+++ b/aos/util/test_data/multinode_extraneous_timestamp_source.json
@@ -0,0 +1,161 @@
+{
+ "channels": [
+ {
+ "name": "/pi1/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi1",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi2",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi1",
+ "frequency": 15,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi1"],
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi2",
+ "frequency": 15,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi1",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi2"],
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.ServerStatistics",
+ "source_node": "pi1",
+ "frequency": 2
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.ServerStatistics",
+ "source_node": "pi2",
+ "frequency": 2
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.ClientStatistics",
+ "source_node": "pi1",
+ "frequency": 15
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.ClientStatistics",
+ "source_node": "pi2",
+ "frequency": 15
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi2/pi1/aos/aos-message_bridge-Timestamp",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi1",
+ "frequency": 15
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi2/pi1/aos/os-message_bridge-Timestamp",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi1",
+ "frequency": 15
+ },
+ {
+ "name": "/pi2/aos/remote_timestamps/pi1/pi2/aos/aos-message_bridge-Timestamp",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi2",
+ "frequency": 15
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi2/test/aos-examples-Ping",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi1"
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi1",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi2",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/test",
+ "type": "aos.examples.Ping",
+ "source_node": "pi1",
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1,
+ "timestamp_logger": "REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi1"]
+ }
+ ],
+ "max_size": 20480
+ }
+ ],
+ "maps": [
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi1"
+ },
+ "rename": {
+ "name": "/pi1/aos"
+ }
+ },
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi2"
+ },
+ "rename": {
+ "name": "/pi2/aos"
+ }
+ }
+ ],
+ "nodes": [
+ {
+ "name": "pi1"
+ },
+ {
+ "name": "pi2"
+ }
+ ]
+}
diff --git a/aos/util/test_data/multinode_invalid_timestamp_logger_list_source.json b/aos/util/test_data/multinode_invalid_timestamp_logger_list_source.json
new file mode 100644
index 0000000..e501437
--- /dev/null
+++ b/aos/util/test_data/multinode_invalid_timestamp_logger_list_source.json
@@ -0,0 +1,154 @@
+{
+ "channels": [
+ {
+ "name": "/pi1/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi1",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi2",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi1",
+ "frequency": 15,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi2"],
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi2",
+ "frequency": 15,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi1",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi2"],
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.ServerStatistics",
+ "source_node": "pi1",
+ "frequency": 2
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.ServerStatistics",
+ "source_node": "pi2",
+ "frequency": 2
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.ClientStatistics",
+ "source_node": "pi1",
+ "frequency": 15
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.ClientStatistics",
+ "source_node": "pi2",
+ "frequency": 15
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi2/pi1/aos/aos-message_bridge-Timestamp",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi1",
+ "frequency": 15
+ },
+ {
+ "name": "/pi2/aos/remote_timestamps/pi1/pi2/aos/aos-message_bridge-Timestamp",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi2",
+ "frequency": 15
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi2/test/aos-examples-Ping",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi1"
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi1",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi2",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/test",
+ "type": "aos.examples.Ping",
+ "source_node": "pi1",
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1,
+ "timestamp_logger": "REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi1"]
+ }
+ ],
+ "max_size": 20480
+ }
+ ],
+ "maps": [
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi1"
+ },
+ "rename": {
+ "name": "/pi1/aos"
+ }
+ },
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi2"
+ },
+ "rename": {
+ "name": "/pi2/aos"
+ }
+ }
+ ],
+ "nodes": [
+ {
+ "name": "pi1"
+ },
+ {
+ "name": "pi2"
+ }
+ ]
+}
diff --git a/aos/util/test_data/multinode_no_logged_timestamps_source.json b/aos/util/test_data/multinode_no_logged_timestamps_source.json
new file mode 100644
index 0000000..a956f73
--- /dev/null
+++ b/aos/util/test_data/multinode_no_logged_timestamps_source.json
@@ -0,0 +1,147 @@
+{
+ "channels": [
+ {
+ "name": "/pi1/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi1",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi2",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi1",
+ "frequency": 15,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi1"],
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi2",
+ "frequency": 15,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi1",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi2"],
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.ServerStatistics",
+ "source_node": "pi1",
+ "frequency": 2
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.ServerStatistics",
+ "source_node": "pi2",
+ "frequency": 2
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.ClientStatistics",
+ "source_node": "pi1",
+ "frequency": 15
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.ClientStatistics",
+ "source_node": "pi2",
+ "frequency": 15
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi2/pi1/aos/aos-message_bridge-Timestamp",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi1",
+ "frequency": 15
+ },
+ {
+ "name": "/pi2/aos/remote_timestamps/pi1/pi2/aos/aos-message_bridge-Timestamp",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi2",
+ "frequency": 15
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi1",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi2",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/test",
+ "type": "aos.examples.Ping",
+ "source_node": "pi1",
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1,
+ "timestamp_logger": "NOT_LOGGED"
+ }
+ ],
+ "max_size": 20480
+ }
+ ],
+ "maps": [
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi1"
+ },
+ "rename": {
+ "name": "/pi1/aos"
+ }
+ },
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi2"
+ },
+ "rename": {
+ "name": "/pi2/aos"
+ }
+ }
+ ],
+ "nodes": [
+ {
+ "name": "pi1"
+ },
+ {
+ "name": "pi2"
+ }
+ ]
+}
diff --git a/aos/util/test_data/multinode_no_statistics_source.json b/aos/util/test_data/multinode_no_statistics_source.json
new file mode 100644
index 0000000..44fa5d8
--- /dev/null
+++ b/aos/util/test_data/multinode_no_statistics_source.json
@@ -0,0 +1,130 @@
+{
+ "channels": [
+ {
+ "name": "/pi1/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi1",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi2",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi1",
+ "frequency": 15,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi1"],
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi2",
+ "frequency": 15,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi1",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi2"],
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi2/pi1/aos/aos-message_bridge-Timestamp",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi1",
+ "frequency": 15
+ },
+ {
+ "name": "/pi2/aos/remote_timestamps/pi1/pi2/aos/aos-message_bridge-Timestamp",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi2",
+ "frequency": 15
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi2/test/aos-examples-Ping",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi1"
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi1",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi2",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/test",
+ "type": "aos.examples.Ping",
+ "source_node": "pi1",
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1,
+ "timestamp_logger": "REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi1"]
+ }
+ ],
+ "max_size": 20480
+ }
+ ],
+ "maps": [
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi1"
+ },
+ "rename": {
+ "name": "/pi1/aos"
+ }
+ },
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi2"
+ },
+ "rename": {
+ "name": "/pi2/aos"
+ }
+ }
+ ],
+ "nodes": [
+ {
+ "name": "pi1"
+ },
+ {
+ "name": "pi2"
+ }
+ ]
+}
diff --git a/aos/util/test_data/multinode_timestamp_typo_source.json b/aos/util/test_data/multinode_timestamp_typo_source.json
new file mode 100644
index 0000000..afb4275
--- /dev/null
+++ b/aos/util/test_data/multinode_timestamp_typo_source.json
@@ -0,0 +1,154 @@
+{
+ "channels": [
+ {
+ "name": "/pi1/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi1",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi2",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi1",
+ "frequency": 15,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi1"],
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi2",
+ "frequency": 15,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi1",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi2"],
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.ServerStatistics",
+ "source_node": "pi1",
+ "frequency": 2
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.ServerStatistics",
+ "source_node": "pi2",
+ "frequency": 2
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.ClientStatistics",
+ "source_node": "pi1",
+ "frequency": 15
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.ClientStatistics",
+ "source_node": "pi2",
+ "frequency": 15
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi2/pi1/aos/aos-message_bridge-Timestam",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi1",
+ "frequency": 15
+ },
+ {
+ "name": "/pi2/aos/remote_timestamps/pi1/pi2/aos/aos-message_bridge-Timestamp",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi2",
+ "frequency": 15
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi2/test/aos-examples-Ping",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi1"
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi1",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi2",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/test",
+ "type": "aos.examples.Ping",
+ "source_node": "pi1",
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1,
+ "timestamp_logger": "REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi1"]
+ }
+ ],
+ "max_size": 20480
+ }
+ ],
+ "maps": [
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi1"
+ },
+ "rename": {
+ "name": "/pi1/aos"
+ }
+ },
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi2"
+ },
+ "rename": {
+ "name": "/pi2/aos"
+ }
+ }
+ ],
+ "nodes": [
+ {
+ "name": "pi1"
+ },
+ {
+ "name": "pi2"
+ }
+ ]
+}
diff --git a/aos/util/test_data/valid_multinode_config_source.json b/aos/util/test_data/valid_multinode_config_source.json
new file mode 100644
index 0000000..0c35251
--- /dev/null
+++ b/aos/util/test_data/valid_multinode_config_source.json
@@ -0,0 +1,154 @@
+{
+ "channels": [
+ {
+ "name": "/pi1/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi1",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "source_node": "pi2",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi1",
+ "frequency": 15,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi1"],
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi2",
+ "frequency": 15,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi1",
+ "priority": 1,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi2"],
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.ServerStatistics",
+ "source_node": "pi1",
+ "frequency": 2
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.ServerStatistics",
+ "source_node": "pi2",
+ "frequency": 2
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.message_bridge.ClientStatistics",
+ "source_node": "pi1",
+ "frequency": 15
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.message_bridge.ClientStatistics",
+ "source_node": "pi2",
+ "frequency": 15
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi2/pi1/aos/aos-message_bridge-Timestamp",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi1",
+ "frequency": 15
+ },
+ {
+ "name": "/pi2/aos/remote_timestamps/pi1/pi2/aos/aos-message_bridge-Timestamp",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi2",
+ "frequency": 15
+ },
+ {
+ "name": "/pi1/aos/remote_timestamps/pi2/test/aos-examples-Ping",
+ "type": "aos.message_bridge.RemoteMessage",
+ "logger": "NOT_LOGGED",
+ "source_node": "pi1"
+ },
+ {
+ "name": "/pi1/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi1",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/pi2/aos",
+ "type": "aos.timing.Report",
+ "source_node": "pi2",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/test",
+ "type": "aos.examples.Ping",
+ "source_node": "pi1",
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1,
+ "timestamp_logger": "REMOTE_LOGGER",
+ "timestamp_logger_nodes": ["pi1"]
+ }
+ ],
+ "max_size": 20480
+ }
+ ],
+ "maps": [
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi1"
+ },
+ "rename": {
+ "name": "/pi1/aos"
+ }
+ },
+ {
+ "match": {
+ "name": "/aos*",
+ "source_node": "pi2"
+ },
+ "rename": {
+ "name": "/pi2/aos"
+ }
+ }
+ ],
+ "nodes": [
+ {
+ "name": "pi1"
+ },
+ {
+ "name": "pi2"
+ }
+ ]
+}
diff --git a/aos/util/test_data/valid_singlenode_config_source.json b/aos/util/test_data/valid_singlenode_config_source.json
new file mode 100644
index 0000000..736eace
--- /dev/null
+++ b/aos/util/test_data/valid_singlenode_config_source.json
@@ -0,0 +1,23 @@
+{
+ "channels": [
+ {
+ "name": "/aos",
+ "type": "aos.logging.LogMessageFbs",
+ "frequency": 200,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/aos",
+ "type": "aos.timing.Report",
+ "frequency": 50,
+ "num_senders": 20,
+ "max_size": 2048
+ },
+ {
+ "name": "/test",
+ "type": "aos.examples.Ping",
+ "max_size": 20480
+ }
+ ]
+}
diff --git a/frc971/control_loops/drivetrain/localization/puppet_localizer_test.cc b/frc971/control_loops/drivetrain/localization/puppet_localizer_test.cc
index d64c419..e45f880 100644
--- a/frc971/control_loops/drivetrain/localization/puppet_localizer_test.cc
+++ b/frc971/control_loops/drivetrain/localization/puppet_localizer_test.cc
@@ -16,7 +16,6 @@
DEFINE_string(output_folder, "",
"If set, logs all channels to the provided logfile.");
-DECLARE_bool(die_on_malloc);
namespace frc971 {
namespace control_loops {
@@ -78,7 +77,6 @@
drivetrain_plant_(drivetrain_plant_event_loop_.get(),
drivetrain_plant_imu_event_loop_.get(), dt_config_,
std::chrono::microseconds(500)) {
- FLAGS_die_on_malloc = true;
set_team_id(frc971::control_loops::testing::kTeamNumber);
set_battery_voltage(12.0);
diff --git a/y2020/BUILD b/y2020/BUILD
index 2f6f963..32d392a 100644
--- a/y2020/BUILD
+++ b/y2020/BUILD
@@ -2,6 +2,12 @@
load("//aos:config.bzl", "aos_config")
load("@com_github_google_flatbuffers//:build_defs.bzl", "flatbuffer_cc_library")
load("//tools/build_rules:template.bzl", "jinja2_template")
+load("//aos/util:config_validator_macro.bzl", "config_validator_test")
+
+config_validator_test(
+ name = "config_validator_test",
+ config = "//y2020:aos_config",
+)
robot_downloader(
binaries = [
diff --git a/y2020/control_loops/drivetrain/BUILD b/y2020/control_loops/drivetrain/BUILD
index fa153f0..8191404 100644
--- a/y2020/control_loops/drivetrain/BUILD
+++ b/y2020/control_loops/drivetrain/BUILD
@@ -207,6 +207,7 @@
"//aos/events:simulated_event_loop",
"//aos/events/logging:log_reader",
"//aos/events/logging:log_writer",
+ "//aos/util:simulation_logger",
"//frc971/control_loops/drivetrain:drivetrain_lib",
"//frc971/control_loops/drivetrain:trajectory_generator",
"//y2020:constants",
diff --git a/y2020/control_loops/drivetrain/drivetrain_replay.cc b/y2020/control_loops/drivetrain/drivetrain_replay.cc
index 57feabb..8373258 100644
--- a/y2020/control_loops/drivetrain/drivetrain_replay.cc
+++ b/y2020/control_loops/drivetrain/drivetrain_replay.cc
@@ -11,6 +11,7 @@
#include "aos/init.h"
#include "aos/json_to_flatbuffer.h"
#include "aos/network/team_number.h"
+#include "aos/util/simulation_logger.h"
#include "frc971/control_loops/drivetrain/drivetrain.h"
#include "frc971/control_loops/drivetrain/trajectory_generator.h"
#include "gflags/gflags.h"
@@ -26,27 +27,6 @@
DEFINE_int32(team, 971, "Team number to use for logfile replay.");
DEFINE_bool(log_all_nodes, false, "Whether to rerun the logger on every node.");
-class LoggerState {
- public:
- LoggerState(aos::logger::LogReader *reader, const aos::Node *node)
- : event_loop_(
- reader->event_loop_factory()->MakeEventLoop("logger", node)),
- namer_(std::make_unique<aos::logger::MultiNodeFilesLogNamer>(
- absl::StrCat(FLAGS_output_folder, "/", node->name()->string_view(),
- "/"),
- event_loop_.get())),
- logger_(std::make_unique<aos::logger::Logger>(event_loop_.get())) {
- event_loop_->SkipTimingReport();
- event_loop_->SkipAosLog();
- event_loop_->OnRun([this]() { logger_->StartLogging(std::move(namer_)); });
- }
-
- private:
- std::unique_ptr<aos::EventLoop> event_loop_;
- std::unique_ptr<aos::logger::LogNamer> namer_;
- std::unique_ptr<aos::logger::Logger> logger_;
-};
-
// TODO(james): Currently, this replay produces logfiles that can't be read due
// to time estimation issues. Pending the active refactorings of the
// timestamp-related code, fix this.
@@ -82,21 +62,17 @@
"y2020.control_loops.superstructure.Output");
reader.Register();
- std::vector<std::unique_ptr<LoggerState>> loggers;
+ std::vector<std::unique_ptr<aos::util::LoggerState>> loggers;
if (FLAGS_log_all_nodes) {
- for (const aos::Node *node :
- aos::configuration::GetNodes(reader.configuration())) {
- loggers.emplace_back(std::make_unique<LoggerState>(&reader, node));
- }
+ loggers = aos::util::MakeLoggersForAllNodes(reader.event_loop_factory(),
+ FLAGS_output_folder);
} else {
// List of nodes to create loggers for (note: currently just roborio; this
// code was refactored to allow easily adding new loggers to accommodate
// debugging and potential future changes).
const std::vector<std::string> nodes_to_log = {"roborio"};
- for (const std::string &node : nodes_to_log) {
- loggers.emplace_back(std::make_unique<LoggerState>(
- &reader, aos::configuration::GetNode(reader.configuration(), node)));
- }
+ loggers = aos::util::MakeLoggersForNodes(reader.event_loop_factory(),
+ nodes_to_log, FLAGS_output_folder);
}
const aos::Node *node = nullptr;
diff --git a/y2020/control_loops/drivetrain/localizer_test.cc b/y2020/control_loops/drivetrain/localizer_test.cc
index d280523..9e9e7dd 100644
--- a/y2020/control_loops/drivetrain/localizer_test.cc
+++ b/y2020/control_loops/drivetrain/localizer_test.cc
@@ -15,7 +15,6 @@
DEFINE_string(output_file, "",
"If set, logs all channels to the provided logfile.");
-DECLARE_bool(die_on_malloc);
// This file tests that the full 2020 localizer behaves sanely.
@@ -147,7 +146,6 @@
CHECK_EQ(aos::configuration::GetNodeIndex(configuration(), pi1_), 1);
set_team_id(frc971::control_loops::testing::kTeamNumber);
set_battery_voltage(12.0);
- FLAGS_die_on_malloc = true;
if (!FLAGS_output_file.empty()) {
logger_event_loop_ = MakeEventLoop("logger", roborio_);
diff --git a/y2020/y2020_pi_template.json b/y2020/y2020_pi_template.json
index cf54c23..c609632 100644
--- a/y2020/y2020_pi_template.json
+++ b/y2020/y2020_pi_template.json
@@ -92,12 +92,19 @@
"time_to_live": 5000000,
"timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
"timestamp_logger_nodes": [
- "roborio"
+ "pi{{ NUM }}"
]
}
]
},
{
+ "name": "/pi{{ NUM }}/aos/remote_timestamps/roborio/pi{{ NUM }}/aos/aos-message_bridge-Timestamp",
+ "type": "aos.message_bridge.RemoteMessage",
+ "frequency": 20,
+ "source_node": "pi{{ NUM }}",
+ "max_size": 208
+ },
+ {
"name": "/pi{{ NUM }}/camera",
"type": "frc971.vision.CameraImage",
"source_node": "pi{{ NUM }}",
diff --git a/y2022/BUILD b/y2022/BUILD
index 1a04c2e..f8bf59f 100644
--- a/y2022/BUILD
+++ b/y2022/BUILD
@@ -2,6 +2,12 @@
load("//aos:config.bzl", "aos_config")
load("@com_github_google_flatbuffers//:build_defs.bzl", "flatbuffer_cc_library")
load("//tools/build_rules:template.bzl", "jinja2_template")
+load("//aos/util:config_validator_macro.bzl", "config_validator_test")
+
+config_validator_test(
+ name = "config_validator_test",
+ config = "//y2022:aos_config",
+)
robot_downloader(
binaries = [
diff --git a/y2022/localizer/BUILD b/y2022/localizer/BUILD
index dd0fb67..7f076e8 100644
--- a/y2022/localizer/BUILD
+++ b/y2022/localizer/BUILD
@@ -150,6 +150,7 @@
"//aos/events:simulated_event_loop",
"//aos/events/logging:log_reader",
"//aos/events/logging:log_writer",
+ "//aos/util:simulation_logger",
"//y2022/control_loops/drivetrain:drivetrain_base",
],
)
diff --git a/y2022/localizer/localizer_replay.cc b/y2022/localizer/localizer_replay.cc
index 0c09535..6dcbb1e 100644
--- a/y2022/localizer/localizer_replay.cc
+++ b/y2022/localizer/localizer_replay.cc
@@ -1,6 +1,7 @@
#include "aos/configuration.h"
#include "aos/events/logging/log_reader.h"
#include "aos/events/logging/log_writer.h"
+#include "aos/util/simulation_logger.h"
#include "aos/events/simulated_event_loop.h"
#include "aos/init.h"
#include "aos/json_to_flatbuffer.h"
@@ -16,27 +17,6 @@
DEFINE_string(output_folder, "/tmp/replayed",
"Name of the folder to write replayed logs to.");
-class LoggerState {
- public:
- LoggerState(aos::logger::LogReader *reader, const aos::Node *node)
- : event_loop_(
- reader->event_loop_factory()->MakeEventLoop("logger", node)),
- namer_(std::make_unique<aos::logger::MultiNodeFilesLogNamer>(
- absl::StrCat(FLAGS_output_folder, "/", node->name()->string_view(),
- "/"),
- event_loop_.get())),
- logger_(std::make_unique<aos::logger::Logger>(event_loop_.get())) {
- event_loop_->SkipTimingReport();
- event_loop_->SkipAosLog();
- event_loop_->OnRun([this]() { logger_->StartLogging(std::move(namer_)); });
- }
-
- private:
- std::unique_ptr<aos::EventLoop> event_loop_;
- std::unique_ptr<aos::logger::LogNamer> namer_;
- std::unique_ptr<aos::logger::Logger> logger_;
-};
-
// TODO(james): Currently, this replay produces logfiles that can't be read due
// to time estimation issues. Pending the active refactorings of the
// timestamp-related code, fix this.
@@ -71,15 +51,13 @@
reader.Register(factory.get());
- std::vector<std::unique_ptr<LoggerState>> loggers;
// List of nodes to create loggers for (note: currently just roborio; this
// code was refactored to allow easily adding new loggers to accommodate
// debugging and potential future changes).
const std::vector<std::string> nodes_to_log = {"imu"};
- for (const std::string &node : nodes_to_log) {
- loggers.emplace_back(std::make_unique<LoggerState>(
- &reader, aos::configuration::GetNode(reader.configuration(), node)));
- }
+ std::vector<std::unique_ptr<aos::util::LoggerState>> loggers =
+ aos::util::MakeLoggersForNodes(reader.event_loop_factory(), nodes_to_log,
+ FLAGS_output_folder);
const aos::Node *node = nullptr;
if (aos::configuration::MultiNode(reader.configuration())) {
diff --git a/y2022/y2022_logger.json b/y2022/y2022_logger.json
index 01800bf..65a6e98 100644
--- a/y2022/y2022_logger.json
+++ b/y2022/y2022_logger.json
@@ -35,7 +35,7 @@
"priority": 2,
"timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
"timestamp_logger_nodes": [
- "roborio"
+ "logger"
],
"time_to_live": 5000000
}
@@ -46,7 +46,7 @@
"type": "aos.message_bridge.RemoteMessage",
"source_node": "logger",
"logger": "NOT_LOGGED",
- "frequency": 20,
+ "frequency": 200,
"num_senders": 2,
"max_size": 200
},
diff --git a/y2022/y2022_pi_template.json b/y2022/y2022_pi_template.json
index 4d3c427..cb90fee 100644
--- a/y2022/y2022_pi_template.json
+++ b/y2022/y2022_pi_template.json
@@ -104,7 +104,7 @@
"time_to_live": 5000000,
"timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
"timestamp_logger_nodes": [
- "roborio"
+ "pi{{ NUM }}"
]
},
{
@@ -113,7 +113,7 @@
"time_to_live": 5000000,
"timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
"timestamp_logger_nodes": [
- "imu"
+ "pi{{ NUM }}"
]
}
]
diff --git a/y2022/y2022_roborio.json b/y2022/y2022_roborio.json
index 068c543..d046568 100644
--- a/y2022/y2022_roborio.json
+++ b/y2022/y2022_roborio.json
@@ -459,7 +459,7 @@
"priority": 5,
"timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
"timestamp_logger_nodes": [
- "imu"
+ "roborio"
],
"time_to_live": 0
}
diff --git a/y2023/BUILD b/y2023/BUILD
index 6be5cac..667b59d 100644
--- a/y2023/BUILD
+++ b/y2023/BUILD
@@ -2,9 +2,9 @@
load("//aos:config.bzl", "aos_config")
load("//tools/build_rules:template.bzl", "jinja2_template")
load("@com_github_google_flatbuffers//:build_defs.bzl", "flatbuffer_cc_library")
-load("//aos/util:config_validator_macro.bzl", "config_validator_rule")
+load("//aos/util:config_validator_macro.bzl", "config_validator_test")
-config_validator_rule(
+config_validator_test(
name = "config_validator_test",
config = "//y2023:aos_config",
)
diff --git a/y2023/localizer/BUILD b/y2023/localizer/BUILD
index 3ef024c..f9d0d28 100644
--- a/y2023/localizer/BUILD
+++ b/y2023/localizer/BUILD
@@ -220,6 +220,7 @@
"//aos/events:simulated_event_loop",
"//aos/events/logging:log_reader",
"//aos/events/logging:log_writer",
+ "//aos/util:simulation_logger",
"//y2023/control_loops/drivetrain:drivetrain_base",
],
)
diff --git a/y2023/localizer/localizer_replay.cc b/y2023/localizer/localizer_replay.cc
index c74b708..27f0d18 100644
--- a/y2023/localizer/localizer_replay.cc
+++ b/y2023/localizer/localizer_replay.cc
@@ -5,9 +5,10 @@
#include "aos/init.h"
#include "aos/json_to_flatbuffer.h"
#include "aos/network/team_number.h"
-#include "y2023/localizer/localizer.h"
+#include "aos/util/simulation_logger.h"
#include "gflags/gflags.h"
#include "y2023/control_loops/drivetrain/drivetrain_base.h"
+#include "y2023/localizer/localizer.h"
DEFINE_string(config, "y2023/aos_config.json",
"Name of the config file to replay using.");
@@ -15,27 +16,6 @@
DEFINE_string(output_folder, "/tmp/replayed",
"Name of the folder to write replayed logs to.");
-class LoggerState {
- public:
- LoggerState(aos::logger::LogReader *reader, const aos::Node *node)
- : event_loop_(
- reader->event_loop_factory()->MakeEventLoop("logger", node)),
- namer_(std::make_unique<aos::logger::MultiNodeFilesLogNamer>(
- absl::StrCat(FLAGS_output_folder, "/", node->name()->string_view(),
- "/"),
- event_loop_.get())),
- logger_(std::make_unique<aos::logger::Logger>(event_loop_.get())) {
- event_loop_->SkipTimingReport();
- event_loop_->SkipAosLog();
- event_loop_->OnRun([this]() { logger_->StartLogging(std::move(namer_)); });
- }
-
- private:
- std::unique_ptr<aos::EventLoop> event_loop_;
- std::unique_ptr<aos::logger::LogNamer> namer_;
- std::unique_ptr<aos::logger::Logger> logger_;
-};
-
int main(int argc, char **argv) {
aos::InitGoogle(&argc, &argv);
@@ -69,15 +49,13 @@
reader.Register(factory.get());
- std::vector<std::unique_ptr<LoggerState>> loggers;
// List of nodes to create loggers for (note: currently just roborio; this
// code was refactored to allow easily adding new loggers to accommodate
// debugging and potential future changes).
const std::vector<std::string> nodes_to_log = {"imu"};
- for (const std::string &node : nodes_to_log) {
- loggers.emplace_back(std::make_unique<LoggerState>(
- &reader, aos::configuration::GetNode(reader.configuration(), node)));
- }
+ std::vector<std::unique_ptr<aos::util::LoggerState>> loggers =
+ aos::util::MakeLoggersForNodes(reader.event_loop_factory(), nodes_to_log,
+ FLAGS_output_folder);
const aos::Node *node = nullptr;
if (aos::configuration::MultiNode(reader.configuration())) {
diff --git a/y2023/localizer/localizer_test.cc b/y2023/localizer/localizer_test.cc
index e8b7d56..2cb5756 100644
--- a/y2023/localizer/localizer_test.cc
+++ b/y2023/localizer/localizer_test.cc
@@ -14,7 +14,6 @@
DEFINE_string(output_folder, "",
"If set, logs all channels to the provided logfile.");
-DECLARE_bool(die_on_malloc);
DECLARE_double(max_distance_to_target);
namespace y2023::localizer::testing {
@@ -75,7 +74,6 @@
status_fetcher_(
imu_test_event_loop_->MakeFetcher<Status>("/localizer")) {
FLAGS_max_distance_to_target = 100.0;
- FLAGS_die_on_malloc = true;
{
aos::TimerHandler *timer = roborio_test_event_loop_->AddTimer([this]() {
{
diff --git a/y2023/y2023_roborio.json b/y2023/y2023_roborio.json
index 13e48dc..172d11c 100644
--- a/y2023/y2023_roborio.json
+++ b/y2023/y2023_roborio.json
@@ -18,15 +18,6 @@
]
},
{
- "name": "/roborio/aos/remote_timestamps/imu/roborio/aos/aos-JoystickState",
- "type": "aos.message_bridge.RemoteMessage",
- "source_node": "roborio",
- "logger": "NOT_LOGGED",
- "frequency": 300,
- "num_senders": 2,
- "max_size": 200
- },
- {
"name": "/roborio/aos",
"type": "aos.RobotState",
"source_node": "roborio",
@@ -234,15 +225,6 @@
]
},
{
- "name": "/roborio/aos/remote_timestamps/imu/drivetrain/frc971-control_loops-drivetrain-Output",
- "type": "aos.message_bridge.RemoteMessage",
- "source_node": "roborio",
- "logger": "NOT_LOGGED",
- "frequency": 400,
- "num_senders": 2,
- "max_size": 200
- },
- {
"name": "/drivetrain",
"type": "frc971.control_loops.drivetrain.Status",
"source_node": "roborio",
@@ -339,9 +321,6 @@
{
"name": "drivetrain",
"executable_name": "drivetrain",
- "args": [
- "--die_on_malloc"
- ],
"nodes": [
"roborio"
]
@@ -349,9 +328,6 @@
{
"name": "trajectory_generator",
"executable_name": "trajectory_generator",
- "args": [
- "--die_on_malloc"
- ],
"nodes": [
"roborio"
]
@@ -359,9 +335,6 @@
{
"name": "superstructure",
"executable_name": "superstructure",
- "args": [
- "--die_on_malloc"
- ],
"nodes": [
"roborio"
]
@@ -389,6 +362,9 @@
{
"name": "wpilib_interface",
"executable_name": "wpilib_interface",
+ "args": [
+ "--nodie_on_malloc"
+ ],
"nodes": [
"roborio"
]