Merge "Put blob detection results in a struct"
diff --git a/.bazelrc b/.bazelrc
index c41b14b..1c6dbde 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -24,6 +24,7 @@
build:k8 --platforms=//tools/platforms:linux_x86
build:roborio --platforms=//tools/platforms:linux_roborio
build:armv7 --platforms=//tools/platforms:linux_armv7
+build:arm64 --platforms=//tools/platforms:linux_arm64
build:cortex-m4f --platforms=//tools/platforms:cortex_m4f
build:rp2040 --platforms=//tools/platforms:rp2040
diff --git a/.gitignore b/.gitignore
index 8c6aa64..8eeac66 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,10 @@
# default. We don't want folks to check it in.
/ldap.json
+# The scraping library uses looks for this config file by default,
+# you don't want to get that checked in
+/scouting_config.json
+
# Hide vagrant's files that unfortunately make it into the source tree when you
# run "vagrant up".
/vm/.vagrant/
diff --git a/WORKSPACE b/WORKSPACE
index 199822b..1ea9cce 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -51,6 +51,10 @@
python_gtk_debs = "files",
)
load(
+ "//debian:opencv_arm64.bzl",
+ opencv_arm64_debs = "files",
+)
+load(
"//debian:opencv_armhf.bzl",
opencv_armhf_debs = "files",
)
@@ -104,6 +108,8 @@
generate_repositories_for_debs(python_gtk_debs)
+generate_repositories_for_debs(opencv_arm64_debs)
+
generate_repositories_for_debs(opencv_armhf_debs)
generate_repositories_for_debs(opencv_amd64_debs)
@@ -139,6 +145,12 @@
llvm_version = llvm_version,
)
+llvm(
+ name = "llvm_aarch64",
+ distribution = "clang+llvm-%s-aarch64-linux-gnu.tar.xz" % llvm_version,
+ llvm_version = llvm_version,
+)
+
llvm_conlyopts = [
"-std=gnu99",
]
@@ -189,40 +201,49 @@
conlyopts = {
"linux-x86_64": llvm_conlyopts,
"linux-armv7": llvm_conlyopts,
+ "linux-aarch64": llvm_conlyopts,
},
copts = {
"linux-x86_64": llvm_copts,
"linux-armv7": llvm_copts,
+ "linux-aarch64": llvm_copts,
},
cxxopts = {
"linux-x86_64": llvm_cxxopts,
"linux-armv7": llvm_cxxopts,
+ "linux-aarch64": llvm_cxxopts,
},
dbg_copts = {
"linux-x86_64": llvm_dbg_copts,
"linux-armv7": llvm_dbg_copts,
+ "linux-aarch64": llvm_dbg_copts,
},
fastbuild_copts = {
"linux-x86_64": llvm_fastbuild_copts,
"linux-armv7": llvm_fastbuild_copts,
+ "linux-aarch64": llvm_fastbuild_copts,
},
llvm_version = llvm_version,
opt_copts = {
"linux-x86_64": llvm_opt_copts,
"linux-armv7": llvm_opt_copts,
+ "linux-aarch64": llvm_opt_copts,
},
standard_libraries = {
"linux-x86_64": "libstdc++-10",
"linux-armv7": "libstdc++-10",
+ "linux-aarch64": "libstdc++-10",
},
static_libstdcxx = False,
sysroot = {
"linux-x86_64": "@amd64_debian_sysroot//:sysroot_files",
"linux-armv7": "@armhf_debian_rootfs//:sysroot_files",
+ "linux-aarch64": "@arm64_debian_rootfs//:sysroot_files",
},
target_toolchain_roots = {
"linux-x86_64": "@llvm_k8//",
"linux-armv7": "@llvm_armv7//",
+ "linux-aarch64": "@llvm_aarch64//",
},
toolchain_roots = {
"linux-x86_64": "@llvm_k8//",
@@ -384,6 +405,17 @@
url = "https://www.frc971.org/Build-Dependencies/2021-10-30-raspios-bullseye-armhf-lite_rootfs.tar.bz2",
)
+# The main partition from https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2021-11-08/2021-10-30-raspios-bullseye-armhf-lite.zip.sig
+# The following files and folders are removed to make bazel happy with it:
+# usr/share/ca-certificates
+# lib/systemd/system/system-systemd\\x2dcryptsetup.slice
+http_archive(
+ name = "arm64_debian_rootfs",
+ build_file = "@//:compilers/debian_rootfs.BUILD",
+ sha256 = "7e6ad432fec0a36f8b66c3fc2ab8795ea446e61f7dce7a206b55602677cf0904",
+ url = "https://www.frc971.org/Build-Dependencies/2021-10-30-raspios-bullseye-arm64-lite_rootfs.tar.bz2",
+)
+
# Created with:
# `debootstrap buster buster_sysroot`
# and then chrooting in and running:
@@ -813,6 +845,7 @@
extra_target_triples = [
"arm-unknown-linux-gnueabi",
"armv7-unknown-linux-gnueabihf",
+ "aarch64-unknown-linux-gnu",
],
version = "1.56.1",
)
@@ -856,6 +889,14 @@
url = "https://www.frc971.org/Build-Dependencies/2021-10-03_superstructure_shoot_balls.tar.gz",
)
+# OpenCV arm64 (for raspberry pi)
+http_archive(
+ name = "opencv_arm64",
+ build_file = "@//debian:opencv.BUILD",
+ sha256 = "d284fae46ca710cf24c81ff7ace34929773466bff38f365a80371bea3b36a2ed",
+ url = "https://www.frc971.org/Build-Dependencies/opencv_arm64.tar.gz",
+)
+
# OpenCV armhf (for raspberry pi)
http_archive(
name = "opencv_armhf",
@@ -883,6 +924,19 @@
)
# Downloaded from:
+# https://github.com/halide/Halide/releases/download/v8.0.0/halide-arm64-linux-64-trunk-65c26cba6a3eca2d08a0bccf113ca28746012cc3.tgz
+# which is "Halide 8.0.0" at https://github.com/halide/Halide/releases.
+# The "2019/08/27" release was renamed as per the release notes:
+# https://github.com/halide/Halide/releases/tag/v8.0.0
+http_archive(
+ name = "halide_arm64",
+ build_file = "@//debian:halide.BUILD",
+ sha256 = "97b3e54565cd9df52abdd6452f3720ffd38861524154d74ae3d20dc949ed2a63",
+ strip_prefix = "halide/",
+ url = "https://www.frc971.org/Build-Dependencies/halide-arm64-linux-64-trunk-65c26cba6a3eca2d08a0bccf113ca28746012cc3.tgz",
+)
+
+# Downloaded from:
# https://github.com/halide/Halide/releases/download/release_2019_08_27/halide-arm32-linux-32-trunk-65c26cba6a3eca2d08a0bccf113ca28746012cc3.tgz
# which is "Halide 2019/08/27" at https://github.com/halide/Halide/releases.
http_archive(
diff --git a/aos/actions/actor.h b/aos/actions/actor.h
index 33e70f2..b906059 100644
--- a/aos/actions/actor.h
+++ b/aos/actions/actor.h
@@ -90,7 +90,7 @@
::aos::EventLoop *event_loop() { return event_loop_; }
- ::aos::monotonic_clock::time_point monotonic_now() {
+ ::aos::monotonic_clock::time_point monotonic_now() const {
return event_loop_->monotonic_now();
}
diff --git a/aos/events/event_loop.h b/aos/events/event_loop.h
index c81fa1e..11368ae 100644
--- a/aos/events/event_loop.h
+++ b/aos/events/event_loop.h
@@ -542,8 +542,8 @@
virtual ~EventLoop();
// Current time.
- virtual monotonic_clock::time_point monotonic_now() = 0;
- virtual realtime_clock::time_point realtime_now() = 0;
+ virtual monotonic_clock::time_point monotonic_now() const = 0;
+ virtual realtime_clock::time_point realtime_now() const = 0;
template <typename T>
const Channel *GetChannel(const std::string_view channel_name) {
diff --git a/aos/events/logging/BUILD b/aos/events/logging/BUILD
index 6d28343..d190120 100644
--- a/aos/events/logging/BUILD
+++ b/aos/events/logging/BUILD
@@ -54,7 +54,7 @@
"@boringssl//:crypto",
] + select({
"//tools:cpu_k8": [":lzma_encoder"],
- "//tools:cpu_aarch64": [":lzma_encoder"],
+ "//tools:cpu_arm64": [":lzma_encoder"],
"//conditions:default": [],
}),
)
@@ -421,7 +421,7 @@
srcs = ["logger_test.cc"],
copts = select({
"//tools:cpu_k8": ["-DLZMA=1"],
- "//tools:cpu_aarch64": ["-DLZMA=1"],
+ "//tools:cpu_arm64": ["-DLZMA=1"],
"//conditions:default": [],
}),
data = [
diff --git a/aos/events/logging/log_namer.cc b/aos/events/logging/log_namer.cc
index b41212c..f269f43 100644
--- a/aos/events/logging/log_namer.cc
+++ b/aos/events/logging/log_namer.cc
@@ -18,10 +18,13 @@
namespace logger {
NewDataWriter::NewDataWriter(LogNamer *log_namer, const Node *node,
+ const Node *logger_node,
std::function<void(NewDataWriter *)> reopen,
std::function<void(NewDataWriter *)> close)
: node_(node),
node_index_(configuration::GetNodeIndex(log_namer->configuration_, node)),
+ logger_node_index_(
+ configuration::GetNodeIndex(log_namer->configuration_, logger_node)),
log_namer_(log_namer),
reopen_(std::move(reopen)),
close_(std::move(close)) {
@@ -59,6 +62,13 @@
monotonic_clock::max_time;
state.oldest_local_unreliable_monotonic_timestamp =
monotonic_clock::max_time;
+ state.oldest_remote_reliable_monotonic_timestamp =
+ monotonic_clock::max_time;
+ state.oldest_local_reliable_monotonic_timestamp = monotonic_clock::max_time;
+ state.oldest_logger_remote_unreliable_monotonic_timestamp =
+ monotonic_clock::max_time;
+ state.oldest_logger_local_unreliable_monotonic_timestamp =
+ monotonic_clock::max_time;
}
state_[node_index_].boot_uuid = source_node_boot_uuid;
@@ -78,8 +88,8 @@
void NewDataWriter::UpdateRemote(
const size_t remote_node_index, const UUID &remote_node_boot_uuid,
const monotonic_clock::time_point monotonic_remote_time,
- const monotonic_clock::time_point monotonic_event_time,
- const bool reliable) {
+ const monotonic_clock::time_point monotonic_event_time, const bool reliable,
+ monotonic_clock::time_point monotonic_timestamp_time) {
// Trigger rotation if anything in the header changes.
bool rotate = false;
CHECK_LT(remote_node_index, state_.size());
@@ -96,6 +106,13 @@
monotonic_clock::max_time;
state.oldest_local_unreliable_monotonic_timestamp =
monotonic_clock::max_time;
+ state.oldest_remote_reliable_monotonic_timestamp =
+ monotonic_clock::max_time;
+ state.oldest_local_reliable_monotonic_timestamp = monotonic_clock::max_time;
+ state.oldest_logger_remote_unreliable_monotonic_timestamp =
+ monotonic_clock::max_time;
+ state.oldest_logger_local_unreliable_monotonic_timestamp =
+ monotonic_clock::max_time;
rotate = true;
}
@@ -112,6 +129,38 @@
state.oldest_local_unreliable_monotonic_timestamp = monotonic_event_time;
rotate = true;
}
+ } else {
+ if (state.oldest_remote_reliable_monotonic_timestamp >
+ monotonic_remote_time) {
+ VLOG(1) << filename() << " Remote " << remote_node_index
+ << " oldest_remote_reliable_monotonic_timestamp updated from "
+ << state.oldest_remote_reliable_monotonic_timestamp << " to "
+ << monotonic_remote_time;
+ state.oldest_remote_reliable_monotonic_timestamp = monotonic_remote_time;
+ state.oldest_local_reliable_monotonic_timestamp = monotonic_event_time;
+ rotate = true;
+ }
+ }
+
+ // Track the logger timestamps too.
+ if (monotonic_timestamp_time != monotonic_clock::min_time) {
+ State &logger_state = state_[node_index_];
+ CHECK_EQ(remote_node_index, logger_node_index_);
+ if (monotonic_event_time <
+ logger_state.oldest_logger_remote_unreliable_monotonic_timestamp) {
+ VLOG(1)
+ << filename() << " Remote " << node_index_
+ << " oldest_logger_remote_unreliable_monotonic_timestamp updated "
+ "from "
+ << logger_state.oldest_logger_remote_unreliable_monotonic_timestamp
+ << " to " << monotonic_event_time;
+ logger_state.oldest_logger_remote_unreliable_monotonic_timestamp =
+ monotonic_event_time;
+ logger_state.oldest_logger_local_unreliable_monotonic_timestamp =
+ monotonic_timestamp_time;
+
+ rotate = true;
+ }
}
// Did any of the timestamps change?
@@ -219,7 +268,7 @@
const UUID &source_node_boot_uuid = state[node_index].boot_uuid;
const Node *const source_node =
configuration::GetNode(configuration_, node_index);
- CHECK_EQ(LogFileHeader::MiniReflectTypeTable()->num_elems, 28u);
+ CHECK_EQ(LogFileHeader::MiniReflectTypeTable()->num_elems, 32u);
flatbuffers::FlatBufferBuilder fbb;
fbb.ForceDefaults(true);
@@ -279,22 +328,36 @@
int64_t *unused;
flatbuffers::Offset<flatbuffers::Vector<int64_t>>
- oldest_remote_monotonic_timestamps_offset = fbb.CreateUninitializedVector(
- state.size(), &unused);
+ oldest_remote_monotonic_timestamps_offset =
+ fbb.CreateUninitializedVector(state.size(), &unused);
flatbuffers::Offset<flatbuffers::Vector<int64_t>>
- oldest_local_monotonic_timestamps_offset = fbb.CreateUninitializedVector(
- state.size(), &unused);
+ oldest_local_monotonic_timestamps_offset =
+ fbb.CreateUninitializedVector(state.size(), &unused);
flatbuffers::Offset<flatbuffers::Vector<int64_t>>
oldest_remote_unreliable_monotonic_timestamps_offset =
- fbb.CreateUninitializedVector(
- state.size(), &unused);
+ fbb.CreateUninitializedVector(state.size(), &unused);
flatbuffers::Offset<flatbuffers::Vector<int64_t>>
oldest_local_unreliable_monotonic_timestamps_offset =
- fbb.CreateUninitializedVector(
- state.size(), &unused);
+ fbb.CreateUninitializedVector(state.size(), &unused);
+
+ flatbuffers::Offset<flatbuffers::Vector<int64_t>>
+ oldest_remote_reliable_monotonic_timestamps_offset =
+ fbb.CreateUninitializedVector(state.size(), &unused);
+
+ flatbuffers::Offset<flatbuffers::Vector<int64_t>>
+ oldest_local_reliable_monotonic_timestamps_offset =
+ fbb.CreateUninitializedVector(state.size(), &unused);
+
+ flatbuffers::Offset<flatbuffers::Vector<int64_t>>
+ oldest_logger_remote_unreliable_monotonic_timestamps_offset =
+ fbb.CreateUninitializedVector(state.size(), &unused);
+
+ flatbuffers::Offset<flatbuffers::Vector<int64_t>>
+ oldest_logger_local_unreliable_monotonic_timestamps_offset =
+ fbb.CreateUninitializedVector(state.size(), &unused);
for (size_t i = 0; i < state.size(); ++i) {
if (state[i].boot_uuid != UUID::Zero()) {
@@ -311,6 +374,14 @@
monotonic_clock::max_time);
CHECK_EQ(state[i].oldest_local_unreliable_monotonic_timestamp,
monotonic_clock::max_time);
+ CHECK_EQ(state[i].oldest_remote_reliable_monotonic_timestamp,
+ monotonic_clock::max_time);
+ CHECK_EQ(state[i].oldest_local_reliable_monotonic_timestamp,
+ monotonic_clock::max_time);
+ CHECK_EQ(state[i].oldest_logger_remote_unreliable_monotonic_timestamp,
+ monotonic_clock::max_time);
+ CHECK_EQ(state[i].oldest_logger_local_unreliable_monotonic_timestamp,
+ monotonic_clock::max_time);
}
flatbuffers::GetMutableTemporaryPointer(
@@ -326,12 +397,40 @@
flatbuffers::GetMutableTemporaryPointer(
fbb, oldest_remote_unreliable_monotonic_timestamps_offset)
->Mutate(i, state[i]
- .oldest_remote_unreliable_monotonic_timestamp.time_since_epoch()
+ .oldest_remote_unreliable_monotonic_timestamp
+ .time_since_epoch()
.count());
flatbuffers::GetMutableTemporaryPointer(
fbb, oldest_local_unreliable_monotonic_timestamps_offset)
->Mutate(i, state[i]
- .oldest_local_unreliable_monotonic_timestamp.time_since_epoch()
+ .oldest_local_unreliable_monotonic_timestamp
+ .time_since_epoch()
+ .count());
+
+ flatbuffers::GetMutableTemporaryPointer(
+ fbb, oldest_remote_reliable_monotonic_timestamps_offset)
+ ->Mutate(i, state[i]
+ .oldest_remote_reliable_monotonic_timestamp
+ .time_since_epoch()
+ .count());
+ flatbuffers::GetMutableTemporaryPointer(
+ fbb, oldest_local_reliable_monotonic_timestamps_offset)
+ ->Mutate(
+ i, state[i]
+ .oldest_local_reliable_monotonic_timestamp.time_since_epoch()
+ .count());
+
+ flatbuffers::GetMutableTemporaryPointer(
+ fbb, oldest_logger_remote_unreliable_monotonic_timestamps_offset)
+ ->Mutate(i, state[i]
+ .oldest_logger_remote_unreliable_monotonic_timestamp
+ .time_since_epoch()
+ .count());
+ flatbuffers::GetMutableTemporaryPointer(
+ fbb, oldest_logger_local_unreliable_monotonic_timestamps_offset)
+ ->Mutate(i, state[i]
+ .oldest_logger_local_unreliable_monotonic_timestamp
+ .time_since_epoch()
.count());
}
@@ -415,6 +514,16 @@
oldest_remote_unreliable_monotonic_timestamps_offset);
log_file_header_builder.add_oldest_local_unreliable_monotonic_timestamps(
oldest_local_unreliable_monotonic_timestamps_offset);
+ log_file_header_builder.add_oldest_remote_reliable_monotonic_timestamps(
+ oldest_remote_reliable_monotonic_timestamps_offset);
+ log_file_header_builder.add_oldest_local_reliable_monotonic_timestamps(
+ oldest_local_reliable_monotonic_timestamps_offset);
+ log_file_header_builder
+ .add_oldest_logger_remote_unreliable_monotonic_timestamps(
+ oldest_logger_remote_unreliable_monotonic_timestamps_offset);
+ log_file_header_builder
+ .add_oldest_logger_local_unreliable_monotonic_timestamps(
+ oldest_logger_local_unreliable_monotonic_timestamps_offset);
fbb.FinishSizePrefixed(log_file_header_builder.Finish());
aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> result(
fbb.Release());
@@ -556,13 +665,14 @@
nodes_.emplace_back(source_node);
}
- NewDataWriter data_writer(this, source_node,
- [this, channel](NewDataWriter *data_writer) {
- OpenWriter(channel, data_writer);
- },
- [this](NewDataWriter *data_writer) {
- CloseWriter(&data_writer->writer);
- });
+ NewDataWriter data_writer(
+ this, source_node, node_,
+ [this, channel](NewDataWriter *data_writer) {
+ OpenWriter(channel, data_writer);
+ },
+ [this](NewDataWriter *data_writer) {
+ CloseWriter(&data_writer->writer);
+ });
return &(
data_writers_.emplace(channel, std::move(data_writer)).first->second);
}
@@ -580,14 +690,14 @@
nodes_.emplace_back(node);
}
- NewDataWriter data_writer(this, configuration::GetNode(configuration_, node),
- [this, channel](NewDataWriter *data_writer) {
- OpenForwardedTimestampWriter(channel,
- data_writer);
- },
- [this](NewDataWriter *data_writer) {
- CloseWriter(&data_writer->writer);
- });
+ NewDataWriter data_writer(
+ this, configuration::GetNode(configuration_, node), node_,
+ [this, channel](NewDataWriter *data_writer) {
+ OpenForwardedTimestampWriter(channel, data_writer);
+ },
+ [this](NewDataWriter *data_writer) {
+ CloseWriter(&data_writer->writer);
+ });
return &(
data_writers_.emplace(channel, std::move(data_writer)).first->second);
}
@@ -652,7 +762,7 @@
void MultiNodeLogNamer::OpenDataWriter() {
if (!data_writer_) {
data_writer_ = std::make_unique<NewDataWriter>(
- this, node_,
+ this, node_, node_,
[this](NewDataWriter *writer) {
std::string name;
if (node() != nullptr) {
diff --git a/aos/events/logging/log_namer.h b/aos/events/logging/log_namer.h
index c3fc5d4..4a1e74f 100644
--- a/aos/events/logging/log_namer.h
+++ b/aos/events/logging/log_namer.h
@@ -36,7 +36,7 @@
// node is the node whom's prespective we are logging from.
// reopen is called whenever a file needs to be reopened.
// close is called to close that file and extract any statistics.
- NewDataWriter(LogNamer *log_namer, const Node *node,
+ NewDataWriter(LogNamer *log_namer, const Node *node, const Node *logger_node,
std::function<void(NewDataWriter *)> reopen,
std::function<void(NewDataWriter *)> close);
@@ -50,10 +50,14 @@
// Rotates the log file, delaying writing the new header until data arrives.
void Rotate();
+ // Updates all the metadata in the log file about the remote node which this
+ // message is from.
void UpdateRemote(size_t remote_node_index, const UUID &remote_node_boot_uuid,
monotonic_clock::time_point monotonic_remote_time,
monotonic_clock::time_point monotonic_event_time,
- bool reliable);
+ bool reliable,
+ monotonic_clock::time_point monotonic_timestamp_time =
+ monotonic_clock::min_time);
// Queues up a message with the provided boot UUID.
void QueueMessage(flatbuffers::FlatBufferBuilder *fbb,
const UUID &node_boot_uuid,
@@ -98,6 +102,27 @@
// oldest_local_unreliable_monotonic_timestamp.
monotonic_clock::time_point oldest_local_unreliable_monotonic_timestamp =
monotonic_clock::max_time;
+
+ // Timestamp on the remote monotonic clock of the oldest message sent to
+ // node_index_, only including messages forwarded with time_to_live() == 0.
+ monotonic_clock::time_point oldest_remote_reliable_monotonic_timestamp =
+ monotonic_clock::max_time;
+ // Timestamp on the local monotonic clock of the message in
+ // oldest_local_reliable_monotonic_timestamp.
+ monotonic_clock::time_point oldest_local_reliable_monotonic_timestamp =
+ monotonic_clock::max_time;
+
+ // Timestamp on the remote monotonic clock of the oldest message timestamp
+ // sent back to logger_node_index_. The remote here will be the node this
+ // part is from the perspective of, ie node_index_.
+ monotonic_clock::time_point
+ oldest_logger_remote_unreliable_monotonic_timestamp =
+ monotonic_clock::max_time;
+ // The time on the monotonic clock of the logger when this timestamp made it
+ // back to the logger (logger_node_index_).
+ monotonic_clock::time_point
+ oldest_logger_local_unreliable_monotonic_timestamp =
+ monotonic_clock::max_time;
};
private:
@@ -113,6 +138,7 @@
const Node *node_ = nullptr;
size_t node_index_ = 0;
+ size_t logger_node_index_ = 0;
LogNamer *log_namer_;
UUID parts_uuid_ = UUID::Random();
size_t parts_index_ = 0;
@@ -271,14 +297,15 @@
const aos::Node *node)
: LogNamer(event_loop->configuration(), event_loop, node),
base_name_(base_name),
- data_writer_(this, node,
- [this](NewDataWriter *writer) {
- writer->writer = std::make_unique<DetachedBufferWriter>(
- absl::StrCat(base_name_, ".part",
- writer->parts_index(), ".bfbs"),
- std::make_unique<aos::logger::DummyEncoder>());
- },
- [](NewDataWriter * /*writer*/) {}) {}
+ data_writer_(
+ this, node, event_loop->node(),
+ [this](NewDataWriter *writer) {
+ writer->writer = std::make_unique<DetachedBufferWriter>(
+ absl::StrCat(base_name_, ".part", writer->parts_index(),
+ ".bfbs"),
+ std::make_unique<aos::logger::DummyEncoder>());
+ },
+ [](NewDataWriter * /*writer*/) {}) {}
LocalLogNamer(const LocalLogNamer &) = delete;
LocalLogNamer(LocalLogNamer &&) = delete;
diff --git a/aos/events/logging/log_reader.cc b/aos/events/logging/log_reader.cc
index 63eba05..e58ccd2 100644
--- a/aos/events/logging/log_reader.cc
+++ b/aos/events/logging/log_reader.cc
@@ -292,8 +292,7 @@
states_[configuration::GetNodeIndex(configuration(), node)].get();
CHECK(state != nullptr) << ": Unknown node " << FlatbufferToJson(node);
- // TODO(austin): Un-hard-code the 0 boot count.
- return state->monotonic_start_time(0);
+ return state->monotonic_start_time(state->boot_count());
}
realtime_clock::time_point LogReader::realtime_start_time(
@@ -302,8 +301,7 @@
states_[configuration::GetNodeIndex(configuration(), node)].get();
CHECK(state != nullptr) << ": Unknown node " << FlatbufferToJson(node);
- // TODO(austin): Un-hard-code the 0 boot count.
- return state->realtime_start_time(0);
+ return state->realtime_start_time(state->boot_count());
}
void LogReader::OnStart(std::function<void()> fn) {
diff --git a/aos/events/logging/log_reader.h b/aos/events/logging/log_reader.h
index 5cccdeb..3eebba7 100644
--- a/aos/events/logging/log_reader.h
+++ b/aos/events/logging/log_reader.h
@@ -400,7 +400,7 @@
->node_event_loop_factory_->node();
}
- monotonic_clock::time_point monotonic_now() {
+ monotonic_clock::time_point monotonic_now() const {
return node_event_loop_factory_->monotonic_now();
}
diff --git a/aos/events/logging/log_writer.cc b/aos/events/logging/log_writer.cc
index 8eaeb73..0e1dece 100644
--- a/aos/events/logging/log_writer.cc
+++ b/aos/events/logging/log_writer.cc
@@ -36,7 +36,33 @@
timer_handler_->set_name("channel_poll");
VLOG(1) << "Creating logger for " << FlatbufferToJson(node_);
- std::map<const Channel *, const Node *> timestamp_logger_channels;
+ // When we are logging remote timestamps, we need to be able to translate from
+ // the channel index that the event loop uses to the channel index in the
+ // config in the log file.
+ event_loop_to_logged_channel_index_.resize(
+ event_loop->configuration()->channels()->size(), -1);
+ for (size_t event_loop_channel_index = 0;
+ event_loop_channel_index <
+ event_loop->configuration()->channels()->size();
+ ++event_loop_channel_index) {
+ const Channel *event_loop_channel =
+ event_loop->configuration()->channels()->Get(event_loop_channel_index);
+
+ const Channel *logged_channel = aos::configuration::GetChannel(
+ configuration_, event_loop_channel->name()->string_view(),
+ event_loop_channel->type()->string_view(), "", node_);
+
+ if (logged_channel != nullptr) {
+ event_loop_to_logged_channel_index_[event_loop_channel_index] =
+ configuration::ChannelIndex(configuration_, logged_channel);
+ }
+ }
+
+ // Map to match source channels with the timestamp logger, if the contents
+ // should be reliable, and a list of all channels logged on it to be treated
+ // as reliable.
+ std::map<const Channel *, std::tuple<const Node *, bool, std::vector<bool>>>
+ timestamp_logger_channels;
message_bridge::ChannelTimestampFinder finder(event_loop_);
for (const Channel *channel : *event_loop_->configuration()->channels()) {
@@ -46,6 +72,9 @@
if (!channel->has_destination_nodes()) {
continue;
}
+ const size_t channel_index =
+ configuration::ChannelIndex(event_loop_->configuration(), channel);
+
for (const Connection *connection : *channel->destination_nodes()) {
if (configuration::ConnectionDeliveryTimeIsLoggedOnNode(
connection, event_loop_->node())) {
@@ -54,8 +83,37 @@
VLOG(1) << "Timestamps are logged from "
<< FlatbufferToJson(other_node);
- timestamp_logger_channels.insert(
- std::make_pair(finder.ForChannel(channel, connection), other_node));
+ // True if each channel's remote timestamps are split into a separate
+ // RemoteMessage channel.
+ const bool is_split =
+ finder.SplitChannelForChannel(channel, connection) != nullptr;
+
+ const Channel *const timestamp_logger_channel =
+ finder.ForChannel(channel, connection);
+
+ auto it = timestamp_logger_channels.find(timestamp_logger_channel);
+ if (it != timestamp_logger_channels.end()) {
+ CHECK(!is_split);
+ CHECK_LT(channel_index, std::get<2>(it->second).size());
+ std::get<2>(it->second)[channel_index] = (connection->time_to_live() == 0);
+ } else {
+ if (is_split) {
+ timestamp_logger_channels.insert(std::make_pair(
+ timestamp_logger_channel,
+ std::make_tuple(other_node, (connection->time_to_live() == 0),
+ std::vector<bool>())));
+ } else {
+ std::vector<bool> channel_reliable_contents(
+ event_loop->configuration()->channels()->size(), false);
+ channel_reliable_contents[channel_index] =
+ (connection->time_to_live() == 0);
+
+ timestamp_logger_channels.insert(std::make_pair(
+ timestamp_logger_channel,
+ std::make_tuple(other_node, false,
+ std::move(channel_reliable_contents))));
+ }
+ }
}
}
}
@@ -86,8 +144,8 @@
const bool is_readable =
configuration::ChannelIsReadableOnNode(config_channel, node_);
- const bool is_logged = configuration::ChannelMessageIsLoggedOnNode(
- config_channel, node_);
+ const bool is_logged =
+ configuration::ChannelMessageIsLoggedOnNode(config_channel, node_);
const bool log_message = is_logged && is_readable;
bool log_delivery_times = false;
@@ -107,8 +165,8 @@
}
}
- // Now, detect a RemoteMessage timestamp logger where we should just log the
- // contents to a file directly.
+ // Now, detect a RemoteMessage timestamp logger where we should just log
+ // the contents to a file directly.
const bool log_contents = timestamp_logger_channels.find(channel) !=
timestamp_logger_channels.end();
@@ -144,7 +202,14 @@
if (log_contents) {
VLOG(1) << "Timestamp logger channel "
<< configuration::CleanedChannelToString(channel);
- fs.timestamp_node = timestamp_logger_channels.find(channel)->second;
+ auto timestamp_logger_channel_info =
+ timestamp_logger_channels.find(channel);
+ CHECK(timestamp_logger_channel_info != timestamp_logger_channels.end());
+ fs.timestamp_node = std::get<0>(timestamp_logger_channel_info->second);
+ fs.reliable_contents =
+ std::get<1>(timestamp_logger_channel_info->second);
+ fs.channel_reliable_contents =
+ std::get<2>(timestamp_logger_channel_info->second);
fs.wants_contents_writer = true;
fs.contents_node_index =
configuration::GetNodeIndex(configuration_, fs.timestamp_node);
@@ -152,36 +217,14 @@
fetchers_.emplace_back(std::move(fs));
}
}
-
- // When we are logging remote timestamps, we need to be able to translate from
- // the channel index that the event loop uses to the channel index in the
- // config in the log file.
- event_loop_to_logged_channel_index_.resize(
- event_loop->configuration()->channels()->size(), -1);
- for (size_t event_loop_channel_index = 0;
- event_loop_channel_index <
- event_loop->configuration()->channels()->size();
- ++event_loop_channel_index) {
- const Channel *event_loop_channel =
- event_loop->configuration()->channels()->Get(event_loop_channel_index);
-
- const Channel *logged_channel = aos::configuration::GetChannel(
- configuration_, event_loop_channel->name()->string_view(),
- event_loop_channel->type()->string_view(), "", node_);
-
- if (logged_channel != nullptr) {
- event_loop_to_logged_channel_index_[event_loop_channel_index] =
- configuration::ChannelIndex(configuration_, logged_channel);
- }
- }
}
Logger::~Logger() {
if (log_namer_) {
// If we are replaying a log file, or in simulation, we want to force the
// last bit of data to be logged. The easiest way to deal with this is to
- // poll everything as we go to destroy the class, ie, shut down the logger,
- // and write it to disk.
+ // poll everything as we go to destroy the class, ie, shut down the
+ // logger, and write it to disk.
StopLogging(event_loop_->monotonic_now());
}
}
@@ -686,10 +729,10 @@
message_header_builder.add_remote_queue_index(
msg->remote_queue_index());
+ const aos::monotonic_clock::time_point monotonic_timestamp_time =
+ f.fetcher->context().monotonic_event_time;
message_header_builder.add_monotonic_timestamp_time(
- f.fetcher->context()
- .monotonic_event_time.time_since_epoch()
- .count());
+ monotonic_timestamp_time.time_since_epoch().count());
fbb.FinishSizePrefixed(message_header_builder.Finish());
const auto end = event_loop_->monotonic_now();
@@ -701,13 +744,18 @@
// Start with recording info about the data flowing from our node to the
// remote.
+ const bool reliable =
+ f.channel_reliable_contents.size() != 0u
+ ? f.channel_reliable_contents[msg->channel_index()]
+ : f.reliable_contents;
+
f.contents_writer->UpdateRemote(
node_index_, event_loop_->boot_uuid(),
monotonic_clock::time_point(
chrono::nanoseconds(msg->monotonic_remote_time())),
monotonic_clock::time_point(
chrono::nanoseconds(msg->monotonic_sent_time())),
- f.reliable_forwarding);
+ reliable, monotonic_timestamp_time);
f.contents_writer->QueueMessage(
&fbb, UUID::FromVector(msg->boot_uuid()), end);
diff --git a/aos/events/logging/log_writer.h b/aos/events/logging/log_writer.h
index 5c8b0c7..6b04f5d 100644
--- a/aos/events/logging/log_writer.h
+++ b/aos/events/logging/log_writer.h
@@ -199,6 +199,17 @@
// If true, this message is being sent over a reliable channel.
bool reliable_forwarding = false;
+
+ // One of the following will be populated. If channel_reliable_contents is
+ // non zero size, it contains a mapping from the event loop channel (not the
+ // logged channel) to a bool telling us if that particular channel is
+ // reliable.
+ //
+ // If channel_reliable_contents is empty, reliable_contents will contain the
+ // same info for all contents logged here. This is the predominant case for
+ // split timestamp channels (the prefered approach).
+ bool reliable_contents = false;
+ std::vector<bool> channel_reliable_contents;
};
// Vector mapping from the channel index from the event loop to the logged
diff --git a/aos/events/logging/logfile_sorting.cc b/aos/events/logging/logfile_sorting.cc
index 5000ff9..1e7c817 100644
--- a/aos/events/logging/logfile_sorting.cc
+++ b/aos/events/logging/logfile_sorting.cc
@@ -93,7 +93,7 @@
}
bool ConfigOnly(const LogFileHeader *header) {
- CHECK_EQ(LogFileHeader::MiniReflectTypeTable()->num_elems, 28u);
+ CHECK_EQ(LogFileHeader::MiniReflectTypeTable()->num_elems, 32u);
if (header->has_monotonic_start_time()) return false;
if (header->has_realtime_start_time()) return false;
if (header->has_max_out_of_order_duration()) return false;
@@ -117,6 +117,10 @@
if (header->has_oldest_local_monotonic_timestamps()) return false;
if (header->has_oldest_remote_unreliable_monotonic_timestamps()) return false;
if (header->has_oldest_local_unreliable_monotonic_timestamps()) return false;
+ if (header->has_oldest_remote_reliable_monotonic_timestamps()) return false;
+ if (header->has_oldest_local_reliable_monotonic_timestamps()) return false;
+ if (header->has_oldest_logger_remote_unreliable_monotonic_timestamps()) return false;
+ if (header->has_oldest_logger_local_unreliable_monotonic_timestamps()) return false;
return header->has_configuration();
}
@@ -259,6 +263,13 @@
// For a pair of nodes, this holds the oldest times that messages were
// transfered.
+//
+// TODO(austin): We are finding that the timestamps in
+// oldest_remote_monotonic_timestamp are not very useful because there may not
+// be reliable messages on both boots in the log. Figure out how to use that
+// data better (or really, use the new reliable remote timestamps field) and
+// update this code. I think we can delay a bit until someone figures out how
+// to get here without other code paths sorting us first.
struct BootPairTimes {
// Pair of local and remote timestamps for the oldest message forwarded to
// this node.
@@ -616,6 +627,21 @@
log_header->message()
.oldest_remote_unreliable_monotonic_timestamps()
->size());
+ CHECK_EQ(log_header->message()
+ .has_oldest_logger_local_unreliable_monotonic_timestamps(),
+ log_header->message()
+ .has_oldest_logger_remote_unreliable_monotonic_timestamps());
+ if (log_header->message()
+ .has_oldest_logger_local_unreliable_monotonic_timestamps()) {
+ CHECK_EQ(boot_uuids_size,
+ log_header->message()
+ .oldest_logger_local_unreliable_monotonic_timestamps()
+ ->size());
+ CHECK_EQ(boot_uuids_size,
+ log_header->message()
+ .oldest_logger_remote_unreliable_monotonic_timestamps()
+ ->size());
+ }
CHECK(!logger_boot_uuid.empty());
CHECK(!source_boot_uuid.empty());
for (size_t node_index = 0; node_index < boot_uuids_size; ++node_index) {
@@ -640,7 +666,26 @@
log_header->message()
.oldest_remote_unreliable_monotonic_timestamps()
->Get(node_index)));
- if (boot_uuid.empty() || boot_uuid == source_boot_uuid) {
+
+ const monotonic_clock::time_point
+ oldest_logger_local_unreliable_monotonic_timestamp =
+ log_header->message()
+ .has_oldest_logger_local_unreliable_monotonic_timestamps()
+ ? monotonic_clock::time_point(chrono::nanoseconds(
+ log_header->message()
+ .oldest_logger_local_unreliable_monotonic_timestamps()
+ ->Get(node_index)))
+ : monotonic_clock::max_time;
+ const monotonic_clock::time_point
+ oldest_logger_remote_unreliable_monotonic_timestamp =
+ log_header->message()
+ .has_oldest_logger_remote_unreliable_monotonic_timestamps()
+ ? monotonic_clock::time_point(chrono::nanoseconds(
+ log_header->message()
+ .oldest_logger_remote_unreliable_monotonic_timestamps()
+ ->Get(node_index)))
+ : monotonic_clock::max_time;
+ if (boot_uuid.empty()) {
CHECK_EQ(oldest_local_monotonic_timestamp, monotonic_clock::max_time);
CHECK_EQ(oldest_remote_monotonic_timestamp,
monotonic_clock::max_time);
@@ -648,9 +693,113 @@
monotonic_clock::max_time);
CHECK_EQ(oldest_remote_unreliable_monotonic_timestamp,
monotonic_clock::max_time);
+ CHECK_EQ(oldest_logger_local_unreliable_monotonic_timestamp,
+ monotonic_clock::max_time);
+ CHECK_EQ(oldest_logger_remote_unreliable_monotonic_timestamp,
+ monotonic_clock::max_time);
continue;
}
+ if (boot_uuid == source_boot_uuid) {
+ CHECK_EQ(oldest_local_monotonic_timestamp, monotonic_clock::max_time);
+ CHECK_EQ(oldest_remote_monotonic_timestamp,
+ monotonic_clock::max_time);
+ CHECK_EQ(oldest_local_unreliable_monotonic_timestamp,
+ monotonic_clock::max_time);
+ CHECK_EQ(oldest_remote_unreliable_monotonic_timestamp,
+ monotonic_clock::max_time);
+ if (oldest_logger_local_unreliable_monotonic_timestamp !=
+ monotonic_clock::max_time) {
+ CHECK_NE(oldest_logger_remote_unreliable_monotonic_timestamp,
+ monotonic_clock::max_time);
+ // Now, we found a timestamp going the other way. Add it in!
+ auto logger_node_boot_times_it = boot_times.find(logger_node);
+ if (logger_node_boot_times_it == boot_times.end()) {
+ logger_node_boot_times_it =
+ boot_times
+ .emplace(
+ logger_node,
+ absl::btree_map<
+ std::string,
+ absl::btree_map<
+ size_t, absl::btree_map<
+ std::string,
+ std::vector<BootPairTimes>>>>())
+ .first;
+ }
+
+ auto logger_source_boot_times_it =
+ logger_node_boot_times_it->second.find(
+ std::string(logger_boot_uuid));
+
+ if (logger_source_boot_times_it ==
+ logger_node_boot_times_it->second.end()) {
+ logger_source_boot_times_it =
+ logger_node_boot_times_it->second
+ .emplace(
+ logger_boot_uuid,
+ absl::btree_map<
+ size_t,
+ absl::btree_map<std::string,
+ std::vector<BootPairTimes>>>())
+ .first;
+ }
+
+ // We need the index of the source node. Luckily, since we are at
+ // the index in the boot UUID list which matches the source node
+ // boot uuid, we know it's index!
+ auto logger_destination_boot_times_it =
+ logger_source_boot_times_it->second.find(node_index);
+ if (logger_destination_boot_times_it ==
+ logger_source_boot_times_it->second.end()) {
+ logger_destination_boot_times_it =
+ logger_source_boot_times_it->second
+ .emplace(node_index,
+ absl::btree_map<std::string,
+ std::vector<BootPairTimes>>())
+ .first;
+ }
+
+ auto logger_boot_times_it =
+ logger_destination_boot_times_it->second.find(
+ std::string(source_boot_uuid));
+
+ if (logger_boot_times_it ==
+ logger_destination_boot_times_it->second.end()) {
+ // We have a new boot UUID pairing. Copy over the data we have.
+ logger_destination_boot_times_it->second.emplace(
+ source_boot_uuid,
+ std::vector<BootPairTimes>{BootPairTimes{
+ .oldest_remote_monotonic_timestamp =
+ monotonic_clock::max_time,
+ .oldest_local_monotonic_timestamp =
+ monotonic_clock::max_time,
+ .oldest_remote_unreliable_monotonic_timestamp =
+ oldest_logger_remote_unreliable_monotonic_timestamp,
+ .oldest_local_unreliable_monotonic_timestamp =
+ oldest_logger_local_unreliable_monotonic_timestamp}});
+ } else {
+ logger_boot_times_it->second.emplace_back(BootPairTimes{
+ .oldest_remote_monotonic_timestamp =
+ monotonic_clock::max_time,
+ .oldest_local_monotonic_timestamp = monotonic_clock::max_time,
+ .oldest_remote_unreliable_monotonic_timestamp =
+ oldest_logger_remote_unreliable_monotonic_timestamp,
+ .oldest_local_unreliable_monotonic_timestamp =
+ oldest_logger_local_unreliable_monotonic_timestamp});
+ }
+ }
+ continue;
+ }
+
+ // There is no supported way to get logger timestamps from anything but
+ // the source node. Since we've already handled that above, we should
+ // always expect max_time here.
+ CHECK_EQ(oldest_logger_local_unreliable_monotonic_timestamp,
+ monotonic_clock::max_time);
+ CHECK_EQ(oldest_logger_remote_unreliable_monotonic_timestamp,
+ monotonic_clock::max_time);
+
// Now, we have a valid pairing.
auto destination_boot_times_it =
source_boot_times_it->second.find(node_index);
@@ -836,24 +985,17 @@
VLOG(1)
<< "Unreliable remote time "
<< next_boot_time.oldest_remote_unreliable_monotonic_timestamp
- << " remote " << boot_time_list.first << " local "
- << source_boot_uuid;
- VLOG(1)
- << "Unreliable local time "
+ << " remote " << boot_time_list.first << " -> local time "
<< next_boot_time.oldest_local_unreliable_monotonic_timestamp
- << " remote " << boot_time_list.first << " local "
- << source_boot_uuid;
+ << " local " << source_boot_uuid;
}
if (next_boot_time.oldest_local_monotonic_timestamp !=
aos::monotonic_clock::max_time) {
VLOG(1) << "Reliable remote time "
<< next_boot_time.oldest_remote_monotonic_timestamp
- << " remote " << boot_time_list.first << " local "
- << source_boot_uuid;
- VLOG(1) << "Reliable local time "
+ << " remote " << boot_time_list.first << " -> local time "
<< next_boot_time.oldest_local_monotonic_timestamp
- << " remote " << boot_time_list.first << " local "
- << source_boot_uuid;
+ << " local " << source_boot_uuid;
}
// If we found an existing entry, update the min to be the min of
// all records. This lets us combine info from multiple part files.
@@ -953,24 +1095,9 @@
source_boot_times.begin(), source_boot_times.end(),
[](const std::tuple<std::string, BootPairTimes, BootPairTimes> &a,
const std::tuple<std::string, BootPairTimes, BootPairTimes> &b) {
- // There are cases where we will only have a reliable timestamp.
- // In that case, we need to use oldest_local_monotonic_timestamp.
- // But, that may result in collisions if the same message gets
- // forwarded to both boots, so it will have the same timestamp.
- // Solve that by breaking the tie with the unreliable messages.
- if (std::get<1>(a).oldest_local_monotonic_timestamp ==
- std::get<1>(b).oldest_local_monotonic_timestamp) {
- CHECK_NE(
- std::get<1>(a).oldest_local_unreliable_monotonic_timestamp,
- std::get<1>(b).oldest_local_unreliable_monotonic_timestamp);
- return std::get<1>(a)
- .oldest_local_unreliable_monotonic_timestamp <
- std::get<1>(b)
- .oldest_local_unreliable_monotonic_timestamp;
- } else {
- return std::get<1>(a).oldest_local_monotonic_timestamp <
- std::get<1>(b).oldest_local_monotonic_timestamp;
- }
+ return std::get<1>(a)
+ .oldest_local_unreliable_monotonic_timestamp <
+ std::get<1>(b).oldest_local_unreliable_monotonic_timestamp;
});
// The last time from the source node on the logger node.
@@ -1094,21 +1221,8 @@
destination_boot_times.begin(), destination_boot_times.end(),
[](const std::pair<std::string, BootPairTimes> &a,
const std::pair<std::string, BootPairTimes> &b) {
- // There are cases where we will only have a reliable timestamp.
- // In that case, we need to use oldest_remote_monotonic_timestamp.
- // But, that may result in collisions if the same message gets
- // forwarded to both boots, so it will have the same timestamp.
- // Solve that by breaking the tie with the unreliable messages.
- if (a.second.oldest_remote_monotonic_timestamp ==
- b.second.oldest_remote_monotonic_timestamp) {
- CHECK_NE(a.second.oldest_remote_unreliable_monotonic_timestamp,
- b.second.oldest_remote_unreliable_monotonic_timestamp);
- return a.second.oldest_remote_unreliable_monotonic_timestamp <
- b.second.oldest_remote_unreliable_monotonic_timestamp;
- } else {
- return a.second.oldest_remote_monotonic_timestamp <
- b.second.oldest_remote_monotonic_timestamp;
- }
+ return a.second.oldest_remote_unreliable_monotonic_timestamp <
+ b.second.oldest_remote_unreliable_monotonic_timestamp;
});
for (size_t boot_id = 0; boot_id < destination_boot_times.size();
diff --git a/aos/events/logging/logger.fbs b/aos/events/logging/logger.fbs
index eb70184..0fe6253 100644
--- a/aos/events/logging/logger.fbs
+++ b/aos/events/logging/logger.fbs
@@ -108,30 +108,60 @@
// across the network and using those to determine constraints so we can sort
// a DAG.
//
- // There are 2 main problem cases. Let's say we have 2 channels. /a which
- // is reliable, and /b which isn't, both sent from the same remote node.
+ // There are 5 main cases. Let's say we have 2 channels. /r which
+ // is reliable, and /u which isn't, both sent from the same remote node.
+ // The examples below are listed as the remote node sending the message, and
+ // then the local node receiving and logging the message.
//
- // case 1: /a -> boot 0 received on boot 0.
- // /b -> boot 1 received on boot 0.
+ // case 0: /r -> boot 0 received on boot 0.
+ // /u -> boot 0 received on boot 0.
+ // We log for a bit, then the remote reboots.
+ // /r -> boot 1 received on boot 0.
+ // /u -> boot 1 received on boot 0.
+ //
+ // case 1: /r -> boot 0 received on boot 0.
+ // /u -> boot 1 received on boot 0.
// We start logging after both messages arrive.
//
- // case 2: /a -> boot 0 received on boot 0.
- // /b -> boot 0 received on boot 0.
+ // case 2: /r -> boot 0 received on boot 0.
+ // /u -> boot 0 received on boot 0.
// We log for a bit, then reboot. More messages show up when we reconnect.
- // /a -> boot 0 received on boot 1.
- // /b -> boot 0 received on boot 1.
+ // /r -> boot 0 received on boot 1.
+ // /u -> boot 0 received on boot 1.
+ //
+ // case 3: /u -> boot 0 received on boot 0.
+ // /r -> boot 1 received on boot 0.
+ // /u -> boot 1 received on boot 0.
+ // We start logging after all three messages arrive.
+ //
+ // case 4: /u -> boot 0 received on boot 0.
+ // /r -> boot 1 received on boot 0.
+ //
+ // In case 0, we have all the messages showing up and a reboot of the remote.
//
// In case 1: we only have a reliable timestamp from boot 0, but that
- // reliable timestamp makes it clear that /a was before /b, so boot 0 was
+ // reliable timestamp makes it clear that /r was before /u, so boot 0 was
// before boot 1.
//
// In case 2: we have the same reliable timestamp, so that tells us nothing.
- // The unreliable timestamps though tell a different story. /b will be after
- // /a, since any messages on /b generated before the reboot won't get
- // delivered. So, we get an ordering constraint saying that any sent /b's
- // on the second boot were after /b on the first boot.
+ // The unreliable timestamps though tell a different story. /u will be after
+ // /r, since any messages on /u generated before the reboot won't get
+ // delivered. So, we get an ordering constraint saying that any sent /u's
+ // on the second boot were after /u on the first boot.
+ //
+ // In case 3: we only got the reliable message on the second boot for some
+ // reason. Reliable messages aren't 100% reliable. In this case, the
+ // reliable timestamps are actually a distraction and are misleading. We
+ // want to use the unreliable timestamps here.
+ //
+ // In case 4: we have utter madness...
+ //
+ // We expect the nominal case to be case 0, or the first half of case 0 if
+ // there are no reboots.
//
// We believe that any other cases are covered by the same mechanism.
+ // TODO(austin/brian): Shore up this and capture the cases that are 100%
+ // ambiguous and we can't sort.
//
// For all channels sent from a specific node, these vectors hold the
// timestamp of the oldest known message from that node, and the
@@ -150,6 +180,17 @@
corrupted_oldest_local_unreliable_monotonic_timestamps:[int64] (id: 23, deprecated);
oldest_remote_unreliable_monotonic_timestamps:[int64] (id: 26);
oldest_local_unreliable_monotonic_timestamps:[int64] (id: 27);
+
+ // For all channels *excluding* the unreliable channels (ttl != 0), record the
+ // same quantity.
+ oldest_remote_reliable_monotonic_timestamps:[int64] (id: 28);
+ oldest_local_reliable_monotonic_timestamps:[int64] (id: 29);
+
+ // For all the remote timestamps which come back to the logger. The "local"
+ // time here is the logger node boot, and "remote" is the node which sent the
+ // timestamps.
+ oldest_logger_remote_unreliable_monotonic_timestamps:[int64] (id: 30);
+ oldest_logger_local_unreliable_monotonic_timestamps:[int64] (id: 31);
}
// Table holding a message.
diff --git a/aos/events/logging/logger_test.cc b/aos/events/logging/logger_test.cc
index 55bf687..1732549 100644
--- a/aos/events/logging/logger_test.cc
+++ b/aos/events/logging/logger_test.cc
@@ -398,8 +398,7 @@
ping_sender.MakeBuilder();
examples::Ping::Builder ping_builder =
builder.MakeBuilder<examples::Ping>();
- CHECK_EQ(builder.Send(ping_builder.Finish()),
- RawSender::Error::kOk);
+ CHECK_EQ(builder.Send(ping_builder.Finish()), RawSender::Error::kOk);
});
// 100 ms / 0.05 ms -> 2000 messages. Should be enough to crash it.
@@ -510,8 +509,15 @@
~LoggerState() {
if (logger) {
- for (const std::string &file : log_namer->all_filenames()) {
+ std::vector<std::string> filenames;
+ AppendAllFilenames(&filenames);
+ std::sort(filenames.begin(), filenames.end());
+ for (const std::string &file : filenames) {
LOG(INFO) << "Wrote to " << file;
+ auto x = ReadHeader(file);
+ if (x) {
+ VLOG(1) << aos::FlatbufferToJson(x.value());
+ }
}
}
}
@@ -598,8 +604,8 @@
std::vector<std::string> MakeLogFiles(std::string logfile_base1,
std::string logfile_base2,
- size_t pi1_data_count = 2,
- size_t pi2_data_count = 2) {
+ size_t pi1_data_count = 3,
+ size_t pi2_data_count = 3) {
std::vector<std::string> result;
result.emplace_back(absl::StrCat(
logfile_base1, "_", std::get<0>(GetParam()).sha256, Extension()));
@@ -638,6 +644,10 @@
"_timestamps/pi1/aos/remote_timestamps/pi2/"
"aos.message_bridge.RemoteMessage.part1" +
Extension());
+ result.emplace_back(logfile_base1 +
+ "_timestamps/pi1/aos/remote_timestamps/pi2/"
+ "aos.message_bridge.RemoteMessage.part2" +
+ Extension());
result.emplace_back(logfile_base2 +
"_timestamps/pi2/aos/remote_timestamps/pi1/"
"aos.message_bridge.RemoteMessage.part0" +
@@ -687,6 +697,8 @@
result.emplace_back(logfile_base1_ + "_pi1_data.part0" + Extension());
result.emplace_back(logfile_base1_ + "_pi1_data.part1" + Extension());
result.emplace_back(logfile_base1_ + "_pi1_data.part2" + Extension());
+ result.emplace_back(logfile_base1_ + "_pi1_data.part3" + Extension());
+ result.emplace_back(logfile_base1_ + "_pi1_data.part4" + Extension());
result.emplace_back(logfile_base1_ +
"_pi2_data/test/aos.examples.Pong.part0" + Extension());
result.emplace_back(logfile_base1_ +
@@ -710,22 +722,13 @@
result.emplace_back(absl::StrCat(
logfile_base1_, "_", std::get<0>(GetParam()).sha256, Extension()));
if (shared()) {
- result.emplace_back(logfile_base1_ +
- "_timestamps/pi1/aos/remote_timestamps/pi2/"
- "aos.message_bridge.RemoteMessage.part0" +
- Extension());
- result.emplace_back(logfile_base1_ +
- "_timestamps/pi1/aos/remote_timestamps/pi2/"
- "aos.message_bridge.RemoteMessage.part1" +
- Extension());
- result.emplace_back(logfile_base1_ +
- "_timestamps/pi1/aos/remote_timestamps/pi2/"
- "aos.message_bridge.RemoteMessage.part2" +
- Extension());
- result.emplace_back(logfile_base1_ +
- "_timestamps/pi1/aos/remote_timestamps/pi2/"
- "aos.message_bridge.RemoteMessage.part3" +
- Extension());
+ for (size_t i = 0; i < 6; ++i) {
+ result.emplace_back(
+ absl::StrCat(logfile_base1_,
+ "_timestamps/pi1/aos/remote_timestamps/pi2/"
+ "aos.message_bridge.RemoteMessage.part",
+ i, Extension()));
+ }
} else {
result.emplace_back(logfile_base1_ +
"_timestamps/pi1/aos/remote_timestamps/pi2/pi1/aos/"
@@ -794,17 +797,24 @@
std::vector<std::vector<std::string>> StructureLogFiles() {
std::vector<std::vector<std::string>> result{
- std::vector<std::string>{logfiles_[2], logfiles_[3]},
- std::vector<std::string>{logfiles_[4], logfiles_[5]},
- std::vector<std::string>{logfiles_[6], logfiles_[7]},
- std::vector<std::string>{logfiles_[8], logfiles_[9]},
+ std::vector<std::string>{logfiles_[2], logfiles_[3], logfiles_[4]},
+ std::vector<std::string>{logfiles_[5], logfiles_[6]},
+ std::vector<std::string>{logfiles_[7], logfiles_[8], logfiles_[9]},
std::vector<std::string>{logfiles_[10], logfiles_[11]},
- std::vector<std::string>{logfiles_[12], logfiles_[13]},
- std::vector<std::string>{logfiles_[14], logfiles_[15]}};
+ std::vector<std::string>{logfiles_[12], logfiles_[13]}};
- if (!shared()) {
+ if (shared()) {
+ result.emplace_back(std::vector<std::string>{logfiles_[14], logfiles_[15],
+ logfiles_[16]});
+ result.emplace_back(
+ std::vector<std::string>{logfiles_[17], logfiles_[18]});
+ } else {
+ result.emplace_back(
+ std::vector<std::string>{logfiles_[14], logfiles_[15]});
result.emplace_back(
std::vector<std::string>{logfiles_[16], logfiles_[17]});
+ result.emplace_back(
+ std::vector<std::string>{logfiles_[18], logfiles_[19]});
}
return result;
@@ -887,7 +897,7 @@
// Test that each list of parts is in order. Don't worry about the ordering
// between part file lists though.
// (inner vectors all need to be in order, but outer one doesn't matter).
- EXPECT_THAT(ToLogReaderVector(sorted_parts),
+ ASSERT_THAT(ToLogReaderVector(sorted_parts),
::testing::UnorderedElementsAreArray(structured_logfiles_));
EXPECT_THAT(logger_nodes, ::testing::UnorderedElementsAre("pi1", "pi2"));
@@ -1010,7 +1020,9 @@
// Tests that we can write and read simple multi-node log files.
TEST_P(MultinodeLoggerTest, SimpleMultiNode) {
+ std::vector<std::string> actual_filenames;
time_converter_.StartEqual();
+
{
LoggerState pi1_logger = MakeLogger(pi1_);
LoggerState pi2_logger = MakeLogger(pi2_);
@@ -1021,8 +1033,13 @@
StartLogger(&pi2_logger);
event_loop_factory_.RunFor(chrono::milliseconds(20000));
+ pi1_logger.AppendAllFilenames(&actual_filenames);
+ pi2_logger.AppendAllFilenames(&actual_filenames);
}
+ ASSERT_THAT(actual_filenames,
+ ::testing::UnorderedElementsAreArray(logfiles_));
+
{
std::set<std::string> logfile_uuids;
std::set<std::string> parts_uuids;
@@ -1048,41 +1065,73 @@
// And confirm everything is on the correct node.
EXPECT_EQ(log_header[2].message().node()->name()->string_view(), "pi1");
EXPECT_EQ(log_header[3].message().node()->name()->string_view(), "pi1");
- EXPECT_EQ(log_header[4].message().node()->name()->string_view(), "pi2");
+ EXPECT_EQ(log_header[4].message().node()->name()->string_view(), "pi1");
+
EXPECT_EQ(log_header[5].message().node()->name()->string_view(), "pi2");
EXPECT_EQ(log_header[6].message().node()->name()->string_view(), "pi2");
+
EXPECT_EQ(log_header[7].message().node()->name()->string_view(), "pi2");
- EXPECT_EQ(log_header[8].message().node()->name()->string_view(), "pi1");
- EXPECT_EQ(log_header[9].message().node()->name()->string_view(), "pi1");
- EXPECT_EQ(log_header[10].message().node()->name()->string_view(), "pi2");
- EXPECT_EQ(log_header[11].message().node()->name()->string_view(), "pi2");
+ EXPECT_EQ(log_header[8].message().node()->name()->string_view(), "pi2");
+ EXPECT_EQ(log_header[9].message().node()->name()->string_view(), "pi2");
+
+ EXPECT_EQ(log_header[10].message().node()->name()->string_view(), "pi1");
+ EXPECT_EQ(log_header[11].message().node()->name()->string_view(), "pi1");
+
EXPECT_EQ(log_header[12].message().node()->name()->string_view(), "pi2");
EXPECT_EQ(log_header[13].message().node()->name()->string_view(), "pi2");
- EXPECT_EQ(log_header[14].message().node()->name()->string_view(), "pi1");
- EXPECT_EQ(log_header[15].message().node()->name()->string_view(), "pi1");
- if (!shared()) {
+
+ if (shared()) {
+ EXPECT_EQ(log_header[14].message().node()->name()->string_view(), "pi2");
+ EXPECT_EQ(log_header[15].message().node()->name()->string_view(), "pi2");
EXPECT_EQ(log_header[16].message().node()->name()->string_view(), "pi2");
- EXPECT_EQ(log_header[17].message().node()->name()->string_view(), "pi2");
+
+ EXPECT_EQ(log_header[17].message().node()->name()->string_view(), "pi1");
+ EXPECT_EQ(log_header[18].message().node()->name()->string_view(), "pi1");
+ } else {
+ EXPECT_EQ(log_header[14].message().node()->name()->string_view(), "pi2");
+ EXPECT_EQ(log_header[15].message().node()->name()->string_view(), "pi2");
+
+ EXPECT_EQ(log_header[16].message().node()->name()->string_view(), "pi1");
+ EXPECT_EQ(log_header[17].message().node()->name()->string_view(), "pi1");
+
+ EXPECT_EQ(log_header[18].message().node()->name()->string_view(), "pi2");
+ EXPECT_EQ(log_header[19].message().node()->name()->string_view(), "pi2");
}
// And the parts index matches.
EXPECT_EQ(log_header[2].message().parts_index(), 0);
EXPECT_EQ(log_header[3].message().parts_index(), 1);
- EXPECT_EQ(log_header[4].message().parts_index(), 0);
- EXPECT_EQ(log_header[5].message().parts_index(), 1);
- EXPECT_EQ(log_header[6].message().parts_index(), 0);
- EXPECT_EQ(log_header[7].message().parts_index(), 1);
- EXPECT_EQ(log_header[8].message().parts_index(), 0);
- EXPECT_EQ(log_header[9].message().parts_index(), 1);
+ EXPECT_EQ(log_header[4].message().parts_index(), 2);
+
+ EXPECT_EQ(log_header[5].message().parts_index(), 0);
+ EXPECT_EQ(log_header[6].message().parts_index(), 1);
+
+ EXPECT_EQ(log_header[7].message().parts_index(), 0);
+ EXPECT_EQ(log_header[8].message().parts_index(), 1);
+ EXPECT_EQ(log_header[9].message().parts_index(), 2);
+
EXPECT_EQ(log_header[10].message().parts_index(), 0);
EXPECT_EQ(log_header[11].message().parts_index(), 1);
+
EXPECT_EQ(log_header[12].message().parts_index(), 0);
EXPECT_EQ(log_header[13].message().parts_index(), 1);
- EXPECT_EQ(log_header[14].message().parts_index(), 0);
- EXPECT_EQ(log_header[15].message().parts_index(), 1);
- if (!shared()) {
+
+ if (shared()) {
+ EXPECT_EQ(log_header[14].message().parts_index(), 0);
+ EXPECT_EQ(log_header[15].message().parts_index(), 1);
+ EXPECT_EQ(log_header[16].message().parts_index(), 2);
+
+ EXPECT_EQ(log_header[17].message().parts_index(), 0);
+ EXPECT_EQ(log_header[18].message().parts_index(), 1);
+ } else {
+ EXPECT_EQ(log_header[14].message().parts_index(), 0);
+ EXPECT_EQ(log_header[15].message().parts_index(), 1);
+
EXPECT_EQ(log_header[16].message().parts_index(), 0);
EXPECT_EQ(log_header[17].message().parts_index(), 1);
+
+ EXPECT_EQ(log_header[18].message().parts_index(), 0);
+ EXPECT_EQ(log_header[19].message().parts_index(), 1);
}
}
@@ -1102,50 +1151,65 @@
EXPECT_THAT(
CountChannelsData(config, logfiles_[3]),
UnorderedElementsAre(
- std::make_tuple("/pi1/aos", "aos.message_bridge.Timestamp", 200),
+ std::make_tuple("/pi1/aos", "aos.message_bridge.Timestamp", 1),
+ std::make_tuple("/pi1/aos", "aos.message_bridge.ClientStatistics",
+ 1)))
+ << " : " << logfiles_[3];
+ EXPECT_THAT(
+ CountChannelsData(config, logfiles_[4]),
+ UnorderedElementsAre(
+ std::make_tuple("/pi1/aos", "aos.message_bridge.Timestamp", 199),
std::make_tuple("/pi1/aos", "aos.message_bridge.ServerStatistics",
20),
std::make_tuple("/pi1/aos", "aos.message_bridge.ClientStatistics",
- 200),
+ 199),
std::make_tuple("/pi1/aos", "aos.timing.Report", 40),
std::make_tuple("/test", "aos.examples.Ping", 2000)))
- << " : " << logfiles_[3];
+ << " : " << logfiles_[4];
// Timestamps for pong
EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[2]),
UnorderedElementsAre())
<< " : " << logfiles_[2];
EXPECT_THAT(
CountChannelsTimestamp(config, logfiles_[3]),
- UnorderedElementsAre(
- std::make_tuple("/test", "aos.examples.Pong", 2001),
- std::make_tuple("/pi2/aos", "aos.message_bridge.Timestamp", 200)))
+ UnorderedElementsAre(std::make_tuple("/test", "aos.examples.Pong", 1)))
<< " : " << logfiles_[3];
+ EXPECT_THAT(
+ CountChannelsTimestamp(config, logfiles_[4]),
+ UnorderedElementsAre(
+ std::make_tuple("/test", "aos.examples.Pong", 2000),
+ std::make_tuple("/pi2/aos", "aos.message_bridge.Timestamp", 200)))
+ << " : " << logfiles_[4];
// Pong data.
EXPECT_THAT(
- CountChannelsData(config, logfiles_[4]),
+ CountChannelsData(config, logfiles_[5]),
UnorderedElementsAre(std::make_tuple("/test", "aos.examples.Pong", 91)))
- << " : " << logfiles_[4];
- EXPECT_THAT(CountChannelsData(config, logfiles_[5]),
+ << " : " << logfiles_[5];
+ EXPECT_THAT(CountChannelsData(config, logfiles_[6]),
UnorderedElementsAre(
std::make_tuple("/test", "aos.examples.Pong", 1910)))
- << " : " << logfiles_[5];
+ << " : " << logfiles_[6];
// No timestamps
- EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[4]),
- UnorderedElementsAre())
- << " : " << logfiles_[4];
EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[5]),
UnorderedElementsAre())
<< " : " << logfiles_[5];
+ EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[6]),
+ UnorderedElementsAre())
+ << " : " << logfiles_[6];
// Timing reports and pongs.
- EXPECT_THAT(CountChannelsData(config, logfiles_[6]),
+ EXPECT_THAT(CountChannelsData(config, logfiles_[7]),
UnorderedElementsAre(std::make_tuple(
"/pi2/aos", "aos.message_bridge.ServerStatistics", 1)))
- << " : " << logfiles_[6];
+ << " : " << logfiles_[7];
EXPECT_THAT(
- CountChannelsData(config, logfiles_[7]),
+ CountChannelsData(config, logfiles_[8]),
+ UnorderedElementsAre(std::make_tuple("/test", "aos.examples.Pong", 1)))
+ << " : " << logfiles_[8];
+ EXPECT_THAT(
+ CountChannelsData(config, logfiles_[9]),
UnorderedElementsAre(
std::make_tuple("/pi2/aos", "aos.message_bridge.Timestamp", 200),
std::make_tuple("/pi2/aos", "aos.message_bridge.ServerStatistics",
@@ -1153,120 +1217,142 @@
std::make_tuple("/pi2/aos", "aos.message_bridge.ClientStatistics",
200),
std::make_tuple("/pi2/aos", "aos.timing.Report", 40),
- std::make_tuple("/test", "aos.examples.Pong", 2001)))
- << " : " << logfiles_[7];
+ std::make_tuple("/test", "aos.examples.Pong", 2000)))
+ << " : " << logfiles_[9];
// And ping timestamps.
- EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[6]),
+ EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[7]),
UnorderedElementsAre())
- << " : " << logfiles_[6];
- EXPECT_THAT(
- CountChannelsTimestamp(config, logfiles_[7]),
- UnorderedElementsAre(
- std::make_tuple("/test", "aos.examples.Ping", 2001),
- std::make_tuple("/pi1/aos", "aos.message_bridge.Timestamp", 200)))
<< " : " << logfiles_[7];
+ EXPECT_THAT(
+ CountChannelsTimestamp(config, logfiles_[8]),
+ UnorderedElementsAre(std::make_tuple("/test", "aos.examples.Ping", 1)))
+ << " : " << logfiles_[8];
+ EXPECT_THAT(
+ CountChannelsTimestamp(config, logfiles_[9]),
+ UnorderedElementsAre(
+ std::make_tuple("/test", "aos.examples.Ping", 2000),
+ std::make_tuple("/pi1/aos", "aos.message_bridge.Timestamp", 200)))
+ << " : " << logfiles_[9];
// And then test that the remotely logged timestamp data files only have
// timestamps in them.
- EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[8]),
- UnorderedElementsAre())
- << " : " << logfiles_[8];
- EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[9]),
- UnorderedElementsAre())
- << " : " << logfiles_[9];
EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[10]),
UnorderedElementsAre())
<< " : " << logfiles_[10];
EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[11]),
UnorderedElementsAre())
<< " : " << logfiles_[11];
-
- EXPECT_THAT(CountChannelsData(config, logfiles_[8]),
- UnorderedElementsAre(std::make_tuple(
- "/pi1/aos", "aos.message_bridge.Timestamp", 9)))
- << " : " << logfiles_[8];
- EXPECT_THAT(CountChannelsData(config, logfiles_[9]),
- UnorderedElementsAre(std::make_tuple(
- "/pi1/aos", "aos.message_bridge.Timestamp", 191)))
- << " : " << logfiles_[9];
+ EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[12]),
+ UnorderedElementsAre())
+ << " : " << logfiles_[12];
+ EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[13]),
+ UnorderedElementsAre())
+ << " : " << logfiles_[13];
EXPECT_THAT(CountChannelsData(config, logfiles_[10]),
UnorderedElementsAre(std::make_tuple(
- "/pi2/aos", "aos.message_bridge.Timestamp", 9)))
+ "/pi1/aos", "aos.message_bridge.Timestamp", 9)))
<< " : " << logfiles_[10];
EXPECT_THAT(CountChannelsData(config, logfiles_[11]),
UnorderedElementsAre(std::make_tuple(
- "/pi2/aos", "aos.message_bridge.Timestamp", 191)))
+ "/pi1/aos", "aos.message_bridge.Timestamp", 191)))
<< " : " << logfiles_[11];
- // Timestamps from pi2 on pi1, and the other way.
EXPECT_THAT(CountChannelsData(config, logfiles_[12]),
- UnorderedElementsAre())
+ UnorderedElementsAre(std::make_tuple(
+ "/pi2/aos", "aos.message_bridge.Timestamp", 9)))
<< " : " << logfiles_[12];
EXPECT_THAT(CountChannelsData(config, logfiles_[13]),
- UnorderedElementsAre())
+ UnorderedElementsAre(std::make_tuple(
+ "/pi2/aos", "aos.message_bridge.Timestamp", 191)))
<< " : " << logfiles_[13];
- EXPECT_THAT(CountChannelsData(config, logfiles_[14]),
- UnorderedElementsAre())
- << " : " << logfiles_[14];
- EXPECT_THAT(CountChannelsData(config, logfiles_[15]),
- UnorderedElementsAre())
- << " : " << logfiles_[15];
- if (!shared()) {
+
+ // Timestamps from pi2 on pi1, and the other way.
+ if (shared()) {
+ EXPECT_THAT(CountChannelsData(config, logfiles_[14]),
+ UnorderedElementsAre())
+ << " : " << logfiles_[14];
+ EXPECT_THAT(CountChannelsData(config, logfiles_[15]),
+ UnorderedElementsAre())
+ << " : " << logfiles_[15];
EXPECT_THAT(CountChannelsData(config, logfiles_[16]),
UnorderedElementsAre())
<< " : " << logfiles_[16];
EXPECT_THAT(CountChannelsData(config, logfiles_[17]),
UnorderedElementsAre())
<< " : " << logfiles_[17];
- }
+ EXPECT_THAT(CountChannelsData(config, logfiles_[18]),
+ UnorderedElementsAre())
+ << " : " << logfiles_[18];
- if (shared()) {
+ EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[14]),
+ UnorderedElementsAre(
+ std::make_tuple("/test", "aos.examples.Ping", 1)))
+ << " : " << logfiles_[14];
EXPECT_THAT(
- CountChannelsTimestamp(config, logfiles_[12]),
+ CountChannelsTimestamp(config, logfiles_[15]),
UnorderedElementsAre(
std::make_tuple("/pi1/aos", "aos.message_bridge.Timestamp", 9),
- std::make_tuple("/test", "aos.examples.Ping", 91)))
- << " : " << logfiles_[12];
+ std::make_tuple("/test", "aos.examples.Ping", 90)))
+ << " : " << logfiles_[15];
EXPECT_THAT(
- CountChannelsTimestamp(config, logfiles_[13]),
+ CountChannelsTimestamp(config, logfiles_[16]),
UnorderedElementsAre(
std::make_tuple("/pi1/aos", "aos.message_bridge.Timestamp", 191),
std::make_tuple("/test", "aos.examples.Ping", 1910)))
- << " : " << logfiles_[13];
- EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[14]),
- UnorderedElementsAre(std::make_tuple(
- "/pi2/aos", "aos.message_bridge.Timestamp", 9)))
- << " : " << logfiles_[14];
- EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[15]),
- UnorderedElementsAre(std::make_tuple(
- "/pi2/aos", "aos.message_bridge.Timestamp", 191)))
- << " : " << logfiles_[15];
- } else {
- EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[12]),
- UnorderedElementsAre(std::make_tuple(
- "/pi1/aos", "aos.message_bridge.Timestamp", 9)))
- << " : " << logfiles_[12];
- EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[13]),
- UnorderedElementsAre(std::make_tuple(
- "/pi1/aos", "aos.message_bridge.Timestamp", 191)))
- << " : " << logfiles_[13];
- EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[14]),
- UnorderedElementsAre(std::make_tuple(
- "/pi2/aos", "aos.message_bridge.Timestamp", 9)))
- << " : " << logfiles_[14];
- EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[15]),
- UnorderedElementsAre(std::make_tuple(
- "/pi2/aos", "aos.message_bridge.Timestamp", 191)))
- << " : " << logfiles_[15];
- EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[16]),
- UnorderedElementsAre(
- std::make_tuple("/test", "aos.examples.Ping", 91)))
<< " : " << logfiles_[16];
EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[17]),
+ UnorderedElementsAre(std::make_tuple(
+ "/pi2/aos", "aos.message_bridge.Timestamp", 9)))
+ << " : " << logfiles_[17];
+ EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[18]),
+ UnorderedElementsAre(std::make_tuple(
+ "/pi2/aos", "aos.message_bridge.Timestamp", 191)))
+ << " : " << logfiles_[18];
+ } else {
+ EXPECT_THAT(CountChannelsData(config, logfiles_[14]),
+ UnorderedElementsAre())
+ << " : " << logfiles_[14];
+ EXPECT_THAT(CountChannelsData(config, logfiles_[15]),
+ UnorderedElementsAre())
+ << " : " << logfiles_[15];
+ EXPECT_THAT(CountChannelsData(config, logfiles_[16]),
+ UnorderedElementsAre())
+ << " : " << logfiles_[16];
+ EXPECT_THAT(CountChannelsData(config, logfiles_[17]),
+ UnorderedElementsAre())
+ << " : " << logfiles_[17];
+ EXPECT_THAT(CountChannelsData(config, logfiles_[18]),
+ UnorderedElementsAre())
+ << " : " << logfiles_[18];
+ EXPECT_THAT(CountChannelsData(config, logfiles_[19]),
+ UnorderedElementsAre())
+ << " : " << logfiles_[19];
+
+ EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[14]),
+ UnorderedElementsAre(std::make_tuple(
+ "/pi1/aos", "aos.message_bridge.Timestamp", 9)))
+ << " : " << logfiles_[14];
+ EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[15]),
+ UnorderedElementsAre(std::make_tuple(
+ "/pi1/aos", "aos.message_bridge.Timestamp", 191)))
+ << " : " << logfiles_[15];
+ EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[16]),
+ UnorderedElementsAre(std::make_tuple(
+ "/pi2/aos", "aos.message_bridge.Timestamp", 9)))
+ << " : " << logfiles_[16];
+ EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[17]),
+ UnorderedElementsAre(std::make_tuple(
+ "/pi2/aos", "aos.message_bridge.Timestamp", 191)))
+ << " : " << logfiles_[17];
+ EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[18]),
+ UnorderedElementsAre(
+ std::make_tuple("/test", "aos.examples.Ping", 91)))
+ << " : " << logfiles_[18];
+ EXPECT_THAT(CountChannelsTimestamp(config, logfiles_[19]),
UnorderedElementsAre(
std::make_tuple("/test", "aos.examples.Ping", 1910)))
- << " : " << logfiles_[17];
+ << " : " << logfiles_[19];
}
}
@@ -1459,6 +1545,8 @@
// time.
TEST_P(MultinodeLoggerTest, StaggeredStart) {
time_converter_.StartEqual();
+ std::vector<std::string> actual_filenames;
+
{
LoggerState pi1_logger = MakeLogger(pi1_);
LoggerState pi2_logger = MakeLogger(pi2_);
@@ -1472,12 +1560,13 @@
StartLogger(&pi2_logger);
event_loop_factory_.RunFor(chrono::milliseconds(20000));
+ pi1_logger.AppendAllFilenames(&actual_filenames);
+ pi2_logger.AppendAllFilenames(&actual_filenames);
}
// Since we delay starting pi2, it already knows about all the timestamps so
// we don't end up with extra parts.
- LogReader reader(
- SortParts(MakeLogFiles(logfile_base1_, logfile_base2_, 2, 1)));
+ LogReader reader(SortParts(actual_filenames));
SimulatedEventLoopFactory log_reader_factory(reader.configuration());
log_reader_factory.set_send_delay(chrono::microseconds(0));
@@ -1788,6 +1877,7 @@
// Tests that we can sort a bunch of parts with the end missing off a
// file. We should use the part we can read.
TEST_P(MultinodeLoggerTest, SortTruncatedParts) {
+ std::vector<std::string> actual_filenames;
time_converter_.StartEqual();
// Make a bunch of parts.
{
@@ -1800,16 +1890,22 @@
StartLogger(&pi2_logger);
event_loop_factory_.RunFor(chrono::milliseconds(2000));
+
+ pi1_logger.AppendAllFilenames(&actual_filenames);
+ pi2_logger.AppendAllFilenames(&actual_filenames);
}
+ ASSERT_THAT(actual_filenames,
+ ::testing::UnorderedElementsAreArray(logfiles_));
+
// Strip off the end of one of the files. Pick one with a lot of data.
// For snappy, needs to have enough data to be >1 chunk of compressed data so
// that we don't corrupt the entire log part.
::std::string compressed_contents =
- aos::util::ReadFileToStringOrDie(logfiles_[3]);
+ aos::util::ReadFileToStringOrDie(logfiles_[4]);
aos::util::WriteStringToFileOrDie(
- logfiles_[3],
+ logfiles_[4],
compressed_contents.substr(0, compressed_contents.size() - 100));
const std::vector<LogFile> sorted_parts = SortParts(logfiles_);
@@ -2238,6 +2334,7 @@
// Tests that we properly populate and extract the logger_start time by setting
// up a clock difference between 2 nodes and looking at the resulting parts.
TEST_P(MultinodeLoggerTest, LoggerStartTime) {
+ std::vector<std::string> actual_filenames;
time_converter_.AddMonotonic(
{BootTimestamp::epoch(), BootTimestamp::epoch() + chrono::seconds(1000)});
{
@@ -2248,8 +2345,14 @@
StartLogger(&pi2_logger);
event_loop_factory_.RunFor(chrono::milliseconds(10000));
+
+ pi1_logger.AppendAllFilenames(&actual_filenames);
+ pi2_logger.AppendAllFilenames(&actual_filenames);
}
+ ASSERT_THAT(actual_filenames,
+ ::testing::UnorderedElementsAreArray(logfiles_));
+
for (const LogFile &log_file : SortParts(logfiles_)) {
for (const LogParts &log_part : log_file.parts) {
if (log_part.node == log_file.logger_node) {
@@ -2322,6 +2425,8 @@
// This should be enough that we can then re-run the logger and get a valid log
// back.
TEST_P(MultinodeLoggerTest, RemoteReboot) {
+ std::vector<std::string> actual_filenames;
+
const UUID pi1_boot0 = UUID::Random();
const UUID pi2_boot0 = UUID::Random();
const UUID pi2_boot1 = UUID::Random();
@@ -2365,8 +2470,15 @@
pi1_boot0);
EXPECT_EQ(event_loop_factory_.GetNodeEventLoopFactory("pi2")->boot_uuid(),
pi2_boot1);
+
+ pi1_logger.AppendAllFilenames(&actual_filenames);
}
+ std::sort(actual_filenames.begin(), actual_filenames.end());
+ std::sort(pi1_reboot_logfiles_.begin(), pi1_reboot_logfiles_.end());
+ ASSERT_THAT(actual_filenames,
+ ::testing::UnorderedElementsAreArray(pi1_reboot_logfiles_));
+
// Confirm that our new oldest timestamps properly update as we reboot and
// rotate.
for (const std::string &file : pi1_reboot_logfiles_) {
@@ -2384,28 +2496,64 @@
log_header->message().source_node_boot_uuid()->string_view());
if (log_header->message().node()->name()->string_view() != "pi1") {
- switch (log_header->message().parts_index()) {
- case 0:
- EXPECT_EQ(source_node_boot_uuid, pi2_boot0);
- EXPECT_EQ(monotonic_start_time, monotonic_clock::min_time);
- break;
- case 1:
- EXPECT_EQ(source_node_boot_uuid, pi2_boot0);
- ASSERT_EQ(monotonic_start_time,
- monotonic_clock::epoch() + chrono::seconds(1));
- break;
- case 2:
- EXPECT_EQ(source_node_boot_uuid, pi2_boot1);
- EXPECT_EQ(monotonic_start_time, monotonic_clock::min_time);
- break;
- case 3:
- EXPECT_EQ(source_node_boot_uuid, pi2_boot1);
- ASSERT_EQ(monotonic_start_time,
- monotonic_clock::epoch() + chrono::nanoseconds(2322999462));
- break;
- default:
- FAIL();
- break;
+ // The remote message channel should rotate later and have more parts.
+ // This only is true on the log files with shared remote messages.
+ //
+ // TODO(austin): I'm not the most thrilled with this test pattern... It
+ // feels brittle in a different way.
+ if (file.find("aos.message_bridge.RemoteMessage") == std::string::npos ||
+ !shared()) {
+ switch (log_header->message().parts_index()) {
+ case 0:
+ EXPECT_EQ(source_node_boot_uuid, pi2_boot0);
+ EXPECT_EQ(monotonic_start_time, monotonic_clock::min_time);
+ break;
+ case 1:
+ EXPECT_EQ(source_node_boot_uuid, pi2_boot0);
+ ASSERT_EQ(monotonic_start_time,
+ monotonic_clock::epoch() + chrono::seconds(1));
+ break;
+ case 2:
+ EXPECT_EQ(source_node_boot_uuid, pi2_boot1);
+ EXPECT_EQ(monotonic_start_time, monotonic_clock::min_time) << file;
+ break;
+ case 3:
+ EXPECT_EQ(source_node_boot_uuid, pi2_boot1);
+ ASSERT_EQ(monotonic_start_time, monotonic_clock::epoch() +
+ chrono::nanoseconds(2322999462))
+ << " on " << file;
+ break;
+ default:
+ FAIL();
+ break;
+ }
+ } else {
+ switch (log_header->message().parts_index()) {
+ case 0:
+ case 1:
+ EXPECT_EQ(source_node_boot_uuid, pi2_boot0);
+ EXPECT_EQ(monotonic_start_time, monotonic_clock::min_time);
+ break;
+ case 2:
+ EXPECT_EQ(source_node_boot_uuid, pi2_boot0);
+ ASSERT_EQ(monotonic_start_time,
+ monotonic_clock::epoch() + chrono::seconds(1));
+ break;
+ case 3:
+ case 4:
+ EXPECT_EQ(source_node_boot_uuid, pi2_boot1);
+ EXPECT_EQ(monotonic_start_time, monotonic_clock::min_time) << file;
+ break;
+ case 5:
+ EXPECT_EQ(source_node_boot_uuid, pi2_boot1);
+ ASSERT_EQ(monotonic_start_time, monotonic_clock::epoch() +
+ chrono::nanoseconds(2322999462))
+ << " on " << file;
+ break;
+ default:
+ FAIL();
+ break;
+ }
}
continue;
}
@@ -2463,6 +2611,34 @@
log_header->message()
.oldest_local_unreliable_monotonic_timestamps()
->Get(1)));
+ const monotonic_clock::time_point
+ oldest_remote_reliable_monotonic_timestamps =
+ monotonic_clock::time_point(chrono::nanoseconds(
+ log_header->message()
+ .oldest_remote_reliable_monotonic_timestamps()
+ ->Get(1)));
+ const monotonic_clock::time_point
+ oldest_local_reliable_monotonic_timestamps =
+ monotonic_clock::time_point(chrono::nanoseconds(
+ log_header->message()
+ .oldest_local_reliable_monotonic_timestamps()
+ ->Get(1)));
+ const monotonic_clock::time_point
+ oldest_logger_remote_unreliable_monotonic_timestamps =
+ monotonic_clock::time_point(chrono::nanoseconds(
+ log_header->message()
+ .oldest_logger_remote_unreliable_monotonic_timestamps()
+ ->Get(0)));
+ const monotonic_clock::time_point
+ oldest_logger_local_unreliable_monotonic_timestamps =
+ monotonic_clock::time_point(chrono::nanoseconds(
+ log_header->message()
+ .oldest_logger_local_unreliable_monotonic_timestamps()
+ ->Get(0)));
+ EXPECT_EQ(oldest_logger_remote_unreliable_monotonic_timestamps,
+ monotonic_clock::max_time);
+ EXPECT_EQ(oldest_logger_local_unreliable_monotonic_timestamps,
+ monotonic_clock::max_time);
switch (log_header->message().parts_index()) {
case 0:
EXPECT_EQ(oldest_remote_monotonic_timestamps,
@@ -2472,6 +2648,10 @@
monotonic_clock::max_time);
EXPECT_EQ(oldest_local_unreliable_monotonic_timestamps,
monotonic_clock::max_time);
+ EXPECT_EQ(oldest_remote_reliable_monotonic_timestamps,
+ monotonic_clock::max_time);
+ EXPECT_EQ(oldest_local_reliable_monotonic_timestamps,
+ monotonic_clock::max_time);
break;
case 1:
EXPECT_EQ(oldest_remote_monotonic_timestamps,
@@ -2482,9 +2662,33 @@
monotonic_clock::time_point(chrono::microseconds(90200)));
EXPECT_EQ(oldest_local_unreliable_monotonic_timestamps,
monotonic_clock::time_point(chrono::microseconds(90350)));
+ EXPECT_EQ(oldest_remote_reliable_monotonic_timestamps,
+ monotonic_clock::max_time);
+ EXPECT_EQ(oldest_local_reliable_monotonic_timestamps,
+ monotonic_clock::max_time);
break;
case 2:
EXPECT_EQ(oldest_remote_monotonic_timestamps,
+ monotonic_clock::time_point(chrono::microseconds(90200)))
+ << file;
+ EXPECT_EQ(oldest_local_monotonic_timestamps,
+ monotonic_clock::time_point(chrono::microseconds(90350)))
+ << file;
+ EXPECT_EQ(oldest_remote_unreliable_monotonic_timestamps,
+ monotonic_clock::time_point(chrono::microseconds(90200)))
+ << file;
+ EXPECT_EQ(oldest_local_unreliable_monotonic_timestamps,
+ monotonic_clock::time_point(chrono::microseconds(90350)))
+ << file;
+ EXPECT_EQ(oldest_remote_reliable_monotonic_timestamps,
+ monotonic_clock::time_point(chrono::microseconds(100000)))
+ << file;
+ EXPECT_EQ(oldest_local_reliable_monotonic_timestamps,
+ monotonic_clock::time_point(chrono::microseconds(100150)))
+ << file;
+ break;
+ case 3:
+ EXPECT_EQ(oldest_remote_monotonic_timestamps,
monotonic_clock::time_point(chrono::milliseconds(1323) +
chrono::microseconds(200)));
EXPECT_EQ(oldest_local_monotonic_timestamps,
@@ -2494,6 +2698,30 @@
chrono::microseconds(200)));
EXPECT_EQ(oldest_local_unreliable_monotonic_timestamps,
monotonic_clock::time_point(chrono::microseconds(10100350)));
+ EXPECT_EQ(oldest_remote_reliable_monotonic_timestamps,
+ monotonic_clock::max_time)
+ << file;
+ EXPECT_EQ(oldest_local_reliable_monotonic_timestamps,
+ monotonic_clock::max_time)
+ << file;
+ break;
+ case 4:
+ EXPECT_EQ(oldest_remote_monotonic_timestamps,
+ monotonic_clock::time_point(chrono::milliseconds(1323) +
+ chrono::microseconds(200)));
+ EXPECT_EQ(oldest_local_monotonic_timestamps,
+ monotonic_clock::time_point(chrono::microseconds(10100350)));
+ EXPECT_EQ(oldest_remote_unreliable_monotonic_timestamps,
+ monotonic_clock::time_point(chrono::milliseconds(1323) +
+ chrono::microseconds(200)));
+ EXPECT_EQ(oldest_local_unreliable_monotonic_timestamps,
+ monotonic_clock::time_point(chrono::microseconds(10100350)));
+ EXPECT_EQ(oldest_remote_reliable_monotonic_timestamps,
+ monotonic_clock::time_point(chrono::microseconds(1423000)))
+ << file;
+ EXPECT_EQ(oldest_local_reliable_monotonic_timestamps,
+ monotonic_clock::time_point(chrono::microseconds(10200150)))
+ << file;
break;
default:
FAIL();
@@ -2569,6 +2797,8 @@
pi1_logger.AppendAllFilenames(&filenames);
}
+ std::sort(filenames.begin(), filenames.end());
+
// Confirm that our new oldest timestamps properly update as we reboot and
// rotate.
size_t timestamp_file_count = 0;
@@ -2605,6 +2835,32 @@
.oldest_local_unreliable_monotonic_timestamps()
->size(),
2u);
+ ASSERT_TRUE(log_header->message()
+ .has_oldest_remote_reliable_monotonic_timestamps());
+ ASSERT_EQ(log_header->message()
+ .oldest_remote_reliable_monotonic_timestamps()
+ ->size(),
+ 2u);
+ ASSERT_TRUE(log_header->message()
+ .has_oldest_local_reliable_monotonic_timestamps());
+ ASSERT_EQ(log_header->message()
+ .oldest_local_reliable_monotonic_timestamps()
+ ->size(),
+ 2u);
+
+ ASSERT_TRUE(
+ log_header->message()
+ .has_oldest_logger_remote_unreliable_monotonic_timestamps());
+ ASSERT_EQ(log_header->message()
+ .oldest_logger_remote_unreliable_monotonic_timestamps()
+ ->size(),
+ 2u);
+ ASSERT_TRUE(log_header->message()
+ .has_oldest_logger_local_unreliable_monotonic_timestamps());
+ ASSERT_EQ(log_header->message()
+ .oldest_logger_local_unreliable_monotonic_timestamps()
+ ->size(),
+ 2u);
if (log_header->message().node()->name()->string_view() != "pi1") {
ASSERT_TRUE(file.find("aos.message_bridge.RemoteMessage") !=
@@ -2623,11 +2879,16 @@
const monotonic_clock::time_point
expected_oldest_remote_monotonic_timestamps(
chrono::nanoseconds(msg->message().monotonic_remote_time()));
+ const monotonic_clock::time_point
+ expected_oldest_timestamp_monotonic_timestamps(
+ chrono::nanoseconds(msg->message().monotonic_timestamp_time()));
EXPECT_NE(expected_oldest_local_monotonic_timestamps,
monotonic_clock::min_time);
EXPECT_NE(expected_oldest_remote_monotonic_timestamps,
monotonic_clock::min_time);
+ EXPECT_NE(expected_oldest_timestamp_monotonic_timestamps,
+ monotonic_clock::min_time);
++timestamp_file_count;
// Since the log file is from the perspective of the other node,
@@ -2651,39 +2912,267 @@
log_header->message()
.oldest_local_unreliable_monotonic_timestamps()
->Get(0)));
- // Confirm that the oldest timestamps match what we expect. Based on what
- // we are doing, we know that the oldest time is the first message's time.
- //
- // This makes the test robust to both the split and combined config tests.
- switch (log_header->message().parts_index()) {
- case 0:
- case 1:
- EXPECT_EQ(oldest_remote_monotonic_timestamps,
- expected_oldest_remote_monotonic_timestamps);
- EXPECT_EQ(oldest_local_monotonic_timestamps,
- expected_oldest_local_monotonic_timestamps);
- EXPECT_EQ(oldest_remote_unreliable_monotonic_timestamps,
- expected_oldest_remote_monotonic_timestamps);
- EXPECT_EQ(oldest_local_unreliable_monotonic_timestamps,
- expected_oldest_local_monotonic_timestamps);
- break;
- default:
- FAIL();
- break;
- }
+ const monotonic_clock::time_point
+ oldest_remote_reliable_monotonic_timestamps =
+ monotonic_clock::time_point(chrono::nanoseconds(
+ log_header->message()
+ .oldest_remote_reliable_monotonic_timestamps()
+ ->Get(0)));
+ const monotonic_clock::time_point
+ oldest_local_reliable_monotonic_timestamps =
+ monotonic_clock::time_point(chrono::nanoseconds(
+ log_header->message()
+ .oldest_local_reliable_monotonic_timestamps()
+ ->Get(0)));
+ const monotonic_clock::time_point
+ oldest_logger_remote_unreliable_monotonic_timestamps =
+ monotonic_clock::time_point(chrono::nanoseconds(
+ log_header->message()
+ .oldest_logger_remote_unreliable_monotonic_timestamps()
+ ->Get(1)));
+ const monotonic_clock::time_point
+ oldest_logger_local_unreliable_monotonic_timestamps =
+ monotonic_clock::time_point(chrono::nanoseconds(
+ log_header->message()
+ .oldest_logger_local_unreliable_monotonic_timestamps()
+ ->Get(1)));
- switch (log_header->message().parts_index()) {
- case 0:
- EXPECT_EQ(source_node_boot_uuid, pi2_boot0);
- EXPECT_EQ(monotonic_start_time, monotonic_clock::min_time);
- break;
- case 1:
- EXPECT_EQ(source_node_boot_uuid, pi2_boot1);
- EXPECT_EQ(monotonic_start_time, monotonic_clock::min_time);
- break;
- default:
- FAIL();
- break;
+ const Channel *channel =
+ event_loop_factory_.configuration()->channels()->Get(
+ msg->message().channel_index());
+ const Connection *connection = configuration::ConnectionToNode(
+ channel, configuration::GetNode(
+ event_loop_factory_.configuration(),
+ log_header->message().node()->name()->string_view()));
+
+ const bool reliable = connection->time_to_live() == 0;
+
+ SCOPED_TRACE(file);
+ SCOPED_TRACE(aos::FlatbufferToJson(
+ *log_header, {.multi_line = true, .max_vector_size = 100}));
+
+ if (shared()) {
+ // Confirm that the oldest timestamps match what we expect. Based on
+ // what we are doing, we know that the oldest time is the first
+ // message's time.
+ //
+ // This makes the test robust to both the split and combined config
+ // tests.
+ switch (log_header->message().parts_index()) {
+ case 0:
+ EXPECT_EQ(oldest_remote_monotonic_timestamps,
+ expected_oldest_remote_monotonic_timestamps);
+ EXPECT_EQ(oldest_local_monotonic_timestamps,
+ expected_oldest_local_monotonic_timestamps);
+ EXPECT_EQ(oldest_logger_remote_unreliable_monotonic_timestamps,
+ expected_oldest_local_monotonic_timestamps) << file;
+ EXPECT_EQ(oldest_logger_local_unreliable_monotonic_timestamps,
+ expected_oldest_timestamp_monotonic_timestamps) << file;
+
+ if (reliable) {
+ EXPECT_EQ(oldest_remote_reliable_monotonic_timestamps,
+ expected_oldest_remote_monotonic_timestamps);
+ EXPECT_EQ(oldest_local_reliable_monotonic_timestamps,
+ expected_oldest_local_monotonic_timestamps);
+ EXPECT_EQ(oldest_remote_unreliable_monotonic_timestamps,
+ monotonic_clock::max_time);
+ EXPECT_EQ(oldest_local_unreliable_monotonic_timestamps,
+ monotonic_clock::max_time);
+ } else {
+ EXPECT_EQ(oldest_remote_reliable_monotonic_timestamps,
+ monotonic_clock::max_time);
+ EXPECT_EQ(oldest_local_reliable_monotonic_timestamps,
+ monotonic_clock::max_time);
+ EXPECT_EQ(oldest_remote_unreliable_monotonic_timestamps,
+ expected_oldest_remote_monotonic_timestamps);
+ EXPECT_EQ(oldest_local_unreliable_monotonic_timestamps,
+ expected_oldest_local_monotonic_timestamps);
+ }
+ break;
+ case 1:
+ EXPECT_EQ(oldest_remote_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(90000000));
+ EXPECT_EQ(oldest_local_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(90150000));
+ EXPECT_EQ(oldest_logger_remote_unreliable_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(90150000));
+ EXPECT_EQ(oldest_logger_local_unreliable_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(90250000));
+ if (reliable) {
+ EXPECT_EQ(oldest_remote_reliable_monotonic_timestamps,
+ expected_oldest_remote_monotonic_timestamps);
+ EXPECT_EQ(oldest_local_reliable_monotonic_timestamps,
+ expected_oldest_local_monotonic_timestamps);
+ EXPECT_EQ(
+ oldest_remote_unreliable_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(90000000));
+ EXPECT_EQ(
+ oldest_local_unreliable_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(90150000));
+ } else {
+ EXPECT_EQ(oldest_remote_reliable_monotonic_timestamps,
+ monotonic_clock::max_time);
+ EXPECT_EQ(oldest_local_reliable_monotonic_timestamps,
+ monotonic_clock::max_time);
+ EXPECT_EQ(oldest_remote_unreliable_monotonic_timestamps,
+ expected_oldest_remote_monotonic_timestamps);
+ EXPECT_EQ(oldest_local_unreliable_monotonic_timestamps,
+ expected_oldest_local_monotonic_timestamps);
+ }
+ break;
+ case 2:
+ EXPECT_EQ(
+ oldest_remote_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(10000000000));
+ EXPECT_EQ(
+ oldest_local_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(1323100000));
+ EXPECT_EQ(oldest_logger_remote_unreliable_monotonic_timestamps,
+ expected_oldest_local_monotonic_timestamps) << file;
+ EXPECT_EQ(oldest_logger_local_unreliable_monotonic_timestamps,
+ expected_oldest_timestamp_monotonic_timestamps) << file;
+ if (reliable) {
+ EXPECT_EQ(oldest_remote_reliable_monotonic_timestamps,
+ expected_oldest_remote_monotonic_timestamps);
+ EXPECT_EQ(oldest_local_reliable_monotonic_timestamps,
+ expected_oldest_local_monotonic_timestamps);
+ EXPECT_EQ(oldest_remote_unreliable_monotonic_timestamps,
+ monotonic_clock::max_time);
+ EXPECT_EQ(oldest_local_unreliable_monotonic_timestamps,
+ monotonic_clock::max_time);
+ } else {
+ EXPECT_EQ(oldest_remote_reliable_monotonic_timestamps,
+ monotonic_clock::max_time);
+ EXPECT_EQ(oldest_local_reliable_monotonic_timestamps,
+ monotonic_clock::max_time);
+ EXPECT_EQ(oldest_remote_unreliable_monotonic_timestamps,
+ expected_oldest_remote_monotonic_timestamps);
+ EXPECT_EQ(oldest_local_unreliable_monotonic_timestamps,
+ expected_oldest_local_monotonic_timestamps);
+ }
+ break;
+
+ case 3:
+ EXPECT_EQ(
+ oldest_remote_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(10000000000));
+ EXPECT_EQ(
+ oldest_local_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(1323100000));
+ EXPECT_EQ(oldest_remote_unreliable_monotonic_timestamps,
+ expected_oldest_remote_monotonic_timestamps);
+ EXPECT_EQ(oldest_local_unreliable_monotonic_timestamps,
+ expected_oldest_local_monotonic_timestamps);
+ EXPECT_EQ(
+ oldest_logger_remote_unreliable_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(1323100000));
+ EXPECT_EQ(
+ oldest_logger_local_unreliable_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(10100200000));
+ break;
+ default:
+ FAIL();
+ break;
+ }
+
+ switch (log_header->message().parts_index()) {
+ case 0:
+ EXPECT_EQ(source_node_boot_uuid, pi2_boot0);
+ EXPECT_EQ(monotonic_start_time, monotonic_clock::min_time);
+ break;
+ case 1:
+ EXPECT_EQ(source_node_boot_uuid, pi2_boot0);
+ EXPECT_EQ(monotonic_start_time, monotonic_clock::min_time);
+ break;
+ case 2:
+ EXPECT_EQ(source_node_boot_uuid, pi2_boot1);
+ EXPECT_EQ(monotonic_start_time, monotonic_clock::min_time);
+ break;
+ case 3:
+ if (shared()) {
+ EXPECT_EQ(source_node_boot_uuid, pi2_boot1);
+ EXPECT_EQ(monotonic_start_time, monotonic_clock::min_time);
+ break;
+ }
+ [[fallthrough]];
+ default:
+ FAIL();
+ break;
+ }
+ } else {
+ switch (log_header->message().parts_index()) {
+ case 0:
+ if (reliable) {
+ EXPECT_EQ(oldest_remote_unreliable_monotonic_timestamps,
+ monotonic_clock::max_time);
+ EXPECT_EQ(oldest_local_unreliable_monotonic_timestamps,
+ monotonic_clock::max_time);
+ EXPECT_EQ(
+ oldest_logger_remote_unreliable_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(100150000))
+ << file;
+ EXPECT_EQ(
+ oldest_logger_local_unreliable_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(100250000))
+ << file;
+ } else {
+ EXPECT_EQ(oldest_remote_unreliable_monotonic_timestamps,
+ expected_oldest_remote_monotonic_timestamps);
+ EXPECT_EQ(oldest_local_unreliable_monotonic_timestamps,
+ expected_oldest_local_monotonic_timestamps);
+ EXPECT_EQ(
+ oldest_logger_remote_unreliable_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(90150000))
+ << file;
+ EXPECT_EQ(
+ oldest_logger_local_unreliable_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(90250000))
+ << file;
+ }
+ break;
+ case 1:
+ if (reliable) {
+ EXPECT_EQ(oldest_remote_unreliable_monotonic_timestamps,
+ monotonic_clock::max_time);
+ EXPECT_EQ(oldest_local_unreliable_monotonic_timestamps,
+ monotonic_clock::max_time);
+ EXPECT_EQ(
+ oldest_logger_remote_unreliable_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(1323100000));
+ EXPECT_EQ(
+ oldest_logger_local_unreliable_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(10100200000));
+ } else {
+ EXPECT_EQ(oldest_remote_unreliable_monotonic_timestamps,
+ expected_oldest_remote_monotonic_timestamps);
+ EXPECT_EQ(oldest_local_unreliable_monotonic_timestamps,
+ expected_oldest_local_monotonic_timestamps);
+ EXPECT_EQ(
+ oldest_logger_remote_unreliable_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(1323150000));
+ EXPECT_EQ(
+ oldest_logger_local_unreliable_monotonic_timestamps,
+ monotonic_clock::epoch() + chrono::nanoseconds(10100250000));
+ }
+ break;
+ default:
+ FAIL();
+ break;
+ }
+
+ switch (log_header->message().parts_index()) {
+ case 0:
+ EXPECT_EQ(source_node_boot_uuid, pi2_boot0);
+ EXPECT_EQ(monotonic_start_time, monotonic_clock::min_time);
+ break;
+ case 1:
+ EXPECT_EQ(source_node_boot_uuid, pi2_boot1);
+ EXPECT_EQ(monotonic_start_time, monotonic_clock::min_time);
+ break;
+ default:
+ FAIL();
+ break;
+ }
}
continue;
@@ -2737,7 +3226,11 @@
}
}
- EXPECT_TRUE(timestamp_file_count == 2u || timestamp_file_count == 4u);
+ if (shared()) {
+ EXPECT_EQ(timestamp_file_count, 4u);
+ } else {
+ EXPECT_EQ(timestamp_file_count, 4u);
+ }
// Confirm that we can actually sort the resulting log and read it.
{
@@ -3108,7 +3601,8 @@
// when a local message is in the log before a forwarded message, so there is no
// point in the interpolation function. This delays the reboot. So, we need to
// recreate that situation and make sure it doesn't come back.
-TEST(MultinodeRebootLoggerTest, LocalMessageBeforeRemoteBeforeStartAfterReboot) {
+TEST(MultinodeRebootLoggerTest,
+ LocalMessageBeforeRemoteBeforeStartAfterReboot) {
aos::FlatbufferDetachedBuffer<aos::Configuration> config =
aos::configuration::ReadConfig(ArtifactPath(
"aos/events/logging/multinode_pingpong_split3_config.json"));
@@ -3163,9 +3657,9 @@
time_converter.AddNextTimestamp(
distributed_clock::epoch() + reboot_time,
{BootTimestamp::epoch() + reboot_time,
- BootTimestamp{
- .boot = 1,
- .time = monotonic_clock::epoch() + reboot_time + chrono::seconds(100)},
+ BootTimestamp{.boot = 1,
+ .time = monotonic_clock::epoch() + reboot_time +
+ chrono::seconds(100)},
BootTimestamp::epoch() + reboot_time});
}
diff --git a/aos/events/shm_event_loop.h b/aos/events/shm_event_loop.h
index 71dff4e..e51f21b 100644
--- a/aos/events/shm_event_loop.h
+++ b/aos/events/shm_event_loop.h
@@ -48,10 +48,10 @@
// Exits the event loop. Async safe.
void Exit();
- aos::monotonic_clock::time_point monotonic_now() override {
+ aos::monotonic_clock::time_point monotonic_now() const override {
return aos::monotonic_clock::now();
}
- aos::realtime_clock::time_point realtime_now() override {
+ aos::realtime_clock::time_point realtime_now() const override {
return aos::realtime_clock::now();
}
diff --git a/aos/events/simulated_event_loop.cc b/aos/events/simulated_event_loop.cc
index 2a276e7..a2428a4 100644
--- a/aos/events/simulated_event_loop.cc
+++ b/aos/events/simulated_event_loop.cc
@@ -584,11 +584,11 @@
send_delay_ = send_delay;
}
- monotonic_clock::time_point monotonic_now() override {
+ monotonic_clock::time_point monotonic_now() const override {
return node_event_loop_factory_->monotonic_now();
}
- realtime_clock::time_point realtime_now() override {
+ realtime_clock::time_point realtime_now() const override {
return node_event_loop_factory_->realtime_now();
}
diff --git a/aos/network/message_bridge_server_lib.cc b/aos/network/message_bridge_server_lib.cc
index 2ca8e48..e8e0261 100644
--- a/aos/network/message_bridge_server_lib.cc
+++ b/aos/network/message_bridge_server_lib.cc
@@ -257,7 +257,7 @@
}) {
CHECK(event_loop_->node() != nullptr) << ": No nodes configured.";
- int32_t max_size = 0;
+ size_t max_size = 0;
// Seed up all the per-node connection state.
// We are making the assumption here that every connection is bidirectional
@@ -269,14 +269,14 @@
// Find the largest connection message so we can size our buffers big enough
// to receive a connection message. The connect message comes from the
// client to the server, so swap the node arguments.
- const int32_t connect_size = static_cast<int32_t>(
+ const size_t connect_size =
MakeConnectMessage(event_loop->configuration(),
configuration::GetNode(event_loop->configuration(),
destination_node_name),
event_loop->node()->name()->string_view(),
UUID::Zero())
.span()
- .size());
+ .size();
VLOG(1) << "Connection to " << destination_node_name << " has size "
<< connect_size;
max_size = std::max(max_size, connect_size);
@@ -310,7 +310,10 @@
any_reliable = true;
}
}
- max_size = std::max(channel->max_size(), max_size);
+ max_size =
+ std::max(static_cast<size_t>(channel->max_size() *
+ channel->destination_nodes()->size()),
+ max_size);
std::unique_ptr<ChannelState> state(new ChannelState{
channel, channel_index,
any_reliable ? event_loop_->MakeRawFetcher(channel) : nullptr});
@@ -371,7 +374,7 @@
// Buffer up the max size a bit so everything fits nicely.
LOG(INFO) << "Max message size for all clients is " << max_size;
- server_.SetMaxSize(max_size + 100);
+ server_.SetMaxSize(max_size + 100u);
}
void MessageBridgeServer::NodeConnected(sctp_assoc_t assoc_id) {
diff --git a/aos/network/sctp_lib.cc b/aos/network/sctp_lib.cc
index 33d3352..5bc1b37 100644
--- a/aos/network/sctp_lib.cc
+++ b/aos/network/sctp_lib.cc
@@ -319,6 +319,9 @@
if (size == -1) {
if (errno == EPIPE || errno == EAGAIN || errno == ESHUTDOWN ||
errno == EINTR) {
+ if (VLOG_IS_ON(1)) {
+ PLOG(WARNING) << "sendmsg on sctp socket failed";
+ }
return false;
}
PLOG(FATAL) << "sendmsg on sctp socket failed";
diff --git a/aos/network/timestamp_channel.cc b/aos/network/timestamp_channel.cc
index 88b6ed0..ab61051 100644
--- a/aos/network/timestamp_channel.cc
+++ b/aos/network/timestamp_channel.cc
@@ -27,6 +27,15 @@
return absl::StrCat("/aos/remote_timestamps/", remote_node);
}
+const Channel *ChannelTimestampFinder::SplitChannelForChannel(
+ const Channel *channel, const Connection *connection) {
+ const std::string split_timestamp_channel_name =
+ SplitChannelName(channel, connection);
+ return configuration::GetChannel(configuration_, split_timestamp_channel_name,
+ RemoteMessage::GetFullyQualifiedName(),
+ name_, node_, true);
+}
+
const Channel *ChannelTimestampFinder::ForChannel(
const Channel *channel, const Connection *connection) {
const std::string split_timestamp_channel_name =
diff --git a/aos/network/timestamp_channel.h b/aos/network/timestamp_channel.h
index b78c83a..e35a170 100644
--- a/aos/network/timestamp_channel.h
+++ b/aos/network/timestamp_channel.h
@@ -15,6 +15,9 @@
// Class to find the corresponding channel where timestamps for a specified data
// channel and connection will be logged.
+//
+// This abstracts (and detects) when we have combined or split remote timestamp
+// logging channels.
class ChannelTimestampFinder {
public:
ChannelTimestampFinder(aos::EventLoop *event_loop)
@@ -23,6 +26,11 @@
ChannelTimestampFinder(const Configuration *configuration,
const std::string_view name, const Node *node);
+ // Returns the split timestamp logging channel for the provide channel and
+ // connection if one exists, or nullptr otherwise.
+ const Channel *SplitChannelForChannel(const Channel *channel,
+ const Connection *connection);
+
// Finds the timestamp logging channel for the provided data channel and
// connection.
const Channel *ForChannel(const Channel *channel,
diff --git a/aos/time/time.cc b/aos/time/time.cc
index a956f94..05510e9 100644
--- a/aos/time/time.cc
+++ b/aos/time/time.cc
@@ -91,11 +91,11 @@
return std::nullopt;
}
- if (now.substr(now.size() - 13, 1) != ".") {
+ if (now[now.size() - 13] != '.') {
return std::nullopt;
}
- bool negative = now.substr(0, 1) == "-";
+ bool negative = now[0] == '-';
std::string sec(
now.substr(negative ? 1 : 0, now.size() - (negative ? 14 : 13)));
@@ -119,7 +119,7 @@
return std::nullopt;
}
- if (now.substr(now.size() - 10, 1) != ".") {
+ if (now[now.size() - 10] != '.') {
return std::nullopt;
}
diff --git a/debian/BUILD b/debian/BUILD
index 76bcf28..2aeabf2 100644
--- a/debian/BUILD
+++ b/debian/BUILD
@@ -47,6 +47,10 @@
python_gtk_debs = "files",
)
load(
+ ":opencv_arm64.bzl",
+ opencv_arm64_debs = "files",
+)
+load(
":opencv_armhf.bzl",
opencv_armhf_debs = "files",
)
@@ -387,6 +391,12 @@
)
generate_deb_tarball(
+ name = "opencv_arm64",
+ files = opencv_arm64_debs,
+ target_compatible_with = ["@platforms//os:linux"],
+)
+
+generate_deb_tarball(
name = "opencv_armhf_v4",
files = opencv_armhf_debs,
target_compatible_with = ["@platforms//os:linux"],
diff --git a/debian/gstreamer.BUILD b/debian/gstreamer.BUILD
index 205db5a..b203772 100644
--- a/debian/gstreamer.BUILD
+++ b/debian/gstreamer.BUILD
@@ -159,6 +159,7 @@
"usr/lib/arm-linux-gnueabihf/libxcb.so.1",
"usr/lib/arm-linux-gnueabihf/libxml2.so.2",
],
+ "arm64": [],
"cortex-m": [],
"cortex-m0plus": [],
}),
@@ -181,6 +182,7 @@
"usr/include/glib-2.0",
"usr/include/gstreamer-1.0",
],
+ "arm64": [],
"roborio": [],
"cortex-m": [],
"cortex-m0plus": [],
diff --git a/debian/opencv.BUILD b/debian/opencv.BUILD
index 0fad68c..c989e22 100644
--- a/debian/opencv.BUILD
+++ b/debian/opencv.BUILD
@@ -266,6 +266,7 @@
"usr/lib/x86_64-linux-gnu/libnuma.so.1",
],
"@platforms//cpu:armv7": [s % "arm-linux-gnueabihf" if "%" in s else s for s in _common_srcs_list],
+ "@platforms//cpu:arm64": [s % "aarch64-linux-gnu" if "%" in s else s for s in _common_srcs_list],
}),
hdrs = glob([
"usr/include/opencv4/**",
@@ -285,6 +286,9 @@
"@platforms//cpu:armv7": [
"@platforms//os:linux",
],
+ "@platforms//cpu:arm64": [
+ "@platforms//os:linux",
+ ],
}),
visibility = ["//visibility:public"],
)
diff --git a/debian/opencv_arm64.bzl b/debian/opencv_arm64.bzl
new file mode 100644
index 0000000..6596271
--- /dev/null
+++ b/debian/opencv_arm64.bzl
@@ -0,0 +1,362 @@
+files = {
+ "adwaita-icon-theme_3.38.0-1_all.deb": "2046876c82fc1c342b38ace9aa0661bcb3e167837c984b4bdc89702bc78df5ac",
+ "coreutils_8.32-4_arm64.deb": "6210c84d6ff84b867dc430f661f22f536e1704c27bdb79de38e26f75b853d9c0",
+ "dconf-gsettings-backend_0.38.0-2_arm64.deb": "8d1df67abd7bca93f361ca26f5bb70821c91b5c4d5924941fef524f76fc6d473",
+ "dconf-service_0.38.0-2_arm64.deb": "b76a7d724cc9f18f91cc22aff58afcb090cc7a28ec9b9a21cdd1aa069c7a4cef",
+ "fontconfig-config_2.13.1-4.2_all.deb": "48afb6ad7d15e6104a343b789f73697301ad8bff77b69927bc998f5a409d8e90",
+ "fontconfig_2.13.1-4.2_arm64.deb": "166e5e6d47af2e1a48eff6fb66e89f0e6e0f390d04f7c9abe8b4f36812378267",
+ "fonts-croscore_20201225-1_all.deb": "64904820b729ff40038f85683004e3b94b328d969bc0fbba263c58d635452923",
+ "fonts-dejavu-core_2.37-2_all.deb": "1f67421437b6eb18669d2868e3e02cb88668683d635198142f48aacc5b397118",
+ "fonts-freefont-otf_20120503-10_all.deb": "0b63996c80c6c660424af6d3832818e647960d6f65a51de010bb57dd0762faa7",
+ "fonts-freefont-ttf_20120503-10_all.deb": "4ca1c21ebc479198a3a5879d236c8317d6f7b2f1c403f7890e24c02eead05615",
+ "fonts-liberation2_2.1.3-1_all.deb": "e0805f0085132f5e6dd30f88c0d7260caf1e5450832fe2e3988a20fa9fa2150e",
+ "fonts-liberation_1.07.4-11_all.deb": "efd381517f958b01969343634ffcbdd60056be7779af84c6f53a005090430204",
+ "fonts-texgyre_20180621-3.1_all.deb": "cb7e9a4b2471cfdd57194c16364f9102f0639816a2662fed4b30d2a158747076",
+ "fonts-urw-base35_20200910-1_all.deb": "f95a139adb7f1b60626e76d4d45d1b35aad1bc2c2597394c291ef5f84b5dcb43",
+ "gdal-data_3.2.2+dfsg-2+deb11u1_all.deb": "3ae44cc2f51dccc023f9c3cfbea3411508e24f1335651fa0e6cba74b7b9b87aa",
+ "glib-networking-common_2.66.0-2_all.deb": "a07370151ce5169e48ee7799b9bd9a7a035467a21f5cf3373b2aff090968609c",
+ "glib-networking-services_2.66.0-2_arm64.deb": "7fc232a31f09d8d08242ce08f53286e3b296c7b146834cdb41da75303f5417e1",
+ "glib-networking_2.66.0-2_arm64.deb": "3da5e3ba93a1e9a18a4cb14cfda44f938b270291004f0e88fa7d6316dfc9aebb",
+ "gsettings-desktop-schemas_3.38.0-2_all.deb": "3758968491a770e50cd85122c00d141944ffb43cb7a5c886a37290fef848cee3",
+ "gtk-update-icon-cache_3.24.24-4_arm64.deb": "00ced0c66d177d53b41739a9e8cc79cb891e2c1675c98feabf30014575cd55ce",
+ "hicolor-icon-theme_0.17-2_all.deb": "20304d34b85a734ec1e4830badf3a3a70a5dc5f9c1afc0b2230ecd760c81b5e0",
+ "iso-codes_4.6.0-1_all.deb": "4e044d72a9f810aabf2c8addf126327fa845eaf8e983b51eb6356b9ed5108348",
+ "libaec0_1.0.4-1_arm64.deb": "d71f8f3448d2aa6df31707bb97db9b307305011f9e3ae19a270f2a8858657a45",
+ "libaom0_1.0.0.errata1-3_arm64.deb": "600536f50bf36cbcfabfc8eacb43a8f26bff7a8f8f52304ce35fc1a117dcd06e",
+ "libarchive13_3.4.3-2+b1_arm64.deb": "b3fea698fc1c3ce6448a7c92e2c3197cfa5d24c42bd74b11f168dfcdc025a217",
+ "libarmadillo10_10.1.2+dfsg-6_arm64.deb": "d937aa4788605c00a4cb6e1bfcc233ab53981761e7f04cc6b1c324607f397bbe",
+ "libarpack2_3.8.0-1_arm64.deb": "d9baf93cd93b528c331f9fd23ed0ad8fa901cd15ff6b07c914ceffa051eb02fe",
+ "libatk-bridge2.0-0_2.38.0-1_arm64.deb": "a977c4a2be6d46185cd4f529ba226017dfcdc9a4056ff80ae97f7c67799885b2",
+ "libatk1.0-0_2.36.0-2_arm64.deb": "53c9c45ba719f1df44f5477396d276530e7e2fc210bc598e48e624194b8a1173",
+ "libatk1.0-data_2.36.0-2_all.deb": "86c1acae473977f8a78b905090847df654306996324493f9a39d9f27807778b2",
+ "libatspi2.0-0_2.38.0-4_arm64.deb": "ca3db48d64bdd6f6e26931a5d52ddb73bc6d915957a678bc2779e03aeacf467b",
+ "libattr1_2.4.48-6_arm64.deb": "cb9b59be719a6fdbaabaa60e22aa6158b2de7a68c88ccd7c3fb7f41a25fb43d0",
+ "libavahi-client3_0.8-5_arm64.deb": "b255f51854f9984a9e69a52bbce6cab5ce6141ed7dc232cc810053c228514ce3",
+ "libavahi-common-data_0.8-5_arm64.deb": "59e6d7f43ab387989e4cb116fe35292e07cb6e6a9125b944acd417c288f88f35",
+ "libavahi-common3_0.8-5_arm64.deb": "bd51e2159b0350ffbaf653f44131ce522f0a6eeab0d60c2ccd72182a0001cb5b",
+ "libavcodec-dev_4.3.3-0+deb11u1_arm64.deb": "189dae2788f471312d20bc57587a2fda447eaf992d6ebfb63657f75254e8e424",
+ "libavcodec58_4.3.3-0+deb11u1_arm64.deb": "f86160f51ad8e2feb6bd6a4c241e9724f6e48248a7d6f54f844df08867f196a7",
+ "libavformat-dev_4.3.3-0+deb11u1_arm64.deb": "321ffce7b99458ea358112d16c803465821370328cbda19d2c3edc3c574ed7c2",
+ "libavformat58_4.3.3-0+deb11u1_arm64.deb": "1d8dcb480bf88ab3920eecbed6531210fd756748e869222e7ed9eeb1e806355f",
+ "libavutil-dev_4.3.3-0+deb11u1_arm64.deb": "c39d6ede6e43123843db5d25ae006c2e7c8cae16cc07053550d1430e62f53b86",
+ "libavutil56_4.3.3-0+deb11u1_arm64.deb": "b0e732ed95860f3098ddcfa95b52e865376a5ad279fe34fa61324e472406094c",
+ "libblas3_3.9.0-3_arm64.deb": "99806c4a490d55ae1ba50640460a2f62c491a8920af4e804560849fc5fe9d73f",
+ "libblkid1_2.36.1-8+deb11u1_arm64.deb": "f6daca6d84eab01e281bf59d7c06f55125b8af443da936afdd255fa32f939928",
+ "libbluray2_1.2.1-4+deb11u1_arm64.deb": "ccc3d64b9f3d5aa37223ce9f331b137ebeb10eb26d933bf83179772d2571fb2a",
+ "libbrotli1_1.0.9-2+b2_arm64.deb": "52ca7f90de6cb6576a0a5cf5712fc4ae7344b79c44b8a1548087fd5d92bf1f64",
+ "libbsd0_0.11.3-1_arm64.deb": "b6e7fa54a05e5a3a5e1ec5dceb57a470e9a0883081594aea643ca58264071e7a",
+ "libcairo-gobject2_1.16.0-5_arm64.deb": "971311ba4fa99a4611205b2f7fbb8278b3798faf3409336e6f0b0d32106f7b7b",
+ "libcairo2_1.16.0-5_arm64.deb": "5b18336974b045dda5fbd64799f06247f6b216ee54f3391adc90f0bf81596de5",
+ "libcap2-bin_2.44-1_arm64.deb": "37915f4cc73cfaef7862043f2bdad062d1b1639df04c511665f1f9321c84d0db",
+ "libcap2_2.44-1_arm64.deb": "7c5729a1cfd14876685217c5f0545301e7ff1b839262fb487d6a778e8cd8c05a",
+ "libcfitsio9_3.490-3_arm64.deb": "b538f9fedc7055a6b878f4de16e0a6de96a26c3e03ddc857aeda22da946be7d8",
+ "libcharls2_2.2.0+dfsg-2_arm64.deb": "bbbf7b985fd559d4fb6226dd04a35eb745bbd22285bea98e2550cef785ff55ac",
+ "libchromaprint1_1.5.0-2_arm64.deb": "b2462da2f97fdd9ff14861451309ce3d86c4a579422c41dbdf858b2d1cfd237d",
+ "libcodec2-0.9_0.9.2-4_arm64.deb": "325c2106a587e3c054b31cf6ab8325f61a32f141eee2cea3f2eaa0423b3398d5",
+ "libcolord2_1.4.5-3_arm64.deb": "6b0d8c1debb73d9d4a97f38804e46e384d28869c6d4458d5fe807d207eecf7a6",
+ "libcups2_2.3.3op2-3+deb11u1_arm64.deb": "5badecf1e5c921ae50584378af49e4e185221e855d209ba9f2c4955d825de06d",
+ "libcurl3-gnutls_7.74.0-1.3+deb11u1_arm64.deb": "8e8ae4b7c49ff981aab871069a6457f3413d3648597b26c73500cbe7474141c4",
+ "libcurl4_7.74.0-1.3+deb11u1_arm64.deb": "1ec03ae6ededc45d9bc0906259e72ec27dd91d6d4b50e69904a5c1de13606176",
+ "libdap27_3.20.7-6_arm64.deb": "0df6382891cf0196c94d3d3edfb85991c85cc0345aa007011b69be5f3afce520",
+ "libdapclient6v5_3.20.7-6_arm64.deb": "12dd444ba9a7e2f4949af376dc874cd0cf2bdcb6fc15beba0855de0b8d861088",
+ "libdatrie1_0.2.13-1_arm64.deb": "e2953eec7abf0addebb53d6690a5b52b0e8429492328636e495ce016d819c4c7",
+ "libdav1d4_0.7.1-3_arm64.deb": "4ec26df2faceb35a9d0d821e4ad4a15ff899b07dea83263ccd51d38c57d8c760",
+ "libdb5.3_5.3.28+dfsg1-0.8_arm64.deb": "cf9aa3eae9cfc4c84f93e32f3d11e2707146e4d9707712909e3c61530b50353e",
+ "libdbus-1-3_1.12.20-2_arm64.deb": "2c8e07966435aae522bca50c4d699d940b34931f7906202e83b36982d9783c2b",
+ "libdc1394-25_2.2.6-3_arm64.deb": "09ba9019e1797379a0b662fe24309887a2c6c3e6e8de32f6e33704c65bccdbd4",
+ "libdc1394-dev_2.2.6-3_arm64.deb": "947c7302121d61af8e8b7dfdde910bc2b4342d239fbf5da719bc1b37dc7b5174",
+ "libdconf1_0.38.0-2_arm64.deb": "c89b11d34fbba55da7fc8a43fda7975eec59cd1b65695d485b7139815c44fb65",
+ "libde265-0_1.0.8-1_arm64.deb": "34097553c0c1075d16e59a8514f318000e26961592e135bf9c0f742a49f9f7d8",
+ "libdeflate-dev_1.7-1_arm64.deb": "782494693fb9e1306dfd0933f16d3be46fb1295666f3f7665a585555fddcf842",
+ "libdeflate0_1.7-1_arm64.deb": "a1adc22600ea5e44e8ea715972ac2af7994cc7ff4d94bba8e8b01abb9ddbdfd0",
+ "libdouble-conversion3_3.1.5-6.1_arm64.deb": "63b41ee55d95505e4dcd7ec6f7bb840fdc5a0527f6ecd29069f6ce7ff8536ba2",
+ "libdpkg-perl_1.20.9_all.deb": "134bd00e60fa30d39d5f676d306d6f1d61c7f6ec6086c1785dbc355ce6190f29",
+ "libdrm-amdgpu1_2.4.104-1_arm64.deb": "660f8dced02cb67826e61a91e09101d70c904ff0231d6696d6b45dbd789e86cb",
+ "libdrm-common_2.4.104-1_all.deb": "60c69026fb8e4cfdf8d80a4a86ee30516c611dcc4de4aa1c8ccbf06dff563e2b",
+ "libdrm-nouveau2_2.4.104-1_arm64.deb": "d54e628289717d6751367767e9b1867ed8b863890a64724cda4e76f3be773cde",
+ "libdrm-radeon1_2.4.104-1_arm64.deb": "e8ecdbc425f3ce293d85eb43bd759f657db9eccef24637c4edb36a147fed93cb",
+ "libdrm2_2.4.104-1_arm64.deb": "1dc606aa361307a8c5277c2a5ddedea40a4125874e887c98e82b33dacaa853b0",
+ "libdw1_0.183-1_arm64.deb": "66805e4b00001a0e76ea5291705942b542be53e8d1149867cfbd4cd5fd92285f",
+ "libedit2_3.1-20191231-2+b1_arm64.deb": "43cbfd69ef591a66cfd06aedf930e3fc3c370b3a7ad514a33399d0e1a4d7343e",
+ "libelf1_0.183-1_arm64.deb": "12e14110cd66b3bf0564e3b184985b3e91c9cd76e909531a7f7bd2cb9b35a1f3",
+ "libepoxy0_1.5.5-1_arm64.deb": "cc66fbfd7b08de406873d792b07f17e19e70e37c8255fe33335b57d17379fdd5",
+ "libepsilon1_0.9.2+dfsg-5_arm64.deb": "d39e9ac0d3fe3fc6824c902f9085b70d70af2b40ff4a395bea8937e15516b619",
+ "libexif-dev_0.6.22-3_arm64.deb": "9b002b7311fa12eb59366f77e8c6de17932fad1f7c441f7eec91da2addbd8cd3",
+ "libexif12_0.6.22-3_arm64.deb": "2b2f428d688630275822104dad11d08203640076e91ea2df37dabcf2a6ae7e0e",
+ "libexpat1_2.2.10-2_arm64.deb": "b78b5634cf2826451fbf453039fdbcc91fff62cc3fc51c839537b013f3abc2ac",
+ "libffi7_3.3-6_arm64.deb": "eb748e33ae4ed46f5a4c14b7a2a09792569f2029ede319d0979c373829ba1532",
+ "libfontconfig1_2.13.1-4.2_arm64.deb": "18b13ef8a46e9d79ba6a6ba2db0c86e42583277b5d47f6942f3223e349c22641",
+ "libfreetype6_2.10.4+dfsg-1_arm64.deb": "43e3b1cc3dd70e00f930bca8b69488f5979ab358e53084361424cca7c49a3fa0",
+ "libfreexl1_1.0.6-1_arm64.deb": "8bdabfe5826a662d1082bf58bfb5d15c95082a5438f57239b9e5427b6f4701cf",
+ "libfribidi0_1.0.8-2_arm64.deb": "c6c92f3dbe2f1894b09aa1eeb74933d4dc0df6dacfadaa1ff66d96ee753d41de",
+ "libfyba0_4.1.1-7_arm64.deb": "3a7578a717d1104c55486dbd4a69271b44ce72ce0ac5d34d216417cc171a8dbb",
+ "libgcc-s1_10.2.1-6_arm64.deb": "e2fcdb378d3c1ad1bcb64d4fb6b37aab44011152beca12a4944f435a2582df1f",
+ "libgcrypt20_1.8.7-6_arm64.deb": "61ec779149f20923b30adad7bdf4732957e88a5b6a26d94b2210dfe79409959b",
+ "libgd3_2.3.0-2_arm64.deb": "1e6d6af0c90fe62193b3e51e45f69d075b86d7bde3fb4fd30b60da763aeca43f",
+ "libgdal28_3.2.2+dfsg-2+deb11u1_arm64.deb": "bc5ad6148ce06f07397f07f7a96bc865975248821fb01124184049d741e93c1a",
+ "libgdcm-dev_3.0.8-2_arm64.deb": "1795d571d83196ec248fcdfc6153fe2d21599aa1debf40ebdeec00393764ca0c",
+ "libgdcm3.0_3.0.8-2_arm64.deb": "59472494d90363690f34a7cbe3f5ba66028fab6f242161785401ba7d7c223ef7",
+ "libgdk-pixbuf-2.0-0_2.42.2+dfsg-1_arm64.deb": "e35997839534e69d093cef790dc4a43d0b102e7aed5f21492d330f810717f4ce",
+ "libgdk-pixbuf2.0-common_2.42.2+dfsg-1_all.deb": "61ff764860dafbd7e3fe2050b9c17db3ae109dea15ac748212eff56fdb3111e1",
+ "libgeos-3.9.0_3.9.0-1_arm64.deb": "9f8b32c8814c8ad72df61bc8cdf0eb95b4d625f06fb7a945e146e523c664b874",
+ "libgeos-c1v5_3.9.0-1_arm64.deb": "efffd604a25acd4088a2f34e24c779490f1389f0dd34e2b62cb1e3bc364d8ec9",
+ "libgeotiff5_1.6.0-1_arm64.deb": "94e2329dcc4236aa1bb455613f9b1795b8f8974ac40f8a77568312c677bd14fe",
+ "libgfortran5_10.2.1-6_arm64.deb": "967866fc2caf00fd28a3a515234131d7301043288b4a36e46b7dc16050a6f862",
+ "libgif7_5.1.9-2_arm64.deb": "90751dcd054948236424aac6bddc0666013621fc6d3aec9fba62e275ba12db17",
+ "libgl1-mesa-dri_20.3.5-1_arm64.deb": "13bc343bc0bb7ec8bf52ecceb2c41ac8b70bc80d4d76784252caceadf0c83b73",
+ "libgl1_1.3.2-1_arm64.deb": "18075b79c22b06bb25b9b4f3fa44a42127c40e573b34703af3f4c0b780cae29a",
+ "libgl2ps1.4_1.4.2+dfsg1-1_arm64.deb": "989c066bd91aa9ac18d9d571fba404038e25893da99c0fd86fbecda15be89372",
+ "libglapi-mesa_20.3.5-1_arm64.deb": "2678cf4bcfe4dfeba85deabaf869cb78a2f3263388baaff53e826f52cf17088b",
+ "libglew2.1_2.1.0-4+b1_arm64.deb": "68ed07175805f1b827ff4db50387ca8dfb5bdfffa98e23c897de34706c04b42d",
+ "libglib2.0-0_2.66.8-1_arm64.deb": "667d1b891bcf9b8cc47385c19b39271c74f48fd2b6b457474184f85ce63ea261",
+ "libglvnd0_1.3.2-1_arm64.deb": "5d7a05a966d1df43ca440245dfc7e18a51fc974f665441fc87180a605a0481d9",
+ "libglx-mesa0_20.3.5-1_arm64.deb": "b1f653df820ecda31ed3d6a39429befbced67093dc9b964cbfa5f7991a23eda8",
+ "libglx0_1.3.2-1_arm64.deb": "a53aec87ff8d3ae181c320c01bc190f752a276d5e5ca87e624f9058a4b520bf2",
+ "libgme0_0.6.3-2_arm64.deb": "2324dc1a5c06845f56eac43d9efb2446fb3119758eee46e793e33aa158a3325d",
+ "libgmp10_6.2.1+dfsg-1+deb11u1_arm64.deb": "d52619b6ff8829aa5424dfe3189dd05f22118211e69273e9576030584ffcce80",
+ "libgnutls30_3.7.1-5_arm64.deb": "6b7429aba5f642c258d351b2ebac5a00170f6761331f6b367edfb9401c10d8da",
+ "libgomp1_10.2.1-6_arm64.deb": "813af2e0b8ba0a7cea18c988cd843412ef6d0415700fc860d62816750e741670",
+ "libgpg-error0_1.38-2_arm64.deb": "d1116f4281d6db35279799a21051e0d0e2600d110d7ee2b95b3cca6bec28067c",
+ "libgphoto2-6_2.5.27-1_arm64.deb": "b83525eb32514b47362ab6b8c16f37f002d9a531405b1dbdaed9dd20de19b61a",
+ "libgphoto2-dev_2.5.27-1_arm64.deb": "8a13c502539d0ff3d431f5d1099c784f0ab358c81065af788d0bba65e46ba24f",
+ "libgphoto2-port12_2.5.27-1_arm64.deb": "64892ba635e3f67816748d33700262d73a0b727fd31c89c5f260eceff0c9bbea",
+ "libgraphite2-3_1.3.14-1_arm64.deb": "473362a74ba74ae630fc43675460fb5a1058564a635a301875e00f1c6f9b27cb",
+ "libgsm1_1.0.18-2_arm64.deb": "f7ea67da4f664d6c7b49ca7f31bf6c5fee61e2ff679da61d0613efbb0fdd6cb9",
+ "libgstreamer-plugins-base1.0-0_1.18.4-2_arm64.deb": "276163043a18cabe6b65b6116072cfd0c186b8aa69e5150b1e556edc5c227af2",
+ "libgstreamer1.0-0_1.18.4-2.1_arm64.deb": "0aee9a595e4fe784a59244dfe10567fd6b12cd94c86657a1dd6d07453154d21f",
+ "libgtk-3-0_3.24.24-4_arm64.deb": "245dfac9509c4e5d98386d5a5429f13ba40e2c599bbfff4dc052262d2ce79cb1",
+ "libgtk-3-common_3.24.24-4_all.deb": "c7ce143bed115bc868976538089dc15c0c469ea67cbf84ab412e55d95ee5b488",
+ "libharfbuzz0b_2.7.4-1_arm64.deb": "d9f0345391cc661503d1508ccd318b3db48add354e706cf9d66fa16bf99e2d03",
+ "libhdf4-0-alt_4.2.15-3_arm64.deb": "5ce23c00a3726049c74e149cf08ea46809143eb106a49e38aad973287abafdb3",
+ "libhdf5-103-1_1.10.6+repack-4+deb11u1_arm64.deb": "5f051dfa0c2f6d369e9322fa0c175717c61dce21d3bfa316e0271c949b3b834d",
+ "libhdf5-hl-100_1.10.6+repack-4+deb11u1_arm64.deb": "d3748cffd412ae9dce22016a3a4106a6e0cd2eedf79ae98a79873a0b2f64d9e7",
+ "libheif1_1.11.0-1_arm64.deb": "a8db0c9a9e0311ad2624319f1ef76e89b1c188f424adf101a581a62c28853381",
+ "libhogweed6_3.7.3-1_arm64.deb": "3e9eea5e474dd98a7de9e4c1ecfbfd6f6efb1d40bf51d6473de9713cf41d2191",
+ "libicu67_67.1-7_arm64.deb": "776303db230b275d8a17dfe8f0012bf61093dfc910f0d51f175be36707686efe",
+ "libidn2-0_2.3.0-5_arm64.deb": "0d2e6d39bf65f16861f284be567c1a6c5d4dc6b54dcfdf9dba631546ff4e6796",
+ "libilmbase-dev_2.5.4-1_arm64.deb": "f7efd0776113ae36e03f08d349ba7c17862746c076f54a27f366d0927579923f",
+ "libilmbase25_2.5.4-1_arm64.deb": "9d09d54ad2ceb8148fd1be0fe2e065a7c35ffecb48b329592271aaf860f620e9",
+ "libjbig-dev_2.1-3.1+b2_arm64.deb": "611c5cdf980db966432c39b8ae3cf6449f84e224e9369b7c8fe492d2d48378b1",
+ "libjbig0_2.1-3.1+b2_arm64.deb": "b71b3e62e162f64cb24466bf7c6e40b05ce2a67ca7fed26d267d498f2896d549",
+ "libjpeg-dev_2.0.6-4_arm64.deb": "ce0f75c7f632be0c980d01f2e2ae863089f99bca47e4cff3015a490e1c886d41",
+ "libjpeg62-turbo-dev_2.0.6-4_arm64.deb": "8535e4bd12e026ff79991f69c62ead5ff1750df980a20a6a7ab540587439f06f",
+ "libjpeg62-turbo_2.0.6-4_arm64.deb": "8903394de23dc6ead5abfc80972c8fd44300c9903ad4589d0df926e71977d881",
+ "libjson-c5_0.15-2_arm64.deb": "451820024e4d1023cc74758bc22beccf1aa235227026b3e24bafa67bf81d215e",
+ "libjson-glib-1.0-0_1.6.2-1_arm64.deb": "a2167dd43e00154f2418f19e819cd90db1559f78cd8d783cfac1063bba7509e8",
+ "libjson-glib-1.0-common_1.6.2-1_all.deb": "a938ec35a20dca2e5878a8750fb44683b67a5f7c2d23d383963803a9fcfac1a3",
+ "libjsoncpp24_1.9.4-4_arm64.deb": "b8e04a858f2059d4e9d66b79f359995e38ff8c9509175647d6f98ba1f8b29b2a",
+ "libkmlbase1_1.3.0-9_arm64.deb": "e8c4b8513c10b763ec97133bc1c6180e027aeddeaba8965cbed8fba790a8a30b",
+ "libkmldom1_1.3.0-9_arm64.deb": "301808035031ac8c3c39baf0a854a3fc4c9e4de36a45ba5cd9abf4b06cbe4432",
+ "libkmlengine1_1.3.0-9_arm64.deb": "a6cf70031c33b85d5be04d1ad0ab4ff4b580674431e1ec0ba0596017b75045cf",
+ "liblapack3_3.9.0-3_arm64.deb": "30858f2b44bd6e24100bf119ab7e4da0e9db9df1ade3957e65952d95d4c308e6",
+ "liblcms2-2_2.12~rc1-2_arm64.deb": "6d92ee1f0d427b88ab9bff32c769b61e2597c8fb289589ca0731a7e77d490d6e",
+ "libldap-2.4-2_2.4.57+dfsg-3_arm64.deb": "443959ae0679f73a1587e26f0f5a78e2c9909d1328c0f267c71da349dfccfe8e",
+ "liblept5_1.79.0-1.1_arm64.deb": "134c600adc419c058777f87dd5254932a03e4591e6e230a290747657d7557390",
+ "libllvm11_11.0.1-2_arm64.deb": "0f4d1c3e259e203cc25ca5e07a4f88a018a775bdbf4376f33b06c8ffb7bb688b",
+ "libltdl7_2.4.6-15_arm64.deb": "e13bc091b1493c3bbb0e6554e73bd4d913358fdcd267152d1dcb32a5a3b94e27",
+ "liblz4-1_1.9.3-2_arm64.deb": "83f0ee547cd42854e1b2a2e4c1a5705e28259ee5fa6560119f918f961a5dada2",
+ "liblzma-dev_5.2.5-2_arm64.deb": "44c7d3b32401fa0406fd9e271b10038342b7d04bba377a917847b063eb428e9b",
+ "libmariadb3_10.5.12-0+deb11u1_arm64.deb": "822972508938f2bcc2e5afc392a69259cab349968fc16e5249b25bd0e2025461",
+ "libmd0_1.0.3-3_arm64.deb": "3c490cdcce9d25e702e6587b6166cd8e7303fce8343642d9d5d99695282a9e5c",
+ "libminizip1_1.1-8+b1_arm64.deb": "9db7fb955cc168a591fa5cc052f02606a39333ce4b15177426baf2d956018400",
+ "libmount1_2.36.1-8+deb11u1_arm64.deb": "fd1dff93bdaba84d3f45f25448e2ada8c867674cd4e8af9fe25604ddf9a2f8de",
+ "libmp3lame0_3.100-3_arm64.deb": "ad02a132721a5569476235b3fbe8f88af0c9e06fa37d8d68f5eda604cfe8ec92",
+ "libmpg123-0_1.26.4-1_arm64.deb": "4a7351593002fc11acc50d4d86a457332b6f3de488491803e1c3eafdeb440630",
+ "libnetcdf18_4.7.4-1_arm64.deb": "48e40a3ed8d48043996d858b8a28277f239cb43685cf13f047b3084aaeec0794",
+ "libnettle8_3.7.3-1_arm64.deb": "5061c931f95dc7277d95fc58bce7c17b1a95c6aa9a9aac781784f3b3dc909047",
+ "libnghttp2-14_1.43.0-1_arm64.deb": "317b58d2654d5875eee1dbf147ea810b8e3eb007f3bf4e2dcbca8ed76e425763",
+ "libnorm1_1.5.9+dfsg-2_arm64.deb": "3974ddba57ed9bbfff10a144baae64d647adbb38ebce5fa122e4f0dd16b77866",
+ "libnspr4_4.29-1_arm64.deb": "f83f450a49499e320b1d7723a596be2414bf7adfd20315ba72df0e7735d98bc5",
+ "libnss3_3.61-1+deb11u2_arm64.deb": "c10ac0797a3c5f694ab86251d22d8d1452b03e50fc30999edd91a45bc202696d",
+ "libnuma1_2.0.12-1+b1_arm64.deb": "4eda519ae1f36f6376380fb2798ca0f50e104930845d8c51561ec455e98c57fc",
+ "libodbc1_2.3.6-0.1+b1_arm64.deb": "bd0060e4333b038300fb9f4a225adebbdd5054fb6298960eef36f52c50733bc8",
+ "libogdi4.1_4.1.0+ds-5_arm64.deb": "48903ccacb992d04131def3accceac424f6226aa4059201303131cf89eaeddbe",
+ "libogg0_1.3.4-0.1_arm64.deb": "910d1f3893a9340ea83bf19deebbc4e0d2362f22c274c2c2d3f00e4ba386c871",
+ "libopencv-calib3d-dev_4.5.1+dfsg-5_arm64.deb": "9b32b570cb47735db6879a925956168e85155ef048d9895c7cbce014ef9b4529",
+ "libopencv-calib3d4.5_4.5.1+dfsg-5_arm64.deb": "189fbfc0fa850bf8ee4f3484c5a9a71d38bf1ba7694ebe0a218cf398a755f1d5",
+ "libopencv-contrib-dev_4.5.1+dfsg-5_arm64.deb": "36838790208c16db93236aee39bf77fee8ab8bff6ef4b35c22e9ebfd0d900099",
+ "libopencv-contrib4.5_4.5.1+dfsg-5_arm64.deb": "7d80388fc6ce9662a2706cd0371ec1b75b2a0473cc17536d6bb329c597317e65",
+ "libopencv-core-dev_4.5.1+dfsg-5_arm64.deb": "b8d8873d726345a0029f524dd7791b581a8379e1cd6e3aed6cc462b47c495fe0",
+ "libopencv-core4.5_4.5.1+dfsg-5_arm64.deb": "4736fc101e6fffd1f11f7dba48a1fc72aca5683cac123c25160718628a682e9f",
+ "libopencv-dev_4.5.1+dfsg-5_arm64.deb": "cca973f700a3177cfeb77bbdf0fbb34d49a894b93719b3e3fc13dea145ed9d6b",
+ "libopencv-dnn-dev_4.5.1+dfsg-5_arm64.deb": "e506228fc13a8533bec5a88b6b73cc931f31e0032218465dac81d2fb659d8988",
+ "libopencv-dnn4.5_4.5.1+dfsg-5_arm64.deb": "0c121b36ba1cd55c9355aa9f2dc42b78afe83a5ab3734e3a5b16ba68ae5ef7d1",
+ "libopencv-features2d-dev_4.5.1+dfsg-5_arm64.deb": "d9cc52691782a9e0714814f33ce177cfe56d78e2b6a828e920a0739286806ae3",
+ "libopencv-features2d4.5_4.5.1+dfsg-5_arm64.deb": "ac5c82fff13f36eea33fb92e04cfd7078642cca527382ce8cb2e5e7c75b1d698",
+ "libopencv-flann-dev_4.5.1+dfsg-5_arm64.deb": "ef70c57db440c2805be002d5638fb381727e9b0c9faf3462857565884eed81b9",
+ "libopencv-flann4.5_4.5.1+dfsg-5_arm64.deb": "20c490a68d367e4ac47b443850697757d97dc30e74ffd31b0025841f2829ad80",
+ "libopencv-highgui-dev_4.5.1+dfsg-5_arm64.deb": "27c41d0d726870b9245327857e6207657971b023d5fe251fd0a539ff84ce69ba",
+ "libopencv-highgui4.5_4.5.1+dfsg-5_arm64.deb": "5af44ec6da17dc8d000215ff9377a6eff00ab7970c078f3cd46492ceec1be7c8",
+ "libopencv-imgcodecs-dev_4.5.1+dfsg-5_arm64.deb": "27ec650759d661651dbfa9fe9d571b106048f0e6a7863c6fa8e9c224b6c64ad6",
+ "libopencv-imgcodecs4.5_4.5.1+dfsg-5_arm64.deb": "8d68e9a89517628082fb5d58d47d865c69c7e070bd91fcab8240d8f082e85e54",
+ "libopencv-imgproc-dev_4.5.1+dfsg-5_arm64.deb": "22fd1fd47d57f4085472653b420fcfd8d4180d85ccfb8b9ffaab7e110643fbe0",
+ "libopencv-imgproc4.5_4.5.1+dfsg-5_arm64.deb": "fcbf06a539696d53a1386a1609f51c0890a7d58a58fd67e153facd8e13728754",
+ "libopencv-ml-dev_4.5.1+dfsg-5_arm64.deb": "fb7022c93d9c4f4bc050d9f3ca545133d1d5a70efcc692ef7fc78f4d902fcf5c",
+ "libopencv-ml4.5_4.5.1+dfsg-5_arm64.deb": "b7eb23cc968b4409d044addcad4b534282fc208eb4958b811f4404eebb3c52a7",
+ "libopencv-objdetect-dev_4.5.1+dfsg-5_arm64.deb": "db41fa5f2a3eee924d2bb9cad42e8c416a66f87a498562c4399e6690176d61d0",
+ "libopencv-objdetect4.5_4.5.1+dfsg-5_arm64.deb": "9f3784842133a8ffc764a72572b8631049d8627ea8d7c120ced7e44f8a981641",
+ "libopencv-photo-dev_4.5.1+dfsg-5_arm64.deb": "13db446289d550a9dbf7214e49cbb46c895331c3a7fc7813594423b9f9d6bd3c",
+ "libopencv-photo4.5_4.5.1+dfsg-5_arm64.deb": "af684d483ab247c47941682ec6acd5d045438ccca8085c442973529da783cb4f",
+ "libopencv-shape-dev_4.5.1+dfsg-5_arm64.deb": "04313d5b4408784e50e314fcdf27002cba2c19c8b28d1ef3d4b3ea75d92ca303",
+ "libopencv-shape4.5_4.5.1+dfsg-5_arm64.deb": "b30cf313926d1064a676c857b2f57afba78e0a3942bdd0993b83eb17144b9a8d",
+ "libopencv-stitching-dev_4.5.1+dfsg-5_arm64.deb": "e0998b49adf404ff376c725bb6ad62663ef50bac4ec9bacebeda9fc19fe61dc8",
+ "libopencv-stitching4.5_4.5.1+dfsg-5_arm64.deb": "2316563a7b487d0b245f84e87eb0db61423f3f730c474a7776d3910ac1a1b210",
+ "libopencv-superres-dev_4.5.1+dfsg-5_arm64.deb": "f8524762200001fbe8acff2d2dd1e110ac37ed5e124837778d0acb703fdc9d22",
+ "libopencv-superres4.5_4.5.1+dfsg-5_arm64.deb": "1da58f51aa2ca2fe8a425a7f6f84ee5252c90fef10cfc90d2ccd2190ebaa8e43",
+ "libopencv-video-dev_4.5.1+dfsg-5_arm64.deb": "a37ea4ef728084858601f8a61b018e28e146951311dd55b4b75b83e4788b8f49",
+ "libopencv-video4.5_4.5.1+dfsg-5_arm64.deb": "c49598f7fe96b473c8a6c0e091a407a50f1b9514c398800497c0ae2691be3dc7",
+ "libopencv-videoio-dev_4.5.1+dfsg-5_arm64.deb": "60464425514a01b82dc4798d777d17cb9802119b05283cc4a44623a60bc98a48",
+ "libopencv-videoio4.5_4.5.1+dfsg-5_arm64.deb": "ab79662142f6aa93b54c30eb0f388da3445fc11fb56b7888825b9b6bde2a7536",
+ "libopencv-videostab-dev_4.5.1+dfsg-5_arm64.deb": "2c517c7d4394cd702e417c33b7fa5154ba650d2850ee2530dcc3b86943a471a7",
+ "libopencv-videostab4.5_4.5.1+dfsg-5_arm64.deb": "ce078bd6fe7da56efbdb4b03b7bde1db3fdf8923a70e99528e9eaf3a966e89b5",
+ "libopencv-viz-dev_4.5.1+dfsg-5_arm64.deb": "e0ca0f7422c742889831ee3a9836e6efa15beef70784e7a93f03187efd073d34",
+ "libopencv-viz4.5_4.5.1+dfsg-5_arm64.deb": "fb92c16c42a4faf7f3393f82428865929225a073894100f75c4535cf56e06f97",
+ "libopenexr-dev_2.5.4-2_arm64.deb": "fc0a9ffdda657e668ee164ffeebfbc27f9c5fb80556567f9c25bd1cbec05ff8d",
+ "libopenexr25_2.5.4-2_arm64.deb": "15bc59e1f8abf11d17e9b028a54b15f8f9b9448b6ea98175ddfb3acdd6f8ecd4",
+ "libopengl0_1.3.2-1_arm64.deb": "0dcd4846079fd8106f92f943331e4d035b3c79d9eae5a337135ced490a0deb15",
+ "libopenjp2-7_2.4.0-3_arm64.deb": "4fb3637093bbbde4499f1344b399c6eb61bbe51bdc3c40a09c5fcc1efec660cb",
+ "libopenmpt0_0.4.11-1_arm64.deb": "a78a20536f771b360f757999dc02e2c6c9033491afd5f7875d0561ec28caf19e",
+ "libopus0_1.3.1-0.1_arm64.deb": "86d96e6e99820be150e4e1d335cf8503c5802a3ac47103ba25eebf77a0699a13",
+ "liborc-0.4-0_0.4.32-1_arm64.deb": "45948981ff4afa3890eca22edf709445088b301b593a3ad3eb5f8e524f661261",
+ "libp11-kit0_0.23.22-1_arm64.deb": "ac6e8eda3277708069bc6f03aff06dc319855d64ede9fca219938e52f92ee09c",
+ "libpango-1.0-0_1.46.2-3_arm64.deb": "6bbb5d942bf5075e07ba2290687cf03939e29dd89c67cba4d868a5d5ca94d360",
+ "libpangocairo-1.0-0_1.46.2-3_arm64.deb": "6947061235f6a3fce541b985ae38509298f7b46299e31fd985d7c596e31e4bbf",
+ "libpangoft2-1.0-0_1.46.2-3_arm64.deb": "75c9b7f28b80822b6e4edea5e2235257f877ac6cade7930c6683e24395e952ec",
+ "libpcre3_8.39-13_arm64.deb": "21cac4064a41dbc354295c437f37bf623f9004513a97a6fa030248566aa986e9",
+ "libpgm-5.3-0_5.3.128~dfsg-2_arm64.deb": "6a632aa13cafe154d6212a57710f058405dd895fb62d464e16961b28d7325cbb",
+ "libpixman-1-0_0.40.0-1_arm64.deb": "0be923132af8fa7102c09a3c8d200cd8475b633a9a5f1609f7ad653851fb6448",
+ "libpng-dev_1.6.37-3_arm64.deb": "15da2d4389f62ac5daf0f91f0a34db8007986ce77f079ea1f6f2ee92ea6a620d",
+ "libpng16-16_1.6.37-3_arm64.deb": "f5f61274aa5f71b9e44b077bd7b9fa9cd5ff71d8b8295f47dc1b2d45378aa73e",
+ "libpoppler102_20.09.0-3.1_arm64.deb": "cb9d93e535446d9662722614109a3d9c2df89dd970a473623f31895c37be1513",
+ "libpq5_13.5-0+deb11u1_arm64.deb": "84daa5d6407c51e5cab185d053c896c58a6035e05157adb197d077f2d417f7af",
+ "libproj19_7.2.1-1_arm64.deb": "535a698960d6fe73de45099fa289b7e9382319b8ea3ab3ec535db52f893e7af2",
+ "libprotobuf23_3.12.4-1_arm64.deb": "f3935bf86962ef3df68aca17c9f1fa0a55a9a0c74a78a225c2dbf78ed5746cfa",
+ "libproxy1v5_0.4.17-1_arm64.deb": "b81ca7f47e384ed7117a75cfe9798bd11d0085b1b5edce4f9ffcf4e678e24b71",
+ "libpsl5_0.21.0-1.2_arm64.deb": "12637647316e770c37a4bfec7aef27ed472f2850b5f59dd508505dda32519584",
+ "libqhull8.0_2020.2-3_arm64.deb": "e42e31c799c6017158dcc6b5fce9ccc68550b54255df50f52c947a34f2a63943",
+ "librabbitmq4_0.10.0-1_arm64.deb": "9460d0c3c016883bd5bfb5e66a47cae445f93e2e6f43c1c951738ab51d2ec982",
+ "libraw1394-11_2.1.2-2_arm64.deb": "81a1e9bd2b790aa7ce7c426dd1742d3310894b01f124bc41962331b834da6cc3",
+ "libraw1394-dev_2.1.2-2_arm64.deb": "84d7280c5e0d38a77c7d8ccc6d5d2986b42bd64245533947aa9c684077fa7243",
+ "librest-0.7-0_0.8.1-1.1_arm64.deb": "c1b636d7a66996743d248cb9e6042167b03b442150d39b349b12348a31690ee0",
+ "librsvg2-2_2.50.3+dfsg-1_arm64.deb": "79688f922da4c45e546c77667f1ea9480e3b96dd65e8eef88bcb44ef335cd6bd",
+ "librtmp1_2.4+20151223.gitfa8646d.1-2+b2_arm64.deb": "a3a1a6e4b02bcd3254e932b1972ed744082fd7dd5cc1545eec3dd3d539ce6c93",
+ "librttopo1_1.1.0-2_arm64.deb": "f20685ed048635be16e8abf790e14d7e88bb4e2335d371dff631b4adbdebc6a8",
+ "libsasl2-2_2.1.27+dfsg-2.1_arm64.deb": "e96088393cfe034560c2d3f84931b7e17d69f8af4faf34e3cb790560a1ae937d",
+ "libsasl2-modules-db_2.1.27+dfsg-2.1_arm64.deb": "71db9d46ed83c02d1cb3ed51b0a7aedddc54ce60ae00fd846c3dd26989a91089",
+ "libsensors-config_3.6.0-7_all.deb": "4265811140a591d27c99d026b63707d8235d98c73d7543c66ab9ec73c28523fc",
+ "libsensors5_3.6.0-7_arm64.deb": "fd5533c6293d881d67e488ad0549c0b0351e1b770037a08018e5e88996cb95dc",
+ "libshine3_3.1.1-2_arm64.deb": "d64982ddfbdfcef714dc35a760a752ab1f3739aaee8011bc5c3f03afa73f465f",
+ "libsnappy1v5_1.1.8-1_arm64.deb": "48cb00030ca2e87780f815e6b5e354c76642c6de5c7a287a18d70b24880c70d1",
+ "libsocket++1_1.12.13-11_arm64.deb": "ea74ce1260340fedc20131f4f8183c5ee55fdc4e153c2850bceb0fc7173f4de3",
+ "libsodium23_1.0.18-1_arm64.deb": "f00a5c6ccaeb6a70bc07c7a56d61c605281c2b1cbea29611b69dfc789274db0c",
+ "libsoup-gnome2.4-1_2.72.0-2_arm64.deb": "7e0aacd0e2e484844b86fce2dcaf842ff95f209ad353c52d685ed1b2bc346eb2",
+ "libsoup2.4-1_2.72.0-2_arm64.deb": "c11fd27b2cd0639d642d1b852c6e89f94742228d5a866d1e32f122bdb2b324d1",
+ "libsoxr0_0.1.3-4_arm64.deb": "8f1b1780a5ced7a0821a8040d0f3ab5815acd5d2f38b12cca915a9a9d0ee28b7",
+ "libspatialite7_5.0.1-2_arm64.deb": "0268c4e54141c222e6ae6984f1c402f78a806e0e70e411235524445b0e2beb2a",
+ "libspeex1_1.2~rc1.2-1.1_arm64.deb": "5bdef83a0a8f0afda640f0166a64f9160c459faa0c2681f9cef7a96933733340",
+ "libsqlite3-0_3.34.1-3_arm64.deb": "1e33cd39dc4fff2a7edd7bb7e90a71e20fb528f6a581fe0287652e4dae77e0d0",
+ "libsrt1.4-gnutls_1.4.2-1.3_arm64.deb": "08a94b07becea150a1385614fb7b4c1b09f864db5ab8185b586d099ffdf11b8a",
+ "libssh-gcrypt-4_0.9.5-1+deb11u1_arm64.deb": "ab5a221194b84dbbb75961555efd98eca8d84d568a15dd971a8c5579c0c4d9dd",
+ "libssh2-1_1.9.0-2_arm64.deb": "c3847ce093a395c4425f23c0a1601516248e2d241bedaab94ecd9686536214a7",
+ "libstdc++6_10.2.1-6_arm64.deb": "7869aa540cc46e9f3d4267d5bde2af0e5b429a820c1d6f1a4cfccfe788c31890",
+ "libsuperlu5_5.2.2+dfsg1-2_arm64.deb": "28d7257ec368508de05df08d87bdf1a535469758744e20ed93fc7c401b2740b9",
+ "libswresample-dev_4.3.3-0+deb11u1_arm64.deb": "790d775017119434a6eddbb68b85e29392119aaae5ed52bbe7a2734a45cb0b64",
+ "libswresample3_4.3.3-0+deb11u1_arm64.deb": "58ae7a8c97a8763ef3e3352915f65b9bb2bbfa1c977042fb7761a89c2f1f1e93",
+ "libswscale-dev_4.3.3-0+deb11u1_arm64.deb": "bbf45093f7ca81b3b3f3cfdcac5c01546e0ae5e7f99029361ab1b78672917c73",
+ "libswscale5_4.3.3-0+deb11u1_arm64.deb": "e29e8d8a7e4f1ad8f71e1c0b05859e1fe6840cbe36a3955aaf8536b587dfd028",
+ "libsystemd0_247.3-6_arm64.deb": "d75536a275b2cca3054459ae5d1a54ff7bdd4e9bf7a389e02f07bce57ef605f0",
+ "libsz2_1.0.4-1_arm64.deb": "8ed09313ff4408291fd0e3942b4129280babf882f2d3fe8ba48aac4be6def051",
+ "libtasn1-6_4.16.0-2_arm64.deb": "a89b659a3cf2d040885a7d00f3c547b6e362cdfeb5f89d0c777495d82a58e64f",
+ "libtbb-dev_2020.3-1_arm64.deb": "85bc6b8697aac9f2c481589de3471ca44402c383b3738304fc325ea8ec776b14",
+ "libtbb2_2020.3-1_arm64.deb": "c69b242838b3829c16b9580fa218387f0f0f7f976062df8a66c7d9c0a6e95a9e",
+ "libtcl8.6_8.6.11+dfsg-1_arm64.deb": "39047359624bb8229a0e26291ad56012ae6ad17e443e73dd9f38635102c9a0e4",
+ "libtesseract4_4.1.1-2.1_arm64.deb": "762932cc1bc533cf6d1f6ad08c108fb1f5f13131cc4c5a37ce777e5715fb56a0",
+ "libthai-data_0.1.28-3_all.deb": "64750cb822e54627a25b5a00cde06e233b5dea28571690215f672af97937f01b",
+ "libthai0_0.1.28-3_arm64.deb": "e7ac0d861936385cca0ea7e3b9b04d20e85a7dfbfaa801e093d9f7fcbcf841f6",
+ "libtheora0_1.1.1+dfsg.1-15_arm64.deb": "e1ca65eaa5c90af2f88a5ba157e4b61b38b3cdf7ca8b83f20db4ee2dc271c344",
+ "libtiff-dev_4.2.0-1_arm64.deb": "279b1e5116d83babba81f28cf87ffd2d4c6abe2f1d76a08936c60e4618f81a7a",
+ "libtiff5_4.2.0-1_arm64.deb": "31765435a432e1804e93d9b20a5f2733f23e602ecf6fab1336eb04c30aeae043",
+ "libtiffxx5_4.2.0-1_arm64.deb": "34375252037a41491957e0a5060383f058be9ba1543760fccace516ae2b6e2bb",
+ "libtinfo6_6.2+20201114-2_arm64.deb": "21c0c33e00d091d0f326a083a77531270b8c56468500f0948d149f3e20b95385",
+ "libtk8.6_8.6.11-2_arm64.deb": "c189bdef93e4caebd58f952e99dfd1922ca182996fd4d0b4b33fb5fa5828dfa8",
+ "libtwolame0_0.4.0-2_arm64.deb": "73780c555796569a252ec5cf1ea919e59ef4031298bc4be1516a6a0e83f06b5a",
+ "libudev1_247.3-6_arm64.deb": "97236008d585a478916e6e1bb0f3edfe2f32336a48e2a95aba19dce801ed18d9",
+ "libudfread0_1.1.1-1_arm64.deb": "a54c888952f0eab05024e26a09cef78182144f7f11763af5d24c3947a8002648",
+ "libunistring2_0.9.10-4_arm64.deb": "53ff395ea4d8cf17c52155a452a0dc15af0ee2fa5cb3b0085b9c7335de8d5f7f",
+ "libunwind8_1.3.2-2_arm64.deb": "2a93283482cd89b6e6d3e1ee16497c911d6aacdc7414b9d24ca6851e221cf66c",
+ "liburiparser1_0.9.4+dfsg-1+deb11u1_arm64.deb": "4a184076b07eb3f6a409db354b651b5c024e3c783e35702038782c4300dbd037",
+ "libusb-1.0-0_1.0.24-3_arm64.deb": "61ca0a0412c4182cb007f4e447608857cd4c70ddbe730ecda15a495de7d2178f",
+ "libuuid1_2.36.1-8+deb11u1_arm64.deb": "3d677da6a22e9cac519fed5a2ed5b20a4217f51ca420fce57434b5e813c26e03",
+ "libva-drm2_2.10.0-1_arm64.deb": "45bf4248d72ce42940ee658bbba3025db0a233587bc49d773b1e3e9606d613a3",
+ "libva-x11-2_2.10.0-1_arm64.deb": "d6a845c9456a8b22366a25cbcd68a4ced425243a924e4b0048dc82f8b1f8853c",
+ "libva2_2.10.0-1_arm64.deb": "6dbb204d43bcaed495719fc6a793dcbce77f4ab12bc0ea4617ae4e816139d5a1",
+ "libvdpau1_1.4-3_arm64.deb": "9f44679cee3e9ed99525b2becff5b0ef040736b1a450e68fd3e2e536de52b99a",
+ "libvorbis0a_1.3.7-1_arm64.deb": "2f902ae456bcada7b0d494d7bd7c994feb81c4158209d8a12c0b2d9e255edda7",
+ "libvorbisenc2_1.3.7-1_arm64.deb": "f1089e220c81e267caec859bf2e440bb78ed9f318bbb51cfd6c85d35bf80144b",
+ "libvorbisfile3_1.3.7-1_arm64.deb": "f8f418e15f99905d4a2d532617511a11d700e814f8ead1a883deea2f7241970c",
+ "libvpx6_1.9.0-1_arm64.deb": "878f04bcd1089f8f2aa47d20b7ec4922877e31e562c90f613756d5452b3814fd",
+ "libvtk9_9.0.1+dfsg1-8_arm64.deb": "3271f972b20a26fed4781466dbaff083833c30208ac7b71376d96dec2a402aa2",
+ "libvulkan1_1.2.162.0-1_arm64.deb": "303d48229ccab57342ad9cc36174429ee7b8b0cdb6e6e292b87ce3415fe5d65a",
+ "libwavpack1_5.4.0-1_arm64.deb": "81317be561d0cdb1239225772a8a9260d8178634a41263f072c340448c029490",
+ "libwayland-client0_1.18.0-2~exp1.1_arm64.deb": "7bde74828c3508b5b81400c4b9ef51d65b566d7645747b777ec171eb8ecfce47",
+ "libwayland-cursor0_1.18.0-2~exp1.1_arm64.deb": "1c201b449abf41e864dd48c960c9ef7f180b5164a3e7562ae5fd467d60fd074e",
+ "libwayland-egl1_1.18.0-2~exp1.1_arm64.deb": "8b5e2d8975330f308c226e3b51624a76ea8ebe5115dd8e929e05ea23c4169008",
+ "libwebp6_0.6.1-2.1_arm64.deb": "c4e7e63f283aaa9913ac78b9871434f543f87ff4641a9a1737e86a0b32a679a7",
+ "libwebpmux3_0.6.1-2.1_arm64.deb": "bf59f5c2e958af997f9754f78de0ed48178b4c33688c354b52ffdcb970876ca8",
+ "libx11-6_1.7.2-1_arm64.deb": "8872962f2a0a6b9e16cafc6acd2be56cee4ec7a16c2a0abdb9fcda6d0b31be3b",
+ "libx11-data_1.7.2-1_all.deb": "049b7eabced516acfdf44a5e81c26d108b16e4987e5d7604ea53eaade74027fb",
+ "libx11-xcb1_1.7.2-1_arm64.deb": "4714e973f35a3fa8e4687ec86d9239bead13ebdc3990173d5520afa7504ab65d",
+ "libx264-160_0.160.3011+gitcde9a93-2.1_arm64.deb": "3e379d7147e548a43f8a4ca4fb291d839d0a3d8cc07df73e5cbc88b856818b92",
+ "libx265-192_3.4-2_arm64.deb": "4da85b00f95645ab01832af0bec627534608481a6f181c0fbbabeb5c04c2a1cc",
+ "libxau6_1.0.9-1_arm64.deb": "36c2bf400641a80521093771dc2562c903df4065f9eb03add50d90564337ea6c",
+ "libxcb-dri2-0_1.14-3_arm64.deb": "b99d1f4909d0a3fe85dfba6524fb0a8c4d08703366ed99f034417684e91ded5b",
+ "libxcb-dri3-0_1.14-3_arm64.deb": "9a6dc75fd8e9fe720f62f99cfebef0cbe51cf2909aa5e777254cb6a3ddf47f6f",
+ "libxcb-glx0_1.14-3_arm64.deb": "754902f3399fd5c9c851dbf150306f537acab7152b9f32206a7910b7c3f353e1",
+ "libxcb-present0_1.14-3_arm64.deb": "9e84b49e46833fd3fb366b04d19c652a0e8d2ebe3d40f19f9b530763ba2f7b88",
+ "libxcb-render0_1.14-3_arm64.deb": "e794ba2657c5f21dcca327343b41b1997a150b6ac27977970404d60f471be48a",
+ "libxcb-shm0_1.14-3_arm64.deb": "e7f59fc41744fe6b8b9ba97b262a051621173689e2a3e5ebb26dc253c9bdc48b",
+ "libxcb-sync1_1.14-3_arm64.deb": "ba70a2194fa7875f1a3ef6fb31bdd4cca5ca970926e3201994a166c5026ce442",
+ "libxcb-xfixes0_1.14-3_arm64.deb": "8185fca98fda89fc5a020b9a8cd92b093929830a29cf92e6a1eba04bd88bcf5f",
+ "libxcb1_1.14-3_arm64.deb": "48f82b65c93adb7af7757961fdd2730928316459f008d767b7104a56bc20a8f1",
+ "libxcomposite1_0.4.5-1_arm64.deb": "cfe39326fdb822e9d060ed5eb3f95b14459dd6b73793c5290000f9b27f8bad37",
+ "libxcursor1_1.2.0-2_arm64.deb": "2e309833ccf7e6b62560240cd84e325cafcb2ce70b1fb297469957360cee4478",
+ "libxdamage1_1.1.5-2_arm64.deb": "696cb56f414e7c0ea9a3bcbcb63b07f6ed8e980023c1b35006d5c1dc0d0213ed",
+ "libxdmcp6_1.1.2-3_arm64.deb": "e92569ac33247261aa09adfadc34ced3994ac301cf8b58de415a2d5dbf15ccfc",
+ "libxerces-c3.2_3.2.3+debian-3_arm64.deb": "59311a05cc1d2c693840db899c02c0e8d1ba2e9e66961e8284053255d4c38866",
+ "libxext6_1.3.3-1.1_arm64.deb": "57237ecf54662372e206b154c0ab6096e05955e048552575b45d3ad14a6ff6e5",
+ "libxfixes3_5.0.3-2_arm64.deb": "eb70f12af1d13e1632ee29ddf103617af00a078faf6c3a2531ab3d01b395606b",
+ "libxft2_2.3.2-2_arm64.deb": "6941176bcc78bf02d1635575a8cc726a7eb0628d8476efca7607718bdc1f50c5",
+ "libxi6_1.7.10-1_arm64.deb": "0a6788844441f160d970fc7d61004607fe92cfad8966d0b371291703201b3971",
+ "libxinerama1_1.1.4-2_arm64.deb": "bbdef95d025fb804797a5b1a71f319f5f74c0114e90d759b3e9ecf7654442598",
+ "libxkbcommon0_1.0.3-2_arm64.deb": "91c19f642af34ae8cb909d00d08bc83f0b4f8e87ddde6984bd8bff0f7bf83204",
+ "libxml2_2.9.10+dfsg-6.7_arm64.deb": "655b9374cdb737f2c12e021cb0a705c8225b25f5a9bee2eeab9c485303cd115e",
+ "libxpm4_3.5.12-1_arm64.deb": "48ae9f8f91e36956e25bf724fc0fb815ce6202ca610570bd6eb5a077f3580b5a",
+ "libxrandr2_1.5.1-1_arm64.deb": "04c9c59ba1e9648e0ec80d66fa34bab7bf039822f32eb31f706656968052e915",
+ "libxrender1_0.9.10-1_arm64.deb": "fcae69900b599e7b31b31eafa203a184e00376ade1d2f74f9b3d7b24991573a0",
+ "libxshmfence1_1.3-1_arm64.deb": "fa2edaae6902681ecd02534dcdf8389ac48714d3385d572cc17747160957acc8",
+ "libxss1_1.2.3-1_arm64.deb": "e0ff80e309eacda5face68b9a3bd56718fed2750af429324c35d7c9491c335f4",
+ "libxvidcore4_1.3.7-1_arm64.deb": "b950edcb42803f158f94cd2ee44850082c98c92d54e20982ad933936f5f1d181",
+ "libxxf86vm1_1.1.4-1+b2_arm64.deb": "8a4826772ac480804d6b8d776a4130d95260c036b9b218de4c8a0f07eb9c5bba",
+ "libz3-4_4.8.10-1_arm64.deb": "ae1ea58ecfdd4b5ec53d734e60ac2df37fddb11888b7730a19335f1a9b09f489",
+ "libzmq5_4.3.4-1_arm64.deb": "e5eaecc454d6792848a2ff866d73e3123b9ccdd08c9bd9e6096b52f08e484a13",
+ "libzstd1_1.4.8+dfsg-2.1_arm64.deb": "dd01659c6c122f983a3369a04ede63539f666585d52a03f8aa2c27b307e547e0",
+ "libzvbi-common_0.2.35-18_all.deb": "53ed21370b937a9385e1fcf1626400891bd4fd86a76b31654fb45e0875d8bfb8",
+ "libzvbi0_0.2.35-18_arm64.deb": "40626984d48d486b62f08c255eb9397a04f52ca2983e8d689e5de5e94e29a4b1",
+ "lsb-base_11.1.0_all.deb": "89ed6332074d827a65305f9a51e591dff20641d61ff5e11f4e1822a9987e96fe",
+ "mariadb-common_10.5.12-0+deb11u1_all.deb": "08df6568dd15e9e4b72717539e84ada558a868472e26144f114db82bc22f6421",
+ "mysql-common_5.8+1.0.7_all.deb": "22b3130e002c2c2fa6a1124aaccbe3a6ddbbb4d6bf03beed8a6f044027dcb720",
+ "ocl-icd-libopencl1_2.2.14-2_arm64.deb": "988af69eca9c4b7433572d11ecbc048a7680ae15afa78941782945b18ff185d7",
+ "odbcinst1debian2_2.3.6-0.1+b1_arm64.deb": "3b22a944687b3007a3484673a68e2a4483fd9237530b0ec359e423c8a745ca0e",
+ "odbcinst_2.3.6-0.1+b1_arm64.deb": "c0a9b4ade83bc9a28967e1e981cd1c93f9e6654cf6d53f1fa86db84b3bb8ad89",
+ "perl_5.32.1-4+deb11u2_arm64.deb": "625a2d0cafb5089953012d60d3a5ba726b692d9d49955a251b78b8cce884d05b",
+ "pkg-config_0.29.2-1_arm64.deb": "074d64f7a6bb5fb9139661aea20815438d8ffe8d7bb44b7c3f58e220c153fdbd",
+ "proj-data_7.2.1-1_all.deb": "40c64f7808d8233c08f3aa2745211e705828b4ae6fc5dbd62a934d8c3e9fd6e5",
+ "sensible-utils_0.0.14_all.deb": "b9a447dc4ec8714196b037e20a2209e62cd669f5450222952f259bda4416b71f",
+ "shared-mime-info_2.0-1_arm64.deb": "33257bc679bee7b2627a001eb747f3378ea8c8063863d2cb6edb6ad7f37f280a",
+ "ttf-bitstream-vera_1.10-8.1_all.deb": "ba622edf73744b2951bbd20bfc113a1a875a9b0c6fed1ac9e9c7f4b54dd8a048",
+ "tzdata_2021a-1+deb11u2_all.deb": "4a34cbe17d391e6351386f3530b7ffd096c2cc8582e970f745addc636fa7c397",
+ "ucf_3.0043_all.deb": "ebef6bcd777b5c0cc2699926f2159db08433aed07c50cb321fd828b28c5e8d53",
+ "x11-common_7.7+22_all.deb": "5d1c3287826f60c3a82158b803b9c0489b8aad845ca23a53a982eba3dbb82aa3",
+ "xkb-data_2.29-2_all.deb": "9122cccc67e6b3c3aef2fa9c50ef9d793a12f951c76698a02b1f4ceb9e3634e5",
+ "zlib1g-dev_1.2.11.dfsg-2_arm64.deb": "1e6ed652eebd3761454d75db5459c247cd5d29e58bbe9f6b4b62a62d96f7c279",
+}
diff --git a/frc971/control_loops/python/angular_system.py b/frc971/control_loops/python/angular_system.py
index 7ccb4af..cad3221 100755
--- a/frc971/control_loops/python/angular_system.py
+++ b/frc971/control_loops/python/angular_system.py
@@ -193,6 +193,8 @@
x_plot = []
v_plot = []
a_plot = []
+ motor_current_plot = []
+ battery_current_plot = []
x_goal_plot = []
v_goal_plot = []
x_hat_plot = []
@@ -239,7 +241,14 @@
v_goal_plot.append(end_goal[1, 0])
U = U_uncapped.copy()
+
U[0, 0] = numpy.clip(U[0, 0], -vbat, vbat)
+
+ motor_current = (U[0, 0] - plant.X[1, 0] / plant.G / plant.motor.Kv
+ ) / plant.motor.resistance
+ motor_current_plot.append(motor_current)
+ battery_current = U[0, 0] * motor_current / 12.0
+ battery_current_plot.append(battery_current)
x_plot.append(plant.X[0, 0])
if v_plot:
@@ -282,8 +291,16 @@
pylab.plot(t_plot, offset_plot, label='voltage_offset')
pylab.legend()
- pylab.subplot(3, 1, 3)
- pylab.plot(t_plot, a_plot, label='a')
+ ax1 = pylab.subplot(3, 1, 3)
+ ax1.set_xlabel("time(s)")
+ ax1.set_ylabel("rad/s^2")
+ ax1.plot(t_plot, a_plot, label='a')
+
+ ax2 = ax1.twinx()
+ ax2.set_xlabel("time(s)")
+ ax2.set_ylabel("Amps")
+ ax2.plot(t_plot, battery_current_plot, 'g', label='battery')
+ ax2.plot(t_plot, motor_current_plot, 'r', label='motor')
pylab.legend()
pylab.show()
diff --git a/go.mod b/go.mod
index a05f6de..fb6a573 100644
--- a/go.mod
+++ b/go.mod
@@ -12,6 +12,7 @@
require (
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
+ github.com/davecgh/go-spew v1.1.0
github.com/google/go-querystring v1.1.0 // indirect
golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd // indirect
diff --git a/go.sum b/go.sum
index c832e6c..9ac49b8 100644
--- a/go.sum
+++ b/go.sum
@@ -15,6 +15,7 @@
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
diff --git a/platform_mappings b/platform_mappings
index 04f55df..1816850 100644
--- a/platform_mappings
+++ b/platform_mappings
@@ -15,6 +15,9 @@
//tools/platforms:rp2040
--cpu=rp2040
+ //tools/platforms:linux_arm64
+ --cpu=arm64
+
flags:
--cpu=k8
//tools/platforms:linux_x86
@@ -30,3 +33,6 @@
--cpu=rp2040
//tools/platforms:rp2040
+
+ --cpu=arm64
+ //tools/platforms:linux_arm64
diff --git a/scouting/BUILD b/scouting/BUILD
index 8188a20..836e5c3 100644
--- a/scouting/BUILD
+++ b/scouting/BUILD
@@ -13,5 +13,5 @@
importpath = "github.com/frc971/971-Robot-Code/scouting",
target_compatible_with = ["@platforms//cpu:x86_64"],
visibility = ["//visibility:private"],
- deps = ["@com_github_mattn_go_sqlite3//:go_default_library"],
+ deps = ["@com_github_mattn_go_sqlite3//:go-sqlite3"],
)
diff --git a/scouting/scraping/BUILD b/scouting/scraping/BUILD
new file mode 100644
index 0000000..d9248f8
--- /dev/null
+++ b/scouting/scraping/BUILD
@@ -0,0 +1,23 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
+
+go_library(
+ name = "scraping",
+ srcs = [
+ "scrape.go",
+ "types.go",
+ ],
+ importpath = "github.com/frc971/971-Robot-Code/scouting/scraping",
+ target_compatible_with = ["@platforms//cpu:x86_64"],
+ visibility = ["//visibility:public"],
+)
+
+go_binary(
+ name = "scraping_demo",
+ srcs = ["scraping_demo.go"],
+ target_compatible_with = ["@platforms//cpu:x86_64"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "scraping",
+ "@com_github_davecgh_go_spew//spew:go_default_library",
+ ],
+)
diff --git a/scouting/scraping/scrape.go b/scouting/scraping/scrape.go
new file mode 100644
index 0000000..fa20f7b
--- /dev/null
+++ b/scouting/scraping/scrape.go
@@ -0,0 +1,86 @@
+package scraping
+
+// A library to grab match data from The Blue Alliance.
+import (
+ "encoding/json"
+ "errors"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+)
+
+// Stores the TBA API key to access the API.
+type params struct {
+ ApiKey string `json:"api_key"`
+}
+
+// Takes in year and FIRST event code and returns all matches in that event according to TBA.
+// Also takes in a file path to the JSON config file that contains your TBA API key.
+// It defaults to <workspace root>/config.json
+// the config is expected to have the following contents:
+//{
+// api_key:"myTBAapiKey"
+//}
+func AllMatches(year, eventCode, filePath string) ([]Match, error) {
+ if filePath == "" {
+ filePath = os.Getenv("BUILD_WORKSPACE_DIRECTORY") + "/scouting_config.json"
+ }
+ // Takes the filepath and grabs the api key from the json.
+ content, err := ioutil.ReadFile(filePath)
+ if err != nil {
+ log.Fatal(err)
+ }
+ // Parses the JSON parameters into a struct.
+ var passed_params params
+ error := json.Unmarshal([]byte(content), &passed_params)
+ if error != nil {
+ log.Fatalf("You forgot to add the api_key parameter in the json file")
+ log.Fatalf("%s", err)
+ }
+
+ // Create the TBA event key for the year and event code.
+ eventKey := year + eventCode
+
+ // Create the client for HTTP requests.
+ client := &http.Client{}
+
+ // Create a get request for the match info.
+ req, err := http.NewRequest("GET", "https://www.thebluealliance.com/api/v3/event/"+eventKey+"/matches", nil)
+
+ if err != nil {
+ return nil, errors.New("failed to build http request")
+ }
+
+ // Add the auth key header to the request.
+ req.Header.Add("X-TBA-Auth-Key", passed_params.ApiKey)
+
+ // Make the API request
+ resp, err := client.Do(req)
+
+ if err != nil {
+ return nil, err
+ }
+
+ if resp.Status != "200 OK" {
+ return nil, errors.New("Recieved a status of " + resp.Status + " expected : 200 OK")
+ }
+
+ // Wait until the response is done.
+ defer resp.Body.Close()
+
+ // Get all bytes from response body.
+ bodyBytes, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, errors.New("failed to read response body with error :" + err.Error())
+ }
+
+ var matches []Match
+ // Unmarshal json into go usable format.
+ jsonError := json.Unmarshal([]byte(bodyBytes), &matches)
+ if jsonError != nil {
+ return nil, errors.New("failed to unmarshal json recieved from TBA")
+ }
+
+ return matches, nil
+}
diff --git a/scouting/scraping/scraping_demo.go b/scouting/scraping/scraping_demo.go
new file mode 100644
index 0000000..1d727f3
--- /dev/null
+++ b/scouting/scraping/scraping_demo.go
@@ -0,0 +1,20 @@
+package main
+
+// To run the demo, ensure that you have a file named scouting_config.json at the workspace root with your TBA api key in it.
+import (
+ "log"
+
+ "github.com/davecgh/go-spew/spew"
+ "github.com/frc971/971-Robot-Code/scouting/scraping"
+)
+
+func main() {
+ // Get all the matches.
+ matches, err := scraping.AllMatches("2016", "nytr", "")
+ // Fail on error.
+ if err != nil {
+ log.Fatal("Error:", err.Error)
+ }
+ // Dump the matches.
+ spew.Dump(matches)
+}
diff --git a/scouting/scraping/types.go b/scouting/scraping/types.go
new file mode 100644
index 0000000..aa0d4be
--- /dev/null
+++ b/scouting/scraping/types.go
@@ -0,0 +1,82 @@
+package scraping
+
+// Match holds the TBA data for a given match
+type Match struct {
+ Key string
+ CompLevel string `json:"comp_level"`
+ SetNumber int `json:"set_number"`
+ MatchNumber int `json:"match_number"`
+ Alliances Alliances `json:"alliances"`
+ WinningAlliance string `json:"winning_alliance"`
+ EventKey string `json:"event_key"`
+ Time int `json:"time"`
+ PredictedTime int `json:"predicted_time"`
+ ActualTime int `json:"actual_time"`
+ PostResultTime int `json:"post_result_time"`
+ ScoreBreakdowns ScoreBreakdowns `json:"score_breakdowns"`
+}
+
+// Holds score breakdowns for each alliance
+type ScoreBreakdowns struct {
+ Blue ScoreBreakdownAlliance `json:"blue"`
+ Red ScoreBreakdownAlliance `json:"red"`
+}
+
+// Stores the actual data for the breakdown
+type ScoreBreakdownAlliance struct {
+ TaxiRobot1 string `json:"taxiRobot1"`
+ EndgameRobot1 string `json:"endgameRobot1"`
+ TaxiRobot2 string `json:"taxiRobot2"`
+ EndgameRobot2 string `json:"endgameRobot2"`
+ TaxiRobot3 string `json:"taxiRobot3"`
+ EndgameRobot3 string `json:"endgameRobot3"`
+
+ AutoCargoLowerNear int `json:"autoCargoLowerNear"`
+ AutoCargoLowerFar int `json:"autoCargoLowerFar"`
+ AutoCargoLowerBlue int `json:"autoCargoLowerBlue"`
+ AutoCargoLowerRed int `json:"autoCargoLowerRed"`
+ AutoCargoUpperNear int `json:"autoCargoUpperNear"`
+ AutoCargoUpperFar int `json:"autoCargoUpperFar"`
+ AutoCargoUpperBlue int `json:"autoCargoUpperBlue"`
+ AutoCargoUpperRed int `json:"autoCargoUpperRed"`
+ AutoCargoTotal int `json:"autoCargoTotal"`
+ TeleOpCargoLowerNear int `json:"teleopCargoLowerNear"`
+ TeleOpCargoLowerFar int `json:"teleopCargoLowerFar"`
+ TeleOpCargoLowerBlue int `json:"teleopCargoLowerBlue"`
+ TeleOpCargoLowerRed int `json:"teleopCargoLowerRed"`
+ TeleOpCargoUpperNear int `json:"teleopCargoUpperNear"`
+ TeleOpCargoUpperFar int `json:"teleopCargoUpperFar"`
+ TeleOpCargoUpperBlue int `json:"teleopCargoUpperBlue"`
+ TeleOpCargoUpperRed int `json:"teleopCargoUpperRed"`
+ TeleopCargoTotal int `json:"teleopCargoTotal"`
+ MatchCargoTotal int `json:"matchCargoTotal"`
+ AutoTaxiPoints int `json:"autoTaxiPoints"`
+ AutoCargoPoints int `json:"autoCargoPoints"`
+ AutoPoints int `json:"autoPoints"`
+ QuintetAchieved bool `json:"quintetAchieved"`
+ TeleOpCargoPoints int `json:"teleopCargoPoints"`
+ EndgamePoints int `json:"endgamePoints"`
+ TeleOpPoints int `json:"teleopPoints"`
+ CargoBonusRankingPoint bool `json:"cargoBonusRankingPoint"`
+ HangerBonusRankingPoint bool `json:"hangarBonusRankingPoint"`
+ FoulCount bool `json:"foulCount"`
+ TechFoulCount int `json:"techFoulCount"`
+ AdjustPoints int `json:"adjustPoints"`
+ FoulPoints int `json:"foulPoints"`
+ RankingPoints int `json:"rp"`
+ TotalPoints int `json:"totalPoints"`
+}
+
+// Alliances holds the two alliances for a match
+type Alliances struct {
+ Red Alliance `json:"red"`
+ Blue Alliance `json:"blue"`
+}
+
+// Alliance holds the info for the alliance
+type Alliance struct {
+ Score int `json:"score"`
+ TeamKeys []string `json:"team_keys"`
+ SurrogateTeamKeys []string `json:"surrogate_team_keys"`
+ DqTeamKeys []string `json:"dq_team_keys"`
+}
diff --git a/third_party/BUILD b/third_party/BUILD
index 040d3f7..aff739f 100644
--- a/third_party/BUILD
+++ b/third_party/BUILD
@@ -42,6 +42,7 @@
deps = select({
"//tools:cpu_k8": ["@opencv_k8//:opencv"],
"//tools:cpu_armhf": ["@opencv_armhf//:opencv"],
+ "//tools:cpu_arm64": ["@opencv_arm64//:opencv"],
"//conditions:default": [":unavailable"],
}),
)
@@ -86,6 +87,7 @@
deps = select({
"//tools:cpu_k8": ["@halide_k8//:runtime"],
"//tools:cpu_armhf": ["@halide_armhf//:runtime"],
+ "//tools:cpu_arm64": ["@halide_arm64//:runtime"],
"//conditions:default": [":unavailable"],
}),
)
@@ -95,7 +97,7 @@
visibility = ["//visibility:public"],
deps = select({
"//tools:cpu_k8": ["@lzma_amd64//:lib"],
- "//tools:cpu_aarch64": ["@lzma_arm64//:lib"],
+ "//tools:cpu_arm64": ["@lzma_arm64//:lib"],
"//conditions:default": [":unavailable"],
}),
)
diff --git a/third_party/bazel-toolchain/toolchain/internal/llvm_distributions.bzl b/third_party/bazel-toolchain/toolchain/internal/llvm_distributions.bzl
index 4ba4601..4cdbf75 100644
--- a/third_party/bazel-toolchain/toolchain/internal/llvm_distributions.bzl
+++ b/third_party/bazel-toolchain/toolchain/internal/llvm_distributions.bzl
@@ -180,6 +180,7 @@
"clang+llvm-13.0.0-x86_64-apple-darwin.tar.xz": "d051234eca1db1f5e4bc08c64937c879c7098900f7a0370f3ceb7544816a8b09",
"clang+llvm-13.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz": "76d0bf002ede7a893f69d9ad2c4e101d15a8f4186fbfe24e74856c8449acd7c1",
"clang+llvm-13.0.0-x86_64-linux-gnu-ubuntu-20.04.tar.xz": "2c2fb857af97f41a5032e9ecadf7f78d3eff389a5cd3c9ec620d24f134ceb3c8",
+ "clang+llvm-13.0.0-aarch64-linux-gnu.tar.xz": "968d65d2593850ee9b37fcda074fb7641529bd45d2f976af6c8197de3c22612f",
}
# Note: Unlike the user-specified llvm_mirror attribute, the URL prefixes in
diff --git a/third_party/eigen/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h b/third_party/eigen/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h
index e5ebbcf..3519611 100644
--- a/third_party/eigen/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h
+++ b/third_party/eigen/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h
@@ -264,7 +264,7 @@
struct matrix_exp_computeUV<MatrixType, long double>
{
template <typename ArgType>
- static void run(const ArgType& arg, MatrixType& U, MatrixType& V, int& squarings)
+ static void run(const ArgType& arg, [[maybe_unused]] MatrixType& U, [[maybe_unused]] MatrixType& V, int& squarings)
{
#if LDBL_MANT_DIG == 53 // double precision
matrix_exp_computeUV<MatrixType, double>::run(arg, U, V, squarings);
@@ -273,7 +273,7 @@
using std::frexp;
using std::pow;
- const long double l1norm = arg.cwiseAbs().colwise().sum().maxCoeff();
+ [[maybe_unused]] const long double l1norm = arg.cwiseAbs().colwise().sum().maxCoeff();
squarings = 0;
#if LDBL_MANT_DIG <= 64 // extended precision
diff --git a/third_party/gperftools/BUILD b/third_party/gperftools/BUILD
index 7dd86dc..2022ee6 100644
--- a/third_party/gperftools/BUILD
+++ b/third_party/gperftools/BUILD
@@ -102,12 +102,18 @@
"-DPRIuS=\\\"lu\\\"",
"-DPRIxS=\\\"lx\\\"",
],
- "arm": [
+ "arm32": [
"-DPC_FROM_UCONTEXT=uc_mcontext.arm_pc",
"-DPRIdS=\\\"d\\\"",
"-DPRIuS=\\\"u\\\"",
"-DPRIxS=\\\"x\\\"",
],
+ "arm64": [
+ "-DPC_FROM_UCONTEXT=uc_mcontext.pc",
+ "-DPRIdS=\\\"ld\\\"",
+ "-DPRIuS=\\\"lu\\\"",
+ "-DPRIxS=\\\"lx\\\"",
+ ],
}) + compiler_select({
"clang": [
"-Wno-unused-const-variable",
diff --git a/tools/BUILD b/tools/BUILD
index 2e470fa..a7d165d 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -47,11 +47,14 @@
config_setting(
name = "cpu_armhf",
- constraint_values = ["@//tools/platforms/hardware:raspberry_pi"],
+ constraint_values = [
+ "@platforms//cpu:armv7",
+ "//tools/platforms/hardware:raspberry_pi",
+ ],
)
config_setting(
- name = "cpu_aarch64",
+ name = "cpu_arm64",
constraint_values = ["@platforms//cpu:arm64"],
)
diff --git a/tools/build_rules/select.bzl b/tools/build_rules/select.bzl
index cdeeb25..5d78a2b 100644
--- a/tools/build_rules/select.bzl
+++ b/tools/build_rules/select.bzl
@@ -7,6 +7,7 @@
"amd64",
"roborio",
"armhf",
+ "arm64",
"cortex-m",
"cortex-m0plus",
]
@@ -30,10 +31,23 @@
if cpu != "arm":
new_values[cpu] = values[cpu]
new_values["armhf"] = values["arm"]
+ new_values["arm64"] = values["arm"]
new_values["roborio"] = values["arm"]
new_values["cortex-m"] = values["arm"]
new_values["cortex-m0plus"] = values["arm"]
values = new_values
+ elif "arm32" in values:
+ if "arm64" not in values:
+ fail("Need to handle 'arm64' CPU if handling 'arm32' CPU.")
+ new_values = {}
+ for cpu in values:
+ if cpu != "arm32":
+ new_values[cpu] = values[cpu]
+ new_values["armhf"] = values["arm32"]
+ new_values["roborio"] = values["arm32"]
+ new_values["cortex-m"] = values["arm32"]
+ new_values["cortex-m0plus"] = values["arm32"]
+ values = new_values
for cpu in all_cpus:
if cpu not in values:
if "else" in values:
@@ -47,6 +61,7 @@
"@//tools:cpu_k8": values["amd64"],
"@//tools:cpu_roborio": values["roborio"],
"@//tools:cpu_armhf": values["armhf"],
+ "@//tools:cpu_arm64": values["arm64"],
"@//tools:cpu_cortex_m4f": values["cortex-m"],
"@//tools:cpu_cortex_m0plus": values["cortex-m0plus"],
# TODO(phil): Support this properly.
@@ -67,6 +82,7 @@
fail("Need to handle 64 bit addresses!", "values")
return select({
"@//tools:cpu_k8": values["64"],
+ "@//tools:cpu_arm64": values["64"],
"@//tools:cpu_roborio": values["32"],
"@//tools:cpu_armhf": values["32"],
"@//tools:cpu_cortex_m4f": values["32"],
diff --git a/tools/ci/BUILD b/tools/ci/BUILD
index 7fa13d5..59b38ea 100644
--- a/tools/ci/BUILD
+++ b/tools/ci/BUILD
@@ -6,7 +6,7 @@
importpath = "github.com/frc971/971-Robot-Code/tools/ci",
target_compatible_with = ["@platforms//cpu:x86_64"],
visibility = ["//visibility:private"],
- deps = ["@com_github_buildkite_go_buildkite//buildkite:go_default_library"],
+ deps = ["@com_github_buildkite_go_buildkite//buildkite"],
)
go_binary(
diff --git a/tools/ci/buildkite.yaml b/tools/ci/buildkite.yaml
index b2ee1e3..c4af4a4 100644
--- a/tools/ci/buildkite.yaml
+++ b/tools/ci/buildkite.yaml
@@ -29,6 +29,11 @@
- tools/ci/clean-disk.sh
- tools/bazel ${STARTUP} --output_base=../armv7_output_base build ${COMMON} --config=armv7 ${TARGETS}
+ - label: "arm64"
+ commands:
+ - tools/ci/clean-disk.sh
+ - tools/bazel ${STARTUP} --output_base=../arm64_output_base build ${COMMON} --config=arm64 ${TARGETS}
+
- label: "cortex-m4f"
commands:
- tools/ci/clean-disk.sh
diff --git a/tools/cpp/BUILD b/tools/cpp/BUILD
index 6ff661a..db5fa06 100644
--- a/tools/cpp/BUILD
+++ b/tools/cpp/BUILD
@@ -7,6 +7,7 @@
toolchains = {
"k8": "@llvm_toolchain//:cc-clang-x86_64-linux",
"armv7": "@llvm_toolchain//:cc-clang-armv7-linux",
+ "arm64": "@llvm_toolchain//:cc-clang-aarch64-linux",
"roborio": ":cc-compiler-roborio",
"cortex-m4f": ":cc-compiler-cortex-m4f",
"rp2040": ":cc-compiler-rp2040",
@@ -20,7 +21,6 @@
cpu = cpu,
)
for cpu in [
- "armeabi-v7a",
"cortex-m4f",
"cortex-m4f-k22",
"rp2040",
diff --git a/tools/cpp/toolchain_config.bzl b/tools/cpp/toolchain_config.bzl
index fb6576c..526d831 100644
--- a/tools/cpp/toolchain_config.bzl
+++ b/tools/cpp/toolchain_config.bzl
@@ -19,25 +19,19 @@
toolchain_identifier = "cortex-m4f-k22"
elif ctx.attr.cpu == "roborio":
toolchain_identifier = "roborio_linux"
- elif ctx.attr.cpu == "armeabi-v7a":
- toolchain_identifier = "stub_armeabi-v7a"
else:
fail("Unreachable")
- if ctx.attr.cpu == "armeabi-v7a":
- host_system_name = "armeabi-v7a"
- elif (ctx.attr.cpu == "rp2040" or
- ctx.attr.cpu == "cortex-m4f" or
- ctx.attr.cpu == "cortex-m4f-k22"):
+ if (ctx.attr.cpu == "rp2040" or
+ ctx.attr.cpu == "cortex-m4f" or
+ ctx.attr.cpu == "cortex-m4f-k22"):
host_system_name = "local"
elif ctx.attr.cpu == "roborio":
host_system_name = "roborio"
else:
fail("Unreachable")
- if ctx.attr.cpu == "armeabi-v7a":
- target_system_name = "armeabi-v7a"
- elif ctx.attr.cpu == "rp2040":
+ if ctx.attr.cpu == "rp2040":
target_system_name = "rp2040"
elif ctx.attr.cpu == "cortex-m4f":
target_system_name = "cortex-m4f"
@@ -48,9 +42,7 @@
else:
fail("Unreachable")
- if ctx.attr.cpu == "armeabi-v7a":
- target_cpu = "armeabi-v7a"
- elif ctx.attr.cpu == "rp2040":
+ if ctx.attr.cpu == "rp2040":
target_cpu = "rp2040"
elif ctx.attr.cpu == "cortex-m4f":
target_cpu = "cortex-m4f"
@@ -61,9 +53,7 @@
else:
fail("Unreachable")
- if ctx.attr.cpu == "armeabi-v7a":
- target_libc = "armeabi-v7a"
- elif ctx.attr.cpu == "rp2040":
+ if ctx.attr.cpu == "rp2040":
target_libc = "rp2040"
elif ctx.attr.cpu == "cortex-m4f":
target_libc = "cortex-m4f"
@@ -74,19 +64,15 @@
else:
fail("Unreachable")
- if ctx.attr.cpu == "armeabi-v7a":
- compiler = "compiler"
- elif (ctx.attr.cpu == "rp2040" or
- ctx.attr.cpu == "cortex-m4f" or
- ctx.attr.cpu == "cortex-m4f-k22" or
- ctx.attr.cpu == "roborio"):
+ if (ctx.attr.cpu == "rp2040" or
+ ctx.attr.cpu == "cortex-m4f" or
+ ctx.attr.cpu == "cortex-m4f-k22" or
+ ctx.attr.cpu == "roborio"):
compiler = "gcc"
else:
fail("Unreachable")
- if ctx.attr.cpu == "armeabi-v7a":
- abi_version = "armeabi-v7a"
- elif ctx.attr.cpu == "rp2040":
+ if ctx.attr.cpu == "rp2040":
abi_version = "rp2040"
elif ctx.attr.cpu == "cortex-m4f":
abi_version = "cortex-m4f"
@@ -97,9 +83,7 @@
else:
fail("Unreachable")
- if ctx.attr.cpu == "armeabi-v7a":
- abi_libc_version = "armeabi-v7a"
- elif ctx.attr.cpu == "rp2040":
+ if ctx.attr.cpu == "rp2040":
abi_libc_version = "rp2040"
elif ctx.attr.cpu == "cortex-m4f":
abi_libc_version = "cortex-m4f"
@@ -183,12 +167,10 @@
else:
objcopy_embed_data_action = None
- if ctx.attr.cpu == "armeabi-v7a":
- action_configs = []
- elif (ctx.attr.cpu == "rp2040" or
- ctx.attr.cpu == "cortex-m4f" or
- ctx.attr.cpu == "cortex-m4f-k22" or
- ctx.attr.cpu == "roborio"):
+ if (ctx.attr.cpu == "rp2040" or
+ ctx.attr.cpu == "cortex-m4f" or
+ ctx.attr.cpu == "cortex-m4f-k22" or
+ ctx.attr.cpu == "roborio"):
action_configs = [objcopy_embed_data_action]
else:
fail("Unreachable")
@@ -1162,14 +1144,10 @@
sysroot_feature,
unfiltered_compile_flags_feature,
]
- elif ctx.attr.cpu == "armeabi-v7a":
- features = [supports_pic_feature]
else:
fail("Unreachable")
- if ctx.attr.cpu == "armeabi-v7a":
- cxx_builtin_include_directories = []
- elif ctx.attr.cpu == "roborio":
+ if ctx.attr.cpu == "roborio":
cxx_builtin_include_directories = [
"%package(@arm_frc_linux_gnueabi_repo//arm-frc2020-linux-gnueabi/usr/lib/gcc/arm-frc2020-linux-gnueabi/7.3.0/include)%",
"%package(@arm_frc_linux_gnueabi_repo//arm-frc2020-linux-gnueabi/usr/lib/gcc/arm-frc2020-linux-gnueabi/7.3.0/include-fixed)%",
@@ -1289,20 +1267,6 @@
path = "gcc_arm_none_eabi/arm-none-eabi-strip",
),
]
- elif ctx.attr.cpu == "armeabi-v7a":
- tool_paths = [
- tool_path(name = "ar", path = "/bin/false"),
- tool_path(name = "compat-ld", path = "/bin/false"),
- tool_path(name = "cpp", path = "/bin/false"),
- tool_path(name = "dwp", path = "/bin/false"),
- tool_path(name = "gcc", path = "/bin/false"),
- tool_path(name = "gcov", path = "/bin/false"),
- tool_path(name = "ld", path = "/bin/false"),
- tool_path(name = "nm", path = "/bin/false"),
- tool_path(name = "objcopy", path = "/bin/false"),
- tool_path(name = "objdump", path = "/bin/false"),
- tool_path(name = "strip", path = "/bin/false"),
- ]
else:
fail("Unreachable")
@@ -1336,7 +1300,7 @@
cc_toolchain_config = rule(
implementation = _impl,
attrs = {
- "cpu": attr.string(mandatory = True, values = ["armeabi-v7a", "cortex-m4f", "cortex-m4f-k22", "roborio", "rp2040"]),
+ "cpu": attr.string(mandatory = True, values = ["cortex-m4f", "cortex-m4f-k22", "roborio", "rp2040"]),
},
provides = [CcToolchainConfigInfo],
executable = True,
diff --git a/tools/go/BUILD b/tools/go/BUILD
index 25b3adb..2c251a0 100644
--- a/tools/go/BUILD
+++ b/tools/go/BUILD
@@ -6,6 +6,13 @@
srcs = [
"mirror_lib.py",
],
+ data = [
+ "@com_github_bazelbuild_buildtools//buildifier",
+ ],
+ target_compatible_with = ["@platforms//cpu:x86_64"],
+ deps = [
+ "@bazel_tools//tools/python/runfiles",
+ ],
)
py_binary(
@@ -25,13 +32,12 @@
"mirror_go_repos.py",
],
data = [
- "@com_github_bazelbuild_buildtools//buildifier",
"@go_sdk//:bin/go",
],
target_compatible_with = ["@platforms//cpu:x86_64"],
+ visibility = ["//visibility:public"],
deps = [
":mirror_lib",
- "@bazel_tools//tools/python/runfiles",
],
)
diff --git a/tools/go/go_mirrors.bzl b/tools/go/go_mirrors.bzl
index 0e8f394..bfee0a9 100644
--- a/tools/go/go_mirrors.bzl
+++ b/tools/go/go_mirrors.bzl
@@ -147,6 +147,13 @@
"strip_prefix": "github.com/grpc-ecosystem/grpc-gateway@v1.16.0",
"version": "v1.16.0",
},
+ "com_github_mattn_go_sqlite3": {
+ "filename": "com_github_mattn_go_sqlite3__v1.14.10.zip",
+ "importpath": "github.com/mattn/go-sqlite3",
+ "sha256": "3c1e6497b023fc4741bf1bbfb39ae657b99cf44cfb33f5cbf1e88b315d25a306",
+ "strip_prefix": "github.com/mattn/go-sqlite3@v1.14.10",
+ "version": "v1.14.10",
+ },
"com_github_pmezard_go_difflib": {
"filename": "com_github_pmezard_go_difflib__v1.0.0.zip",
"importpath": "github.com/pmezard/go-difflib",
@@ -315,11 +322,4 @@
"strip_prefix": "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
"version": "v0.0.0-20200804184101-5ec99f83aff1",
},
- "com_github_mattn_go_sqlite3": {
- "filename": "com_github_mattn_go_sqlite3__v1.14.10.zip",
- "importpath": "github.com/mattn/go-sqlite3",
- "sha256": "3c1e6497b023fc4741bf1bbfb39ae657b99cf44cfb33f5cbf1e88b315d25a306",
- "strip_prefix": "github.com/mattn/go-sqlite3@v1.14.10",
- "version": "v1.14.10",
- },
}
diff --git a/tools/go/mirror_go_repos.py b/tools/go/mirror_go_repos.py
index 52e9e26..ae8f722 100644
--- a/tools/go/mirror_go_repos.py
+++ b/tools/go/mirror_go_repos.py
@@ -14,9 +14,6 @@
import sys
import tarfile
from typing import List, Dict
-import urllib.request
-
-from bazel_tools.tools.python.runfiles import runfiles
# Need a fully qualified import here because @bazel_tools interferes.
import org_frc971.tools.go.mirror_lib
@@ -101,7 +98,14 @@
def main(argv):
parser = argparse.ArgumentParser()
- parser.add_argument(
+ group = parser.add_mutually_exclusive_group()
+ group.add_argument(
+ "--prune",
+ action="store_true",
+ help=("When set, makes the tool prune go_mirrors_bzl to match the "
+ "repositories specified in go_deps_bzl. Incompatible with "
+ "--ssh_host."))
+ group.add_argument(
"--ssh_host",
type=str,
help=("The SSH host to copy the downloaded Go repositories to. This "
@@ -121,37 +125,38 @@
else:
existing_mirrored_repos = {}
- with tarfile.open("go_deps.tar", "w") as tar:
- cached_info = download_repos(repos, existing_mirrored_repos, tar)
- num_not_already_mirrored = len(tar.getnames())
+ exit_code = 0
- print(f"Found {num_not_already_mirrored}/{len(cached_info)} libraries "
- "that need to be mirrored.")
-
- # Only mirror the deps if we've specified an SSH host and we actually have
- # something to mirror.
- if args.ssh_host and num_not_already_mirrored:
- copy_to_host_and_unpack("go_deps.tar", args.ssh_host)
+ if args.prune:
+ # Delete all mirror info that is not needed anymore.
+ existing_cache_info = org_frc971.tools.go.mirror_lib.parse_go_mirror_info(args.go_mirrors_bzl)
+ cached_info = {}
+ for repo in repos:
+ try:
+ cached_info[repo["name"]] = existing_cache_info[repo["name"]]
+ except KeyError:
+ print(f"{repo['name']} needs to be mirrored still.")
+ exit_code = 1
else:
- print("Skipping mirroring because of lack of --ssh_host or there's "
- "nothing to actually mirror.")
+ # Download all the repositories that need to be mirrored.
+ with tarfile.open("go_deps.tar", "w") as tar:
+ cached_info = download_repos(repos, existing_mirrored_repos, tar)
+ num_not_already_mirrored = len(tar.getnames())
- with open(args.go_mirrors_bzl, "w") as file:
- file.write("# This file is auto-generated. Do not edit.\n")
- file.write("GO_MIRROR_INFO = ")
- # Format as JSON first. It's parsable as Starlark.
- json.dump(cached_info, file, indent=4, sort_keys=True)
- file.write("\n")
+ print(f"Found {num_not_already_mirrored}/{len(cached_info)} libraries "
+ "that need to be mirrored.")
+ # Only mirror the deps if we've specified an SSH host and we actually have
+ # something to mirror.
+ if args.ssh_host and num_not_already_mirrored:
+ copy_to_host_and_unpack("go_deps.tar", args.ssh_host)
+ else:
+ print("Skipping mirroring because of lack of --ssh_host or there's "
+ "nothing to actually mirror.")
- # Properly format the file now so that the linter doesn't complain.
- r = runfiles.Create()
- subprocess.run(
- [
- r.Rlocation("com_github_bazelbuild_buildtools/buildifier/buildifier_/buildifier"),
- args.go_mirrors_bzl,
- ],
- check=True)
+ org_frc971.tools.go.mirror_lib.write_go_mirror_info(args.go_mirrors_bzl, cached_info)
+
+ return exit_code
if __name__ == "__main__":
diff --git a/tools/go/mirror_lib.py b/tools/go/mirror_lib.py
index faad896..ea23abc 100644
--- a/tools/go/mirror_lib.py
+++ b/tools/go/mirror_lib.py
@@ -1,7 +1,11 @@
"""Provides helper functions for mirroring Go repositories."""
-import unittest.mock
+import json
+import subprocess
from typing import List, Dict
+import unittest.mock
+
+from bazel_tools.tools.python.runfiles import runfiles
def read_file(filepath: str) -> str:
@@ -39,3 +43,30 @@
return repositories
+
+def parse_go_mirror_info(filepath: str) -> Dict[str, Dict[str, str]]:
+ """Parses the tools/go/go_mirrors.bzl file and returns the GO_MIRROR_INFO dictionary."""
+ global_data = {}
+ compiled_code = compile(read_file(filepath), filepath, "exec")
+ eval(compiled_code, global_data)
+
+ return global_data["GO_MIRROR_INFO"]
+
+
+def write_go_mirror_info(filepath: str, mirror_info: Dict[str, Dict[str, str]]):
+ """Saves the specified mirror_info as GO_MIRROR_INFO into tools/go/go_mirrors.bzl."""
+ with open(filepath, "w") as file:
+ file.write("# This file is auto-generated. Do not edit.\n")
+ file.write("GO_MIRROR_INFO = ")
+ # Format as JSON first. It's parsable as Starlark.
+ json.dump(mirror_info, file, indent=4, sort_keys=True)
+ file.write("\n")
+
+ # Properly format the file now so that the linter doesn't complain.
+ r = runfiles.Create()
+ subprocess.run(
+ [
+ r.Rlocation("com_github_bazelbuild_buildtools/buildifier/buildifier_/buildifier"),
+ filepath,
+ ],
+ check=True)
diff --git a/tools/go/tweak_gazelle_go_deps.py b/tools/go/tweak_gazelle_go_deps.py
index 7189d43..d9d7901 100644
--- a/tools/go/tweak_gazelle_go_deps.py
+++ b/tools/go/tweak_gazelle_go_deps.py
@@ -12,14 +12,14 @@
import sys
import textwrap
-import tools.go.mirror_lib
+import org_frc971.tools.go.mirror_lib
def main(argv):
parser = argparse.ArgumentParser()
parser.add_argument("go_deps_bzl", type=str)
args = parser.parse_args(argv[1:])
- repos = tools.go.mirror_lib.parse_go_repositories(args.go_deps_bzl)
+ repos = org_frc971.tools.go.mirror_lib.parse_go_repositories(args.go_deps_bzl)
with open(args.go_deps_bzl, "w") as file:
file.write(textwrap.dedent("""\
diff --git a/tools/lint/BUILD b/tools/lint/BUILD
index d33d786..8d9b150 100644
--- a/tools/lint/BUILD
+++ b/tools/lint/BUILD
@@ -32,6 +32,7 @@
":buildifier",
":gofmt",
"//:gazelle-runner",
+ "//tools/go:mirror_go_repos",
"//tools/go:tweak_gazelle_go_deps",
"@go_sdk//:bin/go",
],
diff --git a/tools/lint/run-ci.sh b/tools/lint/run-ci.sh
index 6228552..32f110a 100755
--- a/tools/lint/run-ci.sh
+++ b/tools/lint/run-ci.sh
@@ -34,6 +34,12 @@
}
update_repos() {
+ # Clear out the go_deps.bzl file so that gazelle won't hesitate to update
+ # it. Without this step gazelle would never try to remove a dependency.
+ cat > "${BUILD_WORKSPACE_DIRECTORY}"/go_deps.bzl <<EOF
+def go_dependencies():
+ pass
+EOF
./gazelle-runner.bash update-repos \
-from_file=go.mod \
-to_macro=go_deps.bzl%go_dependencies \
@@ -50,6 +56,10 @@
"${tweaker}" ./go_deps.bzl
}
+clean_up_go_mirrors() {
+ ./tools/go/mirror_go_repos --prune
+}
+
buildifier() {
./tools/lint/buildifier
}
@@ -69,6 +79,7 @@
update_repos
gazelle
tweak_gazelle_go_deps
+ clean_up_go_mirrors
buildifier
git_status_is_clean # This must the last linter.
)
diff --git a/tools/platforms/BUILD b/tools/platforms/BUILD
index d6a63c7..4127883 100644
--- a/tools/platforms/BUILD
+++ b/tools/platforms/BUILD
@@ -26,6 +26,7 @@
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:arm64",
+ "//tools/platforms/hardware:raspberry_pi",
"//tools/platforms/go:lacks_support",
"//tools/platforms/rust:has_support",
],
diff --git a/y2020/vision/BUILD b/y2020/vision/BUILD
index 5156b59..75ef7aa 100644
--- a/y2020/vision/BUILD
+++ b/y2020/vision/BUILD
@@ -106,7 +106,10 @@
"//y2020:config",
],
target_compatible_with = ["@platforms//os:linux"],
- visibility = ["//y2020:__subpackages__"],
+ visibility = [
+ "//y2020:__subpackages__",
+ "//y2022:__subpackages__",
+ ],
deps = [
":charuco_lib",
"//aos:init",
diff --git a/y2020/vision/sift/fast_gaussian.bzl b/y2020/vision/sift/fast_gaussian.bzl
index fdf5afe..1560f6a 100644
--- a/y2020/vision/sift/fast_gaussian.bzl
+++ b/y2020/vision/sift/fast_gaussian.bzl
@@ -38,15 +38,16 @@
"amd64": "k8",
"roborio": "roborio",
"armhf": "armv7",
+ "arm64": "aarch64",
"cortex-m": "cortex-m",
"cortex-m0plus": "cortex-m0plus",
}),
outs = headers + objects + htmls,
- # The tool doesn't support anything other than k8 and armv7.
- # right now.
+ # The tool doesn't support everything right now.
target_compatible_with = platforms.any_of([
+ "@platforms//cpu:arm64",
"@platforms//cpu:x86_64",
- "//tools/platforms/hardware:raspberry_pi",
+ "//tools:cpu_armhf",
]),
)
diff --git a/y2020/vision/sift/fast_gaussian_runner.py b/y2020/vision/sift/fast_gaussian_runner.py
index cc45208..d812f3f 100755
--- a/y2020/vision/sift/fast_gaussian_runner.py
+++ b/y2020/vision/sift/fast_gaussian_runner.py
@@ -16,6 +16,7 @@
target_cpu = sys.argv[3]
target = {
+ 'aarch64': 'arm-64-linux-no_asserts',
'armv7': 'arm-32-linux-no_asserts',
'k8': 'x86-64-linux-no_asserts',
}[target_cpu]
diff --git a/y2022/BUILD b/y2022/BUILD
index 812cac1..67e8587 100644
--- a/y2022/BUILD
+++ b/y2022/BUILD
@@ -26,6 +26,7 @@
robot_downloader(
name = "pi_download",
binaries = [
+ "//y2020/vision:calibration",
"//y2022/vision:viewer",
],
data = [
diff --git a/y2022/vision/calib_files/calibration_pi-971-1_2021-09-11_17-49-00.000000000.json b/y2022/vision/calib_files/calibration_pi-971-1_2021-09-11_17-49-00.000000000.json
deleted file mode 100644
index 39c7911..0000000
--- a/y2022/vision/calib_files/calibration_pi-971-1_2021-09-11_17-49-00.000000000.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "node_name": "pi1",
- "team_number": 971,
- "intrinsics": [
- 392.276093,
- 0.0,
- 293.934753,
- 0.0,
- 392.30838,
- 212.287537,
- 0.0,
- 0.0,
- 1.0
- ],
- "dist_coeffs": [
- 0.149561,
- -0.261432,
- -0.000182,
- -0.000697,
- 0.099255
- ],
- "calibration_timestamp": 1597994992500905688
-}
diff --git a/y2022/vision/calib_files/calibration_pi-971-1_2022-02-06_15-19-00.000000000.json b/y2022/vision/calib_files/calibration_pi-971-1_2022-02-06_15-19-00.000000000.json
new file mode 100755
index 0000000..6a4f05c
--- /dev/null
+++ b/y2022/vision/calib_files/calibration_pi-971-1_2022-02-06_15-19-00.000000000.json
@@ -0,0 +1,23 @@
+{
+ "node_name": "pi1",
+ "team_number": 971,
+ "intrinsics": [
+ 398.312439,
+ 0.0,
+ 348.653015,
+ 0.0,
+ 397.627533,
+ 257.368805,
+ 0.0,
+ 0.0,
+ 1.0
+ ],
+ "dist_coeffs": [
+ 0.143741,
+ -0.274336,
+ -0.000311,
+ -0.000171,
+ 0.10252
+ ],
+ "calibration_timestamp": 1635600750700335075
+}
diff --git a/y2022/vision/viewer.cc b/y2022/vision/viewer.cc
index 7c13d1b..6ee6144 100644
--- a/y2022/vision/viewer.cc
+++ b/y2022/vision/viewer.cc
@@ -68,8 +68,10 @@
// TODO(Milind) Store the target estimates and match them by timestamp to make
// sure we're getting the right one.
- CHECK(target_estimate_fetcher.FetchNext());
- const TargetEstimate *target = target_estimate_fetcher.get();
+ const TargetEstimate *target_est = nullptr;
+ if (target_estimate_fetcher.Fetch()) {
+ target_est = target_estimate_fetcher.get();
+ }
// Create color image:
cv::Mat image_color_mat(cv::Size(image->cols(), image->rows()), CV_8UC2,
@@ -82,19 +84,23 @@
return false;
}
- LOG(INFO) << image->monotonic_timestamp_ns()
- << ": # blobs: " << target->blob_result()->filtered_blobs()->size();
+ LOG(INFO) << image->monotonic_timestamp_ns() << ": # unfiltered blobs: "
+ << target_est->blob_result()->unfiltered_blobs()->size()
+ << "; # filtered blobs: "
+ << target_est->blob_result()->filtered_blobs()->size();
- cv::Mat ret_image;
- BlobDetector::DrawBlobs(
- ret_image, FbsToCvBlobs(*target->blob_result()->filtered_blobs()),
- FbsToCvBlobs(*target->blob_result()->unfiltered_blobs()),
- FbsToBlobStats(*target->blob_result()->blob_stats()),
- cv::Point{target->blob_result()->centroid()->x(),
- target->blob_result()->centroid()->y()});
+ cv::Mat ret_image(cv::Size(image->cols(), image->rows()), CV_8UC3);
+ if (target_est != nullptr) {
+ BlobDetector::DrawBlobs(
+ ret_image, FbsToCvBlobs(*target_est->blob_result()->filtered_blobs()),
+ FbsToCvBlobs(*target_est->blob_result()->unfiltered_blobs()),
+ FbsToBlobStats(*target_est->blob_result()->blob_stats()),
+ cv::Point{target_est->blob_result()->centroid()->x(),
+ target_est->blob_result()->centroid()->y()});
+ cv::imshow("blobs", ret_image);
+ }
cv::imshow("image", rgb_image);
- cv::imshow("blobs", ret_image);
int keystroke = cv::waitKey(1);
if ((keystroke & 0xFF) == static_cast<int>('c')) {
@@ -119,6 +125,9 @@
image_fetcher =
event_loop.MakeFetcher<frc971::vision::CameraImage>(FLAGS_channel);
+ target_estimate_fetcher =
+ event_loop.MakeFetcher<y2022::vision::TargetEstimate>(FLAGS_channel);
+
// Run the display loop
event_loop.AddPhasedLoop(
[&event_loop](int) {