Move constants to JSON file

Change-Id: Icdfc76712bbec326823f1536d0b9fd890f4002f7
Signed-off-by: Henry Speiser <100027428@mvla.net>
Signed-off-by: Henry Speiser <henry@speiser.net>
diff --git a/frc971/constants/BUILD b/frc971/constants/BUILD
new file mode 100644
index 0000000..891067c
--- /dev/null
+++ b/frc971/constants/BUILD
@@ -0,0 +1,63 @@
+cc_library(
+    name = "constants_sender_lib",
+    hdrs = [
+        "constants_sender_lib.h",
+    ],
+    target_compatible_with = ["@platforms//os:linux"],
+    deps = [
+        "//aos:flatbuffer_merge",
+        "//aos:json_to_flatbuffer",
+        "//aos/events:event_loop",
+        "//aos/network:team_number",
+        "@com_github_gflags_gflags//:gflags",
+        "@com_github_google_glog//:glog",
+    ],
+)
+
+cc_binary(
+    name = "constants_sender_example",
+    srcs = [
+        "constants_sender_example.cc",
+    ],
+    data = [
+        "//frc971/constants/testdata:aos_config",
+    ],
+    target_compatible_with = ["@platforms//os:linux"],
+    deps = [
+        "//aos:configuration",
+        "//aos:init",
+        "//aos:json_to_flatbuffer",
+        "//aos/events:shm_event_loop",
+        "//frc971/constants:constants_sender_lib",
+        "//frc971/constants/testdata:constants_data_fbs",
+        "//frc971/constants/testdata:constants_list_fbs",
+        "@com_github_gflags_gflags//:gflags",
+        "@com_github_google_glog//:glog",
+    ],
+)
+
+cc_test(
+    name = "constants_sender_test",
+    srcs = [
+        "constants_sender_test.cc",
+    ],
+    data = [
+        "//frc971/constants/testdata:aos_config",
+        "//frc971/constants/testdata:test_constants.json",
+    ],
+    target_compatible_with = ["@platforms//os:linux"],
+    deps = [
+        ":constants_sender_lib",
+        "//aos:configuration",
+        "//aos:flatbuffer_merge",
+        "//aos:flatbuffers",
+        "//aos:json_to_flatbuffer",
+        "//aos/events:event_loop",
+        "//aos/events:simulated_event_loop",
+        "//aos/testing:googletest",
+        "//aos/testing:path",
+        "//frc971/constants/testdata:constants_data_fbs",
+        "//frc971/constants/testdata:constants_list_fbs",
+        "@com_github_google_glog//:glog",
+    ],
+)
diff --git a/frc971/constants/constants_sender_example.cc b/frc971/constants/constants_sender_example.cc
new file mode 100644
index 0000000..6f9ba01
--- /dev/null
+++ b/frc971/constants/constants_sender_example.cc
@@ -0,0 +1,27 @@
+#include "aos/configuration.h"
+#include "aos/events/shm_event_loop.h"
+#include "aos/init.h"
+#include "aos/json_to_flatbuffer.h"
+#include "constants_sender_lib.h"
+#include "frc971/constants/testdata/constants_data_generated.h"
+#include "frc971/constants/testdata/constants_list_generated.h"
+#include "gflags/gflags.h"
+#include "glog/logging.h"
+
+DEFINE_string(config, "frc971/constants/testdata/aos_config.json",
+              "Path to the config.");
+DEFINE_string(constants_path, "frc971/constants/testdata/test_constants.json",
+              "Path to the constant file");
+// This is just a sample binary
+int main(int argc, char **argv) {
+  aos::InitGoogle(&argc, &argv);
+  aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+      aos::configuration::ReadConfig(FLAGS_config);
+  aos::ShmEventLoop event_loop(&config.message());
+  frc971::constants::ConstantSender<frc971::constants::testdata::ConstantsData,
+                                    frc971::constants::testdata::ConstantsList>
+      constants_sender(&event_loop, FLAGS_constants_path);
+  event_loop.Run();
+
+  return 0;
+}
diff --git a/frc971/constants/constants_sender_lib.h b/frc971/constants/constants_sender_lib.h
new file mode 100644
index 0000000..6c849c3
--- /dev/null
+++ b/frc971/constants/constants_sender_lib.h
@@ -0,0 +1,71 @@
+#ifndef FRC971_CONSTANTS_CONSTANTS_SENDER_H_
+#define FRC971_CONSTANTS_CONSTANTS_SENDER_H_
+
+#include "aos/events/event_loop.h"
+#include "aos/flatbuffer_merge.h"
+#include "aos/json_to_flatbuffer.h"
+#include "aos/network/team_number.h"
+#include "gflags/gflags.h"
+#include "glog/logging.h"
+
+namespace frc971::constants {
+
+// Publishes the constants specific to the current robot
+template <typename ConstantsData, typename ConstantsList>
+class ConstantSender {
+ public:
+  ConstantSender<ConstantsData, ConstantsList>(
+      aos::EventLoop *event_loop, std::string constants_path,
+      std::string_view channel_name = "/constants")
+      : ConstantSender<ConstantsData, ConstantsList>(
+            event_loop, constants_path, aos::network::GetTeamNumber(),
+            channel_name) {}
+
+  ConstantSender<ConstantsData, ConstantsList>(aos::EventLoop *event_loop,
+                                               std::string constants_path,
+                                               const uint16_t team_number,
+                                               std::string_view channel_name)
+      : team_number_(team_number),
+        channel_name_(channel_name),
+        constants_path_(constants_path),
+        event_loop_(event_loop),
+        sender_(event_loop_->MakeSender<ConstantsData>(channel_name_)) {
+    event_loop->OnRun([this]() {
+      typename aos::Sender<ConstantsData>::Builder builder =
+          sender_.MakeBuilder();
+      builder.CheckOk(builder.Send(GetConstantsForTeamNumber(builder.fbb())));
+    });
+  }
+
+ private:
+  const uint16_t team_number_ = 0;
+  std::string_view channel_name_;
+  flatbuffers::Offset<ConstantsData> GetConstantsForTeamNumber(
+      flatbuffers::FlatBufferBuilder *fbb) {
+    aos::FlatbufferDetachedBuffer<ConstantsList> fb =
+        aos::JsonFileToFlatbuffer<ConstantsList>(constants_path_);
+    const ConstantsList &message = fb.message();
+    const auto *constants = message.constants();
+    // Search through the constants for the one matching our team number.
+    for (const auto &constant_data : *constants) {
+      if (team_number_ == constant_data->team()) {
+        // Values is equal to the constants meant for the specific robot.
+        const ConstantsData *values = constant_data->data();
+        flatbuffers::Offset<ConstantsData> flatbuffer_constants =
+            aos::RecursiveCopyFlatBuffer(values, fbb);
+        return flatbuffer_constants;
+      }
+    }
+    LOG(FATAL) << "There was no match for " << team_number_
+               << ". Check the constants.json file for the team number that is "
+                  "missing.";
+  }
+
+  std::string constants_path_;
+  aos::EventLoop *event_loop_;
+  aos::Sender<ConstantsData> sender_;
+};
+
+}  // namespace frc971::constants
+
+#endif  // FRC971_CONSTANTS_CONSTANTS_SENDER_H_
diff --git a/frc971/constants/constants_sender_test.cc b/frc971/constants/constants_sender_test.cc
new file mode 100644
index 0000000..3edc320
--- /dev/null
+++ b/frc971/constants/constants_sender_test.cc
@@ -0,0 +1,97 @@
+#include "aos/configuration.h"
+#include "aos/events/event_loop.h"
+#include "aos/events/simulated_event_loop.h"
+#include "aos/flatbuffer_merge.h"
+#include "aos/json_to_flatbuffer.h"
+#include "aos/testing/path.h"
+#include "frc971/constants/constants_sender_lib.h"
+#include "frc971/constants/testdata/constants_data_generated.h"
+#include "frc971/constants/testdata/constants_list_generated.h"
+#include "glog/logging.h"
+#include "gtest/gtest.h"
+
+namespace frc971::constants {
+namespace testing {
+
+using aos::testing::ArtifactPath;
+
+class ConstantSenderTest : public ::testing::Test {
+ public:
+  ConstantSenderTest()
+      : config_(aos::configuration::ReadConfig(
+            ArtifactPath("frc971/constants/testdata/aos_config.json"))),
+        event_loop_factory_(&config_.message()),
+        constants_sender_event_loop_(
+            event_loop_factory_.MakeEventLoop("sender")) {}
+
+  aos::FlatbufferDetachedBuffer<aos::Configuration> config_;
+  aos::SimulatedEventLoopFactory event_loop_factory_;
+  std::unique_ptr<aos::EventLoop> constants_sender_event_loop_;
+};
+
+// For team 971, compares the data that is recived from the program, to the data
+// that is expected
+
+TEST_F(ConstantSenderTest, HasData971) {
+  aos::network::OverrideTeamNumber(971);
+
+  std::unique_ptr<aos::EventLoop> test_event_loop =
+      event_loop_factory_.MakeEventLoop("constants");
+  ConstantSender<testdata::ConstantsData, testdata::ConstantsList> test971(
+      constants_sender_event_loop_.get(),
+      "frc971/constants/testdata/test_constants.json", "/constants");
+  test_event_loop->MakeWatcher("/constants",
+                               [](const testdata::ConstantsData &data) {
+                                 EXPECT_EQ(data.max_roller_voltage(), 12);
+                                 EXPECT_EQ(data.min_roller_voltage(), -12);
+                               });
+  event_loop_factory_.RunFor(std::chrono::seconds(1));
+}
+
+// For team 9971, compares the data that is recived from the program, to the
+// data that is expected.
+
+TEST_F(ConstantSenderTest, HasData9971) {
+  std::unique_ptr<aos::EventLoop> test_event_loop =
+      event_loop_factory_.MakeEventLoop("constants");
+  ConstantSender<testdata::ConstantsData, testdata::ConstantsList> test971(
+      constants_sender_event_loop_.get(),
+      "frc971/constants/testdata/test_constants.json", 9971, "/constants");
+  test_event_loop->MakeWatcher("/constants",
+                               [](const testdata::ConstantsData &data) {
+                                 EXPECT_EQ(data.max_roller_voltage(), 6);
+                                 EXPECT_EQ(data.min_roller_voltage(), -6);
+                               });
+  event_loop_factory_.RunFor(std::chrono::seconds(1));
+}
+
+// When given a team number that it not recognized we kill the program.
+
+TEST_F(ConstantSenderTest, TeamNotFound) {
+  EXPECT_DEATH(
+      ({
+        ConstantSender<testdata::ConstantsData, testdata::ConstantsList>
+            test_no_team(constants_sender_event_loop_.get(),
+                         "frc971/constants/testdata/test_constants.json", 254,
+                         "/constants");
+        event_loop_factory_.RunFor(std::chrono::seconds(1));
+      }),
+      "There was no match for 254");
+}
+
+// If the json file has syntax errors it will die.
+
+TEST_F(ConstantSenderTest, SyntaxErrorDeath) {
+  EXPECT_DEATH(
+      ({
+        ConstantSender<testdata::ConstantsData, testdata::ConstantsList>
+            test_syntax(constants_sender_event_loop_.get(),
+                        "frc971/constants/testdata/syntaxerror.json", 971,
+                        "/constants");
+        event_loop_factory_.RunFor(std::chrono::seconds(1));
+      }),
+      "Error on line 0");
+}
+
+}  // namespace testing
+}  // namespace frc971::constants
diff --git a/frc971/constants/testdata/BUILD b/frc971/constants/testdata/BUILD
new file mode 100644
index 0000000..cdfb0c9
--- /dev/null
+++ b/frc971/constants/testdata/BUILD
@@ -0,0 +1,37 @@
+load("//aos:config.bzl", "aos_config")
+load("@com_github_google_flatbuffers//:build_defs.bzl", "flatbuffer_cc_library")
+
+exports_files(["test_constants.json"])
+
+flatbuffer_cc_library(
+    name = "constants_list_fbs",
+    srcs = ["constants_list.fbs"],
+    gen_reflections = 1,
+    includes = [
+        ":constants_data_fbs_includes",
+    ],
+    target_compatible_with = ["@platforms//os:linux"],
+    visibility = ["//visibility:public"],
+)
+
+flatbuffer_cc_library(
+    name = "constants_data_fbs",
+    srcs = ["constants_data.fbs"],
+    gen_reflections = 1,
+    target_compatible_with = ["@platforms//os:linux"],
+    visibility = ["//visibility:public"],
+)
+
+aos_config(
+    name = "aos_config",
+    src = "aos_config.json",
+    flatbuffers = [
+        "//frc971/constants/testdata:constants_data_fbs",
+        "//frc971/constants/testdata:constants_list_fbs",
+    ],
+    target_compatible_with = ["@platforms//os:linux"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//aos/events:aos_config",
+    ],
+)
diff --git a/frc971/constants/testdata/aos_config.json b/frc971/constants/testdata/aos_config.json
new file mode 100644
index 0000000..de4d367
--- /dev/null
+++ b/frc971/constants/testdata/aos_config.json
@@ -0,0 +1,14 @@
+{
+  "channels":
+  [
+    {
+      "name": "/constants",
+      "type":"frc971.constants.testdata.ConstantsData",
+      "frequency": 3
+    }
+  ],
+  "imports":
+  [
+    "../../../aos/events/aos.json"
+  ]
+}
diff --git a/frc971/constants/testdata/config.json b/frc971/constants/testdata/config.json
new file mode 100644
index 0000000..de4d367
--- /dev/null
+++ b/frc971/constants/testdata/config.json
@@ -0,0 +1,14 @@
+{
+  "channels":
+  [
+    {
+      "name": "/constants",
+      "type":"frc971.constants.testdata.ConstantsData",
+      "frequency": 3
+    }
+  ],
+  "imports":
+  [
+    "../../../aos/events/aos.json"
+  ]
+}
diff --git a/frc971/constants/testdata/constants_data.fbs b/frc971/constants/testdata/constants_data.fbs
new file mode 100644
index 0000000..e64df68
--- /dev/null
+++ b/frc971/constants/testdata/constants_data.fbs
@@ -0,0 +1,8 @@
+namespace frc971.constants.testdata;
+
+table ConstantsData {
+  max_roller_voltage:float (id: 0);
+  min_roller_voltage:float (id: 1);
+}
+
+root_type ConstantsData;
diff --git a/frc971/constants/testdata/constants_list.fbs b/frc971/constants/testdata/constants_list.fbs
new file mode 100644
index 0000000..8257690
--- /dev/null
+++ b/frc971/constants/testdata/constants_list.fbs
@@ -0,0 +1,14 @@
+include "frc971/constants/testdata/constants_data.fbs";
+
+namespace frc971.constants.testdata;
+
+table ConstantsList {
+  constants:[Constant] (id: 0);
+}
+
+table Constant {
+  team:long (id: 0);
+  data:ConstantsData (id: 1);
+}
+
+root_type ConstantsList;
diff --git a/frc971/constants/testdata/syntax_error.json b/frc971/constants/testdata/syntax_error.json
new file mode 100644
index 0000000..e9f3b4c
--- /dev/null
+++ b/frc971/constants/testdata/syntax_error.json
@@ -0,0 +1,10 @@
+{
+  "constants: [
+  {
+      team": 971,
+      "data": {
+        max_roller_voltage: 12.0,
+        "min_roller_voltage": -12.0
+       }
+  ]
+}
diff --git a/frc971/constants/testdata/test_constants.json b/frc971/constants/testdata/test_constants.json
new file mode 100644
index 0000000..8cd3efa
--- /dev/null
+++ b/frc971/constants/testdata/test_constants.json
@@ -0,0 +1,12 @@
+{
+  "constants": [
+    {
+      "team": 971,
+      "data": {"max_roller_voltage": 12.0, "min_roller_voltage": -12.0}
+    },
+    {
+      "team": 9971,
+      "data": {"max_roller_voltage": 6.0, "min_roller_voltage": -6.0}
+    }
+  ]
+}