Merge "Remove Potentiometer from Intake Pivot"
diff --git a/WORKSPACE b/WORKSPACE
index 7809594..a5da525 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1433,6 +1433,14 @@
)
http_archive(
+ name = "com_github_nghttp2_nghttp2",
+ build_file = "//debian:BUILD.nghttp2.bazel",
+ sha256 = "7da19947b33a07ddcf97b9791331bfee8a8545e6b394275a9971f43cae9d636b",
+ strip_prefix = "nghttp2-1.58.0",
+ url = "https://github.com/nghttp2/nghttp2/archive/refs/tags/v1.58.0.tar.gz",
+)
+
+http_archive(
# No official name exists. Names used in our external dependencies include
# zlib, madler_zlib, com_github_madler_zlib.
name = "zlib",
diff --git a/aos/BUILD b/aos/BUILD
index 4d041ca..7c0ad3d 100644
--- a/aos/BUILD
+++ b/aos/BUILD
@@ -239,6 +239,7 @@
srcs = ["configuration.fbs"],
target_compatible_with = ["@platforms//os:linux"],
visibility = ["//visibility:public"],
+ deps = ["//aos/flatbuffers/reflection:reflection_fbs"],
)
cc_static_flatbuffer(
diff --git a/aos/configuration.fbs b/aos/configuration.fbs
index 0b42f5b..b2b34c1 100644
--- a/aos/configuration.fbs
+++ b/aos/configuration.fbs
@@ -169,6 +169,10 @@
// If set, this is the memory limit to enforce in bytes for the application
// (and it's children)
memory_limit:uint64 = 0 (id: 8);
+
+ // If set, this is the number of nanoseconds the application has to stop. If the application
+ // doesn't stop within the specified time, then it is killed.
+ stop_time:int64 = 1000000000 (id: 9);
}
// Per node data and connection information.
diff --git a/aos/events/logging/log_backend_test.cc b/aos/events/logging/log_backend_test.cc
index d3c83cc..1e95c10 100644
--- a/aos/events/logging/log_backend_test.cc
+++ b/aos/events/logging/log_backend_test.cc
@@ -128,7 +128,17 @@
TEST(QueueAlignmentTest, Cases) {
QueueAligner aligner;
- uint8_t *start = nullptr;
+
+ // Get a 512-byte-aligned pointer to a buffer. That buffer needs to be at
+ // least 3 sectors big for the purposes of this test.
+ uint8_t buffer[FileHandler::kSector * 4];
+ void *aligned_start = buffer;
+ size_t size = sizeof(buffer);
+ ASSERT_TRUE(std::align(FileHandler::kSector, FileHandler::kSector * 3,
+ aligned_start, size) != nullptr);
+ ASSERT_GE(size, FileHandler::kSector * 3);
+
+ uint8_t *start = static_cast<uint8_t *>(aligned_start);
{
// Only prefix
std::vector<absl::Span<const uint8_t>> queue;
diff --git a/aos/events/logging/logfile_utils_out_of_space_test.sh b/aos/events/logging/logfile_utils_out_of_space_test.sh
index f412e0d..6a32347 100755
--- a/aos/events/logging/logfile_utils_out_of_space_test.sh
+++ b/aos/events/logging/logfile_utils_out_of_space_test.sh
@@ -23,7 +23,7 @@
rm -rf "${TMPFS}"
mkdir "${TMPFS}"
-function test {
+function run_test {
SIZE="$1"
echo "Running test with ${SIZE}..." >&2
unshare --mount --map-root-user bash <<END
@@ -37,10 +37,10 @@
}
# Run out of space exactly at the beginning of a block.
-test 81920
+run_test 81920
# Run out of space 1 byte into a block.
-test 81921
+run_test 81921
# Run out of space in the middle of a block.
-test 87040
+run_test 87040
diff --git a/aos/events/logging/multinode_logger_test_lib.h b/aos/events/logging/multinode_logger_test_lib.h
index 63604d6..8f64f66 100644
--- a/aos/events/logging/multinode_logger_test_lib.h
+++ b/aos/events/logging/multinode_logger_test_lib.h
@@ -76,13 +76,13 @@
};
constexpr std::string_view kCombinedConfigSha1() {
- return "32514f3a686e5f8936cc4651e7c81350112f7be8d80dcb8d4afaa29d233c5619";
+ return "71eb8341221fbabefb4ddde43bcebf794fd5855e3ad77786a1db0f9e27a39091";
}
constexpr std::string_view kSplitConfigSha1() {
- return "416da222c09d83325c6f453591d34c7ef12c12c2dd129ddeea657c4bec61b7fd";
+ return "f61d45dc0bda026e852e2da9b3e5c2c7f1c89c9f7958cfba3d02e2c960416f04";
}
constexpr std::string_view kReloggedSplitConfigSha1() {
- return "3fe428684a38298d3323ef087f44517574da3f07dd84b3740829156d6d870108";
+ return "3d8fd3d13955b517ee3d66a50b5e4dd7a13fd648f469d16910990418bcfc6beb";
}
LoggerState MakeLoggerState(NodeEventLoopFactory *node,
diff --git a/aos/flatbuffers.h b/aos/flatbuffers.h
index a299fe9..607303c 100644
--- a/aos/flatbuffers.h
+++ b/aos/flatbuffers.h
@@ -118,6 +118,11 @@
// make attempts to use it fail more obviously.
void Wipe() { memset(span().data(), 0, span().size()); }
+ // Returns true if the flatbuffer is valid. Returns false if either:
+ // * The flatbuffer is incorrectly constructed (e.g., it points to memory
+ // locations outside of the current memory buffer).
+ // * The flatbuffer is too complex, and the flatbuffer verifier chosen to bail
+ // when attempting to traverse the tree of tables.
bool Verify() const {
if (span().size() < 4u) {
return false;
diff --git a/aos/flatbuffers/BUILD b/aos/flatbuffers/BUILD
index 08d548f..32f1d39 100644
--- a/aos/flatbuffers/BUILD
+++ b/aos/flatbuffers/BUILD
@@ -102,6 +102,7 @@
":test_schema",
"//aos:flatbuffers",
"//aos:json_to_flatbuffer",
+ "//aos/flatbuffers/test_dir:include_reflection_fbs",
"//aos/flatbuffers/test_dir:type_coverage_fbs",
"//aos/testing:googletest",
"//aos/testing:path",
diff --git a/aos/flatbuffers/builder.h b/aos/flatbuffers/builder.h
index db89d10..36225c0 100644
--- a/aos/flatbuffers/builder.h
+++ b/aos/flatbuffers/builder.h
@@ -77,6 +77,9 @@
FlatbufferSpan<typename T::Flatbuffer> AsFlatbufferSpan() {
return {buffer()};
}
+ FlatbufferSpan<const typename T::Flatbuffer> AsFlatbufferSpan() const {
+ return {buffer()};
+ }
// Returns true if the flatbuffer is validly constructed. Should always return
// true (barring some sort of memory corruption). Exposed for convenience.
diff --git a/aos/flatbuffers/reflection/BUILD.bazel b/aos/flatbuffers/reflection/BUILD.bazel
new file mode 100644
index 0000000..475f2a2
--- /dev/null
+++ b/aos/flatbuffers/reflection/BUILD.bazel
@@ -0,0 +1,18 @@
+load("@aspect_bazel_lib//lib:copy_file.bzl", "copy_file")
+load("//aos/flatbuffers:generate.bzl", "static_flatbuffer")
+
+copy_file(
+ name = "reflection_fbs_copy",
+ src = "@com_github_google_flatbuffers//reflection:reflection_fbs_schema",
+ out = "reflection.fbs",
+)
+
+# This autogenerates both a reflection_static.h and a reflection_generated.h.
+# However, in order to avoid having two conflicting headers floating around,
+# we forcibly override the #include to use flatbuffers/reflection_generated.h
+# in static_flatbuffers.cc
+static_flatbuffer(
+ name = "reflection_fbs",
+ srcs = ["reflection.fbs"],
+ visibility = ["//visibility:public"],
+)
diff --git a/aos/flatbuffers/static_flatbuffers.cc b/aos/flatbuffers/static_flatbuffers.cc
index 8d8942c..c1b617f 100644
--- a/aos/flatbuffers/static_flatbuffers.cc
+++ b/aos/flatbuffers/static_flatbuffers.cc
@@ -19,6 +19,8 @@
bool is_inline = true;
// Whether this is a struct or not.
bool is_struct = false;
+ // Whether this is a repeated type (vector or string).
+ bool is_repeated = false;
// Full C++ type of this field.
std::string full_type = "";
// Full flatbuffer type for this field.
@@ -118,6 +120,22 @@
const std::string IncludePathForFbs(
std::string_view fbs_file, std::string_view include_suffix = "static") {
+ // Special case for the reflection_generated.h, which is checked into the
+ // repo.
+ // Note that we *do* autogenerated the reflection_static.h but that because
+ // it uses a special import path, we end up overriding the include anyways
+ // (note that we could muck around with the paths on the bazel side to instead
+ // get a cc_library with the correct include paths specified, although it is
+ // not clear that that would be any simpler than the extra else-if).
+ if (fbs_file == "reflection/reflection.fbs") {
+ if (include_suffix == "generated") {
+ return "flatbuffers/reflection_generated.h";
+ } else if (include_suffix == "static") {
+ return "aos/flatbuffers/reflection/reflection_static.h";
+ } else {
+ LOG(FATAL) << "This should be unreachable.";
+ }
+ }
fbs_file.remove_suffix(4);
return absl::StrCat(fbs_file, "_", include_suffix, ".h");
}
@@ -151,12 +169,14 @@
// straightforwards.
field->is_inline = true;
field->is_struct = false;
+ field->is_repeated = false;
field->full_type =
ScalarOrEnumType(schema, type->base_type(), type->index());
return;
case reflection::BaseType::String: {
field->is_inline = false;
field->is_struct = false;
+ field->is_repeated = true;
field->full_type =
absl::StrFormat("::aos::fbs::String<%d>",
GetLengthAttributeOrZero(field_fbs, "static_length"));
@@ -166,6 +186,7 @@
// We need to extract the name of the elements of the vector.
std::string element_type;
bool elements_are_inline = true;
+ field->is_repeated = true;
if (type->base_type() == reflection::BaseType::Vector) {
switch (type->element()) {
case reflection::BaseType::Obj: {
@@ -207,6 +228,7 @@
const reflection::Object *object = GetObject(schema, type->index());
field->is_inline = object->is_struct();
field->is_struct = object->is_struct();
+ field->is_repeated = false;
const std::string flatbuffer_name =
FlatbufferNameToCppName(object->name()->string_view());
if (field->is_inline) {
@@ -437,27 +459,97 @@
absl::StrJoin(clearers, "\n"));
}
+// Creates the FromFlatbuffer() method that copies from a flatbuffer object API
+// object (i.e., the FlatbufferT types).
+std::string MakeObjectCopier(const std::vector<FieldData> &fields) {
+ std::vector<std::string> copiers;
+ for (const FieldData &field : fields) {
+ if (field.is_struct) {
+ // Structs are stored as unique_ptr<FooStruct>
+ copiers.emplace_back(absl::StrFormat(R"code(
+ if (other.%s) {
+ set_%s(*other.%s);
+ }
+ )code",
+ field.name, field.name, field.name));
+ } else if (field.is_inline) {
+ // Inline non-struct elements are stored as FooType.
+ copiers.emplace_back(absl::StrFormat(R"code(
+ set_%s(other.%s);
+ )code",
+ field.name, field.name));
+ } else if (field.is_repeated) {
+ // strings are stored as std::string's.
+ // vectors are stored as std::vector's.
+ copiers.emplace_back(absl::StrFormat(R"code(
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_%s())->FromFlatbuffer(other.%s)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+ )code",
+ field.name, field.name));
+ } else {
+ // Tables are stored as unique_ptr<FooTable>
+ copiers.emplace_back(absl::StrFormat(R"code(
+ if (other.%s) {
+ if (!CHECK_NOTNULL(add_%s())->FromFlatbuffer(*other.%s)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+ }
+ )code",
+ field.name, field.name, field.name));
+ }
+ }
+ return absl::StrFormat(
+ R"code(
+ // Copies the contents of the provided flatbuffer into this flatbuffer,
+ // returning true on success.
+ // Because the Flatbuffer Object API does not provide any concept of an
+ // optionally populated scalar field, all scalar fields will be populated
+ // after a call to FromFlatbufferObject().
+ // This is a deep copy, and will call FromFlatbufferObject on
+ // any constituent objects.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer::NativeTableType &other) {
+ Clear();
+ %s
+ return true;
+ }
+ [[nodiscard]] bool FromFlatbuffer(const flatbuffers::unique_ptr<Flatbuffer::NativeTableType>& other) {
+ return FromFlatbuffer(*other);
+ }
+)code",
+ absl::StrJoin(copiers, "\n"));
+}
+
+// Creates the FromFlatbuffer() method that copies from an actual flatbuffer
+// object.
std::string MakeCopier(const std::vector<FieldData> &fields) {
std::vector<std::string> copiers;
for (const FieldData &field : fields) {
if (field.is_struct) {
copiers.emplace_back(absl::StrFormat(R"code(
- if (other->has_%s()) {
- set_%s(*other->%s());
+ if (other.has_%s()) {
+ set_%s(*other.%s());
}
)code",
field.name, field.name, field.name));
} else if (field.is_inline) {
copiers.emplace_back(absl::StrFormat(R"code(
- if (other->has_%s()) {
- set_%s(other->%s());
+ if (other.has_%s()) {
+ set_%s(other.%s());
}
)code",
field.name, field.name, field.name));
} else {
copiers.emplace_back(absl::StrFormat(R"code(
- if (other->has_%s()) {
- if (!CHECK_NOTNULL(add_%s())->FromFlatbuffer(other->%s())) {
+ if (other.has_%s()) {
+ if (!CHECK_NOTNULL(add_%s())->FromFlatbuffer(other.%s())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
@@ -473,11 +565,16 @@
// returning true on success.
// This is a deep copy, and will call FromFlatbuffer on any constituent
// objects.
- [[nodiscard]] bool FromFlatbuffer(const Flatbuffer *other) {
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer &other) {
Clear();
%s
return true;
}
+ // Equivalent to FromFlatbuffer(const Flatbuffer&); this overload is provided
+ // to ease implementation of the aos::fbs::Vector internals.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer *other) {
+ return FromFlatbuffer(*CHECK_NOTNULL(other));
+ }
)code",
absl::StrJoin(copiers, "\n"));
}
@@ -689,6 +786,7 @@
public:
// The underlying "raw" flatbuffer type for this type.
typedef %s Flatbuffer;
+ typedef flatbuffers::unique_ptr<Flatbuffer::NativeTableType> FlatbufferObjectType;
// Returns this object as a flatbuffer type. This reference may not be valid
// following mutations to the underlying flatbuffer, due to how memory may get
// may get moved around.
@@ -699,6 +797,7 @@
%s
%s
%s
+%s
private:
%s
%s
@@ -708,7 +807,7 @@
)code",
type_namespace, type_name, FlatbufferNameToCppName(fbs_type_name),
constants, MakeConstructor(type_name), type_name, accessors,
- MakeFullClearer(fields), MakeCopier(fields),
+ MakeFullClearer(fields), MakeCopier(fields), MakeObjectCopier(fields),
MakeMoveConstructor(type_name), members, MakeSubObjectList(fields));
GeneratedObject result;
diff --git a/aos/flatbuffers/static_flatbuffers_test.cc b/aos/flatbuffers/static_flatbuffers_test.cc
index fd95db3..52fa01e 100644
--- a/aos/flatbuffers/static_flatbuffers_test.cc
+++ b/aos/flatbuffers/static_flatbuffers_test.cc
@@ -10,6 +10,7 @@
#include "aos/flatbuffers.h"
#include "aos/flatbuffers/builder.h"
#include "aos/flatbuffers/interesting_schemas.h"
+#include "aos/flatbuffers/test_dir/include_reflection_static.h"
#include "aos/flatbuffers/test_dir/type_coverage_static.h"
#include "aos/flatbuffers/test_schema.h"
#include "aos/flatbuffers/test_static.h"
@@ -1042,4 +1043,55 @@
TestMemory(builder.buffer());
}
+// Uses a small example to manually verify that we can copy from the flatbuffer
+// object API.
+TEST_F(StaticFlatbuffersTest, ObjectApiCopy) {
+ aos::fbs::testing::TestTableT object_t;
+ object_t.scalar = 971;
+ object_t.vector_of_strings.push_back("971");
+ object_t.vector_of_structs.push_back({1, 2});
+ object_t.subtable = std::make_unique<SubTableT>();
+ aos::fbs::VectorAllocator allocator;
+ Builder<TestTableStatic> builder(&allocator);
+ ASSERT_TRUE(builder->FromFlatbuffer(object_t));
+ ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
+ // Note that vectors and strings get set to zero-length, but present, values.
+ EXPECT_EQ(
+ "{ \"scalar\": 971, \"vector_of_scalars\": [ ], \"string\": \"\", "
+ "\"vector_of_strings\": [ \"971\" ], \"subtable\": { \"foo\": 0, "
+ "\"baz\": 0.0 }, \"vector_aligned\": [ ], \"vector_of_structs\": [ { "
+ "\"x\": 1.0, \"y\": 2.0 } ], \"vector_of_tables\": [ ], "
+ "\"unspecified_length_vector\": [ ], \"unspecified_length_string\": "
+ "\"\", \"unspecified_length_vector_of_strings\": [ ] }",
+ aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
+}
+
+// More completely covers our object API copying by comparing the flatbuffer
+// Pack() methods to our FromFlatbuffer() methods.
+TEST_F(StaticFlatbuffersTest, FlatbufferObjectTypeCoverage) {
+ VerifyJson<aos::testing::ConfigurationStatic>("{\n\n}");
+ std::string populated_config =
+ aos::util::ReadFileToStringOrDie(aos::testing::ArtifactPath(
+ "aos/flatbuffers/test_dir/type_coverage.json"));
+ Builder<aos::testing::ConfigurationStatic> json_builder =
+ aos::JsonToStaticFlatbuffer<aos::testing::ConfigurationStatic>(
+ populated_config);
+ aos::testing::ConfigurationT object_t;
+ json_builder->AsFlatbuffer().UnPackTo(&object_t);
+
+ Builder<aos::testing::ConfigurationStatic> from_object_static;
+ ASSERT_TRUE(from_object_static->FromFlatbuffer(object_t));
+ flatbuffers::FlatBufferBuilder fbb;
+ fbb.Finish(aos::testing::Configuration::Pack(fbb, &object_t));
+ aos::FlatbufferDetachedBuffer<aos::testing::Configuration> from_object_raw =
+ fbb.Release();
+ EXPECT_EQ(aos::FlatbufferToJson(from_object_raw, {.multi_line = true}),
+ aos::FlatbufferToJson(from_object_static, {.multi_line = true}));
+}
+
+// Tests that we can build code that uses the reflection types.
+TEST_F(StaticFlatbuffersTest, IncludeReflectionTypes) {
+ VerifyJson<::aos::testing::UseSchemaStatic>("{\n\n}");
+}
+
} // namespace aos::fbs::testing
diff --git a/aos/flatbuffers/static_vector.h b/aos/flatbuffers/static_vector.h
index 7349a8d..6133075 100644
--- a/aos/flatbuffers/static_vector.h
+++ b/aos/flatbuffers/static_vector.h
@@ -203,6 +203,11 @@
typename internal::InlineWrapper<T, kInline>::FlatbufferType;
using ConstFlatbufferType =
typename internal::InlineWrapper<T, kInline>::ConstFlatbufferType;
+ // FlatbufferObjectType corresponds to the type used by the flatbuffer
+ // "object" API (i.e. the FlatbufferT types).
+ // This type will be something unintelligble for inline types.
+ using FlatbufferObjectType =
+ typename internal::InlineWrapper<T, kInline>::FlatbufferObjectType;
// flatbuffers::Vector type that corresponds to this Vector.
typedef flatbuffers::Vector<FlatbufferType> Flatbuffer;
typedef const flatbuffers::Vector<ConstFlatbufferType> ConstFlatbuffer;
@@ -329,7 +334,70 @@
// we can allocate through reserve()).
// This is a deep copy, and will call FromFlatbuffer on any constituent
// objects.
- [[nodiscard]] bool FromFlatbuffer(ConstFlatbuffer *vector);
+ [[nodiscard]] bool FromFlatbuffer(ConstFlatbuffer *vector) {
+ return FromFlatbuffer(*CHECK_NOTNULL(vector));
+ }
+ [[nodiscard]] bool FromFlatbuffer(ConstFlatbuffer &vector);
+ // The remaining FromFlatbuffer() overloads are for when using the flatbuffer
+ // "object" API, which uses std::vector's for representing vectors.
+ [[nodiscard]] bool FromFlatbuffer(const std::vector<InlineType> &vector) {
+ static_assert(kInline);
+ return FromData(vector.data(), vector.size());
+ }
+ // Overload for vectors of bools, since the standard library may not use a
+ // full byte per vector element.
+ [[nodiscard]] bool FromFlatbuffer(const std::vector<bool> &vector) {
+ static_assert(kInline);
+ // We won't be able to do a clean memcpy because std::vector<bool> may be
+ // implemented using bit-packing.
+ return FromIterator(vector.cbegin(), vector.cend());
+ }
+ // Overload for non-inline types. Note that to avoid having this overload get
+ // resolved with inline types, we make FlatbufferObjectType != InlineType.
+ [[nodiscard]] bool FromFlatbuffer(
+ const std::vector<FlatbufferObjectType> &vector) {
+ static_assert(!kInline);
+ return FromNotInlineIterable(vector);
+ }
+
+ // Copies values from the provided data pointer into the vector, resizing the
+ // vector as needed to match. Returns false on failure (e.g., if the
+ // underlying allocator has insufficient space to perform the copy). Only
+ // works for inline data types.
+ [[nodiscard]] bool FromData(const InlineType *input_data, size_t input_size) {
+ static_assert(kInline);
+ if (!reserve(input_size)) {
+ return false;
+ }
+
+ // We will be overwriting the whole vector very shortly; there is no need to
+ // clear the buffer to zero.
+ resize_inline(input_size, SetZero::kNo);
+
+ memcpy(inline_data(), input_data, size() * sizeof(InlineType));
+ return true;
+ }
+
+ // Copies values from the provided iterators into the vector, resizing the
+ // vector as needed to match. Returns false on failure (e.g., if the
+ // underlying allocator has insufficient space to perform the copy). Only
+ // works for inline data types.
+ // Does not attempt any optimizations if the iterators meet the
+ // std::contiguous_iterator concept; instead, it simply copies each element
+ // out one-by-one.
+ template <typename Iterator>
+ [[nodiscard]] bool FromIterator(Iterator begin, Iterator end) {
+ static_assert(kInline);
+ resize(0);
+ for (Iterator it = begin; it != end; ++it) {
+ if (!reserve(size() + 1)) {
+ return false;
+ }
+ // Should never fail, due to the reserve() above.
+ CHECK(emplace_back(*it));
+ }
+ return true;
+ }
// Returns the element at the provided index. index must be less than size().
const T &at(size_t index) const {
@@ -569,29 +637,22 @@
}
// Implementation that handles copying from a flatbuffers::Vector of an inline
// data type.
- [[nodiscard]] bool FromInlineFlatbuffer(ConstFlatbuffer *vector) {
- if (!reserve(CHECK_NOTNULL(vector)->size())) {
- return false;
- }
-
- // We will be overwriting the whole vector very shortly; there is no need to
- // clear the buffer to zero.
- resize_inline(vector->size(), SetZero::kNo);
-
- memcpy(inline_data(), vector->Data(), size() * sizeof(InlineType));
- return true;
+ [[nodiscard]] bool FromInlineFlatbuffer(ConstFlatbuffer &vector) {
+ return FromData(reinterpret_cast<const InlineType *>(vector.Data()),
+ vector.size());
}
// Implementation that handles copying from a flatbuffers::Vector of a
// not-inline data type.
- [[nodiscard]] bool FromNotInlineFlatbuffer(const Flatbuffer *vector) {
- if (!reserve(vector->size())) {
+ template <typename Iterable>
+ [[nodiscard]] bool FromNotInlineIterable(const Iterable &vector) {
+ if (!reserve(vector.size())) {
return false;
}
// "Clear" the vector.
resize_not_inline(0);
- for (const typename T::Flatbuffer *entry : *vector) {
+ for (const auto &entry : vector) {
if (!CHECK_NOTNULL(emplace_back())->FromFlatbuffer(entry)) {
return false;
}
@@ -599,6 +660,10 @@
return true;
}
+ [[nodiscard]] bool FromNotInlineFlatbuffer(const Flatbuffer &vector) {
+ return FromNotInlineIterable(vector);
+ }
+
// In order to allow for easy partial template specialization, we use a
// non-member class to call FromInline/FromNotInlineFlatbuffer and
// resize_inline/resize_not_inline. There are not actually any great ways to
@@ -659,6 +724,7 @@
public:
typedef Vector<char, kStaticLength, true, 0, true> VectorType;
typedef flatbuffers::String Flatbuffer;
+ typedef std::string FlatbufferObjectType;
String(std::span<uint8_t> buffer, ResizeableObject *parent)
: VectorType(buffer, parent) {}
virtual ~String() {}
@@ -667,6 +733,10 @@
VectorType::resize_inline(string.size(), SetZero::kNo);
memcpy(VectorType::data(), string.data(), string.size());
}
+ using VectorType::FromFlatbuffer;
+ [[nodiscard]] bool FromFlatbuffer(const std::string &string) {
+ return VectorType::FromData(string.data(), string.size());
+ }
std::string_view string_view() const {
return std::string_view(VectorType::data(), VectorType::size());
}
@@ -690,12 +760,13 @@
typedef T ObjectType;
typedef flatbuffers::Offset<typename T::Flatbuffer> FlatbufferType;
typedef flatbuffers::Offset<typename T::Flatbuffer> ConstFlatbufferType;
+ typedef T::FlatbufferObjectType FlatbufferObjectType;
static_assert((T::kSize % T::kAlign) == 0);
static constexpr size_t kDataAlign = T::kAlign;
static constexpr size_t kDataSize = T::kSize;
template <typename StaticVector>
static bool FromFlatbuffer(
- StaticVector *to, const typename StaticVector::ConstFlatbuffer *from) {
+ StaticVector *to, const typename StaticVector::ConstFlatbuffer &from) {
return to->FromNotInlineFlatbuffer(from);
}
template <typename StaticVector>
@@ -712,11 +783,12 @@
typedef T ObjectType;
typedef T FlatbufferType;
typedef T ConstFlatbufferType;
+ typedef T *FlatbufferObjectType;
static constexpr size_t kDataAlign = alignof(T);
static constexpr size_t kDataSize = sizeof(T);
template <typename StaticVector>
static bool FromFlatbuffer(
- StaticVector *to, const typename StaticVector::ConstFlatbuffer *from) {
+ StaticVector *to, const typename StaticVector::ConstFlatbuffer &from) {
return to->FromInlineFlatbuffer(from);
}
template <typename StaticVector>
@@ -731,11 +803,12 @@
typedef uint8_t ObjectType;
typedef uint8_t FlatbufferType;
typedef uint8_t ConstFlatbufferType;
+ typedef uint8_t *FlatbufferObjectType;
static constexpr size_t kDataAlign = 1u;
static constexpr size_t kDataSize = 1u;
template <typename StaticVector>
static bool FromFlatbuffer(
- StaticVector *to, const typename StaticVector::ConstFlatbuffer *from) {
+ StaticVector *to, const typename StaticVector::ConstFlatbuffer &from) {
return to->FromInlineFlatbuffer(from);
}
template <typename StaticVector>
@@ -753,11 +826,12 @@
typedef T ObjectType;
typedef T *FlatbufferType;
typedef const T *ConstFlatbufferType;
+ typedef T *FlatbufferObjectType;
static constexpr size_t kDataAlign = alignof(T);
static constexpr size_t kDataSize = sizeof(T);
template <typename StaticVector>
static bool FromFlatbuffer(
- StaticVector *to, const typename StaticVector::ConstFlatbuffer *from) {
+ StaticVector *to, const typename StaticVector::ConstFlatbuffer &from) {
return to->FromInlineFlatbuffer(from);
}
template <typename StaticVector>
@@ -770,7 +844,7 @@
template <typename T, size_t kStaticLength, bool kInline, size_t kForceAlign,
bool kNullTerminate>
bool Vector<T, kStaticLength, kInline, kForceAlign,
- kNullTerminate>::FromFlatbuffer(ConstFlatbuffer *vector) {
+ kNullTerminate>::FromFlatbuffer(ConstFlatbuffer &vector) {
return internal::InlineWrapper<T, kInline>::FromFlatbuffer(this, vector);
}
diff --git a/aos/flatbuffers/test_dir/BUILD b/aos/flatbuffers/test_dir/BUILD
index 76f5fb4..a6275a5 100644
--- a/aos/flatbuffers/test_dir/BUILD
+++ b/aos/flatbuffers/test_dir/BUILD
@@ -1,6 +1,13 @@
load("//aos/flatbuffers:generate.bzl", "static_flatbuffer")
static_flatbuffer(
+ name = "include_reflection_fbs",
+ srcs = ["include_reflection.fbs"],
+ visibility = ["//visibility:public"],
+ deps = ["//aos/flatbuffers/reflection:reflection_fbs"],
+)
+
+static_flatbuffer(
name = "include_fbs",
srcs = ["include.fbs"],
visibility = ["//visibility:public"],
diff --git a/aos/flatbuffers/test_dir/include_reflection.fbs b/aos/flatbuffers/test_dir/include_reflection.fbs
new file mode 100644
index 0000000..7eda4f5
--- /dev/null
+++ b/aos/flatbuffers/test_dir/include_reflection.fbs
@@ -0,0 +1,9 @@
+include "reflection/reflection.fbs";
+
+namespace aos.testing;
+
+table UseSchema {
+ schema:reflection.Schema (id: 0);
+}
+
+root_type UseSchema;
diff --git a/aos/flatbuffers/test_dir/sample_test_static.h b/aos/flatbuffers/test_dir/sample_test_static.h
index a6362d7..57ac57a 100644
--- a/aos/flatbuffers/test_dir/sample_test_static.h
+++ b/aos/flatbuffers/test_dir/sample_test_static.h
@@ -14,6 +14,8 @@
public:
// The underlying "raw" flatbuffer type for this type.
typedef aos::fbs::testing::MinimallyAlignedTable Flatbuffer;
+ typedef flatbuffers::unique_ptr<Flatbuffer::NativeTableType>
+ FlatbufferObjectType;
// Returns this object as a flatbuffer type. This reference may not be valid
// following mutations to the underlying flatbuffer, due to how memory may get
// may get moved around.
@@ -135,15 +137,39 @@
// returning true on success.
// This is a deep copy, and will call FromFlatbuffer on any constituent
// objects.
- [[nodiscard]] bool FromFlatbuffer(const Flatbuffer *other) {
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer &other) {
Clear();
- if (other->has_field()) {
- set_field(other->field());
+ if (other.has_field()) {
+ set_field(other.field());
}
return true;
}
+ // Equivalent to FromFlatbuffer(const Flatbuffer&); this overload is provided
+ // to ease implementation of the aos::fbs::Vector internals.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer *other) {
+ return FromFlatbuffer(*CHECK_NOTNULL(other));
+ }
+
+ // Copies the contents of the provided flatbuffer into this flatbuffer,
+ // returning true on success.
+ // Because the Flatbuffer Object API does not provide any concept of an
+ // optionally populated scalar field, all scalar fields will be populated
+ // after a call to FromFlatbufferObject().
+ // This is a deep copy, and will call FromFlatbufferObject on
+ // any constituent objects.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer::NativeTableType &other) {
+ Clear();
+
+ set_field(other.field);
+
+ return true;
+ }
+ [[nodiscard]] bool FromFlatbuffer(
+ const flatbuffers::unique_ptr<Flatbuffer::NativeTableType> &other) {
+ return FromFlatbuffer(*other);
+ }
private:
// We need to provide a MoveConstructor to allow this table to be
@@ -168,6 +194,8 @@
public:
// The underlying "raw" flatbuffer type for this type.
typedef aos::fbs::testing::SubTable Flatbuffer;
+ typedef flatbuffers::unique_ptr<Flatbuffer::NativeTableType>
+ FlatbufferObjectType;
// Returns this object as a flatbuffer type. This reference may not be valid
// following mutations to the underlying flatbuffer, due to how memory may get
// may get moved around.
@@ -314,19 +342,45 @@
// returning true on success.
// This is a deep copy, and will call FromFlatbuffer on any constituent
// objects.
- [[nodiscard]] bool FromFlatbuffer(const Flatbuffer *other) {
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer &other) {
Clear();
- if (other->has_baz()) {
- set_baz(other->baz());
+ if (other.has_baz()) {
+ set_baz(other.baz());
}
- if (other->has_foo()) {
- set_foo(other->foo());
+ if (other.has_foo()) {
+ set_foo(other.foo());
}
return true;
}
+ // Equivalent to FromFlatbuffer(const Flatbuffer&); this overload is provided
+ // to ease implementation of the aos::fbs::Vector internals.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer *other) {
+ return FromFlatbuffer(*CHECK_NOTNULL(other));
+ }
+
+ // Copies the contents of the provided flatbuffer into this flatbuffer,
+ // returning true on success.
+ // Because the Flatbuffer Object API does not provide any concept of an
+ // optionally populated scalar field, all scalar fields will be populated
+ // after a call to FromFlatbufferObject().
+ // This is a deep copy, and will call FromFlatbufferObject on
+ // any constituent objects.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer::NativeTableType &other) {
+ Clear();
+
+ set_baz(other.baz);
+
+ set_foo(other.foo);
+
+ return true;
+ }
+ [[nodiscard]] bool FromFlatbuffer(
+ const flatbuffers::unique_ptr<Flatbuffer::NativeTableType> &other) {
+ return FromFlatbuffer(*other);
+ }
private:
// We need to provide a MoveConstructor to allow this table to be
@@ -354,6 +408,8 @@
public:
// The underlying "raw" flatbuffer type for this type.
typedef aos::fbs::testing::TestTable Flatbuffer;
+ typedef flatbuffers::unique_ptr<Flatbuffer::NativeTableType>
+ FlatbufferObjectType;
// Returns this object as a flatbuffer type. This reference may not be valid
// following mutations to the underlying flatbuffer, due to how memory may get
// may get moved around.
@@ -1060,109 +1116,108 @@
// returning true on success.
// This is a deep copy, and will call FromFlatbuffer on any constituent
// objects.
- [[nodiscard]] bool FromFlatbuffer(const Flatbuffer *other) {
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer &other) {
Clear();
- if (other->has_included_table()) {
+ if (other.has_included_table()) {
if (!CHECK_NOTNULL(add_included_table())
- ->FromFlatbuffer(other->included_table())) {
+ ->FromFlatbuffer(other.included_table())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_scalar()) {
- set_scalar(other->scalar());
+ if (other.has_scalar()) {
+ set_scalar(other.scalar());
}
- if (other->has_string()) {
- if (!CHECK_NOTNULL(add_string())->FromFlatbuffer(other->string())) {
+ if (other.has_string()) {
+ if (!CHECK_NOTNULL(add_string())->FromFlatbuffer(other.string())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_substruct()) {
- set_substruct(*other->substruct());
+ if (other.has_substruct()) {
+ set_substruct(*other.substruct());
}
- if (other->has_subtable()) {
- if (!CHECK_NOTNULL(add_subtable())->FromFlatbuffer(other->subtable())) {
+ if (other.has_subtable()) {
+ if (!CHECK_NOTNULL(add_subtable())->FromFlatbuffer(other.subtable())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_unspecified_length_string()) {
+ if (other.has_unspecified_length_string()) {
if (!CHECK_NOTNULL(add_unspecified_length_string())
- ->FromFlatbuffer(other->unspecified_length_string())) {
+ ->FromFlatbuffer(other.unspecified_length_string())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_unspecified_length_vector()) {
+ if (other.has_unspecified_length_vector()) {
if (!CHECK_NOTNULL(add_unspecified_length_vector())
- ->FromFlatbuffer(other->unspecified_length_vector())) {
+ ->FromFlatbuffer(other.unspecified_length_vector())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_unspecified_length_vector_of_strings()) {
+ if (other.has_unspecified_length_vector_of_strings()) {
if (!CHECK_NOTNULL(add_unspecified_length_vector_of_strings())
- ->FromFlatbuffer(
- other->unspecified_length_vector_of_strings())) {
+ ->FromFlatbuffer(other.unspecified_length_vector_of_strings())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_vector_aligned()) {
+ if (other.has_vector_aligned()) {
if (!CHECK_NOTNULL(add_vector_aligned())
- ->FromFlatbuffer(other->vector_aligned())) {
+ ->FromFlatbuffer(other.vector_aligned())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_vector_of_scalars()) {
+ if (other.has_vector_of_scalars()) {
if (!CHECK_NOTNULL(add_vector_of_scalars())
- ->FromFlatbuffer(other->vector_of_scalars())) {
+ ->FromFlatbuffer(other.vector_of_scalars())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_vector_of_strings()) {
+ if (other.has_vector_of_strings()) {
if (!CHECK_NOTNULL(add_vector_of_strings())
- ->FromFlatbuffer(other->vector_of_strings())) {
+ ->FromFlatbuffer(other.vector_of_strings())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_vector_of_structs()) {
+ if (other.has_vector_of_structs()) {
if (!CHECK_NOTNULL(add_vector_of_structs())
- ->FromFlatbuffer(other->vector_of_structs())) {
+ ->FromFlatbuffer(other.vector_of_structs())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
}
}
- if (other->has_vector_of_tables()) {
+ if (other.has_vector_of_tables()) {
if (!CHECK_NOTNULL(add_vector_of_tables())
- ->FromFlatbuffer(other->vector_of_tables())) {
+ ->FromFlatbuffer(other.vector_of_tables())) {
// Fail if we were unable to copy (e.g., if we tried to copy in a long
// vector and do not have the space for it).
return false;
@@ -1171,6 +1226,140 @@
return true;
}
+ // Equivalent to FromFlatbuffer(const Flatbuffer&); this overload is provided
+ // to ease implementation of the aos::fbs::Vector internals.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer *other) {
+ return FromFlatbuffer(*CHECK_NOTNULL(other));
+ }
+
+ // Copies the contents of the provided flatbuffer into this flatbuffer,
+ // returning true on success.
+ // Because the Flatbuffer Object API does not provide any concept of an
+ // optionally populated scalar field, all scalar fields will be populated
+ // after a call to FromFlatbufferObject().
+ // This is a deep copy, and will call FromFlatbufferObject on
+ // any constituent objects.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer::NativeTableType &other) {
+ Clear();
+
+ if (other.included_table) {
+ if (!CHECK_NOTNULL(add_included_table())
+ ->FromFlatbuffer(*other.included_table)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+ }
+
+ set_scalar(other.scalar);
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_string())->FromFlatbuffer(other.string)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ if (other.substruct) {
+ set_substruct(*other.substruct);
+ }
+
+ if (other.subtable) {
+ if (!CHECK_NOTNULL(add_subtable())->FromFlatbuffer(*other.subtable)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_unspecified_length_string())
+ ->FromFlatbuffer(other.unspecified_length_string)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_unspecified_length_vector())
+ ->FromFlatbuffer(other.unspecified_length_vector)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_unspecified_length_vector_of_strings())
+ ->FromFlatbuffer(other.unspecified_length_vector_of_strings)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_vector_aligned())
+ ->FromFlatbuffer(other.vector_aligned)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_vector_of_scalars())
+ ->FromFlatbuffer(other.vector_of_scalars)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_vector_of_strings())
+ ->FromFlatbuffer(other.vector_of_strings)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_vector_of_structs())
+ ->FromFlatbuffer(other.vector_of_structs)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_vector_of_tables())
+ ->FromFlatbuffer(other.vector_of_tables)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ return true;
+ }
+ [[nodiscard]] bool FromFlatbuffer(
+ const flatbuffers::unique_ptr<Flatbuffer::NativeTableType> &other) {
+ return FromFlatbuffer(*other);
+ }
private:
// We need to provide a MoveConstructor to allow this table to be
diff --git a/aos/json_to_flatbuffer.h b/aos/json_to_flatbuffer.h
index 6ef3544..deafaa7 100644
--- a/aos/json_to_flatbuffer.h
+++ b/aos/json_to_flatbuffer.h
@@ -75,6 +75,12 @@
Flatbuffer<T>::MiniReflectTypeTable(), json_options);
}
+template <typename T, typename Enable = T::Flatbuffer>
+inline ::std::string FlatbufferToJson(const fbs::Builder<T> &flatbuffer,
+ JsonOptions json_options = {}) {
+ return FlatbufferToJson(flatbuffer.AsFlatbufferSpan(), json_options);
+}
+
// Converts a flatbuffer::Table to JSON.
template <typename T>
typename std::enable_if<
diff --git a/aos/network/sctp_lib.cc b/aos/network/sctp_lib.cc
index 829ae67..cf50ad6 100644
--- a/aos/network/sctp_lib.cc
+++ b/aos/network/sctp_lib.cc
@@ -4,6 +4,7 @@
#include <linux/sctp.h>
#include <net/if.h>
#include <netdb.h>
+#include <netinet/ip.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -26,6 +27,25 @@
DEFINE_bool(disable_ipv6, false, "disable ipv6");
DEFINE_int32(rmem, 0, "If nonzero, set rmem to this size.");
+// The Type of Service.
+// https://www.tucny.com/Home/dscp-tos
+//
+// We want to set the highest precedence (i.e. critical) with minimal delay. We
+// also want to be able to stuff the packets into bucket 0 for queue
+// disciplining. Experiments show that 176 works for this. Other values (e.g.
+// DSCP class EF) cannot be stuffed into bucket 0 (for unknown reasons).
+//
+// Note that the two least significant bits are reserved and should always set
+// to zero. Those two bits are the "Explicit Congestion Notification" bits. They
+// are controlled by the IP stack itself (and used by the router). We don't
+// control that via the TOS value we set here.
+DEFINE_int32(
+ sctp_tos, 176,
+ "The Type-Of-Service value to use. Defaults to a critical priority. "
+ "Always set values here whose two least significant bits are set to zero. "
+ "When using tcpdump, the `tos` field may show the least significant two "
+ "bits set to something other than zero.");
+
namespace aos::message_bridge {
namespace {
@@ -272,6 +292,13 @@
LOG(INFO) << "socket(" << Family(sockaddr_local)
<< ", SOCK_SEQPACKET, IPPROTOSCTP) = " << fd_;
{
+ // Set up Type-Of-Service.
+ //
+ // See comments for the --sctp_tos flag for more information.
+ int tos = IPTOS_DSCP(FLAGS_sctp_tos);
+ PCHECK(setsockopt(fd_, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == 0);
+ }
+ {
// Per https://tools.ietf.org/html/rfc6458
// Setting this to !0 allows event notifications to be interleaved
// with data if enabled. This typically only matters during congestion.
diff --git a/aos/starter/starterd_lib.cc b/aos/starter/starterd_lib.cc
index 95210c0..38d519d 100644
--- a/aos/starter/starterd_lib.cc
+++ b/aos/starter/starterd_lib.cc
@@ -19,6 +19,7 @@
DEFINE_uint32(queue_initialization_threads, 0,
"Number of threads to spin up to initialize the queue. 0 means "
"use the main thread.");
+DECLARE_bool(enable_ftrace);
namespace aos::starter {
@@ -214,6 +215,11 @@
if (info.ssi_signo == SIGCHLD) {
// SIGCHLD messages can be collapsed if multiple are received, so all
// applications must check their status.
+ if (FLAGS_enable_ftrace) {
+ ftrace_.FormatMessage("SIGCHLD");
+ ftrace_.TurnOffOrDie();
+ }
+
for (auto iter = applications_.begin(); iter != applications_.end();) {
if (iter->second.MaybeHandleSignal()) {
iter = applications_.erase(iter);
diff --git a/aos/starter/starterd_lib.h b/aos/starter/starterd_lib.h
index 3779c84..1ffc782 100644
--- a/aos/starter/starterd_lib.h
+++ b/aos/starter/starterd_lib.h
@@ -77,6 +77,8 @@
aos::PhasedLoopHandler *status_timer_;
aos::TimerHandler *cleanup_timer_;
+ aos::Ftrace ftrace_;
+
int status_count_ = 0;
const int max_status_count_;
diff --git a/aos/starter/subprocess.cc b/aos/starter/subprocess.cc
index 9606adc..d677922 100644
--- a/aos/starter/subprocess.cc
+++ b/aos/starter/subprocess.cc
@@ -266,6 +266,8 @@
if (application->has_memory_limit() && application->memory_limit() > 0) {
SetMemoryLimit(application->memory_limit());
}
+
+ set_stop_grace_period(std::chrono::nanoseconds(application->stop_time()));
}
void Application::DoStart() {
@@ -496,8 +498,7 @@
// Watchdog timer to SIGKILL application if it is still running 1 second
// after SIGINT
- stop_timer_->Schedule(event_loop_->monotonic_now() +
- std::chrono::seconds(1));
+ stop_timer_->Schedule(event_loop_->monotonic_now() + stop_grace_period_);
queue_restart_ = restart;
OnChange();
break;
diff --git a/aos/starter/subprocess.h b/aos/starter/subprocess.h
index ca7ef9d..eb36874 100644
--- a/aos/starter/subprocess.h
+++ b/aos/starter/subprocess.h
@@ -139,6 +139,14 @@
void set_capture_stderr(bool capture);
void set_run_as_sudo(bool value) { run_as_sudo_ = value; }
+ // Sets the time for a process to stop gracefully. If an application is asked
+ // to stop, but doesn't stop within the specified time limit, then it is
+ // forcefully killed. Defaults to 1 second unless overridden by the
+ // aos::Application instance in the constructor.
+ void set_stop_grace_period(std::chrono::nanoseconds stop_grace_period) {
+ stop_grace_period_ = stop_grace_period;
+ }
+
bool autostart() const { return autostart_; }
bool autorestart() const { return autorestart_; }
@@ -222,6 +230,7 @@
std::optional<uid_t> user_;
std::optional<gid_t> group_;
bool run_as_sudo_ = false;
+ std::chrono::nanoseconds stop_grace_period_ = std::chrono::seconds(1);
bool capture_stdout_ = false;
PipePair stdout_pipes_;
diff --git a/aos/starter/subprocess_reliable_test.cc b/aos/starter/subprocess_reliable_test.cc
index 0460bc3..701de11 100644
--- a/aos/starter/subprocess_reliable_test.cc
+++ b/aos/starter/subprocess_reliable_test.cc
@@ -3,6 +3,7 @@
#include <filesystem>
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "aos/events/shm_event_loop.h"
@@ -124,4 +125,55 @@
ASSERT_TRUE(std::filesystem::exists(shutdown_signal_file));
}
+// Validates that a process that is known to take a while to stop can shut down
+// gracefully without being killed.
+TEST(SubprocessTest, CanSlowlyStopGracefully) {
+ const std::string config_file =
+ ::aos::testing::ArtifactPath("aos/events/pingpong_config.json");
+ aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+ aos::configuration::ReadConfig(config_file);
+ aos::ShmEventLoop event_loop(&config.message());
+
+ // Use a file to signal that the subprocess has started up properly and that
+ // the exit handler has been installed. Otherwise we risk killing the process
+ // uncleanly before the signal handler got installed.
+ auto signal_dir = std::filesystem::path(aos::testing::TestTmpDir()) /
+ "slow_death_startup_file_signals";
+ ASSERT_TRUE(std::filesystem::create_directory(signal_dir));
+ auto startup_signal_file = signal_dir / "startup";
+
+ // Create an application that should never get killed automatically. It should
+ // have plenty of time to shut down on its own. In this case, we use 2 seconds
+ // to mean "plenty of time".
+ auto application = std::make_unique<Application>("/bin/bash", "/bin/bash",
+ &event_loop, [] {});
+ application->set_args(
+ {"-c",
+ absl::StrCat(
+ "trap 'echo got int; sleep 2; echo shutting down; exit 0' SIGINT; "
+ "while true; do sleep 0.1; touch ",
+ startup_signal_file.string(), "; done;")});
+ application->set_capture_stdout(true);
+ application->set_stop_grace_period(std::chrono::seconds(999));
+ application->AddOnChange([&] {
+ if (application->status() == aos::starter::State::STOPPED) {
+ event_loop.Exit();
+ }
+ });
+ application->Start();
+ event_loop
+ .AddTimer([&] {
+ if (std::filesystem::exists(startup_signal_file)) {
+ // Now that the subprocess has properly started up, let's kill it.
+ application->Stop();
+ }
+ })
+ ->Schedule(event_loop.monotonic_now(), std::chrono::milliseconds(100));
+ event_loop.Run();
+
+ EXPECT_EQ(application->exit_code(), 0);
+ EXPECT_THAT(application->GetStdout(), ::testing::HasSubstr("got int"));
+ EXPECT_THAT(application->GetStdout(), ::testing::HasSubstr("shutting down"));
+}
+
} // namespace aos::starter::testing
diff --git a/aos/time/time.cc b/aos/time/time.cc
index 6a25aa9..e75fe45 100644
--- a/aos/time/time.cc
+++ b/aos/time/time.cc
@@ -6,6 +6,7 @@
#include <cstring>
#include <ctime>
#include <iomanip>
+#include <sstream>
#ifdef __linux__
@@ -79,6 +80,18 @@
return stream;
}
+std::string ToString(const aos::monotonic_clock::time_point &now) {
+ std::ostringstream stream;
+ stream << now;
+ return stream.str();
+}
+
+std::string ToString(const aos::realtime_clock::time_point &now) {
+ std::ostringstream stream;
+ stream << now;
+ return stream.str();
+}
+
#ifdef __linux__
std::optional<monotonic_clock::time_point> monotonic_clock::FromString(
const std::string_view now) {
@@ -170,6 +183,16 @@
: std::chrono::duration_cast<std::chrono::seconds>(
now.time_since_epoch());
+ // We can run into some corner cases where the seconds value is large enough
+ // to cause the conversion to nanoseconds to overflow. That is undefined
+ // behaviour so we prevent it with this check here.
+ if (int64_t result;
+ __builtin_mul_overflow(seconds.count(), 1'000'000'000, &result)) {
+ stream << "(unrepresentable realtime " << now.time_since_epoch().count()
+ << ")";
+ return stream;
+ }
+
std::time_t seconds_t = seconds.count();
stream << std::put_time(localtime_r(&seconds_t, &tm), "%Y-%m-%d_%H-%M-%S.")
<< std::setfill('0') << std::setw(9)
diff --git a/aos/time/time.h b/aos/time/time.h
index 1a5cbd1..8462625 100644
--- a/aos/time/time.h
+++ b/aos/time/time.h
@@ -81,6 +81,9 @@
std::ostream &operator<<(std::ostream &stream,
const aos::realtime_clock::time_point &now);
+std::string ToString(const aos::monotonic_clock::time_point &now);
+std::string ToString(const aos::realtime_clock::time_point &now);
+
namespace time {
#ifdef __linux__
diff --git a/aos/time/time_test.cc b/aos/time/time_test.cc
index 63a145b..97116b0 100644
--- a/aos/time/time_test.cc
+++ b/aos/time/time_test.cc
@@ -208,8 +208,9 @@
std::stringstream s;
s << t;
- EXPECT_EQ(s.str(), "1677-09-21_00-12-43.145224192");
- EXPECT_EQ(realtime_clock::FromString(s.str()).value(), t);
+ // min_time happens to be unrepresentable because of rounding and signed
+ // integer overflow.
+ EXPECT_EQ(s.str(), "(unrepresentable realtime -9223372036854775808)");
}
{
@@ -224,4 +225,12 @@
}
}
+// Test that ToString works for monotonic and realtime time points.
+TEST(TimeTest, ToStringTimePoints) {
+ EXPECT_EQ(ToString(realtime_clock::epoch() + std::chrono::hours(5 * 24) +
+ std::chrono::seconds(11) + std::chrono::milliseconds(5)),
+ "1970-01-06_00-00-11.005000000");
+ EXPECT_EQ(ToString(monotonic_clock::min_time), "-9223372036.854775808sec");
+}
+
} // namespace aos::time::testing
diff --git a/aos/util/mcap_logger_test.cc b/aos/util/mcap_logger_test.cc
index c6febf9..3684e93 100644
--- a/aos/util/mcap_logger_test.cc
+++ b/aos/util/mcap_logger_test.cc
@@ -81,6 +81,20 @@
"values": {
"items": {
"properties": {
+ "attributes": {
+ "items": {
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "type": "array"
+ },
"documentation": {
"items": {
"type": "string"
diff --git a/aos/uuid.h b/aos/uuid.h
index 8dd93d4..a2cf8ae 100644
--- a/aos/uuid.h
+++ b/aos/uuid.h
@@ -8,6 +8,7 @@
#include "absl/types/span.h"
#include "flatbuffers/flatbuffers.h"
+#include "glog/logging.h"
namespace aos {
@@ -66,6 +67,11 @@
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> PackVector(
flatbuffers::FlatBufferBuilder *fbb) const;
+ template <typename T>
+ void PackStaticVector(T *static_vector) const {
+ CHECK(static_vector->FromData(data_.data(), data_.size()));
+ }
+
// Returns a human-readable string representing this UUID.
//
// This is done without any memory allocation, which means it's returned in a
diff --git a/debian/BUILD.nghttp2.bazel b/debian/BUILD.nghttp2.bazel
new file mode 100644
index 0000000..cd055cd
--- /dev/null
+++ b/debian/BUILD.nghttp2.bazel
@@ -0,0 +1,71 @@
+load("@bazel_skylib//rules:write_file.bzl", "write_file")
+
+licenses(["notice"]) # MIT/X derivative license
+
+exports_files(["COPYING"])
+
+write_file(
+ name = "nghttp2ver.h",
+ out = "lib/includes/nghttp2/nghttp2ver.h",
+ content = """
+/*
+ * nghttp2 - HTTP/2 C Library
+ *
+ * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP2VER_H
+#define NGHTTP2VER_H
+
+/**
+ * @macro
+ * Version number of the nghttp2 library release
+ */
+#define NGHTTP2_VERSION "1.58.0"
+
+/**
+ * @macro
+ * Numerical representation of the version number of the nghttp2 library
+ * release. This is a 24 bit number with 8 bits for major number, 8 bits
+ * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
+ */
+#define NGHTTP2_VERSION_NUM 0x013a00
+
+#endif /* NGHTTP2VER_H */
+""".splitlines(),
+)
+
+cc_library(
+ name = "nghttp2",
+ srcs = glob(["lib/*.c"]),
+ hdrs = glob(["lib/*.h"]) + [
+ "lib/includes/nghttp2/nghttp2.h",
+ ":nghttp2ver.h",
+ ],
+ defines = [
+ "HAVE_TIME_H",
+ "HAVE_ARPA_INET_H",
+ ],
+ includes = [
+ "lib/includes/",
+ ],
+ visibility = ["//visibility:public"],
+)
diff --git a/debian/curl.BUILD b/debian/curl.BUILD
index a4f6526..1292c5e 100644
--- a/debian/curl.BUILD
+++ b/debian/curl.BUILD
@@ -422,6 +422,7 @@
"-DCURL_DISABLE_NTLM", # turning it off in configure is not enough
"-DHAVE_LIBZ",
"-DHAVE_ZLIB_H",
+ "-DUSE_NGHTTP2",
"-Wno-string-plus-int",
"-Wno-cast-qual",
"-Wno-format-nonliteral",
@@ -466,6 +467,7 @@
# Use the same version of zlib that gRPC does.
"@zlib",
":define-ca-bundle-location",
+ "@com_github_nghttp2_nghttp2//:nghttp2",
] + select({
":windows": [],
"//conditions:default": [
diff --git a/documentation/aos/docs/flatbuffers.md b/documentation/aos/docs/flatbuffers.md
index 132348d..9c82ee3 100644
--- a/documentation/aos/docs/flatbuffers.md
+++ b/documentation/aos/docs/flatbuffers.md
@@ -157,13 +157,30 @@
type using the regular generated flatbuffer API and a `FromFlatbuffer()` method
which attempts to copy the specified flatbuffer into the current object.
+The `FromFlatbuffer()` method works on both the "raw" flatbuffer type, as well
+as on the [Flatbuffer Object
+API](https://flatbuffers.dev/flatbuffers_guide_use_cpp.html) (i.e. the
+`FlatbufferT` types). When copying
+flatbuffers from the object-based API, we apply the same semantics that that the
+`Pack()` method does in the raw flatbuffer type. Namely, all non-table fields
+will be set:
+
+ * Scalar fields are always populated, even if their value is equal to the
+ default.
+ * Vectors are set to zero-length vectors if there is no data in the vector.
+ * Strings are set to the empty string if there is no data in the string.
+
+These limitations are a consequence of how flatbuffers are represented in the
+object API, and is not an issue when copying from regular flatbuffer types.
+For copying from raw flatbuffer objects (which is what most existing code
+uses), these caveats do not apply, and there is no loss of information.
+
### Sample Usage
The below example constructs a table of the above example `TestTable`:
```cpp
-aos::FixedAllocator allocator(TestTableStatic::kUnalignedBufferSize);
-Builder<TestTableStatic> builder(&allocator);
+Builder<TestTableStatic> builder;
TestTableStatic *object = builder.get();
object->set_scalar(123);
{
@@ -436,10 +453,24 @@
space allocated for the vector; returns false on failure (e.g., if you are in
a fixed-size allocator that does not support increasing the size past a
certain point).
-* `bool FromFlatbuffer(const flatbuffers::Vector<>*)`: Attempts to copy an
+* `bool FromFlatbuffer(const flatbuffers::Vector<>&)`: Attempts to copy an
existing vector into this `Vector`. This may attempt to call `reserve()`
if the new vector is longer than `capacity()`. If the copy fails for
any reason, returns `false`.
+* `bool FromFlatbuffer(const std::vector<>&)`: Attempts to copy an
+ existing vector into this `Vector`. This may attempt to call `reserve()`
+ if the new vector is longer than `capacity()`. If the copy fails for
+ any reason, returns `false`. This is called "`FromFlatbuffer`" because
+ the [Flatbuffer Object
+ API](https://flatbuffers.dev/flatbuffers_guide_use_cpp.html) uses
+ `std::vector<>` to represent vectors.
+* `bool FromData(const T*, size_t)`: Attempts to copy a contiguous set of data
+ from the provided pointer. Only applies to inline types. This may attempt to
+ call `reserve()`, and if the call fails, it returns `false`.
+* `bool FromIterator(It begin, It end)`: Attempts to copy data from [begin, end)
+ into the vector. Does not assume that the data is stored contiguously in
+ memory. Only applies to inline types. This may attempt to
+ call `reserve()`, and if the call fails, it returns `false`.
#### Managing Resizing of Vectors
@@ -510,12 +541,25 @@
# upgraded to static_flatbuffer rules.
static_flatbuffer(
name = "test_message_fbs",
- src = "test_message.fbs",
+ srcs = ["test_message.fbs"],
)
```
+Then you must update the `#include` to use a `test_message_static.h` instead of
+the standard `test_message_generated.h` (the `_static.h` will include the
+`_generated.h` itself). Any C++ code can then be updated, noting that any
+AOS Senders that need to use the new API need to have their definitions updated
+to use the `TestMessageStatic` and must call `MakeStaticBuilder` instead of
+`MakeBuilder`. There is currently no support for using static flatbuffers with
+the AOS Fetcher or Watcher interfaces, as these only present immutable objects
+and so do not benefit from the API additions (and attempting to convert them to
+the new API would require additional overhead associated with copying the
+serialized flatbuffer into the correct format).
+
Before:
```cpp
+#include "aos/events/test_message_generated.h"
+...
aos::Sender<TestMessage> sender = loop1->MakeSender<TestMessage>("/test");
loop->OnRun([&]() {
@@ -528,6 +572,8 @@
After:
```cpp
+#include "aos/events/test_message_static.h"
+...
aos::Sender<TestMessageStatic> sender =
loop1->MakeSender<TestMessageStatic>("/test");
diff --git a/frc971/control_loops/drivetrain/BUILD b/frc971/control_loops/drivetrain/BUILD
index afbce9b..c00ce90 100644
--- a/frc971/control_loops/drivetrain/BUILD
+++ b/frc971/control_loops/drivetrain/BUILD
@@ -42,7 +42,10 @@
static_flatbuffer(
name = "drivetrain_status_fbs",
srcs = ["drivetrain_status.fbs"],
- deps = ["//frc971/control_loops:control_loops_fbs"],
+ deps = [
+ "//frc971/control_loops:control_loops_fbs",
+ "//frc971/control_loops:encoder_fault_status_fbs",
+ ],
)
static_flatbuffer(
@@ -62,7 +65,10 @@
name = "drivetrain_status_ts_fbs",
srcs = ["drivetrain_status.fbs"],
target_compatible_with = ["@platforms//os:linux"],
- deps = ["//frc971/control_loops:control_loops_ts_fbs"],
+ deps = [
+ "//frc971/control_loops:control_loops_ts_fbs",
+ "//frc971/control_loops:encoder_fault_status_ts_fbs",
+ ],
)
flatbuffer_ts_library(
@@ -128,7 +134,10 @@
static_flatbuffer(
name = "drivetrain_status_float_fbs",
srcs = ["drivetrain_status_float.fbs"],
- deps = ["//frc971/control_loops:control_loops_fbs"],
+ deps = [
+ "//frc971/control_loops:control_loops_fbs",
+ "//frc971/control_loops:encoder_fault_status_fbs",
+ ],
)
aos_config(
@@ -136,6 +145,7 @@
src = "drivetrain_simulation_channels.json",
flatbuffers = [
":drivetrain_status_fbs",
+ "//frc971/control_loops:encoder_fault_status_fbs",
],
target_compatible_with = ["@platforms//os:linux"],
visibility = ["//visibility:public"],
@@ -162,6 +172,7 @@
":drivetrain_output_fbs",
":drivetrain_status_fbs",
":drivetrain_position_fbs",
+ ":drivetrain_can_position_fbs",
":localizer_fbs",
"//frc971/queues:gyro_fbs",
"//frc971/queues:gyro_uid_fbs",
@@ -789,6 +800,34 @@
],
)
+cc_library(
+ name = "drivetrain_encoder_fault_detector",
+ srcs = ["drivetrain_encoder_fault_detector.cc"],
+ hdrs = ["drivetrain_encoder_fault_detector.h"],
+ target_compatible_with = ["@platforms//os:linux"],
+ deps = [
+ "//aos/events:event_loop",
+ "//frc971/control_loops:encoder_fault_detector",
+ "//frc971/control_loops:encoder_fault_status_fbs",
+ "//frc971/control_loops/drivetrain:drivetrain_can_position_fbs",
+ "//frc971/control_loops/drivetrain:drivetrain_position_fbs",
+ "//frc971/control_loops/drivetrain:drivetrain_status_fbs",
+ ],
+)
+
+cc_test(
+ name = "drivetrain_encoder_fault_detector_test",
+ srcs = ["drivetrain_encoder_fault_detector_test.cc"],
+ data = [":simulation_config"],
+ target_compatible_with = ["@platforms//os:linux"],
+ deps = [
+ ":drivetrain_encoder_fault_detector",
+ "//aos:json_to_flatbuffer",
+ "//aos/events:simulated_event_loop",
+ "//aos/testing:googletest",
+ ],
+)
+
cc_test(
name = "camera_test",
srcs = ["camera_test.cc"],
diff --git a/frc971/control_loops/drivetrain/drivetrain_can_position.fbs b/frc971/control_loops/drivetrain/drivetrain_can_position.fbs
index 29c0b85..539b18e 100644
--- a/frc971/control_loops/drivetrain/drivetrain_can_position.fbs
+++ b/frc971/control_loops/drivetrain/drivetrain_can_position.fbs
@@ -14,6 +14,11 @@
// The ctre::phoenix::StatusCode of the measurement
// Should be OK = 0
status:int (id: 2);
+
+ // These values aren't used yet but are
+ // arrays that represent falcons on left/right sides of the drivetrain
+ left_falcons: [CANTalonFX] (id: 3);
+ right_falcons: [CANTalonFX] (id: 4);
}
root_type CANPosition;
diff --git a/frc971/control_loops/drivetrain/drivetrain_config.json b/frc971/control_loops/drivetrain/drivetrain_config.json
index a74d416..71c7628 100644
--- a/frc971/control_loops/drivetrain/drivetrain_config.json
+++ b/frc971/control_loops/drivetrain/drivetrain_config.json
@@ -43,6 +43,11 @@
},
{
"name": "/drivetrain",
+ "type": "frc971.control_loops.drivetrain.CANPosition",
+ "frequency": 200
+ },
+ {
+ "name": "/drivetrain",
"type": "frc971.control_loops.drivetrain.Status",
"frequency": 200,
"max_size": 2000
diff --git a/frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector.cc b/frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector.cc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector.cc
diff --git a/frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector.h b/frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector.h
new file mode 100644
index 0000000..7513033
--- /dev/null
+++ b/frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector.h
@@ -0,0 +1,115 @@
+#ifndef FRC971_CONTROL_LOOPS_DRIVETRAIN_DRIVETRAIN_ENCODER_FAULT_DETECTOR_H_
+#define FRC971_CONTROL_LOOPS_DRIVETRAIN_DRIVETRAIN_ENCODER_FAULT_DETECTOR_H_
+
+#include "aos/events/event_loop.h"
+#include "frc971/control_loops/drivetrain/drivetrain_can_position_generated.h"
+#include "frc971/control_loops/drivetrain/drivetrain_position_generated.h"
+#include "frc971/control_loops/drivetrain/drivetrain_status_generated.h"
+#include "frc971/control_loops/encoder_fault_detector.h"
+
+namespace frc971 {
+namespace control_loops {
+namespace drivetrain {
+
+// This class will be used to detect any encoder faults within the drivetrain
+// using values from the left/right encoders and motors
+
+template <uint8_t NumberofDrivetrainMotors>
+class DrivetrainEncoderFaultDetector {
+ public:
+ DrivetrainEncoderFaultDetector(aos::EventLoop *event_loop);
+
+ void UpdateDetectors();
+
+ void SendMessage();
+
+ flatbuffers::Offset<drivetrain::Faults> PopulateFaults(
+ flatbuffers::FlatBufferBuilder *fbb);
+
+ private:
+ aos::EventLoop *event_loop_;
+ EncoderFaultDetector<NumberofDrivetrainMotors> left_encoder_fault_detector_;
+ EncoderFaultDetector<NumberofDrivetrainMotors> right_encoder_fault_detector_;
+ aos::Sender<frc971::control_loops::drivetrain::Status>
+ drivetrain_status_sender_;
+ aos::Fetcher<control_loops::drivetrain::Position>
+ drivetrain_position_fetcher_;
+ aos::Fetcher<control_loops::drivetrain::CANPosition>
+ drivetrain_can_position_fetcher_;
+};
+
+template <uint8_t NumberofDrivetrainMotors>
+DrivetrainEncoderFaultDetector<NumberofDrivetrainMotors>::
+ DrivetrainEncoderFaultDetector(aos::EventLoop *event_loop)
+ : event_loop_(event_loop),
+ drivetrain_status_sender_(
+ event_loop->MakeSender<drivetrain::Status>("/drivetrain")),
+ drivetrain_position_fetcher_(
+ event_loop->MakeFetcher<drivetrain::Position>("/drivetrain")),
+ drivetrain_can_position_fetcher_(
+ event_loop->MakeFetcher<drivetrain::CANPosition>("/drivetrain")) {
+ event_loop
+ ->AddTimer([this]() {
+ UpdateDetectors();
+ SendMessage();
+ })
+ ->Schedule(event_loop->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+}
+
+template <uint8_t NumberofDrivetrainMotors>
+void DrivetrainEncoderFaultDetector<
+ NumberofDrivetrainMotors>::UpdateDetectors() {
+ drivetrain_position_fetcher_.Fetch();
+ drivetrain_can_position_fetcher_.Fetch();
+
+ if (drivetrain_position_fetcher_.get() &&
+ drivetrain_can_position_fetcher_.get()) {
+ left_encoder_fault_detector_.Iterate(
+ drivetrain_position_fetcher_->left_encoder(),
+ drivetrain_can_position_fetcher_->left_falcons(),
+ event_loop_->monotonic_now());
+ right_encoder_fault_detector_.Iterate(
+ drivetrain_position_fetcher_->right_encoder(),
+ drivetrain_can_position_fetcher_->right_falcons(),
+ event_loop_->monotonic_now());
+ }
+}
+
+template <uint8_t NumberofDrivetrainMotors>
+void DrivetrainEncoderFaultDetector<NumberofDrivetrainMotors>::SendMessage() {
+ auto builder = drivetrain_status_sender_.MakeBuilder();
+ auto offset = PopulateFaults(builder.fbb());
+
+ drivetrain::Status::Builder drivetrain_status_builder =
+ builder.MakeBuilder<drivetrain::Status>();
+
+ drivetrain_status_builder.add_encoder_faults(offset);
+ builder.CheckOk(builder.Send(drivetrain_status_builder.Finish()));
+}
+
+template <uint8_t NumberofDrivetrainMotors>
+flatbuffers::Offset<drivetrain::Faults>
+DrivetrainEncoderFaultDetector<NumberofDrivetrainMotors>::PopulateFaults(
+ flatbuffers::FlatBufferBuilder *fbb) {
+ flatbuffers::Offset<EncoderFaultStatus> left_encoder_fault_detector_offset =
+ left_encoder_fault_detector_.PopulateStatus(fbb);
+ flatbuffers::Offset<EncoderFaultStatus> right_encoder_fault_detector_offset =
+ right_encoder_fault_detector_.PopulateStatus(fbb);
+
+ drivetrain::Faults::Builder drivetrain_faults_builder(*fbb);
+
+ drivetrain_faults_builder.add_left_faulted(
+ left_encoder_fault_detector_offset);
+ drivetrain_faults_builder.add_right_faulted(
+ right_encoder_fault_detector_offset);
+
+ return drivetrain_faults_builder.Finish();
+}
+
+} // namespace drivetrain
+} // namespace control_loops
+} // namespace frc971
+
+#endif
\ No newline at end of file
diff --git a/frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector_test.cc b/frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector_test.cc
new file mode 100644
index 0000000..adc8972
--- /dev/null
+++ b/frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector_test.cc
@@ -0,0 +1,416 @@
+#include "frc971/control_loops/drivetrain/drivetrain_encoder_fault_detector.h"
+
+#include "gtest/gtest.h"
+
+#include "aos/events/simulated_event_loop.h"
+#include "aos/json_to_flatbuffer.h"
+
+namespace frc971::control_loops::drivetrain::testing {
+
+class EncoderFaultDetectorTest : public ::testing::Test {
+ public:
+ EncoderFaultDetectorTest()
+ : config_(aos::configuration::ReadConfig(
+ "frc971/control_loops/drivetrain/simulation_config.json")),
+ event_loop_factory_(&config_.message()),
+ event_loop_(event_loop_factory_.MakeEventLoop(
+ "drivetrain_encoder_fault_detector", nullptr)),
+ drivetrain_status_fetcher_(
+ event_loop_->MakeFetcher<frc971::control_loops::drivetrain::Status>(
+ "/drivetrain")),
+ drivetrain_position_sender_(
+ event_loop_->MakeSender<drivetrain::Position>("/drivetrain")),
+ drivetrain_can_position_sender_(
+ event_loop_->MakeSender<drivetrain::CANPosition>("/drivetrain")),
+ fault_detector_(event_loop_.get()) {}
+ void ResetEncoders() {
+ left_encoder_ = 0.0;
+ right_encoder_ = 0.0;
+ right_falcons_ = {0.0, 0.0};
+ left_falcons_ = {0.0, 0.0};
+ }
+ aos::FlatbufferDetachedBuffer<control_loops::drivetrain::Position>
+ ConstructPositionFlatBuffer(double left_encoder, double right_encoder) {
+ return aos::JsonToFlatbuffer<control_loops::drivetrain::Position>(
+ absl::StrFormat("{ \"left_encoder\": %f, \"right_encoder\": %f }",
+ left_encoder, right_encoder));
+ }
+
+ aos::FlatbufferDetachedBuffer<control_loops::drivetrain::CANPosition>
+ ConstructCANPositionFlatBuffer(const std::vector<double> &left_falcons,
+ const std::vector<double> &right_falcons) {
+ if (left_falcons.size() == right_falcons.size()) {
+ const size_t num_falcons = left_falcons.size();
+ std::string json = "{ \"left_falcons\":[";
+
+ for (size_t i = 0; i < num_falcons; ++i) {
+ json += absl::StrFormat("{ \"position\": %f }", left_falcons[i]);
+ if (i + 1 < num_falcons) {
+ json += ", ";
+ }
+ }
+
+ json += "], \"right_falcons\":[";
+
+ for (size_t i = 0; i < num_falcons; ++i) {
+ json += absl::StrFormat("{ \"position\": %f }", right_falcons[i]);
+ if (i + 1 < num_falcons) {
+ json += ", ";
+ }
+ }
+
+ json += "]}";
+ return aos::JsonToFlatbuffer<control_loops::drivetrain::CANPosition>(
+ json);
+ }
+ LOG(FATAL) << "You must provide two falcon arrays of equal length";
+ return aos::JsonToFlatbuffer<control_loops::drivetrain::CANPosition>("");
+ }
+
+ void SendPositionMessages() {
+ drivetrain_position_sender_.CheckOk(drivetrain_position_sender_.Send(
+ ConstructPositionFlatBuffer(left_encoder_, right_encoder_)));
+ drivetrain_can_position_sender_.CheckOk(
+ drivetrain_can_position_sender_.Send(
+ ConstructCANPositionFlatBuffer(left_falcons_, right_falcons_)));
+ }
+
+ protected:
+ aos::FlatbufferDetachedBuffer<aos::Configuration> config_;
+ aos::SimulatedEventLoopFactory event_loop_factory_;
+ std::unique_ptr<aos::EventLoop> event_loop_;
+
+ aos::Fetcher<frc971::control_loops::drivetrain::Status>
+ drivetrain_status_fetcher_;
+ aos::Sender<control_loops::drivetrain::Position> drivetrain_position_sender_;
+ aos::Sender<control_loops::drivetrain::CANPosition>
+ drivetrain_can_position_sender_;
+
+ DrivetrainEncoderFaultDetector<2> fault_detector_;
+ double left_encoder_ = 0.0;
+ double right_encoder_ = 0.0;
+ std::vector<double> right_falcons_;
+ std::vector<double> left_falcons_;
+};
+
+// Test simulates if drivetrain encoders are idle
+TEST_F(EncoderFaultDetectorTest, Idle) {
+ ResetEncoders();
+
+ event_loop_->AddTimer([this]() { SendPositionMessages(); })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(1));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+// Test simulates if drivetrain encoders are increasing
+TEST_F(EncoderFaultDetectorTest, Increasing) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ left_encoder_ += 0.1;
+ right_encoder_ += 0.1;
+ for (double &falcon : left_falcons_) {
+ falcon += 0.1;
+ }
+ for (double &falcon : right_falcons_) {
+ falcon += 0.1;
+ }
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+// Test simulates if drivetrain encoders are decreasing
+TEST_F(EncoderFaultDetectorTest, Decreasing) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ left_encoder_ -= 0.1;
+ right_encoder_ -= 0.1;
+ for (double &falcon : left_falcons_) {
+ falcon -= 0.1;
+ }
+ for (double &falcon : right_falcons_) {
+ falcon -= 0.1;
+ }
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+// Test simulates if only the right drivetrain encoders are increasing
+TEST_F(EncoderFaultDetectorTest, OnlyIncreaseRightSide) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ right_encoder_ += 0.1;
+ for (double &falcon : right_falcons_) {
+ falcon += 0.1;
+ }
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+// Test simulates if only the left drivetrain encoders are increasing
+TEST_F(EncoderFaultDetectorTest, OnlyIncreaseLeftSide) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ left_encoder_ += 0.1;
+ for (double &falcon : left_falcons_) {
+ falcon += 0.1;
+ }
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+// Test simulates if only the right drivetrain encoders are decreasing
+TEST_F(EncoderFaultDetectorTest, OnlyDecreaseRightSide) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ right_encoder_ -= 0.1;
+ for (double &falcon : right_falcons_) {
+ falcon -= 0.1;
+ }
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+// Test simulates if only the left drivetrain encoders are decreasing
+TEST_F(EncoderFaultDetectorTest, OnlyDecreaseLeftSide) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ left_encoder_ -= 0.1;
+ for (double &falcon : left_falcons_) {
+ falcon -= 0.1;
+ }
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+// Test simulates that if there is no data for one second that their will be no
+// faults
+TEST_F(EncoderFaultDetectorTest, NoDataForOneSecond) {
+ ResetEncoders();
+
+ SendPositionMessages();
+
+ event_loop_factory_.RunFor(std::chrono::seconds(1));
+
+ left_encoder_ = 1.0;
+ right_encoder_ = 2.0;
+ for (double &falcon : left_falcons_) {
+ falcon = 3.0;
+ }
+ for (double &falcon : right_falcons_) {
+ falcon = 4.0;
+ }
+
+ SendPositionMessages();
+
+ event_loop_factory_.RunFor(std::chrono::seconds(1));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+// Test simulates that only the left encoder is increasing
+TEST_F(EncoderFaultDetectorTest, LeftEncoderFaulted) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ left_encoder_ += 0.1;
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ true,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+// Test simulates that only the right encoder is increasing
+TEST_F(EncoderFaultDetectorTest, RightEncoderFaulted) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ right_encoder_ += 0.1;
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ true,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+// Test simulates that only the left falcons are increasing
+TEST_F(EncoderFaultDetectorTest, LeftFalconsFaulted) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ for (double &falcon : left_falcons_) {
+ falcon += 0.1;
+ }
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ true,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+// Test simulates that only the right falcons are increasing
+TEST_F(EncoderFaultDetectorTest, RightFalconsFaulted) {
+ ResetEncoders();
+
+ event_loop_
+ ->AddTimer([this]() {
+ SendPositionMessages();
+ for (double &falcon : right_falcons_) {
+ falcon += 0.1;
+ }
+ })
+ ->Schedule(event_loop_->monotonic_now(),
+ std::chrono::duration_cast<aos::monotonic_clock::duration>(
+ std::chrono::milliseconds(5)));
+ event_loop_factory_.RunFor(std::chrono::seconds(5));
+
+ CHECK(drivetrain_status_fetcher_.Fetch());
+
+ EXPECT_EQ(
+ true,
+ drivetrain_status_fetcher_->encoder_faults()->right_faulted()->faulted());
+ EXPECT_EQ(
+ false,
+ drivetrain_status_fetcher_->encoder_faults()->left_faulted()->faulted());
+}
+
+} // namespace frc971::control_loops::drivetrain::testing
\ No newline at end of file
diff --git a/frc971/control_loops/drivetrain/drivetrain_status.fbs b/frc971/control_loops/drivetrain/drivetrain_status.fbs
index 8158e75..8e18915 100644
--- a/frc971/control_loops/drivetrain/drivetrain_status.fbs
+++ b/frc971/control_loops/drivetrain/drivetrain_status.fbs
@@ -1,4 +1,5 @@
include "frc971/control_loops/control_loops.fbs";
+include "frc971/control_loops/encoder_fault_status.fbs";
namespace frc971.control_loops.drivetrain;
@@ -210,6 +211,11 @@
accel_z_average:float (id: 8);
}
+table Faults {
+ right_faulted:EncoderFaultStatus (id: 0);
+ left_faulted:EncoderFaultStatus (id: 1);
+}
+
table Status {
// Estimated speed of the center of the robot in m/s (positive forwards).
robot_speed:double (id: 0);
@@ -271,6 +277,8 @@
// Total number of status send failures.
send_failures:uint64 (id: 28);
+
+ encoder_faults:Faults (id: 29);
}
root_type Status;
diff --git a/frc971/vision/BUILD b/frc971/vision/BUILD
index 6cee780..02ab5e2 100644
--- a/frc971/vision/BUILD
+++ b/frc971/vision/BUILD
@@ -328,6 +328,7 @@
"//frc971/control_loops/drivetrain:improved_down_estimator",
"//frc971/vision:charuco_lib",
"//frc971/vision:vision_fbs",
+ "//frc971/vision:vision_util_lib",
"//frc971/wpilib:imu_batch_fbs",
"//frc971/wpilib:imu_fbs",
"//third_party:opencv",
@@ -374,3 +375,14 @@
"@com_github_google_glog//:glog",
],
)
+
+cc_test(
+ name = "vision_util_lib_test",
+ srcs = ["vision_util_lib_test.cc"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//aos/testing:googletest",
+ "//frc971/vision:vision_util_lib",
+ "@com_github_google_glog//:glog",
+ ],
+)
diff --git a/frc971/vision/calibration.fbs b/frc971/vision/calibration.fbs
index df8b7b0..64a5d3c 100644
--- a/frc971/vision/calibration.fbs
+++ b/frc971/vision/calibration.fbs
@@ -48,6 +48,12 @@
// ID for the physical camera hardware (typically will be a string of the form
// YY-NN, with a two-digit year and an index).
camera_id:string (id: 7);
+
+ // The camera number. This may be empty, "0", or "1".
+ camera_number:int (id: 8);
+
+ // The reprojection error associated with the camera calibration.
+ reprojection_error:float (id: 9);
}
// Calibration information for all the cameras we know about.
diff --git a/frc971/vision/intrinsics_calibration.cc b/frc971/vision/intrinsics_calibration.cc
index f98aadb..16a53a7 100644
--- a/frc971/vision/intrinsics_calibration.cc
+++ b/frc971/vision/intrinsics_calibration.cc
@@ -13,15 +13,17 @@
#include "aos/util/file.h"
#include "frc971/vision/intrinsics_calibration_lib.h"
-DEFINE_string(calibration_folder, ".", "Folder to place calibration files.");
-DEFINE_string(camera_id, "", "Camera ID in format YY-NN-- year and number.");
-DEFINE_string(config, "aos_config.json", "Path to the config file to use.");
-DEFINE_bool(display_undistorted, false,
- "If true, display the undistorted image.");
-DEFINE_string(pi, "", "Pi name to calibrate.");
+// TODO: Would be nice to remove this, but it depends on year-by-year Constants
DEFINE_string(base_intrinsics, "",
"Intrinsics to use for estimating board pose prior to solving "
"for the new intrinsics.");
+DEFINE_string(calibration_folder, ".", "Folder to place calibration files.");
+DEFINE_string(camera_id, "", "Camera ID in format YY-NN-- year and number.");
+DEFINE_string(channel, "/camera", "Camera channel to use");
+DEFINE_string(config, "aos_config.json", "Path to the config file to use.");
+DEFINE_string(cpu_name, "", "Pi/Orin name to calibrate.");
+DEFINE_bool(display_undistorted, false,
+ "If true, display the undistorted image.");
namespace frc971::vision {
namespace {
@@ -32,7 +34,7 @@
aos::ShmEventLoop event_loop(&config.message());
- std::string hostname = FLAGS_pi;
+ std::string hostname = FLAGS_cpu_name;
if (hostname == "") {
hostname = aos::network::GetHostname();
LOG(INFO) << "Using pi/orin name from hostname as " << hostname;
@@ -41,9 +43,10 @@
<< "Need a base intrinsics json to use to auto-capture images when the "
"camera moves.";
std::unique_ptr<aos::ExitHandle> exit_handle = event_loop.MakeExitHandle();
- IntrinsicsCalibration extractor(
- &event_loop, hostname, FLAGS_camera_id, FLAGS_base_intrinsics,
- FLAGS_display_undistorted, FLAGS_calibration_folder, exit_handle.get());
+ IntrinsicsCalibration extractor(&event_loop, hostname, FLAGS_channel,
+ FLAGS_camera_id, FLAGS_base_intrinsics,
+ FLAGS_display_undistorted,
+ FLAGS_calibration_folder, exit_handle.get());
event_loop.Run();
diff --git a/frc971/vision/intrinsics_calibration_lib.cc b/frc971/vision/intrinsics_calibration_lib.cc
index a5ffec7..c48c12d 100644
--- a/frc971/vision/intrinsics_calibration_lib.cc
+++ b/frc971/vision/intrinsics_calibration_lib.cc
@@ -1,15 +1,23 @@
#include "frc971/vision/intrinsics_calibration_lib.h"
+DECLARE_bool(visualize);
+
namespace frc971::vision {
+// Found that under 50 ms would fail image too often on intrinsics with
+// visualize on
+constexpr aos::monotonic_clock::duration kMaxImageAge =
+ aos::monotonic_clock::duration(std::chrono::milliseconds(50));
+
IntrinsicsCalibration::IntrinsicsCalibration(
aos::EventLoop *event_loop, std::string_view hostname,
- std::string_view camera_id, std::string_view base_intrinsics_file,
- bool display_undistorted, std::string_view calibration_folder,
- aos::ExitHandle *exit_handle)
+ std::string_view camera_channel, std::string_view camera_id,
+ std::string_view base_intrinsics_file, bool display_undistorted,
+ std::string_view calibration_folder, aos::ExitHandle *exit_handle)
: hostname_(hostname),
cpu_type_(aos::network::ParsePiOrOrin(hostname_)),
cpu_number_(aos::network::ParsePiOrOrinNumber(hostname_)),
+ camera_channel_(camera_channel),
camera_id_(camera_id),
prev_H_camera_board_(Eigen::Affine3d()),
prev_image_H_camera_board_(Eigen::Affine3d()),
@@ -18,7 +26,7 @@
base_intrinsics_file)),
charuco_extractor_(
event_loop, &base_intrinsics_.message(), TargetType::kCharuco,
- "/camera",
+ camera_channel_,
[this](cv::Mat rgb_image, const aos::monotonic_clock::time_point eof,
std::vector<cv::Vec4i> charuco_ids,
std::vector<std::vector<cv::Point2f>> charuco_corners,
@@ -30,17 +38,18 @@
image_callback_(
event_loop,
absl::StrCat("/", aos::network::ParsePiOrOrin(hostname_).value(),
- // TODO: Need to make this work with multiple cameras
- std::to_string(cpu_number_.value()), "/camera"),
+ std::to_string(cpu_number_.value()), camera_channel_),
[this](cv::Mat rgb_image,
const aos::monotonic_clock::time_point eof) {
charuco_extractor_.HandleImage(rgb_image, eof);
},
- std::chrono::milliseconds(5)),
+ kMaxImageAge),
display_undistorted_(display_undistorted),
calibration_folder_(calibration_folder),
exit_handle_(exit_handle) {
- LOG(INFO) << "Hostname is: " << hostname_;
+ LOG(INFO) << "Hostname is: " << hostname_ << " and camera channel is "
+ << camera_channel_;
+
CHECK(cpu_number_) << ": Invalid cpu number " << hostname_
<< ", failed to parse cpu number";
std::regex re{"^[0-9][0-9]-[0-9][0-9]"};
@@ -54,25 +63,28 @@
std::vector<std::vector<cv::Point2f>> charuco_corners, bool valid,
std::vector<Eigen::Vector3d> rvecs_eigen,
std::vector<Eigen::Vector3d> tvecs_eigen) {
- // Reduce resolution displayed on remote viewer to prevent lag
- cv::resize(rgb_image, rgb_image,
- cv::Size(rgb_image.cols / 2, rgb_image.rows / 2));
- cv::imshow("Display", rgb_image);
+ if (FLAGS_visualize) {
+ // Reduce resolution displayed on remote viewer to prevent lag
+ cv::resize(rgb_image, rgb_image,
+ cv::Size(rgb_image.cols / 2, rgb_image.rows / 2));
+ cv::imshow("Display", rgb_image);
- if (display_undistorted_) {
- const cv::Size image_size(rgb_image.cols, rgb_image.rows);
- cv::Mat undistorted_rgb_image(image_size, CV_8UC3);
- cv::undistort(rgb_image, undistorted_rgb_image,
- charuco_extractor_.camera_matrix(),
- charuco_extractor_.dist_coeffs());
+ if (display_undistorted_) {
+ const cv::Size image_size(rgb_image.cols, rgb_image.rows);
+ cv::Mat undistorted_rgb_image(image_size, CV_8UC3);
+ cv::undistort(rgb_image, undistorted_rgb_image,
+ charuco_extractor_.camera_matrix(),
+ charuco_extractor_.dist_coeffs());
- cv::imshow("Display undist", undistorted_rgb_image);
+ cv::imshow("Display undist", undistorted_rgb_image);
+ }
}
int keystroke = cv::waitKey(1);
// If we haven't got a valid pose estimate, don't use these points
if (!valid) {
+ LOG(INFO) << "Skipping because pose is not valid";
return;
}
CHECK(tvecs_eigen.size() == 1)
@@ -99,8 +111,8 @@
bool store_image = false;
double percent_motion =
std::max<double>(r_norm / kDeltaRThreshold, t_norm / kDeltaTThreshold);
- LOG(INFO) << "Captured: " << all_charuco_ids_.size() << " points; Moved "
- << percent_motion << "% of what's needed";
+ LOG(INFO) << "Captured: " << all_charuco_ids_.size() << " points; \nMoved "
+ << static_cast<int>(percent_motion * 100) << "% of what's needed";
// Verify that camera has moved enough from last stored image
if (r_norm > kDeltaRThreshold || t_norm > kDeltaTThreshold) {
// frame_ refers to deltas between current and last captured image
@@ -120,9 +132,11 @@
double percent_stop = std::max<double>(frame_r_norm / kFrameDeltaRLimit,
frame_t_norm / kFrameDeltaTLimit);
LOG(INFO) << "Captured: " << all_charuco_ids_.size()
- << "points; Moved enough (" << percent_motion
- << "%); Need to stop (last motion was " << percent_stop
- << "% of limit; needs to be < 1 to capture)";
+ << "points; \nMoved enough ("
+ << static_cast<int>(percent_motion * 100)
+ << "%); Need to stop (last motion was "
+ << static_cast<int>(percent_stop * 100)
+ << "% of limit; needs to be < 1% to capture)";
}
prev_image_H_camera_board_ = H_camera_board_;
@@ -157,7 +171,9 @@
IntrinsicsCalibration::BuildCalibration(
cv::Mat camera_matrix, cv::Mat dist_coeffs,
aos::realtime_clock::time_point realtime_now, std::string_view cpu_type,
- int cpu_number, std::string_view camera_id, uint16_t team_number) {
+ uint16_t cpu_number, std::string_view camera_channel,
+ std::string_view camera_id, uint16_t team_number,
+ double reprojection_error) {
flatbuffers::FlatBufferBuilder fbb;
flatbuffers::Offset<flatbuffers::String> name_offset =
fbb.CreateString(absl::StrFormat("%s%d", cpu_type, cpu_number));
@@ -169,6 +185,9 @@
reinterpret_cast<double *>(camera_matrix.data)[i]);
});
+ std::optional<uint16_t> camera_number =
+ frc971::vision::CameraNumberFromChannel(std::string(camera_channel));
+
flatbuffers::Offset<flatbuffers::Vector<float>>
distortion_coefficients_offset =
fbb.CreateVector<float>(5u, [&dist_coeffs](size_t i) {
@@ -180,7 +199,10 @@
camera_calibration_builder.add_node_name(name_offset);
camera_calibration_builder.add_team_number(team_number);
+ camera_calibration_builder.add_camera_number(camera_number.value());
camera_calibration_builder.add_camera_id(camera_id_offset);
+ camera_calibration_builder.add_reprojection_error(
+ static_cast<float>(reprojection_error));
camera_calibration_builder.add_calibration_timestamp(
realtime_now.time_since_epoch().count());
camera_calibration_builder.add_intrinsics(intrinsics_offset);
@@ -209,7 +231,7 @@
img_size, camera_matrix, dist_coeffs, rvecs, tvecs,
std_deviations_intrinsics, std_deviations_extrinsics, per_view_errors,
calibration_flags);
- CHECK_LE(reprojection_error, 1.0)
+ CHECK_LE(reprojection_error, 2.0)
<< ": Reproduction error is bad-- greater than 1 pixel.";
LOG(INFO) << "Reprojection Error is " << reprojection_error;
@@ -222,15 +244,23 @@
aos::FlatbufferDetachedBuffer<calibration::CameraCalibration>
camera_calibration = BuildCalibration(
camera_matrix, dist_coeffs, realtime_now, cpu_type_.value(),
- cpu_number_.value(), camera_id_, team_number.value());
+ cpu_number_.value(), camera_channel_, camera_id_,
+ team_number.value(), reprojection_error);
std::stringstream time_ss;
time_ss << realtime_now;
+ std::string camera_number_optional = "";
+ std::optional<uint16_t> camera_number =
+ frc971::vision::CameraNumberFromChannel(camera_channel_);
+ if (camera_number != std::nullopt) {
+ camera_number_optional = "-" + std::to_string(camera_number.value());
+ }
const std::string calibration_filename =
calibration_folder_ +
- absl::StrFormat("/calibration_%s-%d-%d_cam-%s_%s.json",
+ absl::StrFormat("/calibration_%s-%d-%d%s_cam-%s_%s.json",
cpu_type_.value(), team_number.value(),
- cpu_number_.value(), camera_id_, time_ss.str());
+ cpu_number_.value(), camera_number_optional, camera_id_,
+ time_ss.str());
LOG(INFO) << calibration_filename << " -> "
<< aos::FlatbufferToJson(camera_calibration,
diff --git a/frc971/vision/intrinsics_calibration_lib.h b/frc971/vision/intrinsics_calibration_lib.h
index 7f08138..605741f 100644
--- a/frc971/vision/intrinsics_calibration_lib.h
+++ b/frc971/vision/intrinsics_calibration_lib.h
@@ -15,12 +15,14 @@
#include "aos/time/time.h"
#include "aos/util/file.h"
#include "frc971/vision/charuco_lib.h"
+#include "frc971/vision/vision_util_lib.h"
namespace frc971::vision {
class IntrinsicsCalibration {
public:
IntrinsicsCalibration(aos::EventLoop *event_loop, std::string_view hostname,
+ std::string_view camera_channel,
std::string_view camera_id,
std::string_view base_intrinsics_file,
bool display_undistorted,
@@ -39,8 +41,9 @@
static aos::FlatbufferDetachedBuffer<calibration::CameraCalibration>
BuildCalibration(cv::Mat camera_matrix, cv::Mat dist_coeffs,
aos::realtime_clock::time_point realtime_now,
- std::string_view cpu_type, int cpu_number,
- std::string_view camera_id, uint16_t team_number);
+ std::string_view cpu_type, uint16_t cpu_number,
+ std::string_view camera_channel, std::string_view camera_id,
+ uint16_t team_number, double reprojection_error);
private:
static constexpr double kDeltaRThreshold = M_PI / 6.0;
@@ -52,6 +55,7 @@
std::string hostname_;
const std::optional<std::string_view> cpu_type_;
const std::optional<uint16_t> cpu_number_;
+ const std::string camera_channel_;
const std::string camera_id_;
std::vector<std::vector<int>> all_charuco_ids_;
diff --git a/frc971/vision/vision_util_lib.cc b/frc971/vision/vision_util_lib.cc
index b65e883..bfd6209 100644
--- a/frc971/vision/vision_util_lib.cc
+++ b/frc971/vision/vision_util_lib.cc
@@ -43,4 +43,19 @@
return result;
}
+std::optional<uint16_t> CameraNumberFromChannel(std::string camera_channel) {
+ if (camera_channel.find("/camera") == std::string::npos) {
+ return std::nullopt;
+ }
+ // If the string doesn't end in /camera#, return nullopt
+ uint16_t cam_len = std::string("/camera").length();
+ if (camera_channel.length() != camera_channel.find("/camera") + cam_len + 1) {
+ return std::nullopt;
+ }
+
+ uint16_t camera_number = std::stoi(
+ camera_channel.substr(camera_channel.find("/camera") + cam_len, 1));
+ return camera_number;
+}
+
} // namespace frc971::vision
diff --git a/frc971/vision/vision_util_lib.h b/frc971/vision/vision_util_lib.h
index 6fb32eb..8ce651c 100644
--- a/frc971/vision/vision_util_lib.h
+++ b/frc971/vision/vision_util_lib.h
@@ -7,16 +7,24 @@
#include "frc971/vision/calibration_generated.h"
+// Extract the CameraExtrinsics from a CameraCalibration struct
namespace frc971::vision {
std::optional<cv::Mat> CameraExtrinsics(
const frc971::vision::calibration::CameraCalibration *camera_calibration);
+// Extract the CameraIntrinsics from a CameraCalibration struct
cv::Mat CameraIntrinsics(
const frc971::vision::calibration::CameraCalibration *camera_calibration);
+// Extract the CameraDistCoeffs from a CameraCalibration struct
cv::Mat CameraDistCoeffs(
const frc971::vision::calibration::CameraCalibration *camera_calibration);
+// Get the camera number from a camera channel name, e.g., return 2 from
+// "/camera2". Returns nullopt if string doesn't start with "/camera" or does
+// not have a number
+std::optional<uint16_t> CameraNumberFromChannel(std::string camera_channel);
+
} // namespace frc971::vision
#endif // FRC971_VISION_VISION_UTIL_LIB_H_
diff --git a/frc971/vision/vision_util_lib_test.cc b/frc971/vision/vision_util_lib_test.cc
new file mode 100644
index 0000000..ff9c0a3
--- /dev/null
+++ b/frc971/vision/vision_util_lib_test.cc
@@ -0,0 +1,15 @@
+#include "frc971/vision/vision_util_lib.h"
+
+#include "gtest/gtest.h"
+
+namespace frc971::vision {
+// For now, just testing extracting camera number from channel name
+TEST(VisionUtilsTest, CameraNumberFromChannel) {
+ ASSERT_EQ(CameraNumberFromChannel("/camera0").value(), 0);
+ ASSERT_EQ(CameraNumberFromChannel("/camera1").value(), 1);
+ ASSERT_EQ(CameraNumberFromChannel("/camera"), std::nullopt);
+ ASSERT_EQ(CameraNumberFromChannel("/orin1/camera0").value(), 0);
+ ASSERT_EQ(CameraNumberFromChannel("/orin1/camera1").value(), 1);
+ ASSERT_EQ(CameraNumberFromChannel("/orin1"), std::nullopt);
+}
+} // namespace frc971::vision
diff --git a/third_party/flatbuffers/build_defs.bzl b/third_party/flatbuffers/build_defs.bzl
index 5f6d71b..da26cec 100644
--- a/third_party/flatbuffers/build_defs.bzl
+++ b/third_party/flatbuffers/build_defs.bzl
@@ -96,7 +96,7 @@
mnemonic = "Flatc",
progress_message = "Generating flatbuffer files for %{input}:",
)
- return [DefaultInfo(files = depset(outs), runfiles = ctx.runfiles(files = outs)), FlatbufferLibraryInfo(srcs = ctx.files.srcs)]
+ return [DefaultInfo(files = depset(outs)), FlatbufferLibraryInfo(srcs = ctx.files.srcs)]
_flatbuffer_library_compile = rule(
implementation = _flatbuffer_library_compile_impl,
diff --git a/third_party/flatbuffers/include/flatbuffers/flatbuffer_builder.h b/third_party/flatbuffers/include/flatbuffers/flatbuffer_builder.h
index efa4d89..6f9d7c8 100644
--- a/third_party/flatbuffers/include/flatbuffers/flatbuffer_builder.h
+++ b/third_party/flatbuffers/include/flatbuffers/flatbuffer_builder.h
@@ -787,7 +787,7 @@
/// where the vector is stored.
template<typename T>
Offset<Vector<const T *>> CreateVectorOfStructs(const T *v, size_t len) {
- StartVector(len * sizeof(T) / AlignOf<T>(), sizeof(T), AlignOf<T>());
+ StartVector(len, sizeof(T), AlignOf<T>());
if (len > 0) {
PushBytes(reinterpret_cast<const uint8_t *>(v), sizeof(T) * len);
}
@@ -1211,7 +1211,7 @@
// Allocates space for a vector of structures.
// Must be completed with EndVectorOfStructs().
template<typename T> T *StartVectorOfStructs(size_t vector_size) {
- StartVector(vector_size * sizeof(T) / AlignOf<T>(), sizeof(T), AlignOf<T>());
+ StartVector(vector_size, sizeof(T), AlignOf<T>());
return reinterpret_cast<T *>(buf_.make_space(vector_size * sizeof(T)));
}
diff --git a/third_party/flatbuffers/include/flatbuffers/reflection.h b/third_party/flatbuffers/include/flatbuffers/reflection.h
index 1aa0863..84213c7 100644
--- a/third_party/flatbuffers/include/flatbuffers/reflection.h
+++ b/third_party/flatbuffers/include/flatbuffers/reflection.h
@@ -508,14 +508,19 @@
// root should point to the root type for this flatbuffer.
// buf should point to the start of flatbuffer data.
// length specifies the size of the flatbuffer data.
+// Returns true if the flatbuffer is valid. Returns false if either:
+// * The flatbuffer is incorrectly constructed (e.g., it points to memory
+// locations outside of the current memory buffer).
+// * The flatbuffer is too complex, and the flatbuffer verifier chosen to bail
+// when attempting to traverse the tree of tables.
bool Verify(const reflection::Schema &schema, const reflection::Object &root,
const uint8_t *buf, size_t length, uoffset_t max_depth = 64,
- uoffset_t max_tables = 1000000);
+ uoffset_t max_tables = 3000000);
bool VerifySizePrefixed(const reflection::Schema &schema,
const reflection::Object &root, const uint8_t *buf,
size_t length, uoffset_t max_depth = 64,
- uoffset_t max_tables = 1000000);
+ uoffset_t max_tables = 3000000);
} // namespace flatbuffers
diff --git a/third_party/flatbuffers/include/flatbuffers/reflection_generated.h b/third_party/flatbuffers/include/flatbuffers/reflection_generated.h
index 6a99e66..b340086 100644
--- a/third_party/flatbuffers/include/flatbuffers/reflection_generated.h
+++ b/third_party/flatbuffers/include/flatbuffers/reflection_generated.h
@@ -523,6 +523,7 @@
int64_t value = 0;
flatbuffers::unique_ptr<reflection::TypeT> union_type{};
std::vector<std::string> documentation{};
+ std::vector<flatbuffers::unique_ptr<reflection::KeyValueT>> attributes{};
EnumValT() = default;
EnumValT(const EnumValT &o);
EnumValT(EnumValT&&) FLATBUFFERS_NOEXCEPT = default;
@@ -603,6 +604,9 @@
const flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>> *attributes() const {
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>> *>(VT_ATTRIBUTES);
}
+ flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>> *mutable_attributes() {
+ return GetPointer<flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>> *>(VT_ATTRIBUTES);
+ }
void clear_attributes() {
ClearField(VT_ATTRIBUTES);
}
@@ -2462,7 +2466,8 @@
(lhs.name == rhs.name) &&
(lhs.value == rhs.value) &&
((lhs.union_type == rhs.union_type) || (lhs.union_type && rhs.union_type && *lhs.union_type == *rhs.union_type)) &&
- (lhs.documentation == rhs.documentation);
+ (lhs.documentation == rhs.documentation) &&
+ (lhs.attributes.size() == rhs.attributes.size() && std::equal(lhs.attributes.cbegin(), lhs.attributes.cend(), rhs.attributes.cbegin(), [](flatbuffers::unique_ptr<reflection::KeyValueT> const &a, flatbuffers::unique_ptr<reflection::KeyValueT> const &b) { return (a == b) || (a && b && *a == *b); }));
}
inline bool operator!=(const EnumValT &lhs, const EnumValT &rhs) {
@@ -2475,6 +2480,8 @@
value(o.value),
union_type((o.union_type) ? new reflection::TypeT(*o.union_type) : nullptr),
documentation(o.documentation) {
+ attributes.reserve(o.attributes.size());
+ for (const auto &attributes_ : o.attributes) { attributes.emplace_back((attributes_) ? new reflection::KeyValueT(*attributes_) : nullptr); }
}
inline EnumValT &EnumValT::operator=(EnumValT o) FLATBUFFERS_NOEXCEPT {
@@ -2482,6 +2489,7 @@
std::swap(value, o.value);
std::swap(union_type, o.union_type);
std::swap(documentation, o.documentation);
+ std::swap(attributes, o.attributes);
return *this;
}
@@ -2498,6 +2506,7 @@
{ auto _e = value(); _o->value = _e; }
{ auto _e = union_type(); if (_e) { if(_o->union_type) { _e->UnPackTo(_o->union_type.get(), _resolver); } else { _o->union_type = flatbuffers::unique_ptr<reflection::TypeT>(_e->UnPack(_resolver)); } } else if (_o->union_type) { _o->union_type.reset(); } }
{ auto _e = documentation(); if (_e) { _o->documentation.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->documentation[_i] = _e->Get(_i)->str(); } } else { _o->documentation.resize(0); } }
+ { auto _e = attributes(); if (_e) { _o->attributes.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { if(_o->attributes[_i]) { _e->Get(_i)->UnPackTo(_o->attributes[_i].get(), _resolver); } else { _o->attributes[_i] = flatbuffers::unique_ptr<reflection::KeyValueT>(_e->Get(_i)->UnPack(_resolver)); }; } } else { _o->attributes.resize(0); } }
}
inline flatbuffers::Offset<EnumVal> EnumVal::Pack(flatbuffers::FlatBufferBuilder &_fbb, const EnumValT* _o, const flatbuffers::rehasher_function_t *_rehasher) {
@@ -2512,12 +2521,14 @@
auto _value = _o->value;
auto _union_type = _o->union_type ? CreateType(_fbb, _o->union_type.get(), _rehasher) : 0;
auto _documentation = _fbb.CreateVectorOfStrings(_o->documentation);
+ auto _attributes = _fbb.CreateVector<flatbuffers::Offset<reflection::KeyValue>> (_o->attributes.size(), [](size_t i, _VectorArgs *__va) { return CreateKeyValue(*__va->__fbb, __va->__o->attributes[i].get(), __va->__rehasher); }, &_va );
return reflection::CreateEnumVal(
_fbb,
_name,
_value,
_union_type,
- _documentation);
+ _documentation,
+ _attributes);
}
@@ -3211,21 +3222,24 @@
{ flatbuffers::ET_LONG, 0, -1 },
{ flatbuffers::ET_SEQUENCE, 0, 0 },
{ flatbuffers::ET_SEQUENCE, 0, 1 },
- { flatbuffers::ET_STRING, 1, -1 }
+ { flatbuffers::ET_STRING, 1, -1 },
+ { flatbuffers::ET_SEQUENCE, 1, 2 }
};
static const flatbuffers::TypeFunction type_refs[] = {
reflection::ObjectTypeTable,
- reflection::TypeTypeTable
+ reflection::TypeTypeTable,
+ reflection::KeyValueTypeTable
};
static const char * const names[] = {
"name",
"value",
"object",
"union_type",
- "documentation"
+ "documentation",
+ "attributes"
};
static const flatbuffers::TypeTable tt = {
- flatbuffers::ST_TABLE, 5, type_codes, type_refs, nullptr, nullptr, names
+ flatbuffers::ST_TABLE, 6, type_codes, type_refs, nullptr, nullptr, names
};
return &tt;
}
diff --git a/y2024/constants/calib_files/calibration_orin-971-1-0_cam-24-01_2024-02-07_20-11-35.566609408.json b/y2024/constants/calib_files/calibration_orin-971-1-0_cam-24-01_2024-02-07_20-11-35.566609408.json
new file mode 100755
index 0000000..63f4ea8
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_orin-971-1-0_cam-24-01_2024-02-07_20-11-35.566609408.json
@@ -0,0 +1,25 @@
+{
+ "node_name": "orin1",
+ "team_number": 971,
+ "intrinsics": [
+ 646.870789,
+ 0.0,
+ 731.468811,
+ 0.0,
+ 646.616333,
+ 570.003723,
+ 0.0,
+ 0.0,
+ 1.0
+ ],
+ "dist_coeffs": [
+ -0.249077,
+ 0.063132,
+ 0.000082,
+ 0.00026,
+ -0.006916
+ ],
+ "calibration_timestamp": 1707365495566609408,
+ "camera_id": "24-01",
+ "camera_number": 0
+}
\ No newline at end of file
diff --git a/y2024/constants/calib_files/calibration_orin-971-1-0_cam-24-04_2024-02-07_20-45-22.787382400.json b/y2024/constants/calib_files/calibration_orin-971-1-0_cam-24-04_2024-02-07_20-45-22.787382400.json
new file mode 100755
index 0000000..a3ea964
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_orin-971-1-0_cam-24-04_2024-02-07_20-45-22.787382400.json
@@ -0,0 +1,25 @@
+{
+ "node_name": "orin1",
+ "team_number": 971,
+ "intrinsics": [
+ 642.80365,
+ 0.0,
+ 718.017517,
+ 0.0,
+ 642.83667,
+ 555.022461,
+ 0.0,
+ 0.0,
+ 1.0
+ ],
+ "dist_coeffs": [
+ -0.239969,
+ 0.055889,
+ 0.000086,
+ 0.000099,
+ -0.005468
+ ],
+ "calibration_timestamp": 1707367522787382400,
+ "camera_id": "24-04",
+ "camera_number": 0
+}
\ No newline at end of file
diff --git a/y2024/constants/calib_files/calibration_orin-971-1-1_cam-24-02_2024-02-07_20-11-32.368359264.json b/y2024/constants/calib_files/calibration_orin-971-1-1_cam-24-02_2024-02-07_20-11-32.368359264.json
new file mode 100755
index 0000000..482158c
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_orin-971-1-1_cam-24-02_2024-02-07_20-11-32.368359264.json
@@ -0,0 +1,25 @@
+{
+ "node_name": "orin1",
+ "team_number": 971,
+ "intrinsics": [
+ 644.604858,
+ 0.0,
+ 752.152954,
+ 0.0,
+ 644.477173,
+ 558.911682,
+ 0.0,
+ 0.0,
+ 1.0
+ ],
+ "dist_coeffs": [
+ -0.242432,
+ 0.057303,
+ -0.000057,
+ 0.000015,
+ -0.005636
+ ],
+ "calibration_timestamp": 1707365492368359264,
+ "camera_id": "24-02",
+ "camera_number": 1
+}
\ No newline at end of file
diff --git a/y2024/constants/calib_files/calibration_orin-971-1-1_cam-24-03_2024-02-07_20-40-34.928600992.json b/y2024/constants/calib_files/calibration_orin-971-1-1_cam-24-03_2024-02-07_20-40-34.928600992.json
new file mode 100755
index 0000000..febd9ef
--- /dev/null
+++ b/y2024/constants/calib_files/calibration_orin-971-1-1_cam-24-03_2024-02-07_20-40-34.928600992.json
@@ -0,0 +1,25 @@
+{
+ "node_name": "orin1",
+ "team_number": 971,
+ "intrinsics": [
+ 648.13446,
+ 0.0,
+ 759.733093,
+ 0.0,
+ 648.40332,
+ 557.951538,
+ 0.0,
+ 0.0,
+ 1.0
+ ],
+ "dist_coeffs": [
+ -0.258663,
+ 0.071646,
+ 0.000113,
+ -0.000061,
+ -0.00879
+ ],
+ "calibration_timestamp": 1707367234928600992,
+ "camera_id": "24-03",
+ "camera_number": 1
+}
\ No newline at end of file
diff --git a/y2024/constants/common.json b/y2024/constants/common.json
index 554f1e8..3fc95fe 100644
--- a/y2024/constants/common.json
+++ b/y2024/constants/common.json
@@ -5,7 +5,9 @@
"distance_from_goal": 0.0,
"shot_params": {
"shot_velocity": 0.0,
- "shot_angle": 0.0
+ "shot_altitude_angle": 0.0,
+ "shot_catapult_angle": 0.0,
+ "shot_speed_over_ground": 0.0
}
}
],
@@ -78,5 +80,7 @@
"upper": 2.0
},
"loop": {% include 'y2024/control_loops/superstructure/climber/integral_climber_plant.json' %}
- }
+ },
+ "turret_loading_position": 0.0,
+ "catapult_return_position": 0.0
}
diff --git a/y2024/constants/constants.fbs b/y2024/constants/constants.fbs
index 2aaac3e..f7f9e36 100644
--- a/y2024/constants/constants.fbs
+++ b/y2024/constants/constants.fbs
@@ -12,7 +12,15 @@
table ShotParams {
shot_velocity: double (id: 0);
- shot_angle: double (id: 1);
+
+ // Angle of the altitude
+ shot_altitude_angle: double (id: 1);
+
+ // Angle of the catapult
+ shot_catapult_angle: double (id: 2);
+
+ // Speed over ground to use for shooting on the fly
+ shot_speed_over_ground: double (id: 3);
}
table InterpolationTablePoint {
@@ -87,6 +95,8 @@
transfer_roller_voltages:TransferRollerVoltages (id: 7);
climber:frc971.control_loops.StaticZeroingSingleDOFProfiledSubsystemCommonParams (id: 8);
climber_set_points:ClimberSetPoints (id: 9);
+ turret_loading_position: double (id: 10);
+ catapult_return_position: double (id: 11);
}
table Constants {
diff --git a/y2024/control_loops/superstructure/superstructure_can_position.fbs b/y2024/control_loops/superstructure/superstructure_can_position.fbs
index 6cd3f1e..69bcdd0 100644
--- a/y2024/control_loops/superstructure/superstructure_can_position.fbs
+++ b/y2024/control_loops/superstructure/superstructure_can_position.fbs
@@ -23,6 +23,9 @@
// CAN Position of the climber falcon
climber:frc971.control_loops.CANTalonFX (id: 5);
+
+ // CAN Position of the retention roller falcon
+ retention_roller:frc971.control_loops.CANTalonFX (id: 6);
}
-root_type CANPosition;
\ No newline at end of file
+root_type CANPosition;
diff --git a/y2024/control_loops/superstructure/superstructure_goal.fbs b/y2024/control_loops/superstructure/superstructure_goal.fbs
index bb7b706..6ec954f 100644
--- a/y2024/control_loops/superstructure/superstructure_goal.fbs
+++ b/y2024/control_loops/superstructure/superstructure_goal.fbs
@@ -35,11 +35,25 @@
RETRACT = 2,
}
+table ShooterGoal {
+ catapult_goal:frc971.control_loops.catapult.CatapultGoal (id: 0);
+ fire: bool (id: 1);
+ // If true we ignore the other provided positions
+ auto_aim: bool (id: 2);
+
+ // Position for the turret when we aren't auto aiming
+ turret_position: frc971.control_loops.StaticZeroingSingleDOFProfiledSubsystemGoal (id: 3);
+
+ // Position for the altitude when we aren't auto aiming
+ altitude_position: frc971.control_loops.StaticZeroingSingleDOFProfiledSubsystemGoal (id: 4);
+}
+
table Goal {
intake_roller_goal:IntakeRollerGoal (id: 0);
intake_pivot_goal:IntakePivotGoal (id: 1);
catapult_goal:frc971.control_loops.catapult.CatapultGoal (id: 2);
transfer_roller_goal:TransferRollerGoal (id: 3);
climber_goal:ClimberGoal (id: 4);
+ shooter_goal:ShooterGoal (id: 5);
}
root_type Goal;
diff --git a/y2024/control_loops/superstructure/superstructure_output.fbs b/y2024/control_loops/superstructure/superstructure_output.fbs
index a429976..ddfc5ac 100644
--- a/y2024/control_loops/superstructure/superstructure_output.fbs
+++ b/y2024/control_loops/superstructure/superstructure_output.fbs
@@ -25,6 +25,10 @@
// Positive voltage is for climber up
// Negative voltage is for climber down
climber_voltage:double (id: 6);
+
+ // Voltage of the retention rollers
+ // Positive voltage will hold the game piece in the catapult.
+ retention_roller_voltage: double (id: 7);
}
root_type Output;
diff --git a/y2024/control_loops/superstructure/superstructure_position.fbs b/y2024/control_loops/superstructure/superstructure_position.fbs
index 1c81332..8d2b7ca 100644
--- a/y2024/control_loops/superstructure/superstructure_position.fbs
+++ b/y2024/control_loops/superstructure/superstructure_position.fbs
@@ -22,6 +22,9 @@
// Values of the encoder and potentiometer at the climber.
// Zero is fully retracted, positive is extended upward.
climber:frc971.PotAndAbsolutePosition (id: 5);
+
+ // True if there is a game piece in the catapult
+ catapult_beam_break:bool (id: 6);
}
root_type Position;
diff --git a/y2024/control_loops/superstructure/superstructure_status.fbs b/y2024/control_loops/superstructure/superstructure_status.fbs
index 24b0e17..3a4b443 100644
--- a/y2024/control_loops/superstructure/superstructure_status.fbs
+++ b/y2024/control_loops/superstructure/superstructure_status.fbs
@@ -10,6 +10,15 @@
INTAKING = 2,
}
+enum CatapultStatus: ubyte {
+ // Means we are waiting for a game piece
+ IDLE = 0,
+ // Means we have a game piece
+ LOADED = 1,
+ // Means we are firing a game piece
+ FIRING = 2,
+}
+
table ShooterStatus {
// Estimated angle and angular velocitiy of the turret.
turret_state:frc971.control_loops.PotAndAbsoluteEncoderProfiledJointStatus (id: 0);
@@ -19,6 +28,8 @@
// Estimated angle and angular velocitiy of the altitude.
altitude_state:frc971.control_loops.PotAndAbsoluteEncoderProfiledJointStatus (id: 2);
+
+ catapult_status: CatapultStatus (id: 3);
}
// Contains status of transfer rollers
diff --git a/y2024/wpilib_interface.cc b/y2024/wpilib_interface.cc
index c2114de..b396426 100644
--- a/y2024/wpilib_interface.cc
+++ b/y2024/wpilib_interface.cc
@@ -296,42 +296,42 @@
c_Phoenix_Diagnostics_Dispose();
}
- std::vector<ctre::phoenix6::BaseStatusSignal *> signals_registry;
+ std::vector<ctre::phoenix6::BaseStatusSignal *> canivore_signal_registry;
+ std::vector<ctre::phoenix6::BaseStatusSignal *> rio_signal_registry;
const CurrentLimits *current_limits =
robot_constants->common()->current_limits();
std::shared_ptr<TalonFX> right_front = std::make_shared<TalonFX>(
- 0, false, "Drivetrain Bus", &signals_registry,
+ 0, false, "Drivetrain Bus", &canivore_signal_registry,
current_limits->drivetrain_supply_current_limit(),
current_limits->drivetrain_stator_current_limit());
std::shared_ptr<TalonFX> right_back = std::make_shared<TalonFX>(
- 1, false, "Drivetrain Bus", &signals_registry,
+ 1, false, "Drivetrain Bus", &canivore_signal_registry,
current_limits->drivetrain_supply_current_limit(),
current_limits->drivetrain_stator_current_limit());
std::shared_ptr<TalonFX> left_front = std::make_shared<TalonFX>(
- 2, false, "Drivetrain Bus", &signals_registry,
+ 2, false, "Drivetrain Bus", &canivore_signal_registry,
current_limits->drivetrain_supply_current_limit(),
current_limits->drivetrain_stator_current_limit());
std::shared_ptr<TalonFX> left_back = std::make_shared<TalonFX>(
- 3, false, "Drivetrain Bus", &signals_registry,
+ 3, false, "Drivetrain Bus", &canivore_signal_registry,
current_limits->drivetrain_supply_current_limit(),
current_limits->drivetrain_stator_current_limit());
std::shared_ptr<TalonFX> intake_pivot = std::make_shared<TalonFX>(
- 4, false, "Drivetrain Bus", &signals_registry,
+ 4, false, "Drivetrain Bus", &canivore_signal_registry,
current_limits->intake_pivot_stator_current_limit(),
current_limits->intake_pivot_supply_current_limit());
std::shared_ptr<TalonFX> intake_roller = std::make_shared<TalonFX>(
- 5, false, "Drivetrain Bus", &signals_registry,
+ 5, false, "rio", &rio_signal_registry,
current_limits->intake_roller_stator_current_limit(),
current_limits->intake_roller_supply_current_limit());
std::shared_ptr<TalonFX> transfer_roller = std::make_shared<TalonFX>(
- 6, false, "Drivetrain Bus", &signals_registry,
+ 6, false, "rio", &rio_signal_registry,
current_limits->transfer_roller_stator_current_limit(),
current_limits->transfer_roller_supply_current_limit());
-
std::shared_ptr<TalonFX> climber = std::make_shared<TalonFX>(
- 7, false, "Drivetrain Bus", &signals_registry,
+ 7, false, "rio", &rio_signal_registry,
current_limits->climber_stator_current_limit(),
current_limits->climber_supply_current_limit());
@@ -343,18 +343,25 @@
::aos::ShmEventLoop can_sensor_reader_event_loop(&config.message());
can_sensor_reader_event_loop.set_name("CANSensorReader");
+ ::aos::ShmEventLoop rio_sensor_reader_event_loop(&config.message());
+ rio_sensor_reader_event_loop.set_name("RioSensorReader");
+
// Creating list of talonfx for CANSensorReader
std::vector<std::shared_ptr<TalonFX>> drivetrain_talonfxs;
- std::vector<std::shared_ptr<TalonFX>> talonfxs;
+ std::vector<std::shared_ptr<TalonFX>> canivore_talonfxs;
+ std::vector<std::shared_ptr<TalonFX>> rio_talonfxs;
for (auto talonfx : {right_front, right_back, left_front, left_back}) {
drivetrain_talonfxs.push_back(talonfx);
- talonfxs.push_back(talonfx);
+ canivore_talonfxs.push_back(talonfx);
}
- for (auto talonfx :
- {intake_pivot, intake_roller, transfer_roller, climber}) {
- talonfxs.push_back(talonfx);
+ for (auto talonfx : {intake_pivot}) {
+ canivore_talonfxs.push_back(talonfx);
+ }
+
+ for (auto talonfx : {intake_roller, transfer_roller, climber}) {
+ rio_talonfxs.push_back(talonfx);
}
aos::Sender<frc971::control_loops::drivetrain::CANPositionStatic>
@@ -367,12 +374,12 @@
superstructure_can_position_sender =
can_sensor_reader_event_loop.MakeSender<
y2024::control_loops::superstructure::CANPositionStatic>(
- "/superstructure");
+ "/superstructure/canivore");
- frc971::wpilib::CANSensorReader can_sensor_reader(
- &can_sensor_reader_event_loop, std::move(signals_registry), talonfxs,
- [drivetrain_talonfxs, &intake_pivot, &intake_roller, &transfer_roller,
- &climber, &drivetrain_can_position_sender,
+ frc971::wpilib::CANSensorReader canivore_can_sensor_reader(
+ &can_sensor_reader_event_loop, std::move(canivore_signal_registry),
+ canivore_talonfxs,
+ [drivetrain_talonfxs, &intake_pivot, &drivetrain_can_position_sender,
&superstructure_can_position_sender](
ctre::phoenix::StatusCode status) {
aos::Sender<frc971::control_loops::drivetrain::CANPositionStatic>::
@@ -400,12 +407,35 @@
StaticBuilder superstructure_can_builder =
superstructure_can_position_sender.MakeStaticBuilder();
- intake_roller->SerializePosition(
- superstructure_can_builder->add_intake_roller(),
- control_loops::drivetrain::kHighOutputRatio);
intake_pivot->SerializePosition(
superstructure_can_builder->add_intake_pivot(),
control_loops::drivetrain::kHighOutputRatio);
+
+ superstructure_can_builder->set_timestamp(
+ intake_pivot->GetTimestamp());
+ superstructure_can_builder->set_status(static_cast<int>(status));
+ superstructure_can_builder.CheckOk(superstructure_can_builder.Send());
+ });
+
+ aos::Sender<y2024::control_loops::superstructure::CANPositionStatic>
+ superstructure_rio_position_sender =
+ rio_sensor_reader_event_loop.MakeSender<
+ y2024::control_loops::superstructure::CANPositionStatic>(
+ "/superstructure/rio");
+
+ frc971::wpilib::CANSensorReader rio_can_sensor_reader(
+ &rio_sensor_reader_event_loop, std::move(rio_signal_registry),
+ rio_talonfxs,
+ [&intake_roller, &transfer_roller, &climber,
+ &superstructure_rio_position_sender](
+ ctre::phoenix::StatusCode status) {
+ aos::Sender<y2024::control_loops::superstructure::CANPositionStatic>::
+ StaticBuilder superstructure_can_builder =
+ superstructure_rio_position_sender.MakeStaticBuilder();
+
+ intake_roller->SerializePosition(
+ superstructure_can_builder->add_intake_roller(),
+ control_loops::drivetrain::kHighOutputRatio);
transfer_roller->SerializePosition(
superstructure_can_builder->add_transfer_roller(),
control_loops::drivetrain::kHighOutputRatio);
@@ -420,6 +450,7 @@
});
AddLoop(&can_sensor_reader_event_loop);
+ AddLoop(&rio_sensor_reader_event_loop);
// Thread 5.
::aos::ShmEventLoop can_output_event_loop(&config.message());
diff --git a/y2024/y2024_orin_template.json b/y2024/y2024_orin_template.json
index 2bb2f46..9b165fc 100644
--- a/y2024/y2024_orin_template.json
+++ b/y2024/y2024_orin_template.json
@@ -110,18 +110,7 @@
"max_size": 208
},
{
- "name": "/orin{{ NUM }}/camera1",
- "type": "frc971.vision.CameraImage",
- "source_node": "orin{{ NUM }}",
- "channel_storage_duration": 1000000000,
- "frequency": 65,
- "max_size": 4752384,
- "num_readers": 6,
- "read_method": "PIN",
- "num_senders": 18
- },
- {
- "name": "/orin{{ NUM }}/camera2",
+ "name": "/orin{{ NUM }}/camera0",
"type": "frc971.vision.CameraImage",
"source_node": "orin{{ NUM }}",
"channel_storage_duration": 1000000000,
@@ -133,6 +122,17 @@
},
{
"name": "/orin{{ NUM }}/camera1",
+ "type": "frc971.vision.CameraImage",
+ "source_node": "orin{{ NUM }}",
+ "channel_storage_duration": 1000000000,
+ "frequency": 65,
+ "max_size": 4752384,
+ "num_readers": 6,
+ "read_method": "PIN",
+ "num_senders": 18
+ },
+ {
+ "name": "/orin{{ NUM }}/camera0",
"type": "foxglove.CompressedImage",
"source_node": "orin{{ NUM }}",
"channel_storage_duration": 1000000000,
@@ -140,7 +140,7 @@
"max_size": 622384
},
{
- "name": "/orin{{ NUM }}/camera2",
+ "name": "/orin{{ NUM }}/camera1",
"type": "foxglove.CompressedImage",
"source_node": "orin{{ NUM }}",
"channel_storage_duration": 1000000000,
@@ -148,6 +148,13 @@
"max_size": 622384
},
{
+ "name": "/orin{{ NUM }}/camera0",
+ "type": "foxglove.ImageAnnotations",
+ "source_node": "orin{{ NUM }}",
+ "frequency": 65,
+ "max_size": 50000
+ },
+ {
"name": "/orin{{ NUM }}/camera1",
"type": "foxglove.ImageAnnotations",
"source_node": "orin{{ NUM }}",
@@ -155,11 +162,27 @@
"max_size": 50000
},
{
- "name": "/orin{{ NUM }}/camera2",
- "type": "foxglove.ImageAnnotations",
+ "name": "/orin{{ NUM }}/camera0",
+ "type": "frc971.vision.TargetMap",
"source_node": "orin{{ NUM }}",
"frequency": 65,
- "max_size": 50000
+ "num_senders": 2,
+ "max_size": 1024,
+ "logger": "LOCAL_AND_REMOTE_LOGGER",
+ "logger_nodes": [
+ "imu"
+ ],
+ "destination_nodes": [
+ {
+ "name": "imu",
+ "priority": 4,
+ "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
+ "timestamp_logger_nodes": [
+ "orin{{ NUM }}"
+ ],
+ "time_to_live": 5000000
+ }
+ ]
},
{
"name": "/orin{{ NUM }}/camera1",
@@ -185,37 +208,14 @@
]
},
{
- "name": "/orin{{ NUM }}/camera2",
- "type": "frc971.vision.TargetMap",
- "source_node": "orin{{ NUM }}",
- "frequency": 65,
- "num_senders": 2,
- "max_size": 1024,
- "logger": "LOCAL_AND_REMOTE_LOGGER",
- "logger_nodes": [
- "imu"
- ],
- "destination_nodes": [
- {
- "name": "imu",
- "priority": 4,
- "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
- "timestamp_logger_nodes": [
- "orin{{ NUM }}"
- ],
- "time_to_live": 5000000
- }
- ]
- },
- {
- "name": "/orin{{ NUM }}/aos/remote_timestamps/imu/orin{{ NUM }}/camera1/frc971-vision-TargetMap",
+ "name": "/orin{{ NUM }}/aos/remote_timestamps/imu/orin{{ NUM }}/camera0/frc971-vision-TargetMap",
"type": "aos.message_bridge.RemoteMessage",
"frequency": 80,
"source_node": "orin{{ NUM }}",
"max_size": 208
},
{
- "name": "/orin{{ NUM }}/aos/remote_timestamps/imu/orin{{ NUM }}/camera2/frc971-vision-TargetMap",
+ "name": "/orin{{ NUM }}/aos/remote_timestamps/imu/orin{{ NUM }}/camera1/frc971-vision-TargetMap",
"type": "aos.message_bridge.RemoteMessage",
"frequency": 80,
"source_node": "orin{{ NUM }}",
@@ -296,22 +296,22 @@
]
},
{
- "name": "foxglove_image_converter1",
+ "name": "foxglove_image_converter0",
"executable_name": "foxglove_image_converter",
"user": "pi",
"args": [
- "--channel", "/camera1"
+ "--channel", "/camera0"
],
"nodes": [
"orin{{ NUM }}"
]
},
{
- "name": "foxglove_image_converter2",
+ "name": "foxglove_image_converter1",
"executable_name": "foxglove_image_converter",
"user": "pi",
"args": [
- "--channel", "/camera2"
+ "--channel", "/camera1"
],
"nodes": [
"orin{{ NUM }}"
@@ -326,11 +326,24 @@
]
},
{
- "name": "argus_camera1",
+ "name": "argus_camera0",
"executable_name": "argus_camera",
"args": [
"--enable_ftrace",
"--camera=0",
+ "--channel=/camera0",
+ ],
+ "user": "pi",
+ "nodes": [
+ "orin{{ NUM }}"
+ ]
+ },
+ {
+ "name": "argus_camera1",
+ "executable_name": "argus_camera",
+ "args": [
+ "--enable_ftrace",
+ "--camera=1",
"--channel=/camera1",
],
"user": "pi",
@@ -339,12 +352,10 @@
]
},
{
- "name": "argus_camera2",
- "executable_name": "argus_camera",
+ "name": "apriltag_detector0",
+ "executable_name": "apriltag_detector",
"args": [
- "--enable_ftrace",
- "--camera=1",
- "--channel=/camera2",
+ "--channel=/camera0",
],
"user": "pi",
"nodes": [
@@ -361,17 +372,6 @@
"nodes": [
"orin{{ NUM }}"
]
- },
- {
- "name": "apriltag_detector2",
- "executable_name": "apriltag_detector",
- "args": [
- "--channel=/camera2",
- ],
- "user": "pi",
- "nodes": [
- "orin{{ NUM }}"
- ]
}
],
"maps": [
diff --git a/y2024/y2024_roborio.json b/y2024/y2024_roborio.json
index 93caacc..6af86f0 100644
--- a/y2024/y2024_roborio.json
+++ b/y2024/y2024_roborio.json
@@ -139,6 +139,22 @@
"max_size": 448
},
{
+ "name": "/superstructure/canivore",
+ "type": "y2024.control_loops.superstructure.CANPosition",
+ "source_node": "roborio",
+ "frequency": 220,
+ "num_senders": 2,
+ "max_size": 400
+ },
+ {
+ "name": "/superstructure/rio",
+ "type": "y2024.control_loops.superstructure.CANPosition",
+ "source_node": "roborio",
+ "frequency": 220,
+ "num_senders": 2,
+ "max_size": 400
+ },
+ {
"name": "/can",
"type": "frc971.can_logger.CanFrame",
"source_node": "roborio",