Merge "Add support for multiple hostnames for a given node"
diff --git a/aos/configuration.cc b/aos/configuration.cc
index 1ca28ad..ac897bf 100644
--- a/aos/configuration.cc
+++ b/aos/configuration.cc
@@ -289,7 +289,7 @@
}
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
// Start by building the vectors. They need to come before the final table.
// Channels
@@ -492,7 +492,7 @@
const Flatbuffer<Configuration> &config,
const std::vector<aos::FlatbufferString<reflection::Schema>> &schemas) {
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Channel>>>
channels_offset;
@@ -500,7 +500,7 @@
std::vector<flatbuffers::Offset<Channel>> channel_offsets;
for (const Channel *c : *config.message().channels()) {
flatbuffers::FlatBufferBuilder channel_fbb;
- channel_fbb.ForceDefaults(1);
+ channel_fbb.ForceDefaults(true);
// Search for a schema with a matching type.
const aos::FlatbufferString<reflection::Schema> *found_schema = nullptr;
diff --git a/aos/events/BUILD b/aos/events/BUILD
index 1f7a3d7..a905728 100644
--- a/aos/events/BUILD
+++ b/aos/events/BUILD
@@ -141,6 +141,7 @@
":ping_fbs",
":pong_fbs",
"//aos/network:message_bridge_client_fbs",
+ "//aos/network:timestamp_fbs",
"//aos/network:message_bridge_server_fbs",
],
deps = [":config"],
diff --git a/aos/events/event_loop.cc b/aos/events/event_loop.cc
index b50162b..2ffad3b 100644
--- a/aos/events/event_loop.cc
+++ b/aos/events/event_loop.cc
@@ -209,7 +209,7 @@
// Now, build up a report with everything pre-filled out.
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
// Pre-fill in the defaults for timers.
std::vector<flatbuffers::Offset<timing::Timer>> timer_offsets;
diff --git a/aos/events/event_loop.h b/aos/events/event_loop.h
index 4294226..6047cbc 100644
--- a/aos/events/event_loop.h
+++ b/aos/events/event_loop.h
@@ -252,7 +252,7 @@
allocator_(allocator),
sender_(sender) {
CheckChannelDataAlignment(allocator->data(), allocator->size());
- fbb_.ForceDefaults(1);
+ fbb_.ForceDefaults(true);
}
Builder() {}
Builder(const Builder &) = delete;
@@ -306,6 +306,16 @@
return sender_ ? true : false;
}
+ // Returns the time_points that the last message was sent at.
+ aos::monotonic_clock::time_point monotonic_sent_time() const {
+ return sender_->monotonic_sent_time();
+ }
+ aos::realtime_clock::time_point realtime_sent_time() const {
+ return sender_->realtime_sent_time();
+ }
+ // Returns the queue index that this was sent with.
+ uint32_t sent_queue_index() const { return sender_->sent_queue_index(); }
+
private:
friend class EventLoop;
Sender(std::unique_ptr<RawSender> sender) : sender_(std::move(sender)) {}
diff --git a/aos/events/logging/log_edit.cc b/aos/events/logging/log_edit.cc
index 5db503a..02b7bd7 100644
--- a/aos/events/logging/log_edit.cc
+++ b/aos/events/logging/log_edit.cc
@@ -27,7 +27,7 @@
const ::std::string header_json =
aos::util::ReadFileToStringOrDie(FLAGS_header);
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
flatbuffers::Offset<aos::logger::LogFileHeader> header =
aos::JsonToFlatbuffer<aos::logger::LogFileHeader>(header_json, &fbb);
diff --git a/aos/events/logging/logger.cc b/aos/events/logging/logger.cc
index 57c2c2c..ddd3dac 100644
--- a/aos/events/logging/logger.cc
+++ b/aos/events/logging/logger.cc
@@ -124,7 +124,7 @@
void Logger::WriteHeader(const Node *node) {
// Now write the header with this timestamp in it.
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
flatbuffers::Offset<aos::Configuration> configuration_offset =
CopyFlatBuffer(event_loop_->configuration(), &fbb);
@@ -230,7 +230,7 @@
// Write!
flatbuffers::FlatBufferBuilder fbb(f.fetcher->context().size +
max_header_size_);
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
fbb.FinishSizePrefixed(PackMessage(&fbb, f.fetcher->context(),
f.channel_index, f.log_type));
@@ -252,7 +252,7 @@
if (f.timestamp_writer != nullptr) {
// And now handle timestamps.
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
fbb.FinishSizePrefixed(PackMessage(&fbb, f.fetcher->context(),
f.channel_index,
@@ -898,13 +898,13 @@
// This is the builder that we use for the config containing all the new
// channels.
flatbuffers::FlatBufferBuilder new_config_fbb;
- new_config_fbb.ForceDefaults(1);
+ new_config_fbb.ForceDefaults(true);
std::vector<flatbuffers::Offset<Channel>> channel_offsets;
for (auto &pair : remapped_channels_) {
// This is the builder that we use for creating the Channel with just the
// new name.
flatbuffers::FlatBufferBuilder new_name_fbb;
- new_name_fbb.ForceDefaults(1);
+ new_name_fbb.ForceDefaults(true);
const flatbuffers::Offset<flatbuffers::String> name_offset =
new_name_fbb.CreateString(pair.second);
ChannelBuilder new_name_builder(new_name_fbb);
diff --git a/aos/events/multinode_pingpong.json b/aos/events/multinode_pingpong.json
index c9bc53e..54cc658 100644
--- a/aos/events/multinode_pingpong.json
+++ b/aos/events/multinode_pingpong.json
@@ -26,6 +26,56 @@
},
{
"name": "/aos/pi1",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi1",
+ "frequency": 10,
+ "num_senders": 2,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1,
+ "time_to_live": 5000000
+ },
+ {
+ "name": "pi3",
+ "priority": 1,
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/aos/pi2",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi2",
+ "frequency": 10,
+ "num_senders": 2,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi1",
+ "priority": 1,
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/aos/pi3",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi3",
+ "frequency": 10,
+ "num_senders": 2,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi1",
+ "priority": 1,
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/aos/pi1",
"type": "aos.message_bridge.ServerStatistics",
"source_node": "pi1",
"frequency": 2
@@ -141,7 +191,6 @@
{
"match": {
"name": "/aos",
- "type": "aos.logging.LogMessageFbs",
"source_node": "pi1"
},
"rename": {
@@ -151,7 +200,6 @@
{
"match": {
"name": "/aos",
- "type": "aos.logging.LogMessageFbs",
"source_node": "pi2"
},
"rename": {
@@ -161,97 +209,6 @@
{
"match": {
"name": "/aos",
- "type": "aos.logging.LogMessageFbs",
- "source_node": "pi3"
- },
- "rename": {
- "name": "/aos/pi3"
- }
- },
- {
- "match": {
- "name": "/aos",
- "type": "aos.timing.Report",
- "source_node": "pi1"
- },
- "rename": {
- "name": "/aos/pi1"
- }
- },
- {
- "match": {
- "name": "/aos",
- "type": "aos.timing.Report",
- "source_node": "pi2"
- },
- "rename": {
- "name": "/aos/pi2"
- }
- },
- {
- "match": {
- "name": "/aos",
- "type": "aos.timing.Report",
- "source_node": "pi3"
- },
- "rename": {
- "name": "/aos/pi3"
- }
- },
- {
- "match": {
- "name": "/aos",
- "type": "aos.message_bridge.ServerStatistics",
- "source_node": "pi1"
- },
- "rename": {
- "name": "/aos/pi1"
- }
- },
- {
- "match": {
- "name": "/aos",
- "type": "aos.message_bridge.ServerStatistics",
- "source_node": "pi2"
- },
- "rename": {
- "name": "/aos/pi2"
- }
- },
- {
- "match": {
- "name": "/aos",
- "type": "aos.message_bridge.ServerStatistics",
- "source_node": "pi3"
- },
- "rename": {
- "name": "/aos/pi3"
- }
- },
- {
- "match": {
- "name": "/aos",
- "type": "aos.message_bridge.ClientStatistics",
- "source_node": "pi1"
- },
- "rename": {
- "name": "/aos/pi1"
- }
- },
- {
- "match": {
- "name": "/aos",
- "type": "aos.message_bridge.ClientStatistics",
- "source_node": "pi2"
- },
- "rename": {
- "name": "/aos/pi2"
- }
- },
- {
- "match": {
- "name": "/aos",
- "type": "aos.message_bridge.ClientStatistics",
"source_node": "pi3"
},
"rename": {
diff --git a/aos/flatbuffer_merge.cc b/aos/flatbuffer_merge.cc
index 1f5c8a0..e548f70 100644
--- a/aos/flatbuffer_merge.cc
+++ b/aos/flatbuffer_merge.cc
@@ -518,7 +518,7 @@
const uint8_t *data2) {
// Build up a builder.
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
// Finish up the buffer and return it.
fbb.Finish(MergeFlatBuffers(typetable, data1, data2, &fbb));
@@ -537,10 +537,10 @@
// implementation is fine for the usages that we have now. We are better off
// abstracting this into a library call where we can fix it later easily.
flatbuffers::FlatBufferBuilder fbb1;
- fbb1.ForceDefaults(1);
+ fbb1.ForceDefaults(true);
fbb1.Finish(MergeFlatBuffers(typetable, t1, nullptr, &fbb1));
flatbuffers::FlatBufferBuilder fbb2;
- fbb2.ForceDefaults(1);
+ fbb2.ForceDefaults(true);
fbb2.Finish(MergeFlatBuffers(typetable, t2, nullptr, &fbb2));
if (fbb1.GetSize() != fbb2.GetSize()) return false;
diff --git a/aos/flatbuffer_merge.h b/aos/flatbuffer_merge.h
index 5e84160..418ea5e 100644
--- a/aos/flatbuffer_merge.h
+++ b/aos/flatbuffer_merge.h
@@ -55,7 +55,7 @@
inline aos::FlatbufferDetachedBuffer<T> MergeFlatBuffers(const T *fb1,
const T *fb2) {
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
fbb.Finish(MergeFlatBuffers<T>(
reinterpret_cast<const flatbuffers::Table *>(fb1),
reinterpret_cast<const flatbuffers::Table *>(fb2), &fbb));
@@ -72,7 +72,7 @@
template <class T>
inline FlatbufferDetachedBuffer<T> CopyFlatBuffer(const T *t) {
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
fbb.Finish(CopyFlatBuffer<T>(t, &fbb));
return FlatbufferDetachedBuffer<T>(fbb.Release());
}
diff --git a/aos/flatbuffer_merge_test.cc b/aos/flatbuffer_merge_test.cc
index 371639a..890a831 100644
--- a/aos/flatbuffer_merge_test.cc
+++ b/aos/flatbuffer_merge_test.cc
@@ -136,7 +136,7 @@
out);
{
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
fbb.Finish(MergeFlatBuffers(
ConfigurationTypeTable(),
flatbuffers::GetRoot<flatbuffers::Table>(fb1.data()),
@@ -145,7 +145,7 @@
}
{
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
fbb.Finish(MergeFlatBuffers<Configuration>(
flatbuffers::GetRoot<flatbuffers::Table>(fb1.data()),
flatbuffers::GetRoot<flatbuffers::Table>(fb2.data()), &fbb));
diff --git a/aos/flatbuffers.h b/aos/flatbuffers.h
index 15740a1..6e86d35 100644
--- a/aos/flatbuffers.h
+++ b/aos/flatbuffers.h
@@ -216,7 +216,7 @@
// Constructs an empty flatbuffer of type T.
static FlatbufferDetachedBuffer<T> Empty() {
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
const auto end = fbb.EndTable(fbb.StartTable());
fbb.Finish(flatbuffers::Offset<flatbuffers::Table>(end));
return FlatbufferDetachedBuffer<T>(fbb.Release());
@@ -238,6 +238,7 @@
public:
FlatbufferFixedAllocatorArray() : buffer_(), allocator_(&buffer_[0], Size) {
builder_ = flatbuffers::FlatBufferBuilder(Size, &allocator_);
+ builder_.ForceDefaults(true);
}
flatbuffers::FlatBufferBuilder *Builder() {
diff --git a/aos/json_to_flatbuffer.cc b/aos/json_to_flatbuffer.cc
index 249a59e..95c95dd 100644
--- a/aos/json_to_flatbuffer.cc
+++ b/aos/json_to_flatbuffer.cc
@@ -777,7 +777,7 @@
const std::string_view data,
const flatbuffers::TypeTable *typetable) {
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
const flatbuffers::Offset<flatbuffers::Table> result =
JsonToFlatbuffer(data, typetable, &fbb);
diff --git a/aos/network/BUILD b/aos/network/BUILD
index 11eebdb..96d561f 100644
--- a/aos/network/BUILD
+++ b/aos/network/BUILD
@@ -14,6 +14,15 @@
)
flatbuffer_cc_library(
+ name = "timestamp_fbs",
+ srcs = ["timestamp.fbs"],
+ gen_reflections = 1,
+ includes = [
+ "//aos:configuration_fbs_includes",
+ ],
+)
+
+flatbuffer_cc_library(
name = "message_bridge_client_fbs",
srcs = ["message_bridge_client.fbs"],
gen_reflections = 1,
@@ -41,9 +50,8 @@
"team_number.h",
],
deps = [
- "//aos/logging",
"//aos/util:string_to_num",
- "@com_google_absl//absl/base",
+ "@com_github_google_glog//:glog",
],
)
@@ -96,9 +104,19 @@
cc_library(
name = "message_bridge_protocol",
+ srcs = [
+ "message_bridge_protocol.cc",
+ ],
hdrs = [
"message_bridge_protocol.h",
],
+ deps = [
+ ":connect_fbs",
+ "//aos:configuration",
+ "//aos:flatbuffer_merge",
+ "//aos:flatbuffers",
+ "@com_github_google_flatbuffers//:flatbuffers",
+ ],
)
cc_library(
@@ -114,10 +132,12 @@
],
deps = [
":connect_fbs",
+ ":message_bridge_client_fbs",
":message_bridge_protocol",
":message_bridge_server_fbs",
":sctp_lib",
":sctp_server",
+ ":timestamp_fbs",
"//aos:unique_malloc_ptr",
"//aos/events:shm_event_loop",
"//aos/events/logging:logger",
@@ -172,6 +192,7 @@
":message_bridge_protocol",
":message_bridge_server_fbs",
":sctp_client",
+ ":timestamp_fbs",
"//aos/events:shm_event_loop",
"//aos/events/logging:logger",
],
@@ -201,6 +222,7 @@
"//aos/events:pong_fbs",
"//aos/network:message_bridge_client_fbs",
"//aos/network:message_bridge_server_fbs",
+ "//aos/network:timestamp_fbs",
],
deps = ["//aos/events:config"],
)
@@ -240,11 +262,11 @@
flatbuffer_cc_library(
name = "web_proxy_fbs",
srcs = ["web_proxy.fbs"],
+ gen_reflections = True,
includes = [
":connect_fbs_includes",
"//aos:configuration_fbs_includes",
],
- gen_reflections = True,
)
flatbuffer_ts_library(
@@ -258,8 +280,8 @@
cc_library(
name = "web_proxy_utils",
- hdrs = ["web_proxy_utils.h"],
srcs = ["web_proxy_utils.cc"],
+ hdrs = ["web_proxy_utils.h"],
deps = [
":connect_fbs",
":web_proxy_fbs",
@@ -300,25 +322,25 @@
cc_binary(
name = "web_proxy_main",
srcs = ["web_proxy_main.cc"],
- deps = [
- ":gen_embedded",
- ":web_proxy",
- "//aos/events:shm_event_loop",
- "//aos:init",
- "//aos/seasocks:seasocks_logger",
- "//third_party/seasocks",
- "@com_github_google_flatbuffers//:flatbuffers"
- ],
copts = [
"-DWEBRTC_POSIX",
"-Wno-unused-parameter",
],
data = [
- "//aos/network/www:files",
- "//aos/network/www:main_bundle",
- "//aos/network/www:flatbuffers",
- "@com_github_google_flatbuffers//:flatjs",
"//aos/events:pingpong_config.json",
+ "//aos/network/www:files",
+ "//aos/network/www:flatbuffers",
+ "//aos/network/www:main_bundle",
+ "@com_github_google_flatbuffers//:flatjs",
+ ],
+ deps = [
+ ":gen_embedded",
+ ":web_proxy",
+ "//aos:init",
+ "//aos/events:shm_event_loop",
+ "//aos/seasocks:seasocks_logger",
+ "//third_party/seasocks",
+ "@com_github_google_flatbuffers//:flatbuffers",
],
)
diff --git a/aos/network/message_bridge_client.fbs b/aos/network/message_bridge_client.fbs
index 58b653a..df3f02f 100644
--- a/aos/network/message_bridge_client.fbs
+++ b/aos/network/message_bridge_client.fbs
@@ -13,6 +13,11 @@
// Number of packets received on all channels.
received_packets:uint;
+ // This is the measured monotonic offset for just the server -> client
+ // direction measured in nanoseconds. Subtract this from our monotonic time
+ // to get their monotonic time.
+ monotonic_offset:int64;
+
// TODO(austin): Per channel counts?
}
diff --git a/aos/network/message_bridge_client_lib.cc b/aos/network/message_bridge_client_lib.cc
index 39c3d17..263a757 100644
--- a/aos/network/message_bridge_client_lib.cc
+++ b/aos/network/message_bridge_client_lib.cc
@@ -9,6 +9,7 @@
#include "aos/network/message_bridge_client_generated.h"
#include "aos/network/message_bridge_protocol.h"
#include "aos/network/sctp_client.h"
+#include "aos/network/timestamp_generated.h"
#include "aos/unique_malloc_ptr.h"
#include "gflags/gflags.h"
#include "glog/logging.h"
@@ -26,40 +27,6 @@
namespace {
namespace chrono = std::chrono;
-aos::FlatbufferDetachedBuffer<aos::message_bridge::Connect> MakeConnectMessage(
- const Configuration *config, const Node *my_node,
- std::string_view remote_name) {
- CHECK(config->has_nodes()) << ": Config must have nodes to transfer.";
-
- flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
-
- flatbuffers::Offset<Node> node_offset = CopyFlatBuffer<Node>(my_node, &fbb);
- const std::string_view node_name = my_node->name()->string_view();
-
- std::vector<flatbuffers::Offset<Channel>> channel_offsets;
- for (const Channel *channel : *config->channels()) {
- if (channel->has_destination_nodes()) {
- for (const Connection *connection : *channel->destination_nodes()) {
- if (connection->name()->string_view() == node_name &&
- channel->source_node()->string_view() == remote_name) {
- channel_offsets.emplace_back(CopyFlatBuffer<Channel>(channel, &fbb));
- }
- }
- }
- }
-
- flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Channel>>>
- channels_offset = fbb.CreateVector(channel_offsets);
-
- Connect::Builder connect_builder(fbb);
- connect_builder.add_channels_to_transfer(channels_offset);
- connect_builder.add_node(node_offset);
- fbb.Finish(connect_builder.Finish());
-
- return fbb.Release();
-}
-
std::vector<int> StreamToChannel(const Configuration *config,
const Node *my_node, const Node *other_node) {
std::vector<int> stream_to_channel;
@@ -107,7 +74,7 @@
aos::FlatbufferDetachedBuffer<aos::logger::MessageHeader>
MakeMessageHeaderReply() {
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
logger::MessageHeader::Builder message_header_builder(fbb);
message_header_builder.add_channel_index(0);
message_header_builder.add_monotonic_sent_time(0);
@@ -120,20 +87,25 @@
}
FlatbufferDetachedBuffer<ClientStatistics> MakeClientStatistics(
- const std::vector<std::string_view> &source_node_names,
- const Configuration *configuration) {
+ const std::vector<std::string_view> &source_node_names) {
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
std::vector<flatbuffers::Offset<ClientConnection>> connection_offsets;
for (const std::string_view node_name : source_node_names) {
- flatbuffers::Offset<Node> node_offset =
- CopyFlatBuffer(configuration::GetNode(configuration, node_name), &fbb);
+ flatbuffers::Offset<flatbuffers::String> node_name_offset =
+ fbb.CreateString(node_name);
+
+ Node::Builder node_builder(fbb);
+ node_builder.add_name(node_name_offset);
+ flatbuffers::Offset<Node> node_offset = node_builder.Finish();
+
ClientConnection::Builder connection_builder(fbb);
connection_builder.add_node(node_offset);
connection_builder.add_state(State::DISCONNECTED);
// TODO(austin): Track dropped packets.
connection_builder.add_received_packets(0);
+ connection_builder.add_monotonic_offset(0);
connection_offsets.emplace_back(connection_builder.Finish());
}
flatbuffers::Offset<
@@ -266,6 +238,26 @@
chrono::nanoseconds(message_header->realtime_sent_time())),
message_header->queue_index());
+ const std::chrono::nanoseconds offset =
+ sender->monotonic_sent_time() -
+ aos::monotonic_clock::time_point(
+ chrono::nanoseconds(message_header->monotonic_sent_time()));
+
+ // If this is our first observation, use that to seed the base offset. That
+ // gets us in the ballpark.
+ if (!filter_.has_sample()) {
+ filter_.set_base_offset(offset);
+ }
+
+ // We can now measure the latency!
+ filter_.Sample(sender->monotonic_sent_time(), offset);
+
+ connection_->mutate_monotonic_offset(
+ (chrono::duration_cast<chrono::nanoseconds>(
+ chrono::duration<double>(filter_.offset())) +
+ filter_.base_offset())
+ .count());
+
if (stream_reply_with_timestamp_[stream]) {
// TODO(austin): Send back less if we are only acking. Maybe only a
// stream id? Nothing if we are only forwarding?
@@ -316,8 +308,9 @@
sender_(event_loop_->MakeSender<ClientStatistics>("/aos")),
source_node_names_(configuration::SourceNodeNames(
event_loop->configuration(), event_loop->node())),
- statistics_(MakeClientStatistics(source_node_names_,
- event_loop->configuration())) {
+ statistics_(MakeClientStatistics(source_node_names_)) {
+ client_connection_offsets_.reserve(
+ statistics_.message().connections()->size());
std::string_view node_name = event_loop->node()->name()->string_view();
// Find all the channels which are supposed to be delivered to us.
@@ -355,10 +348,54 @@
// And kick it all off.
statistics_timer_ = event_loop_->AddTimer([this]() { SendStatistics(); });
event_loop_->OnRun([this]() {
- statistics_timer_->Setup(event_loop_->monotonic_now() + chrono::seconds(1),
- chrono::seconds(1));
+ statistics_timer_->Setup(
+ event_loop_->monotonic_now() + chrono::milliseconds(100),
+ chrono::milliseconds(100));
});
}
+void MessageBridgeClient::SendStatistics() {
+ // Copy from statistics_ and drop monotonic_offset if it isn't populated yet.
+ // There doesn't exist a good way to drop fields otherwise.
+ aos::Sender<ClientStatistics>::Builder builder = sender_.MakeBuilder();
+ client_connection_offsets_.clear();
+
+ for (const ClientConnection *connection :
+ *statistics_.message().connections()) {
+ flatbuffers::Offset<flatbuffers::String> node_name_offset =
+ builder.fbb()->CreateString(connection->node()->name()->string_view());
+ Node::Builder node_builder = builder.MakeBuilder<Node>();
+ node_builder.add_name(node_name_offset);
+ flatbuffers::Offset<Node> node_offset = node_builder.Finish();
+
+ ClientConnection::Builder client_connection_builder =
+ builder.MakeBuilder<ClientConnection>();
+
+ client_connection_builder.add_node(node_offset);
+ client_connection_builder.add_state(connection->state());
+ client_connection_builder.add_received_packets(
+ connection->received_packets());
+
+ // Strip out the monotonic offset if it isn't populated.
+ if (connection->monotonic_offset() != 0) {
+ client_connection_builder.add_monotonic_offset(
+ connection->monotonic_offset());
+ }
+
+ client_connection_offsets_.emplace_back(client_connection_builder.Finish());
+ }
+
+ flatbuffers::Offset<
+ flatbuffers::Vector<flatbuffers::Offset<ClientConnection>>>
+ client_connections_offset =
+ builder.fbb()->CreateVector(client_connection_offsets_);
+
+ ClientStatistics::Builder client_statistics_builder =
+ builder.MakeBuilder<ClientStatistics>();
+ client_statistics_builder.add_connections(client_connections_offset);
+
+ builder.Send(client_statistics_builder.Finish());
+}
+
} // namespace message_bridge
} // namespace aos
diff --git a/aos/network/message_bridge_client_lib.h b/aos/network/message_bridge_client_lib.h
index 77bf2b7..2811553 100644
--- a/aos/network/message_bridge_client_lib.h
+++ b/aos/network/message_bridge_client_lib.h
@@ -7,6 +7,7 @@
#include "aos/events/logging/logger_generated.h"
#include "aos/events/shm_event_loop.h"
#include "aos/network/connect_generated.h"
+#include "aos/network/timestamp_filter.h"
#include "aos/network/message_bridge_client_generated.h"
#include "aos/network/sctp_client.h"
#include "aos/network/sctp_lib.h"
@@ -76,6 +77,9 @@
// id of the server once known. This is only valid if connection_ says
// connected.
sctp_assoc_t remote_assoc_id_ = 0;
+
+ // Filter for the timestamp offset for this connection.
+ TimestampFilter filter_;
};
// This encapsulates the state required to talk to *all* the servers from this
@@ -89,7 +93,7 @@
private:
// Sends out the statistics that are continually updated by the
// SctpClientConnections.
- void SendStatistics() { sender_.Send(statistics_); }
+ void SendStatistics();
// Event loop to schedule everything on.
aos::ShmEventLoop *event_loop_;
@@ -102,6 +106,9 @@
// Data to publish.
FlatbufferDetachedBuffer<ClientStatistics> statistics_;
+ // Reserved memory for the client connection offsets to reduce heap
+ // allocations.
+ std::vector<flatbuffers::Offset<ClientConnection>> client_connection_offsets_;
// Channels to send data over.
std::vector<std::unique_ptr<aos::RawSender>> channels_;
diff --git a/aos/network/message_bridge_protocol.cc b/aos/network/message_bridge_protocol.cc
new file mode 100644
index 0000000..9663edf
--- /dev/null
+++ b/aos/network/message_bridge_protocol.cc
@@ -0,0 +1,49 @@
+#include "aos/network/message_bridge_protocol.h"
+
+#include <string_view>
+
+#include "aos/configuration.h"
+#include "aos/flatbuffer_merge.h"
+#include "aos/flatbuffers.h"
+#include "aos/network/connect_generated.h"
+#include "flatbuffers/flatbuffers.h"
+
+namespace aos {
+namespace message_bridge {
+
+aos::FlatbufferDetachedBuffer<aos::message_bridge::Connect> MakeConnectMessage(
+ const Configuration *config, const Node *my_node,
+ std::string_view remote_name) {
+ CHECK(config->has_nodes()) << ": Config must have nodes to transfer.";
+
+ flatbuffers::FlatBufferBuilder fbb;
+ fbb.ForceDefaults(true);
+
+ flatbuffers::Offset<Node> node_offset = CopyFlatBuffer<Node>(my_node, &fbb);
+ const std::string_view node_name = my_node->name()->string_view();
+
+ std::vector<flatbuffers::Offset<Channel>> channel_offsets;
+ for (const Channel *channel : *config->channels()) {
+ if (channel->has_destination_nodes()) {
+ for (const Connection *connection : *channel->destination_nodes()) {
+ if (connection->name()->string_view() == node_name &&
+ channel->source_node()->string_view() == remote_name) {
+ channel_offsets.emplace_back(CopyFlatBuffer<Channel>(channel, &fbb));
+ }
+ }
+ }
+ }
+
+ flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Channel>>>
+ channels_offset = fbb.CreateVector(channel_offsets);
+
+ Connect::Builder connect_builder(fbb);
+ connect_builder.add_channels_to_transfer(channels_offset);
+ connect_builder.add_node(node_offset);
+ fbb.Finish(connect_builder.Finish());
+
+ return fbb.Release();
+}
+
+} // namespace message_bridge
+} // namespace aos
diff --git a/aos/network/message_bridge_protocol.h b/aos/network/message_bridge_protocol.h
index 1136188..7b6a248 100644
--- a/aos/network/message_bridge_protocol.h
+++ b/aos/network/message_bridge_protocol.h
@@ -1,6 +1,11 @@
#ifndef AOS_NETWORK_MESSAGE_BRIDGE_PROTOCOL_H_
#define AOS_NETWORK_MESSAGE_BRIDGE_PROTOCOL_H_
+#include <string_view>
+
+#include "aos/configuration.h"
+#include "aos/network/connect_generated.h"
+
namespace aos {
namespace message_bridge {
@@ -25,6 +30,11 @@
// The stream on which timestamp replies are sent.
constexpr size_t kTimestampStream() { return 1; }
+// Builds up a subscription request for my_node to remote_name.
+aos::FlatbufferDetachedBuffer<aos::message_bridge::Connect> MakeConnectMessage(
+ const Configuration *config, const Node *my_node,
+ std::string_view remote_name);
+
} // namespace message_bridge
} // namespace aos
diff --git a/aos/network/message_bridge_server.fbs b/aos/network/message_bridge_server.fbs
index 75a6a15..8f06fdb 100644
--- a/aos/network/message_bridge_server.fbs
+++ b/aos/network/message_bridge_server.fbs
@@ -22,6 +22,11 @@
// Number of packets received on all channels.
sent_packets:uint;
+ // This is the measured monotonic offset for the connected node in
+ // nanoseconds. Add this to our monotonic time to get their
+ // monotonic time.
+ monotonic_offset:int64;
+
// TODO(austin): Per channel counts?
}
diff --git a/aos/network/message_bridge_server_lib.cc b/aos/network/message_bridge_server_lib.cc
index 38958ba..f54e2bf 100644
--- a/aos/network/message_bridge_server_lib.cc
+++ b/aos/network/message_bridge_server_lib.cc
@@ -22,6 +22,7 @@
const std::vector<std::string_view> &source_node_names,
const Configuration *configuration) {
flatbuffers::FlatBufferBuilder fbb;
+ fbb.ForceDefaults(true);
std::vector<flatbuffers::Offset<ServerConnection>> connection_offsets;
for (const std::string_view node_name : source_node_names) {
@@ -32,6 +33,7 @@
connection_builder.add_state(State::DISCONNECTED);
connection_builder.add_dropped_packets(0);
connection_builder.add_sent_packets(0);
+ connection_builder.add_monotonic_offset(0);
connection_offsets.emplace_back(connection_builder.Finish());
}
flatbuffers::Offset<
@@ -78,6 +80,7 @@
// TODO(austin): I don't like allocating this buffer when we are just freeing
// it at the end of the function.
flatbuffers::FlatBufferBuilder fbb(channel_->max_size() + 100);
+ fbb.ForceDefaults(true);
VLOG(1) << "Found " << peers_.size() << " peers on channel "
<< channel_->name()->string_view() << " size " << context.size;
@@ -209,11 +212,52 @@
configuration::DestinationNodeNames(event_loop->configuration(),
event_loop->node()),
event_loop->configuration())),
+ timestamp_sender_(event_loop_->MakeSender<Timestamp>("/aos")),
+ client_statistics_fetcher_(
+ event_loop_->MakeFetcher<ClientStatistics>("/aos")),
server_("::", event_loop->node()->port()) {
CHECK(event_loop_->node() != nullptr) << ": No nodes configured.";
- // TODO(austin): Time sync. sctp gives us filtered round trip time, not
- // target time.
+ server_connection_offsets_.reserve(
+ statistics_.message().connections()->size());
+
+ int32_t max_size = 0;
+ timestamp_fetchers_.resize(event_loop->configuration()->nodes()->size());
+ filters_.resize(event_loop->configuration()->nodes()->size());
+ server_connection_.resize(event_loop->configuration()->nodes()->size());
+
+ // Seed up all the per-node connection state.
+ for (std::string_view destination_node_name :
+ configuration::DestinationNodeNames(event_loop->configuration(),
+ event_loop->node())) {
+ // Find the largest connection message so we can size our buffers big enough
+ // to receive a connection message.
+ max_size = std::max(
+ max_size,
+ static_cast<int32_t>(MakeConnectMessage(event_loop->configuration(),
+ event_loop->node(),
+ destination_node_name)
+ .size()));
+ const Node *destination_node = configuration::GetNode(
+ event_loop->configuration(), destination_node_name);
+
+ const int node_index = configuration::GetNodeIndex(
+ event_loop->configuration(), destination_node);
+
+ // Now find the timestamp channel forwarded from the other node.
+ const Channel *const other_timestamp_channel =
+ configuration::GetChannel(event_loop_->configuration(), "/aos",
+ Timestamp::GetFullyQualifiedName(),
+ event_loop_->name(), destination_node);
+
+ timestamp_fetchers_[node_index] = event_loop_->MakeFetcher<Timestamp>(
+ other_timestamp_channel->name()->string_view());
+
+ // And then find the server connection that we should be populating
+ // statistics into.
+ server_connection_[node_index] = FindServerConnection(
+ statistics_.mutable_message(), destination_node->name()->string_view());
+ }
// TODO(austin): Logging synchronization.
//
@@ -226,11 +270,16 @@
LOG(INFO) << "Hostname: " << event_loop_->node()->hostname()->string_view();
int channel_index = 0;
+ const Channel *const timestamp_channel = configuration::GetChannel(
+ event_loop_->configuration(), "/aos", Timestamp::GetFullyQualifiedName(),
+ event_loop_->name(), event_loop_->node());
+
for (const Channel *channel : *event_loop_->configuration()->channels()) {
CHECK(channel->has_source_node());
- if (channel->source_node()->string_view() ==
- event_loop_->node()->name()->string_view() &&
+
+ if (configuration::ChannelIsSendableOnNode(channel, event_loop_->node()) &&
channel->has_destination_nodes()) {
+ max_size = std::max(channel->max_size(), max_size);
std::unique_ptr<ChannelState> state(
new ChannelState{channel, channel_index});
@@ -244,13 +293,20 @@
configuration::ChannelMessageIsLoggedOnNode(channel, other_node));
}
- // Call SendData for every message.
- ChannelState *state_ptr = state.get();
- event_loop_->MakeRawWatcher(
- channel,
- [this, state_ptr](const Context &context, const void * /*message*/) {
- state_ptr->SendData(&server_, context);
- });
+ // Don't subscribe to timestamps on the timestamp channel. Those get
+ // handled specially.
+ if (channel != timestamp_channel) {
+ // Call SendData for every message.
+ ChannelState *state_ptr = state.get();
+ event_loop_->MakeRawWatcher(
+ channel, [this, state_ptr](const Context &context,
+ const void * /*message*/) {
+ state_ptr->SendData(&server_, context);
+ });
+ } else {
+ CHECK(timestamp_state_ == nullptr);
+ timestamp_state_ = state.get();
+ }
channels_.emplace_back(std::move(state));
} else {
channels_.emplace_back(nullptr);
@@ -258,10 +314,13 @@
++channel_index;
}
- statistics_timer_ = event_loop_->AddTimer([this]() { SendStatistics(); });
+ // Buffer up the max size a bit so everything fits nicely.
+ server_.SetMaxSize(max_size + 100);
+
+ statistics_timer_ = event_loop_->AddTimer([this]() { Tick(); });
event_loop_->OnRun([this]() {
- statistics_timer_->Setup(event_loop_->monotonic_now() + chrono::seconds(1),
- chrono::seconds(1));
+ statistics_timer_->Setup(event_loop_->monotonic_now() + kPingPeriod,
+ kPingPeriod);
});
}
@@ -363,5 +422,173 @@
}
}
+void MessageBridgeServer::SendStatistics() {
+ aos::Sender<ServerStatistics>::Builder builder = sender_.MakeBuilder();
+
+ server_connection_offsets_.clear();
+
+ // Copy the statistics over, but only add monotonic_offset if it is valid.
+ for (const ServerConnection *connection :
+ *statistics_.message().connections()) {
+ flatbuffers::Offset<flatbuffers::String> node_name_offset =
+ builder.fbb()->CreateString(connection->node()->name()->string_view());
+ Node::Builder node_builder = builder.MakeBuilder<Node>();
+ node_builder.add_name(node_name_offset);
+ flatbuffers::Offset<Node> node_offset = node_builder.Finish();
+
+ ServerConnection::Builder server_connection_builder =
+ builder.MakeBuilder<ServerConnection>();
+ server_connection_builder.add_node(node_offset);
+ server_connection_builder.add_state(connection->state());
+ server_connection_builder.add_dropped_packets(
+ connection->dropped_packets());
+ server_connection_builder.add_sent_packets(connection->sent_packets());
+
+ // TODO(austin): If it gets stale, drop it too.
+ if (connection->monotonic_offset() != 0) {
+ server_connection_builder.add_monotonic_offset(
+ connection->monotonic_offset());
+ }
+
+ server_connection_offsets_.emplace_back(server_connection_builder.Finish());
+ }
+
+ flatbuffers::Offset<
+ flatbuffers::Vector<flatbuffers::Offset<ServerConnection>>>
+ server_connections_offset =
+ builder.fbb()->CreateVector(server_connection_offsets_);
+
+ ServerStatistics::Builder server_statistics_builder =
+ builder.MakeBuilder<ServerStatistics>();
+ server_statistics_builder.add_connections(server_connections_offset);
+ builder.Send(server_statistics_builder.Finish());
+}
+
+void MessageBridgeServer::Tick() {
+ // Send statistics every kStatisticsPeriod. Use the context so we don't get
+ // caught up with the wakeup delay and jitter.
+ if (event_loop_->context().monotonic_event_time >=
+ last_statistics_send_time_ + kStatisticsPeriod) {
+ SendStatistics();
+ last_statistics_send_time_ = event_loop_->context().monotonic_event_time;
+ }
+
+ // The message_bridge_client application measures and filters the offsets from
+ // all messages it receives. It then sends this on in the ClientStatistics
+ // message. Collect that up and forward it back over the Timestamp message so
+ // we have guarenteed traffic on the other node for timestamping. This also
+ // moves the offsets back across the network so both directions can be
+ // observed.
+ client_statistics_fetcher_.Fetch();
+
+ // Build up the timestamp message. Do it here so that we don't have invalid
+ // data in it.
+ FlatbufferFixedAllocatorArray<Timestamp, 1000> timestamp_copy;
+ flatbuffers::FlatBufferBuilder *fbb = timestamp_copy.Builder();
+
+ if (client_statistics_fetcher_.get()) {
+ // Build up the list of client offsets.
+ std::vector<flatbuffers::Offset<ClientOffset>> client_offsets;
+
+ // Iterate through the connections this node has made.
+ for (const ClientConnection *connection :
+ *client_statistics_fetcher_->connections()) {
+ // Filter out the ones which aren't connected.
+ if (connection->state() != State::CONNECTED) continue;
+ // And the ones without monotonic offsets.
+ if (!connection->has_monotonic_offset()) continue;
+
+ const int node_index = configuration::GetNodeIndex(
+ event_loop_->configuration(),
+ connection->node()->name()->string_view());
+
+ timestamp_fetchers_[node_index].Fetch();
+
+ // Find the offset computed on their node for this client connection
+ // using their timestamp message.
+ bool has_their_offset = false;
+ std::chrono::nanoseconds their_offset = std::chrono::nanoseconds(0);
+ if (timestamp_fetchers_[node_index].get() != nullptr) {
+ for (const ClientOffset *client_offset :
+ *timestamp_fetchers_[node_index]->offsets()) {
+ if (client_offset->node()->name()->string_view() ==
+ event_loop_->node()->name()->string_view()) {
+ if (client_offset->has_monotonic_offset()) {
+ their_offset =
+ std::chrono::nanoseconds(client_offset->monotonic_offset());
+ has_their_offset = true;
+ }
+ break;
+ }
+ }
+ }
+
+ if (has_their_offset) {
+ // Update the filters.
+ if (filters_[node_index].MissingSamples()) {
+ // Update the offset the first time. This should be representative.
+ filters_[node_index].set_base_offset(
+ std::chrono::nanoseconds(connection->monotonic_offset()));
+ }
+ // The message_bridge_clients are the ones running the first filter. So
+ // set the values from that and let the averaging filter run from there.
+ filters_[node_index].FwdSet(
+ timestamp_fetchers_[node_index].context().monotonic_remote_time,
+ std::chrono::nanoseconds(connection->monotonic_offset()));
+ filters_[node_index].RevSet(
+ client_statistics_fetcher_.context().monotonic_event_time,
+ their_offset);
+
+ // Publish!
+ server_connection_[node_index]->mutate_monotonic_offset(
+ -filters_[node_index].offset().count());
+ }
+
+ // Now fill out the Timestamp message with the offset from the client.
+ flatbuffers::Offset<flatbuffers::String> node_name_offset =
+ fbb->CreateString(connection->node()->name()->string_view());
+
+ Node::Builder node_builder(*fbb);
+ node_builder.add_name(node_name_offset);
+ flatbuffers::Offset<Node> node_offset = node_builder.Finish();
+
+ ClientOffset::Builder client_offset_builder(*fbb);
+ client_offset_builder.add_node(node_offset);
+ client_offset_builder.add_monotonic_offset(
+ connection->monotonic_offset());
+ client_offsets.emplace_back(client_offset_builder.Finish());
+ }
+ flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ClientOffset>>>
+ offsets_offset = fbb->CreateVector(client_offsets);
+
+ Timestamp::Builder builder(*fbb);
+ builder.add_offsets(offsets_offset);
+ timestamp_copy.Finish(builder.Finish());
+ } else {
+ // Publish an empty timestamp if we have nothing.
+ flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ClientOffset>>>
+ offsets_offset =
+ fbb->CreateVector(std::vector<flatbuffers::Offset<ClientOffset>>{});
+ Timestamp::Builder builder(*fbb);
+ builder.add_offsets(offsets_offset);
+ timestamp_copy.Finish(builder.Finish());
+ }
+
+ // Send it out over shm, and using that timestamp, then send it out over sctp.
+ // This avoid some context switches.
+ timestamp_sender_.Send(timestamp_copy);
+
+ Context context;
+ context.monotonic_event_time = timestamp_sender_.monotonic_sent_time();
+ context.realtime_event_time = timestamp_sender_.realtime_sent_time();
+ context.queue_index = timestamp_sender_.sent_queue_index();
+ context.size = timestamp_copy.size();
+ context.data = timestamp_copy.data();
+
+ // Since we are building up the timestamp to send here, we need to trigger the
+ // SendData call ourselves.
+ timestamp_state_->SendData(&server_, context);
+}
+
} // namespace message_bridge
} // namespace aos
diff --git a/aos/network/message_bridge_server_lib.h b/aos/network/message_bridge_server_lib.h
index f202fc9..d271c8c 100644
--- a/aos/network/message_bridge_server_lib.h
+++ b/aos/network/message_bridge_server_lib.h
@@ -8,8 +8,10 @@
#include "aos/events/logging/logger_generated.h"
#include "aos/events/shm_event_loop.h"
#include "aos/network/connect_generated.h"
+#include "aos/network/message_bridge_client_generated.h"
#include "aos/network/message_bridge_server_generated.h"
#include "aos/network/sctp_server.h"
+#include "aos/network/timestamp_generated.h"
#include "glog/logging.h"
namespace aos {
@@ -105,9 +107,12 @@
// received.
void HandleData(const Message *message);
+ // Handle timestamps and statistics.
+ void Tick();
+
// Sends out the statistics that are continually updated by the
// ChannelState's.
- void SendStatistics() { sender_.Send(statistics_); }
+ void SendStatistics();
// Event loop to schedule everything on.
aos::ShmEventLoop *event_loop_;
@@ -116,9 +121,33 @@
aos::Sender<ServerStatistics> sender_;
aos::TimerHandler *statistics_timer_;
FlatbufferDetachedBuffer<ServerStatistics> statistics_;
+ std::vector<flatbuffers::Offset<ServerConnection>> server_connection_offsets_;
+
+ // Sender for the timestamps that we are forwarding over the network.
+ aos::Sender<Timestamp> timestamp_sender_;
+ // ChannelState to send timestamps over the network with.
+ ChannelState *timestamp_state_ = nullptr;
+
+ // Fetcher to grab the measured offsets in the client.
+ aos::Fetcher<ClientStatistics> client_statistics_fetcher_;
+ // All of these are indexed by the other node index.
+ // Fetcher to grab timestamps and therefore offsets from the other nodes.
+ std::vector<aos::Fetcher<Timestamp>> timestamp_fetchers_;
+ // Bidirectional filters for each connection.
+ std::vector<ClippedAverageFilter> filters_;
+ // ServerConnection to fill out the offsets for from each node.
+ std::vector<ServerConnection *> server_connection_;
SctpServer server_;
+ static constexpr std::chrono::nanoseconds kStatisticsPeriod =
+ std::chrono::seconds(1);
+ static constexpr std::chrono::nanoseconds kPingPeriod =
+ std::chrono::milliseconds(100);
+
+ aos::monotonic_clock::time_point last_statistics_send_time_ =
+ aos::monotonic_clock::min_time;
+
// List of channels. The entries that aren't sent from this node are left
// null.
std::vector<std::unique_ptr<ChannelState>> channels_;
diff --git a/aos/network/message_bridge_test.cc b/aos/network/message_bridge_test.cc
index 383c1c4..c1dd1ca 100644
--- a/aos/network/message_bridge_test.cc
+++ b/aos/network/message_bridge_test.cc
@@ -49,8 +49,12 @@
FLAGS_application_name = "pi1_message_bridge_server";
// Force ourselves to be "raspberrypi" and allocate everything.
FLAGS_override_hostname = "raspberrypi";
- aos::ShmEventLoop server_event_loop(&server_config.message());
- MessageBridgeServer message_bridge_server(&server_event_loop);
+ aos::ShmEventLoop pi1_server_event_loop(&server_config.message());
+ MessageBridgeServer pi1_message_bridge_server(&pi1_server_event_loop);
+
+ FLAGS_application_name = "pi1_message_bridge_client";
+ aos::ShmEventLoop pi1_client_event_loop(&server_config.message());
+ MessageBridgeClient pi1_message_bridge_client(&pi1_client_event_loop);
// And build the app which sends the pings.
FLAGS_application_name = "ping";
@@ -61,42 +65,41 @@
// Now do it for "raspberrypi2", the client.
FLAGS_application_name = "pi2_message_bridge_client";
FLAGS_override_hostname = "raspberrypi2";
- aos::ShmEventLoop client_event_loop(&client_config.message());
- MessageBridgeClient message_bridge_client(&client_event_loop);
+ aos::ShmEventLoop pi2_client_event_loop(&client_config.message());
+ MessageBridgeClient pi2_message_bridge_client(&pi2_client_event_loop);
+
+ FLAGS_application_name = "pi2_message_bridge_server";
+ aos::ShmEventLoop pi2_server_event_loop(&client_config.message());
+ MessageBridgeServer pi2_message_bridge_server(&pi2_server_event_loop);
// And build the app which sends the pongs.
FLAGS_application_name = "pong";
aos::ShmEventLoop pong_event_loop(&client_config.message());
+ // And build the app for testing.
+ FLAGS_application_name = "test";
+ aos::ShmEventLoop test_event_loop(&client_config.message());
+
+ aos::Fetcher<ClientStatistics> client_statistics_fetcher =
+ test_event_loop.MakeFetcher<ClientStatistics>("/aos");
+
// Count the pongs.
int pong_count = 0;
pong_event_loop.MakeWatcher(
- "/test2", [&pong_count, &ping_event_loop](const examples::Ping &ping) {
+ "/test2", [&pong_count](const examples::Ping &ping) {
++pong_count;
LOG(INFO) << "Got ping back " << FlatbufferToJson(&ping);
- if (pong_count >= 2) {
- LOG(INFO) << "That's enough bailing early.";
- // And Exit is async safe, so thread safe is easy.
- ping_event_loop.Exit();
- }
});
FLAGS_override_hostname = "";
- // Start everything up. Pong is the only thing we don't know how to wait on,
- // so start it first.
- std::thread pong_thread([&pong_event_loop]() { pong_event_loop.Run(); });
-
- std::thread server_thread(
- [&server_event_loop]() { server_event_loop.Run(); });
- std::thread client_thread(
- [&client_event_loop]() { client_event_loop.Run(); });
-
// Wait until we are connected, then send.
int ping_count = 0;
+ int pi1_server_statistics_count = 0;
ping_event_loop.MakeWatcher(
- "/aos/pi1", [&ping_count, &client_event_loop,
- &ping_sender](const ServerStatistics &stats) {
+ "/aos/pi1",
+ [&ping_count, &pi2_client_event_loop, &ping_sender,
+ &pi1_server_statistics_count](const ServerStatistics &stats) {
LOG(INFO) << FlatbufferToJson(&stats);
ASSERT_TRUE(stats.has_connections());
@@ -104,12 +107,21 @@
bool connected = false;
for (const ServerConnection *connection : *stats.connections()) {
+ // Confirm that we are estimating the server time offset correctly. It
+ // should be about 0 since we are on the same machine here.
+ if (connection->has_monotonic_offset()) {
+ EXPECT_LT(chrono::nanoseconds(connection->monotonic_offset()),
+ chrono::milliseconds(1));
+ EXPECT_GT(chrono::nanoseconds(connection->monotonic_offset()),
+ chrono::milliseconds(-1));
+ ++pi1_server_statistics_count;
+ }
+
if (connection->node()->name()->string_view() ==
- client_event_loop.node()->name()->string_view()) {
+ pi2_client_event_loop.node()->name()->string_view()) {
if (connection->state() == State::CONNECTED) {
connected = true;
}
- break;
}
}
@@ -124,29 +136,136 @@
}
});
- // Time ourselves out after a while if Pong doesn't do it for us.
+ // Confirm both client and server statistics messages have decent offsets in
+ // them.
+ int pi2_server_statistics_count = 0;
+ pong_event_loop.MakeWatcher("/aos/pi2", [&pi2_server_statistics_count](
+ const ServerStatistics &stats) {
+ LOG(INFO) << FlatbufferToJson(&stats);
+ for (const ServerConnection *connection : *stats.connections()) {
+ if (connection->has_monotonic_offset()) {
+ ++pi2_server_statistics_count;
+ // Confirm that we are estimating the server time offset correctly. It
+ // should be about 0 since we are on the same machine here.
+ EXPECT_LT(chrono::nanoseconds(connection->monotonic_offset()),
+ chrono::milliseconds(1));
+ EXPECT_GT(chrono::nanoseconds(connection->monotonic_offset()),
+ chrono::milliseconds(-1));
+ }
+ }
+ });
+
+ int pi1_client_statistics_count = 0;
+ ping_event_loop.MakeWatcher(
+ "/aos/pi1", [&pi1_client_statistics_count](const ClientStatistics &stats) {
+ LOG(INFO) << FlatbufferToJson(&stats);
+
+ for (const ClientConnection *connection : *stats.connections()) {
+ if (connection->has_monotonic_offset()) {
+ ++pi1_client_statistics_count;
+ // It takes at least 10 microseconds to send a message between the
+ // client and server. The min (filtered) time shouldn't be over 10
+ // milliseconds on localhost. This might have to bump up if this is
+ // proving flaky.
+ EXPECT_LT(chrono::nanoseconds(connection->monotonic_offset()),
+ chrono::milliseconds(10));
+ EXPECT_GT(chrono::nanoseconds(connection->monotonic_offset()),
+ chrono::microseconds(10));
+ }
+ }
+ });
+
+ int pi2_client_statistics_count = 0;
+ pong_event_loop.MakeWatcher("/aos/pi2", [&pi2_client_statistics_count](
+ const ClientStatistics &stats) {
+ LOG(INFO) << FlatbufferToJson(&stats);
+
+ for (const ClientConnection *connection : *stats.connections()) {
+ if (connection->has_monotonic_offset()) {
+ ++pi2_client_statistics_count;
+ EXPECT_LT(chrono::nanoseconds(connection->monotonic_offset()),
+ chrono::milliseconds(10));
+ EXPECT_GT(chrono::nanoseconds(connection->monotonic_offset()),
+ chrono::microseconds(10));
+ }
+ }
+ });
+
+ ping_event_loop.MakeWatcher("/aos/pi1", [](const Timestamp ×tamp) {
+ EXPECT_TRUE(timestamp.has_offsets());
+ LOG(INFO) << FlatbufferToJson(×tamp);
+ });
+ pong_event_loop.MakeWatcher("/aos/pi2", [](const Timestamp ×tamp) {
+ EXPECT_TRUE(timestamp.has_offsets());
+ LOG(INFO) << FlatbufferToJson(×tamp);
+ });
+
+ // Run for 5 seconds to make sure we have time to estimate the offset.
aos::TimerHandler *quit = ping_event_loop.AddTimer(
[&ping_event_loop]() { ping_event_loop.Exit(); });
ping_event_loop.OnRun([quit, &ping_event_loop]() {
- quit->Setup(ping_event_loop.monotonic_now() + chrono::seconds(10));
+ // Stop between timestamps, not exactly on them.
+ quit->Setup(ping_event_loop.monotonic_now() + chrono::milliseconds(5050));
});
+ // Start everything up. Pong is the only thing we don't know how to wait on,
+ // so start it first.
+ std::thread pong_thread([&pong_event_loop]() { pong_event_loop.Run(); });
+
+ std::thread pi1_server_thread(
+ [&pi1_server_event_loop]() { pi1_server_event_loop.Run(); });
+ std::thread pi1_client_thread(
+ [&pi1_client_event_loop]() { pi1_client_event_loop.Run(); });
+ std::thread pi2_client_thread(
+ [&pi2_client_event_loop]() { pi2_client_event_loop.Run(); });
+ std::thread pi2_server_thread(
+ [&pi2_server_event_loop]() { pi2_server_event_loop.Run(); });
// And go!
ping_event_loop.Run();
// Shut everyone else down
- server_event_loop.Exit();
- client_event_loop.Exit();
+ pi1_server_event_loop.Exit();
+ pi1_client_event_loop.Exit();
+ pi2_client_event_loop.Exit();
+ pi2_server_event_loop.Exit();
pong_event_loop.Exit();
- server_thread.join();
- client_thread.join();
+ pi1_server_thread.join();
+ pi1_client_thread.join();
+ pi2_client_thread.join();
+ pi2_server_thread.join();
pong_thread.join();
// Make sure we sent something.
EXPECT_GE(ping_count, 1);
// And got something back.
EXPECT_GE(pong_count, 1);
+
+ // Confirm that we are estimating a monotonic offset on the client.
+ ASSERT_TRUE(client_statistics_fetcher.Fetch());
+
+ EXPECT_EQ(client_statistics_fetcher->connections()->size(), 1u);
+ EXPECT_EQ(client_statistics_fetcher->connections()
+ ->Get(0)
+ ->node()
+ ->name()
+ ->string_view(),
+ "pi1");
+
+ // Make sure the offset in one direction is less than a second.
+ EXPECT_GT(
+ client_statistics_fetcher->connections()->Get(0)->monotonic_offset(), 0);
+ EXPECT_LT(
+ client_statistics_fetcher->connections()->Get(0)->monotonic_offset(),
+ 1000000000);
+
+ EXPECT_GE(pi1_server_statistics_count, 2);
+ EXPECT_GE(pi2_server_statistics_count, 2);
+ EXPECT_GE(pi1_client_statistics_count, 2);
+ EXPECT_GE(pi2_client_statistics_count, 2);
+
+ // TODO(austin): Need 2 servers going so we can do the round trip offset
+ // estimation.
}
} // namespace testing
diff --git a/aos/network/message_bridge_test_client.json b/aos/network/message_bridge_test_client.json
index 65d28d2..d748225 100644
--- a/aos/network/message_bridge_test_client.json
+++ b/aos/network/message_bridge_test_client.json
@@ -11,7 +11,7 @@
{
"name": "pi2",
"hostname": "raspberrypi2",
- "port": 9971
+ "port": 9972
}
]
}
diff --git a/aos/network/message_bridge_test_common.json b/aos/network/message_bridge_test_common.json
index a073869..1e5dfce 100644
--- a/aos/network/message_bridge_test_common.json
+++ b/aos/network/message_bridge_test_common.json
@@ -18,6 +18,46 @@
},
{
"name": "/aos/pi1",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi1",
+ "frequency": 10,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi2",
+ "priority": 1
+ }
+ ]
+ },
+ {
+ "name": "/aos/pi2",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi2",
+ "frequency": 10,
+ "max_size": 200,
+ "destination_nodes": [
+ {
+ "name": "pi1",
+ "priority": 1
+ }
+ ]
+ },
+ {
+ "name": "/aos/pi1_forwarded",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi2",
+ "frequency": 10,
+ "max_size": 200
+ },
+ {
+ "name": "/aos/pi2_forwarded",
+ "type": "aos.message_bridge.Timestamp",
+ "source_node": "pi1",
+ "frequency": 10,
+ "max_size": 200
+ },
+ {
+ "name": "/aos/pi1",
"type": "aos.message_bridge.ServerStatistics",
"source_node": "pi1",
"frequency": 2
@@ -90,22 +130,6 @@
]
}
],
- "applications": [
- {
- "name": "pi2_message_bridge_client",
- "maps": [
- {
- "match": {
- "name": "/test",
- "type": "aos.examples.Ping"
- },
- "rename": {
- "name": "/test2"
- }
- }
- ]
- }
- ],
"maps": [
{
"match": {
@@ -124,6 +148,34 @@
"rename": {
"name": "/aos/pi2"
}
+ },
+ {
+ "match": {
+ "name": "/test",
+ "type": "aos.examples.Ping",
+ "source_node": "pi2"
+ },
+ "rename": {
+ "name": "/test2"
+ }
+ },
+ {
+ "match": {
+ "name": "/aos/pi1",
+ "source_node": "pi2"
+ },
+ "rename": {
+ "name": "/aos/pi1_forwarded"
+ }
+ },
+ {
+ "match": {
+ "name": "/aos/pi2",
+ "source_node": "pi1"
+ },
+ "rename": {
+ "name": "/aos/pi2_forwarded"
+ }
}
]
}
diff --git a/aos/network/message_bridge_test_server.json b/aos/network/message_bridge_test_server.json
index eea92e7..35cedb6 100644
--- a/aos/network/message_bridge_test_server.json
+++ b/aos/network/message_bridge_test_server.json
@@ -11,7 +11,7 @@
{
"name": "pi2",
"hostname": "localhost",
- "port": 9971
+ "port": 9972
}
]
}
diff --git a/aos/network/sctp_client.h b/aos/network/sctp_client.h
index 926e59b..5b6df3b 100644
--- a/aos/network/sctp_client.h
+++ b/aos/network/sctp_client.h
@@ -43,6 +43,8 @@
void LogSctpStatus(sctp_assoc_t assoc_id);
+ void set_max_size(size_t max_size) { max_size_ = max_size; }
+
private:
struct sockaddr_storage sockaddr_remote_;
struct sockaddr_storage sockaddr_local_;
diff --git a/aos/network/sctp_lib.cc b/aos/network/sctp_lib.cc
index 1ed816b..5af994e 100644
--- a/aos/network/sctp_lib.cc
+++ b/aos/network/sctp_lib.cc
@@ -175,9 +175,9 @@
memset(&inmessage, 0, sizeof(struct msghdr));
aos::unique_c_ptr<Message> result(
- reinterpret_cast<Message *>(malloc(sizeof(Message) + max_size)));
+ reinterpret_cast<Message *>(malloc(sizeof(Message) + max_size + 1)));
- iov.iov_len = max_size;
+ iov.iov_len = max_size + 1;
iov.iov_base = result->mutable_data();
inmessage.msg_iov = &iov;
@@ -193,6 +193,7 @@
PCHECK((size = recvmsg(fd, &inmessage, 0)) > 0);
result->size = size;
+ CHECK_LE(size, max_size) << ": Message overflowed buffer.";
if ((MSG_NOTIFICATION & inmessage.msg_flags)) {
result->message_type = Message::kNotification;
diff --git a/aos/network/sctp_server.cc b/aos/network/sctp_server.cc
index 70d5b28..5fd9f53 100644
--- a/aos/network/sctp_server.cc
+++ b/aos/network/sctp_server.cc
@@ -63,8 +63,7 @@
PCHECK(listen(fd_, 100) == 0);
- PCHECK(setsockopt(fd_, SOL_SOCKET, SO_RCVBUF, &max_size_,
- sizeof(max_size_)) == 0);
+ SetMaxSize(1000);
}
aos::unique_c_ptr<Message> SctpServer::Read() {
diff --git a/aos/network/sctp_server.h b/aos/network/sctp_server.h
index a3086d9..b702aa8 100644
--- a/aos/network/sctp_server.h
+++ b/aos/network/sctp_server.h
@@ -46,11 +46,19 @@
void SetStreamPriority(sctp_assoc_t assoc_id, int stream_id,
uint16_t priority);
+ void SetMaxSize(size_t max_size) {
+ max_size_ = max_size;
+ // Have the kernel give us a factor of 10 more. This lets us have more than
+ // one full sized packet in flight.
+ max_size = max_size * 10;
+ PCHECK(setsockopt(fd_, SOL_SOCKET, SO_RCVBUF, &max_size,
+ sizeof(max_size)) == 0);
+ }
+
private:
struct sockaddr_storage sockaddr_local_;
int fd_;
- // TODO(austin): Configure this.
size_t max_size_ = 1000;
int ppid_ = 1;
diff --git a/aos/network/team_number.cc b/aos/network/team_number.cc
index 1b90079..14fadab 100644
--- a/aos/network/team_number.cc
+++ b/aos/network/team_number.cc
@@ -5,16 +5,15 @@
#include <unistd.h>
#include <stdlib.h>
-#include <string>
+#include "glog/logging.h"
-#include "absl/base/call_once.h"
-#include "aos/logging/logging.h"
#include "aos/util/string_to_num.h"
namespace aos {
namespace network {
-namespace internal {
-int ParseTeamNumber(const std::string &hostname, uint16_t *teamnumber) {
+namespace team_number_internal {
+
+std::optional<uint16_t> ParseRoborioTeamNumber(const std::string &hostname) {
for (size_t i = 0; i < hostname.size(); i++) {
if (hostname[i] == '-') {
const std::string num_as_s =
@@ -24,45 +23,79 @@
int num;
if (!::aos::util::StringToNumber(num_as_s, &num)) {
- return -1;
+ return std::nullopt;
}
if (hostname.substr(0, i) == "roboRIO" &&
std::to_string(num) == num_as_s) {
- *teamnumber = num;
- return 0;
- } else {
- return -1;
+ return num;
}
+ return std::nullopt;
}
}
- return -1;
+ return std::nullopt;
}
-} // namespace internal
+
+std::optional<uint16_t> ParsePiTeamNumber(const std::string &hostname) {
+ if (hostname.substr(0, 3) != "pi-") {
+ return std::nullopt;
+ }
+ size_t first_separator = hostname.find('-');
+ if (first_separator == hostname.npos ||
+ first_separator >= hostname.size() - 2) {
+ return std::nullopt;
+ }
+ ++first_separator;
+ const size_t second_separator = hostname.find('-', first_separator);
+ if (second_separator == hostname.npos) {
+ return std::nullopt;
+ }
+ const std::string number_string =
+ hostname.substr(first_separator, second_separator - first_separator);
+ int number;
+ if (!util::StringToNumber(number_string, &number)) {
+ return std::nullopt;
+ }
+ return number;
+}
+
+} // namespace team_number_internal
namespace {
uint16_t override_team;
-void DoGetTeamNumber(uint16_t *result) {
+uint16_t DoGetTeamNumber() {
if (override_team != 0) {
- *result = override_team;
- return;
+ return override_team;
}
const char *override_number = getenv("AOS_TEAM_NUMBER");
if (override_number != nullptr) {
- if (!::aos::util::StringToNumber(override_number, result)) {
- AOS_LOG(FATAL, "error parsing AOS_TEAM_NUMBER '%s'\n", override_number);
+ uint16_t result;
+ if (!::aos::util::StringToNumber(override_number, &result)) {
+ LOG(FATAL) << "Error parsing AOS_TEAM_NUMBER: " << override_number;
}
- AOS_LOG(WARNING,
- "team number overridden by AOS_TEAM_NUMBER to %" PRIu16 "\n", *result);
- } else {
- int error = internal::ParseTeamNumber(GetHostname(), result);
- if (error) {
- AOS_LOG(FATAL, "Invalid hostname %s\n", GetHostname().c_str());
- }
- AOS_LOG(INFO, "team number is %" PRIu16 "\n", *result);
+ LOG(WARNING)
+ << "Team number overriden by AOS_TEAM_NUMBER environment variable to "
+ << result;
+ return result;
}
+ const auto hostname = GetHostname();
+ {
+ const auto result = team_number_internal::ParseRoborioTeamNumber(hostname);
+ if (result) {
+ LOG(INFO) << "roboRIO hostname team number is: " << *result;
+ return *result;
+ }
+ }
+ {
+ const auto result = team_number_internal::ParsePiTeamNumber(hostname);
+ if (result) {
+ LOG(INFO) << "Pi hostname team number is: " << *result;
+ return *result;
+ }
+ }
+ LOG(FATAL) << "Failed to parse a team number from hostname: " << hostname;
}
} // namespace
@@ -70,14 +103,12 @@
::std::string GetHostname() {
char buf[256];
buf[sizeof(buf) - 1] = '\0';
- AOS_PCHECK(gethostname(buf, sizeof(buf) - 1));
+ PCHECK(gethostname(buf, sizeof(buf) - 1) == 0);
return buf;
}
uint16_t GetTeamNumber() {
- static absl::once_flag once;
- static uint16_t result;
- absl::call_once(once, DoGetTeamNumber, &result);
+ const static uint16_t result = DoGetTeamNumber();
return result;
}
diff --git a/aos/network/team_number.h b/aos/network/team_number.h
index cacc2b3..618affb 100644
--- a/aos/network/team_number.h
+++ b/aos/network/team_number.h
@@ -3,6 +3,7 @@
#include <stdint.h>
+#include <optional>
#include <string>
namespace aos {
@@ -23,9 +24,13 @@
// Guaranteed to be safe to call during static initialization time.
void OverrideTeamNumber(uint16_t team);
-namespace internal {
-int ParseTeamNumber(const std::string &hostname, uint16_t *teamnumber);
-} // namespace internal
+namespace team_number_internal {
+
+std::optional<uint16_t> ParseRoborioTeamNumber(const std::string &hostname);
+
+std::optional<uint16_t> ParsePiTeamNumber(const std::string &hostname);
+
+} // namespace team_number_internal
} // namespace network
} // namespace aos
diff --git a/aos/network/team_number_test.cc b/aos/network/team_number_test.cc
index ee3f2db..c11f0a0 100644
--- a/aos/network/team_number_test.cc
+++ b/aos/network/team_number_test.cc
@@ -2,32 +2,45 @@
#include "gtest/gtest.h"
-#include "aos/macros.h"
-
namespace aos {
namespace network {
-namespace internal {
namespace testing {
-TEST(TeamNumberTest, Parse2015TeamNumber) {
- uint16_t team_number;
- EXPECT_EQ(0, ParseTeamNumber("roboRIO-971", &team_number));
- EXPECT_EQ(971u, team_number);
+using team_number_internal::ParseRoborioTeamNumber;
+using team_number_internal::ParsePiTeamNumber;
- EXPECT_EQ(0, ParseTeamNumber("roboRIO-8971", &team_number));
- EXPECT_EQ(8971u, team_number);
+TEST(TeamNumberTest, Parse2015TeamNumber) {
+ EXPECT_EQ(971u, *ParseRoborioTeamNumber("roboRIO-971"));
+
+ EXPECT_EQ(8971u, ParseRoborioTeamNumber("roboRIO-8971"));
+
+ EXPECT_FALSE(ParseRoborioTeamNumber("abc"));
+ EXPECT_FALSE(ParseRoborioTeamNumber("roboRIO-8abc"));
+ EXPECT_FALSE(ParseRoborioTeamNumber("roboRIO-"));
}
TEST(TeamNumberTest, Parse2016TeamNumber) {
- uint16_t team_number;
- EXPECT_EQ(0, ParseTeamNumber("roboRIO-971-FRC", &team_number));
- EXPECT_EQ(971u, team_number);
+ EXPECT_EQ(971u, *ParseRoborioTeamNumber("roboRIO-971-FRC"));
- EXPECT_EQ(0, ParseTeamNumber("roboRIO-8971-FRC", &team_number));
- EXPECT_EQ(8971u, team_number);
+ EXPECT_EQ(8971u, *ParseRoborioTeamNumber("roboRIO-8971-FRC"));
+
+ EXPECT_FALSE(ParseRoborioTeamNumber("roboRIO-8abc-FRC"));
+ EXPECT_FALSE(ParseRoborioTeamNumber("roboRIO-8971-FRC2"));
+ EXPECT_FALSE(ParseRoborioTeamNumber("roboRIO-8971-2FRC"));
+ EXPECT_FALSE(ParseRoborioTeamNumber("roboRIO--FRC"));
+}
+
+TEST(TeamNumberTest, ParsePiTeamNumber) {
+ EXPECT_EQ(971u, *ParsePiTeamNumber("pi-971-1"));
+ EXPECT_EQ(8971u, *ParsePiTeamNumber("pi-8971-22"));
+ EXPECT_EQ(8971u, *ParsePiTeamNumber("pi-8971-"));
+
+ EXPECT_FALSE(ParseRoborioTeamNumber("pi"));
+ EXPECT_FALSE(ParseRoborioTeamNumber("pi-"));
+ EXPECT_FALSE(ParseRoborioTeamNumber("pi-971"));
+ EXPECT_FALSE(ParseRoborioTeamNumber("pi-971a-1"));
}
} // namespace testing
-} // namespace internal
} // namespace network
} // namespace aos
diff --git a/aos/network/timestamp.fbs b/aos/network/timestamp.fbs
new file mode 100644
index 0000000..299ecaf
--- /dev/null
+++ b/aos/network/timestamp.fbs
@@ -0,0 +1,15 @@
+include "aos/configuration.fbs";
+
+namespace aos.message_bridge;
+
+table ClientOffset {
+ node:Node;
+
+ monotonic_offset:int64;
+}
+
+table Timestamp {
+ offsets:[ClientOffset];
+}
+
+root_type Timestamp;
diff --git a/aos/network/timestamp_filter.h b/aos/network/timestamp_filter.h
index b4280f6..6f2baca 100644
--- a/aos/network/timestamp_filter.h
+++ b/aos/network/timestamp_filter.h
@@ -24,6 +24,18 @@
// much about precision when solving for the global offset.
class TimestampFilter {
public:
+ // Forces the offset and time to the provided sample without filtering. Used
+ // for syncing with a remote filter calculation.
+ void Set(aos::monotonic_clock::time_point monotonic_now,
+ std::chrono::nanoseconds sample_ns) {
+ const double sample =
+ std::chrono::duration_cast<std::chrono::duration<double>>(sample_ns -
+ base_offset_)
+ .count();
+ offset_ = sample;
+ last_time_ = monotonic_now;
+ }
+
// Updates with a new sample. monotonic_now is the timestamp of the sample on
// the destination node, and sample_ns is destination_time - source_time.
void Sample(aos::monotonic_clock::time_point monotonic_now,
@@ -132,6 +144,13 @@
}
}
+ // Sets the forward sample without filtering. See FwdSample for more details.
+ void FwdSet(aos::monotonic_clock::time_point monotonic_now,
+ std::chrono::nanoseconds sample_ns) {
+ fwd_.Set(monotonic_now, sample_ns);
+ Update(monotonic_now, &last_fwd_time_);
+ }
+
// Adds a forward sample. sample_ns = destination - source; Forward samples
// are from A -> B.
void FwdSample(aos::monotonic_clock::time_point monotonic_now,
@@ -156,6 +175,13 @@
}
}
+ // Sets the forward sample without filtering. See FwdSample for more details.
+ void RevSet(aos::monotonic_clock::time_point monotonic_now,
+ std::chrono::nanoseconds sample_ns) {
+ rev_.Set(monotonic_now, sample_ns);
+ Update(monotonic_now, &last_rev_time_);
+ }
+
// Adds a reverse sample. sample_ns = destination - source; Reverse samples
// are B -> A.
void RevSample(aos::monotonic_clock::time_point monotonic_now,
@@ -214,6 +240,11 @@
last_rev_time_ = aos::monotonic_clock::min_time;
}
+ bool MissingSamples() {
+ return (last_fwd_time_ == aos::monotonic_clock::min_time) ||
+ (last_rev_time_ == aos::monotonic_clock::min_time);
+ }
+
private:
// Updates the offset estimate given the current time, and a pointer to the
// variable holding the last time.
@@ -226,7 +257,6 @@
const double hard_max = fwd_.offset();
const double hard_min = -rev_.offset();
const double average = (hard_max + hard_min) / 2.0;
- LOG(INFO) << "max " << hard_max << " min " << hard_min;
// We don't want to clip the offset to the hard min/max. We really want to
// keep it within a band around the middle. ratio of 0.5 means stay within
// +- 0.25 of the middle of the hard min and max.
diff --git a/frc971/config/BUILD b/frc971/config/BUILD
index 0f4f7ba..098d1f8 100644
--- a/frc971/config/BUILD
+++ b/frc971/config/BUILD
@@ -8,6 +8,7 @@
srcs = ["setup_roborio.sh"],
data = [
":rio_robotCommand",
+ ":sctp.ko",
"@arm_frc_linux_gnueabi_repo//:compiler_pieces",
],
visibility = ["//visibility:public"],
diff --git a/frc971/config/sctp.ko b/frc971/config/sctp.ko
new file mode 100644
index 0000000..81e41ec
--- /dev/null
+++ b/frc971/config/sctp.ko
Binary files differ
diff --git a/frc971/config/setup_roborio.sh b/frc971/config/setup_roborio.sh
index 8c2be86..f288df8 100755
--- a/frc971/config/setup_roborio.sh
+++ b/frc971/config/setup_roborio.sh
@@ -34,6 +34,15 @@
ssh "admin@${ROBOT_HOSTNAME}" ln -s /media/sda1/aos_log-current robot_code/aos_log-current
fi
+if [[ "$(ssh admin@${ROBOT_HOSTNAME} uname -r)" != "4.14.87-rt49-cg-7.0.0f0-xilinx-zynq-189" ]]; then
+ echo "Target roboRIO has the wrong kernel"
+ exit 1
+fi
+
+ssh "admin@${ROBOT_HOSTNAME}" mkdir "/lib/modules/4.14.87-rt49-cg-7.0.0f0-xilinx-zynq-189/kernel/net/sctp/"
+scp frc971/config/sctp.ko "admin@${ROBOT_HOSTNAME}:/lib/modules/4.14.87-rt49-cg-7.0.0f0-xilinx-zynq-189/kernel/net/sctp/sctp.ko"
+ssh "admin@${ROBOT_HOSTNAME}" depmod
+
# This fails if the code isn't running.
ssh "admin@${ROBOT_HOSTNAME}" 'PATH="${PATH}":/usr/local/natinst/bin/ /usr/local/frc/bin/frcKillRobot.sh -r -t' || true
diff --git a/frc971/constants.h b/frc971/constants.h
index a52add2..debfb55 100644
--- a/frc971/constants.h
+++ b/frc971/constants.h
@@ -98,6 +98,8 @@
double upper;
double middle() const { return (lower_hard + upper_hard) / 2.0; }
+
+ double range() const { return upper_hard - lower_hard; }
};
} // namespace constants
diff --git a/frc971/control_loops/drivetrain/improved_down_estimator_test.cc b/frc971/control_loops/drivetrain/improved_down_estimator_test.cc
index 26b27e9..a6d02f6 100644
--- a/frc971/control_loops/drivetrain/improved_down_estimator_test.cc
+++ b/frc971/control_loops/drivetrain/improved_down_estimator_test.cc
@@ -136,7 +136,7 @@
Eigen::AngleAxis<double>(kPitch, uy) *
Eigen::AngleAxis<double>(kRoll, ux));
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
fbb.Finish(dtukf.PopulateStatus(&fbb));
aos::FlatbufferDetachedBuffer<drivetrain::DownEstimatorState> state(
diff --git a/frc971/control_loops/drivetrain/line_follow_drivetrain_test.cc b/frc971/control_loops/drivetrain/line_follow_drivetrain_test.cc
index 180af4b..c6bedf6 100644
--- a/frc971/control_loops/drivetrain/line_follow_drivetrain_test.cc
+++ b/frc971/control_loops/drivetrain/line_follow_drivetrain_test.cc
@@ -51,7 +51,7 @@
void Iterate() {
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
Goal::Builder goal_builder(fbb);
goal_builder.add_throttle(driver_model_(state_));
goal_builder.add_controller_type(freeze_target_
@@ -105,7 +105,7 @@
double GoalTheta(double x, double y, double v, double throttle) {
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
Goal::Builder goal_builder(fbb);
goal_builder.add_throttle(throttle);
fbb.Finish(goal_builder.Finish());
diff --git a/frc971/control_loops/python/angular_system.py b/frc971/control_loops/python/angular_system.py
index 36da41a..bd4447b 100755
--- a/frc971/control_loops/python/angular_system.py
+++ b/frc971/control_loops/python/angular_system.py
@@ -22,6 +22,13 @@
kalman_q_voltage,
kalman_r_position,
dt=0.00505):
+ """Constructs an AngularSystemParams object.
+
+ Args:
+ motor: Motor object with the motor constants.
+ G: float, Gear ratio. Less than 1 means output moves slower than the
+ input.
+ """
self.name = name
self.motor = motor
self.G = G
diff --git a/frc971/control_loops/python/control_loop.py b/frc971/control_loops/python/control_loop.py
index 919968e..f124852 100644
--- a/frc971/control_loops/python/control_loop.py
+++ b/frc971/control_loops/python/control_loop.py
@@ -685,3 +685,7 @@
(12.0 - self.resistance * self.free_current))
# Torque constant
self.Kt = self.stall_torque / self.stall_current
+ # Motor inertia in kg m^2
+ # Diameter of 1.9", weight of: 100 grams
+ # TODO(austin): Get a number from Scott Westbrook for the mass
+ self.motor_inertia = 0.1 * ((0.95 * 0.0254) ** 2.0)
diff --git a/frc971/wpilib/ahal/AnalogTriggerOutput.cc b/frc971/wpilib/ahal/AnalogTriggerOutput.cc
index db55efa..1bbef1b 100644
--- a/frc971/wpilib/ahal/AnalogTriggerOutput.cc
+++ b/frc971/wpilib/ahal/AnalogTriggerOutput.cc
@@ -41,3 +41,5 @@
HAL_Report(HALUsageReporting::kResourceType_AnalogTriggerOutput,
trigger.GetIndex() + 1, static_cast<uint8_t>(outputType) + 1);
}
+
+AnalogTriggerOutput::~AnalogTriggerOutput() {}
diff --git a/frc971/zeroing/imu_zeroer_test.cc b/frc971/zeroing/imu_zeroer_test.cc
index 7a74413..eee3f57 100644
--- a/frc971/zeroing/imu_zeroer_test.cc
+++ b/frc971/zeroing/imu_zeroer_test.cc
@@ -10,7 +10,7 @@
aos::FlatbufferDetachedBuffer<IMUValues> MakeMeasurement(
const Eigen::Vector3d &gyro, const Eigen::Vector3d &accel) {
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
IMUValuesBuilder builder(fbb);
builder.add_gyro_x(gyro.x());
builder.add_gyro_y(gyro.y());
diff --git a/y2019/control_loops/superstructure/collision_avoidance_tests.cc b/y2019/control_loops/superstructure/collision_avoidance_tests.cc
index 6b1f031..90b1659 100644
--- a/y2019/control_loops/superstructure/collision_avoidance_tests.cc
+++ b/y2019/control_loops/superstructure/collision_avoidance_tests.cc
@@ -31,7 +31,7 @@
FlatbufferDetachedBuffer<Goal> MakeZeroGoal() {
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
flatbuffers::Offset<StaticZeroingSingleDOFProfiledSubsystemGoal> wrist_offset;
{
@@ -71,7 +71,7 @@
FlatbufferDetachedBuffer<Status> MakeZeroStatus() {
flatbuffers::FlatBufferBuilder fbb;
- fbb.ForceDefaults(1);
+ fbb.ForceDefaults(true);
flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus> wrist_offset;
{
diff --git a/y2020/BUILD b/y2020/BUILD
index 215c760..9a233d4 100644
--- a/y2020/BUILD
+++ b/y2020/BUILD
@@ -33,6 +33,9 @@
"//frc971/control_loops:pose",
"//frc971/control_loops:static_zeroing_single_dof_profiled_subsystem",
"//y2020/control_loops/drivetrain:polydrivetrain_plants",
+ "//y2020/control_loops/superstructure/accelerator:accelerator_plants",
+ "//y2020/control_loops/superstructure/control_panel:control_panel_plants",
+ "//y2020/control_loops/superstructure/finisher:finisher_plants",
"//y2020/control_loops/superstructure/hood:hood_plants",
"//y2020/control_loops/superstructure/intake:intake_plants",
"//y2020/control_loops/superstructure/turret:turret_plants",
@@ -75,6 +78,7 @@
"//frc971/wpilib:sensor_reader",
"//frc971/wpilib:wpilib_interface",
"//frc971/wpilib:wpilib_robot_base",
+ "//third_party:phoenix",
"//third_party:wpilib",
"//y2020/control_loops/superstructure:superstructure_output_fbs",
"//y2020/control_loops/superstructure:superstructure_position_fbs",
diff --git a/y2020/constants.cc b/y2020/constants.cc
index 6f79b6b..a35798f 100644
--- a/y2020/constants.cc
+++ b/y2020/constants.cc
@@ -12,6 +12,7 @@
#include "aos/logging/logging.h"
#include "aos/mutex/mutex.h"
#include "aos/network/team_number.h"
+#include "y2020/control_loops/superstructure/control_panel/integral_control_panel_plant.h"
#include "y2020/control_loops/superstructure/hood/integral_hood_plant.h"
#include "y2020/control_loops/superstructure/intake/integral_intake_plant.h"
#include "y2020/control_loops/superstructure/turret/integral_turret_plant.h"
@@ -74,7 +75,7 @@
::frc971::zeroing::PotAndAbsoluteEncoderZeroingEstimator>
*const turret_params = &turret->subsystem_params;
- //Turret Constants
+ // Turret Constants
turret_params->zeroing_voltage = 4.0;
turret_params->operating_voltage = 12.0;
// TODO(austin): Tune these.
@@ -91,6 +92,11 @@
turret_params->zeroing_constants.moving_buffer_size = 20;
turret_params->zeroing_constants.allowable_encoder_error = 0.9;
+ CHECK_LE(hood->range.range(),
+ hood->zeroing_constants.one_revolution_distance);
+ CHECK_LE(intake->range.range(),
+ intake->zeroing_constants.one_revolution_distance);
+
switch (team) {
// A set of constants for tests.
case 1:
@@ -149,8 +155,8 @@
static ::aos::Mutex mutex;
::aos::MutexLocker locker(&mutex);
- // IMPORTANT: This declaration has to stay after the mutex is locked to avoid
- // race conditions.
+ // IMPORTANT: This declaration has to stay after the mutex is locked to
+ // avoid race conditions.
static ::std::map<uint16_t, const Values *> values;
if (values.count(team_number) == 0) {
diff --git a/y2020/constants.h b/y2020/constants.h
index d7b9b4b..ed58777 100644
--- a/y2020/constants.h
+++ b/y2020/constants.h
@@ -10,6 +10,9 @@
#include "frc971/control_loops/pose.h"
#include "frc971/control_loops/static_zeroing_single_dof_profiled_subsystem.h"
#include "y2020/control_loops/drivetrain/drivetrain_dog_motor_plant.h"
+#include "y2020/control_loops/superstructure/accelerator/accelerator_plant.h"
+#include "y2020/control_loops/superstructure/control_panel/control_panel_plant.h"
+#include "y2020/control_loops/superstructure/finisher/finisher_plant.h"
#include "y2020/control_loops/superstructure/hood/hood_plant.h"
#include "y2020/control_loops/superstructure/intake/intake_plant.h"
#include "y2020/control_loops/superstructure/turret/turret_plant.h"
@@ -24,10 +27,10 @@
static constexpr double kDrivetrainEncoderCountsPerRevolution() {
return kDrivetrainCyclesPerRevolution() * 4;
}
- static constexpr double kDrivetrainEncoderRatio() { return (24.0 / 52.0); }
+ static constexpr double kDrivetrainEncoderRatio() { return 1.0; }
static constexpr double kMaxDrivetrainEncoderPulsesPerSecond() {
return control_loops::drivetrain::kFreeSpeed / (2.0 * M_PI) *
- control_loops::drivetrain::kHighOutputRatio /
+ control_loops::drivetrain::kHighGearRatio /
constants::Values::kDrivetrainEncoderRatio() *
kDrivetrainEncoderCountsPerRevolution();
}
@@ -35,22 +38,20 @@
// Hood
static constexpr double kHoodEncoderCountsPerRevolution() { return 4096.0; }
- // TODO(sabina): Update constants
- static constexpr double kHoodEncoderRatio() { return 1.0; }
+ static constexpr double kHoodEncoderRatio() { return 8.0 / 72.0; }
static constexpr double kMaxHoodEncoderPulsesPerSecond() {
- return control_loops::superstructure::hood::kFreeSpeed *
+ return control_loops::superstructure::hood::kFreeSpeed / (2.0 * M_PI) *
control_loops::superstructure::hood::kOutputRatio /
- kHoodEncoderRatio() / (2.0 * M_PI) *
- kHoodEncoderCountsPerRevolution();
+ kHoodEncoderRatio() * kHoodEncoderCountsPerRevolution();
}
static constexpr ::frc971::constants::Range kHoodRange() {
return ::frc971::constants::Range{
- 0.00, // Back Hard
- 0.79, // Front Hard
- 0.14, // Back Soft
- 0.78 // Front Soft
+ -0.01, // Back Hard
+ 0.65, // Front Hard
+ 0.0, // Back Soft
+ 0.64 // Front Soft
};
}
@@ -64,10 +65,9 @@
static constexpr double kIntakeEncoderRatio() { return (16.0 / 32.0); }
static constexpr double kMaxIntakeEncoderPulsesPerSecond() {
- return control_loops::superstructure::intake::kFreeSpeed *
+ return control_loops::superstructure::intake::kFreeSpeed / (2.0 * M_PI) *
control_loops::superstructure::intake::kOutputRatio /
- kIntakeEncoderRatio() / (2.0 * M_PI) *
- kIntakeEncoderCountsPerRevolution();
+ kIntakeEncoderRatio() * kIntakeEncoderCountsPerRevolution();
}
// TODO(sabina): update range
@@ -84,22 +84,23 @@
::frc971::zeroing::AbsoluteEncoderZeroingEstimator>
intake;
+ static constexpr double kIntakeRollerSupplyCurrentLimit() { return 30.0; }
+ static constexpr double kIntakeRollerStatorCurrentLimit() { return 40.0; }
+
// Turret
static constexpr double kTurretEncoderCountsPerRevolution() { return 4096.0; }
static constexpr double kTurretEncoderRatio() {
- return 1.0; // TODO (Kai): Get Gear Ratios when ready
+ return (26.0 / 150.0) * (130.0 / 40.0);
}
static constexpr double kMaxTurretEncoderPulsesPerSecond() {
- return control_loops::superstructure::turret::kFreeSpeed *
+ return control_loops::superstructure::turret::kFreeSpeed / (2.0 * M_PI) *
control_loops::superstructure::turret::kOutputRatio /
- kTurretEncoderRatio() / (2.0 * M_PI) *
- kTurretEncoderCountsPerRevolution();
+ kTurretEncoderRatio() * kTurretEncoderCountsPerRevolution();
}
- // TODO(austin): Figure out the actual constant here.
- static constexpr double kTurretPotRatio() { return 1.0; }
+ static constexpr double kTurretPotRatio() { return (26.0 / 150.0); }
static constexpr ::frc971::constants::Range kTurretRange() {
return ::frc971::constants::Range{
@@ -119,6 +120,55 @@
};
PotAndAbsEncoderConstants turret;
+
+ // Control Panel
+
+ // Mag encoder
+ static constexpr double kControlPanelEncoderCountsPerRevolution() {
+ return 4096.0;
+ }
+
+ // Ratio is encoder to output
+ static constexpr double kControlPanelEncoderRatio() { return (56.0 / 28.0); }
+
+ static constexpr double kMaxControlPanelEncoderPulsesPerSecond() {
+ return control_loops::superstructure::control_panel::kFreeSpeed /
+ (2.0 * M_PI) *
+ control_loops::superstructure::control_panel::kOutputRatio /
+ kControlPanelEncoderRatio() *
+ kControlPanelEncoderCountsPerRevolution();
+ }
+
+ // Shooter
+ static constexpr double kFinisherEncoderCountsPerRevolution() {
+ return 4096.0;
+ }
+ static constexpr double kFinisherEncoderRatio() { return 30.0 / 40.0; }
+
+ static constexpr double kMaxFinisherEncoderPulsesPerSecond() {
+ return control_loops::superstructure::finisher::kFreeSpeed / (2.0 * M_PI) *
+ control_loops::superstructure::finisher::kOutputRatio /
+ kFinisherEncoderRatio() * kFinisherEncoderCountsPerRevolution();
+ }
+
+
+ static constexpr double kAcceleratorEncoderCountsPerRevolution() {
+ return 4096.0;
+ }
+ static constexpr double kAcceleratorEncoderRatio() {
+ return (1.2 * 1.2 * 1.2) * (30.0 / 40.0);
+ }
+
+ static constexpr double kMaxAcceleratorEncoderPulsesPerSecond() {
+ return control_loops::superstructure::accelerator::kFreeSpeed /
+ (2.0 * M_PI) *
+ control_loops::superstructure::accelerator::kOutputRatio /
+ kAcceleratorEncoderRatio() *
+ kAcceleratorEncoderCountsPerRevolution();
+ }
+
+ // Climber
+ static constexpr double kClimberSupplyCurrentLimit() { return 60.0; }
};
// Creates (once) a Values instance for ::aos::network::GetTeamNumber() and
diff --git a/y2020/control_loops/python/accelerator.py b/y2020/control_loops/python/accelerator.py
index 257ed23..64ae12e 100644
--- a/y2020/control_loops/python/accelerator.py
+++ b/y2020/control_loops/python/accelerator.py
@@ -13,22 +13,32 @@
gflags.DEFINE_bool('plot', False, 'If true, plot the loop response.')
+# Inertia for a single 4" diameter, 1" wide neopreme wheel.
+J_wheel = 0.000319
+# Gear ratio between wheels (speed up!)
+G_per_wheel = 1.2
+# Gear ratio to the final wheel.
+G = (30.0 / 40.0) * numpy.power(G_per_wheel, 3.0)
+# Overall flywheel inertia.
+J = J_wheel * (
+ 1.0 + numpy.power(G, -2.0) + numpy.power(G, -4.0) + numpy.power(G, -6.0))
+
+# The position and velocity are measured for the final wheel.
kAccelerator = flywheel.FlywheelParams(
name='Accelerator',
motor=control_loop.Falcon(),
- G=1.0,
- J=0.006,
+ G=G,
+ J=J,
q_pos=0.08,
q_vel=4.00,
- q_voltage=0.3,
+ q_voltage=0.4,
r_pos=0.05,
- controller_poles=[.87],
- dt=0.00505)
+ controller_poles=[.80])
def main(argv):
if FLAGS.plot:
- R = numpy.matrix([[0.0], [100.0], [0.0]])
+ R = numpy.matrix([[0.0], [500.0], [0.0]])
flywheel.PlotSpinup(kAccelerator, goal=R, iterations=200)
return 0
diff --git a/y2020/control_loops/python/control_panel.py b/y2020/control_loops/python/control_panel.py
index 206a39e..e02aa13 100644
--- a/y2020/control_loops/python/control_panel.py
+++ b/y2020/control_loops/python/control_panel.py
@@ -17,12 +17,11 @@
except gflags.DuplicateFlagError:
pass
-#TODO(sabina): update moment
kControlPanel = angular_system.AngularSystemParams(
name='ControlPanel',
motor=control_loop.BAG(),
- G=(1.0),
- J=0.3,
+ G=1.0,
+ J=0.000009,
q_pos=0.20,
q_vel=5.0,
kalman_q_pos=0.12,
diff --git a/y2020/control_loops/python/drivetrain.py b/y2020/control_loops/python/drivetrain.py
index 54745dd..95b9fcf 100644
--- a/y2020/control_loops/python/drivetrain.py
+++ b/y2020/control_loops/python/drivetrain.py
@@ -1,6 +1,7 @@
#!/usr/bin/python
from frc971.control_loops.python import drivetrain
+from frc971.control_loops.python import control_loop
import sys
import gflags
@@ -11,19 +12,20 @@
gflags.DEFINE_bool('plot', False, 'If true, plot the loop response.')
kDrivetrain = drivetrain.DrivetrainParams(
- J=1.5,
- mass=38.5,
- robot_radius=0.45 / 2.0,
- wheel_radius=4.0 * 0.0254 / 2.0,
- G=9.0 / 52.0,
+ J=6.0,
+ mass=68.0,
+ # TODO(austin): Measure radius a bit better.
+ robot_radius=0.7 / 2.0,
+ wheel_radius=6.0 * 0.0254 / 2.0,
+ motor_type=control_loop.Falcon(),
+ G=(8.0 / 70.0) * (17.0 / 24.0),
q_pos=0.14,
q_vel=1.30,
efficiency=0.80,
has_imu=True,
force=True,
kf_q_voltage=13.0,
- controller_poles=[0.82, 0.82],
- robot_cg_offset=0.0)
+ controller_poles=[0.82, 0.82])
def main(argv):
@@ -38,5 +40,6 @@
# Write the generated constants out to a file.
drivetrain.WriteDrivetrain(argv[1:3], argv[3:5], 'y2020', kDrivetrain)
+
if __name__ == '__main__':
sys.exit(main(sys.argv))
diff --git a/y2020/control_loops/python/finisher.py b/y2020/control_loops/python/finisher.py
index 0b0fbb4..0d46bb9 100644
--- a/y2020/control_loops/python/finisher.py
+++ b/y2020/control_loops/python/finisher.py
@@ -13,22 +13,32 @@
gflags.DEFINE_bool('plot', False, 'If true, plot the loop response.')
+# Inertia for a single 4" diameter, 2" wide neopreme wheel.
+J_wheel = 0.000319 * 2.0
+# Gear ratio to the final wheel.
+# 40 tooth on the flywheel
+# 48 for the falcon.
+# 60 tooth on the outer wheel.
+G = 48.0 / 40.0
+# Overall flywheel inertia.
+J = J_wheel * (1.0 + (40.0 / 60.0)**2.0)
+
+# The position and velocity are measured for the final wheel.
kFinisher = flywheel.FlywheelParams(
name='Finisher',
motor=control_loop.Falcon(),
- G=1.0,
- J=0.006,
+ G=G,
+ J=J,
q_pos=0.08,
q_vel=4.00,
- q_voltage=0.3,
+ q_voltage=0.4,
r_pos=0.05,
- controller_poles=[.87],
- dt=0.00505)
+ controller_poles=[.80])
def main(argv):
if FLAGS.plot:
- R = numpy.matrix([[0.0], [100.0], [0.0]])
+ R = numpy.matrix([[0.0], [500.0], [0.0]])
flywheel.PlotSpinup(params=kFinisher, goal=R, iterations=200)
return 0
diff --git a/y2020/control_loops/python/flywheel.py b/y2020/control_loops/python/flywheel.py
index 9a6fc46..630d223 100755
--- a/y2020/control_loops/python/flywheel.py
+++ b/y2020/control_loops/python/flywheel.py
@@ -214,7 +214,7 @@
offset.append(observer_flywheel.X_hat[2, 0])
applied_U = U.copy()
- if i > 30:
+ if i > 100:
applied_U += 2
flywheel.Update(applied_U)
@@ -273,10 +273,10 @@
loop_writer = control_loop.ControlLoopWriter(
name, flywheels, namespaces=namespace)
loop_writer.AddConstant(
- control_loop.Constant('kOutputRatio' + params.name, '%f',
+ control_loop.Constant('kOutputRatio', '%f',
flywheels[0].G))
loop_writer.AddConstant(
- control_loop.Constant('kFreeSpeed' + params.name, '%f',
+ control_loop.Constant('kFreeSpeed', '%f',
flywheels[0].motor.free_speed))
loop_writer.Write(plant_files[0], plant_files[1])
diff --git a/y2020/control_loops/python/hood.py b/y2020/control_loops/python/hood.py
index 8c99940..b4d8c84 100644
--- a/y2020/control_loops/python/hood.py
+++ b/y2020/control_loops/python/hood.py
@@ -17,29 +17,33 @@
except gflags.DuplicateFlagError:
pass
-'''
-Hood is an angular subsystem due to the mounting of the encoder on the hood joint.
-We are currently electing to ignore potential non-linearity.
-'''
-#TODO: update constants
+# Hood is an angular subsystem due to the mounting of the encoder on the hood
+# joint. We are currently electing to ignore potential non-linearity.
+
+range_of_travel_radians = (37.0 * numpy.pi / 180.0)
+# 0.083 inches/turn
+# 6.38 inches of travel
+turns_of_leadscrew_per_range_of_travel = 6.38 / 0.083
+
+radians_per_turn = range_of_travel_radians / turns_of_leadscrew_per_range_of_travel
+
kHood = angular_system.AngularSystemParams(
name='Hood',
motor=control_loop.BAG(),
- # meters / rad (used the displacement of the lead screw and the angle)
- G=(0.1601 / 0.6457),
- J=0.3,
- q_pos=0.20,
- q_vel=5.0,
+ G=radians_per_turn,
+ J=0.08389,
+ q_pos=0.55,
+ q_vel=14.0,
kalman_q_pos=0.12,
kalman_q_vel=2.0,
- kalman_q_voltage=4.0,
+ kalman_q_voltage=2.5,
kalman_r_position=0.05)
def main(argv):
if FLAGS.plot:
- R = numpy.matrix([[numpy.pi / 2.0], [0.0]])
+ R = numpy.matrix([[numpy.pi / 4.0], [0.0]])
angular_system.PlotKick(kHood, R)
angular_system.PlotMotion(kHood, R)
diff --git a/y2020/control_loops/python/intake.py b/y2020/control_loops/python/intake.py
index f65679a..9e80244 100644
--- a/y2020/control_loops/python/intake.py
+++ b/y2020/control_loops/python/intake.py
@@ -17,14 +17,13 @@
except gflags.DuplicateFlagError:
pass
-#TODO: update constants
kIntake = angular_system.AngularSystemParams(
name='Intake',
motor=control_loop.BAG(),
- G=(1.0 / 1.0),
- J=0.3,
- q_pos=0.20,
- q_vel=5.0,
+ G=(12.0 / 24.0) * (1.0 / 7.0) * (1.0 / 7.0) * (16.0 / 32.0),
+ J=3.0 * 0.139 * 0.139,
+ q_pos=0.40,
+ q_vel=20.0,
kalman_q_pos=0.12,
kalman_q_vel=2.0,
kalman_q_voltage=4.0,
diff --git a/y2020/control_loops/python/turret.py b/y2020/control_loops/python/turret.py
index 58ea7d8..e276457 100644
--- a/y2020/control_loops/python/turret.py
+++ b/y2020/control_loops/python/turret.py
@@ -21,18 +21,21 @@
kTurret = angular_system.AngularSystemParams(
name='Turret',
motor=control_loop.Vex775Pro(),
- #TODO: Update Gear Ratios when they are ready
- G=(6.0 / 60.0) * (20.0 / 100.0) * (24.0 / 84.0),
- #TODO: Get number from Bryan (moment of inertia)
- J=0.30,
- q_pos=0.20,
- q_vel=5.0,
+ G=(6.0 / 60.0) * (26.0 / 150.0),
+ J=0.11,
+ q_pos=0.40,
+ q_vel=7.0,
kalman_q_pos=0.12,
kalman_q_vel=2.0,
- kalman_q_voltage=4.0,
+ kalman_q_voltage=3.0,
kalman_r_position=0.05)
def main(argv):
+ if FLAGS.plot:
+ R = numpy.matrix([[numpy.pi], [0.0]])
+ angular_system.PlotKick(kTurret, R)
+ angular_system.PlotMotion(kTurret, R)
+
# Write the generated constants out to a file.
if len(argv) != 5:
glog.fatal(
diff --git a/y2020/control_loops/superstructure/BUILD b/y2020/control_loops/superstructure/BUILD
index 87ee689..631a21d 100644
--- a/y2020/control_loops/superstructure/BUILD
+++ b/y2020/control_loops/superstructure/BUILD
@@ -94,6 +94,7 @@
":superstructure_status_fbs",
"//aos:math",
"//aos/controls:control_loop_test",
+ "//aos/events/logging:logger",
"//aos/testing:googletest",
"//aos/time",
"//frc971/control_loops:capped_test_plant",
diff --git a/y2020/control_loops/superstructure/accelerator/BUILD b/y2020/control_loops/superstructure/accelerator/BUILD
index 2c680bf..30fca08 100644
--- a/y2020/control_loops/superstructure/accelerator/BUILD
+++ b/y2020/control_loops/superstructure/accelerator/BUILD
@@ -7,11 +7,28 @@
outs = [
"accelerator_plant.h",
"accelerator_plant.cc",
- "accelerator_integral_plant.h",
- "accelerator_integral_plant.cc",
+ "integral_accelerator_plant.h",
+ "integral_accelerator_plant.cc",
],
cmd = "$(location //y2020/control_loops/python:accelerator) $(OUTS)",
tools = [
"//y2020/control_loops/python:accelerator",
],
)
+
+cc_library(
+ name = "accelerator_plants",
+ srcs = [
+ "accelerator_plant.cc",
+ "integral_accelerator_plant.cc",
+ ],
+ hdrs = [
+ "accelerator_plant.h",
+ "integral_accelerator_plant.h",
+ ],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//frc971/control_loops:hybrid_state_feedback_loop",
+ "//frc971/control_loops:state_feedback_loop",
+ ],
+)
diff --git a/y2020/control_loops/superstructure/finisher/BUILD b/y2020/control_loops/superstructure/finisher/BUILD
index b9bfc4f..2009886 100644
--- a/y2020/control_loops/superstructure/finisher/BUILD
+++ b/y2020/control_loops/superstructure/finisher/BUILD
@@ -7,11 +7,28 @@
outs = [
"finisher_plant.h",
"finisher_plant.cc",
- "finisher_integral_plant.h",
- "finisher_integral_plant.cc",
+ "integral_finisher_plant.h",
+ "integral_finisher_plant.cc",
],
cmd = "$(location //y2020/control_loops/python:finisher) $(OUTS)",
tools = [
"//y2020/control_loops/python:finisher",
],
)
+
+cc_library(
+ name = "finisher_plants",
+ srcs = [
+ "finisher_plant.cc",
+ "integral_finisher_plant.cc",
+ ],
+ hdrs = [
+ "finisher_plant.h",
+ "integral_finisher_plant.h",
+ ],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//frc971/control_loops:hybrid_state_feedback_loop",
+ "//frc971/control_loops:state_feedback_loop",
+ ],
+)
diff --git a/y2020/control_loops/superstructure/shooter/BUILD b/y2020/control_loops/superstructure/shooter/BUILD
index d63231b..fe8a29a 100644
--- a/y2020/control_loops/superstructure/shooter/BUILD
+++ b/y2020/control_loops/superstructure/shooter/BUILD
@@ -4,18 +4,6 @@
cc_library(
name = "shooter_plants",
- srcs = [
- "//y2020/control_loops/superstructure/accelerator:accelerator_integral_plant.cc",
- "//y2020/control_loops/superstructure/accelerator:accelerator_plant.cc",
- "//y2020/control_loops/superstructure/finisher:finisher_integral_plant.cc",
- "//y2020/control_loops/superstructure/finisher:finisher_plant.cc",
- ],
- hdrs = [
- "//y2020/control_loops/superstructure/accelerator:accelerator_integral_plant.h",
- "//y2020/control_loops/superstructure/accelerator:accelerator_plant.h",
- "//y2020/control_loops/superstructure/finisher:finisher_integral_plant.h",
- "//y2020/control_loops/superstructure/finisher:finisher_plant.h",
- ],
deps = [
"//frc971/control_loops:state_feedback_loop",
],
@@ -49,10 +37,11 @@
"flywheel_controller.h",
],
deps = [
- ":shooter_plants",
"//aos/controls:control_loop",
"//frc971/control_loops:profiled_subsystem",
"//y2020/control_loops/superstructure:superstructure_goal_fbs",
"//y2020/control_loops/superstructure:superstructure_status_fbs",
+ "//y2020/control_loops/superstructure/accelerator:accelerator_plants",
+ "//y2020/control_loops/superstructure/finisher:finisher_plants",
],
)
diff --git a/y2020/control_loops/superstructure/shooter/flywheel_controller.h b/y2020/control_loops/superstructure/shooter/flywheel_controller.h
index 20cd681..0d162c4 100644
--- a/y2020/control_loops/superstructure/shooter/flywheel_controller.h
+++ b/y2020/control_loops/superstructure/shooter/flywheel_controller.h
@@ -6,8 +6,8 @@
#include "aos/controls/control_loop.h"
#include "aos/time/time.h"
#include "frc971/control_loops/state_feedback_loop.h"
-#include "y2020/control_loops/superstructure/accelerator/accelerator_integral_plant.h"
-#include "y2020/control_loops/superstructure/finisher/finisher_integral_plant.h"
+#include "y2020/control_loops/superstructure/accelerator/integral_accelerator_plant.h"
+#include "y2020/control_loops/superstructure/finisher/integral_finisher_plant.h"
#include "y2020/control_loops/superstructure/superstructure_status_generated.h"
namespace y2020 {
diff --git a/y2020/control_loops/superstructure/shooter_plot.pb b/y2020/control_loops/superstructure/shooter_plot.pb
new file mode 100644
index 0000000..01a1e20
--- /dev/null
+++ b/y2020/control_loops/superstructure/shooter_plot.pb
@@ -0,0 +1,44 @@
+channel {
+ name: "/superstructure"
+ type: "y2020.control_loops.superstructure.Status"
+ alias: "Status"
+}
+channel {
+ name: "/superstructure"
+ type: "y2020.control_loops.superstructure.Output"
+ alias: "Output"
+}
+channel {
+ name: "/superstructure"
+ type: "y2020.control_loops.superstructure.Position"
+ alias: "Position"
+}
+channel {
+ name: "/superstructure"
+ type: "y2020.control_loops.superstructure.Goal"
+ alias: "Goal"
+}
+
+figure {
+ axes {
+ line {
+ y_signal {
+ channel: "Status"
+ field: "hood.position"
+ }
+ }
+ line {
+ y_signal {
+ channel: "Goal"
+ field: "hood.unsafe_goal"
+ }
+ }
+ line {
+ y_signal {
+ channel: "Position"
+ field: "hood.encoder"
+ }
+ }
+ ylabel: "hood position"
+ }
+}
diff --git a/y2020/control_loops/superstructure/superstructure_lib_test.cc b/y2020/control_loops/superstructure/superstructure_lib_test.cc
index 458a771..fe69479 100644
--- a/y2020/control_loops/superstructure/superstructure_lib_test.cc
+++ b/y2020/control_loops/superstructure/superstructure_lib_test.cc
@@ -4,6 +4,7 @@
#include <memory>
#include "aos/controls/control_loop_test.h"
+#include "aos/events/logging/logger.h"
#include "frc971/control_loops/capped_test_plant.h"
#include "frc971/control_loops/position_sensor_sim.h"
#include "frc971/control_loops/team_number_test_environment.h"
@@ -13,6 +14,9 @@
#include "y2020/control_loops/superstructure/intake/intake_plant.h"
#include "y2020/control_loops/superstructure/superstructure.h"
+DEFINE_string(output_file, "",
+ "If set, logs all channels to the provided logfile.");
+
namespace y2020 {
namespace control_loops {
namespace superstructure {
@@ -315,6 +319,15 @@
superstructure_plant_event_loop_(MakeEventLoop("plant")),
superstructure_plant_(superstructure_plant_event_loop_.get(), dt()) {
set_team_id(::frc971::control_loops::testing::kTeamNumber);
+
+ if (!FLAGS_output_file.empty()) {
+ unlink(FLAGS_output_file.c_str());
+ log_buffer_writer_ = std::make_unique<aos::logger::DetachedBufferWriter>(
+ FLAGS_output_file);
+ logger_event_loop_ = MakeEventLoop("logger");
+ logger_ = std::make_unique<aos::logger::Logger>(log_buffer_writer_.get(),
+ logger_event_loop_.get());
+ }
}
void VerifyNearGoal() {
@@ -372,6 +385,10 @@
::std::unique_ptr<::aos::EventLoop> superstructure_plant_event_loop_;
SuperstructureSimulation superstructure_plant_;
+
+ std::unique_ptr<aos::EventLoop> logger_event_loop_;
+ std::unique_ptr<aos::logger::DetachedBufferWriter> log_buffer_writer_;
+ std::unique_ptr<aos::logger::Logger> logger_;
};
// Tests that the superstructure does nothing when the goal is to remain still.
@@ -418,8 +435,10 @@
SetEnabled(true);
// Set a reasonable goal.
- superstructure_plant_.InitializeHoodPosition(0.7);
- superstructure_plant_.InitializeIntakePosition(0.7);
+ superstructure_plant_.InitializeHoodPosition(
+ constants::Values::kHoodRange().middle());
+ superstructure_plant_.InitializeIntakePosition(
+ constants::Values::kIntakeRange().middle());
WaitUntilZeroed();
{
@@ -427,12 +446,12 @@
flatbuffers::Offset<StaticZeroingSingleDOFProfiledSubsystemGoal>
hood_offset = CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
- *builder.fbb(), 0.2,
+ *builder.fbb(), constants::Values::kHoodRange().upper,
CreateProfileParameters(*builder.fbb(), 1.0, 0.2));
flatbuffers::Offset<StaticZeroingSingleDOFProfiledSubsystemGoal>
intake_offset = CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
- *builder.fbb(), 0.2,
+ *builder.fbb(), constants::Values::kIntakeRange().upper,
CreateProfileParameters(*builder.fbb(), 1.0, 0.2));
flatbuffers::Offset<StaticZeroingSingleDOFProfiledSubsystemGoal>
@@ -552,9 +571,6 @@
SetEnabled(true);
// Set a reasonable goal.
- superstructure_plant_.InitializeHoodPosition(0.7);
- superstructure_plant_.InitializeIntakePosition(0.7);
-
WaitUntilZeroed();
{
auto builder = superstructure_goal_sender_.MakeBuilder();
diff --git a/y2020/control_loops/superstructure/superstructure_output.fbs b/y2020/control_loops/superstructure/superstructure_output.fbs
index 2583106..fd57825 100644
--- a/y2020/control_loops/superstructure/superstructure_output.fbs
+++ b/y2020/control_loops/superstructure/superstructure_output.fbs
@@ -29,9 +29,6 @@
// Voltage sent to the flywheel. Positive is shooting.
finisher_voltage:double;
- // Voltage sent to the motor driving the control panel. Positive is counterclockwise from above.
- control_panel_voltage:double;
-
// Positive is deploying climber and to climb; cannot run in reverse
climber_voltage:double;
}
diff --git a/y2020/control_loops/superstructure/superstructure_status.fbs b/y2020/control_loops/superstructure/superstructure_status.fbs
index 023a242..e8f7637 100644
--- a/y2020/control_loops/superstructure/superstructure_status.fbs
+++ b/y2020/control_loops/superstructure/superstructure_status.fbs
@@ -21,7 +21,7 @@
finisher:FlywheelControllerStatus;
// The subsystem to accelerate the ball before the finisher
- // Velocity is the slowest (lowest) wheel
+ // Velocity is the fastest (top) wheel
accelerator_left:FlywheelControllerStatus;
accelerator_right:FlywheelControllerStatus;
}
@@ -33,7 +33,7 @@
// If true, we have aborted. This is if one or all subsystem estops.
estopped:bool;
- //Subsystem status.
+ // Subsystem status.
hood:frc971.control_loops.AbsoluteEncoderProfiledJointStatus;
intake:frc971.control_loops.AbsoluteEncoderProfiledJointStatus;
turret:frc971.control_loops.PotAndAbsoluteEncoderProfiledJointStatus;
diff --git a/y2020/wpilib_interface.cc b/y2020/wpilib_interface.cc
index 3276722..a1083eb 100644
--- a/y2020/wpilib_interface.cc
+++ b/y2020/wpilib_interface.cc
@@ -15,6 +15,7 @@
#include "frc971/wpilib/ahal/DigitalGlitchFilter.h"
#include "frc971/wpilib/ahal/DriverStation.h"
#include "frc971/wpilib/ahal/Encoder.h"
+#include "frc971/wpilib/ahal/TalonFX.h"
#include "frc971/wpilib/ahal/VictorSP.h"
#undef ERROR
@@ -30,6 +31,7 @@
#include "aos/util/log_interval.h"
#include "aos/util/phased_loop.h"
#include "aos/util/wrapping_counter.h"
+#include "ctre/phoenix/motorcontrol/can/TalonFX.h"
#include "frc971/autonomous/auto_mode_generated.h"
#include "frc971/control_loops/drivetrain/drivetrain_position_generated.h"
#include "frc971/wpilib/ADIS16470.h"
@@ -64,18 +66,6 @@
// DMA stuff and then removing the * 2.0 in *_translate.
// The low bit is direction.
-// TODO(brian): Use ::std::max instead once we have C++14 so that can be
-// constexpr.
-template <typename T>
-constexpr T max(T a, T b) {
- return (a > b) ? a : b;
-}
-
-template <typename T, typename... Rest>
-constexpr T max(T a, T b, T c, Rest... rest) {
- return max(max(a, b), c, rest...);
-}
-
double drivetrain_translate(int32_t in) {
return ((static_cast<double>(in) /
Values::kDrivetrainEncoderCountsPerRevolution()) *
@@ -91,13 +81,24 @@
control_loops::drivetrain::kWheelRadius;
}
-constexpr double kMaxFastEncoderPulsesPerSecond =
- Values::kMaxDrivetrainEncoderPulsesPerSecond();
-static_assert(kMaxFastEncoderPulsesPerSecond <= 1300000,
- "fast encoders are too fast");
-constexpr double kMaxMediumEncoderPulsesPerSecond = kMaxFastEncoderPulsesPerSecond;
+double turret_pot_translate(double voltage) {
+ return voltage * Values::kTurretPotRatio() *
+ (10.0 /*turns*/ / 5.0 /*volts*/) * (2 * M_PI /*radians*/);
+}
-static_assert(kMaxMediumEncoderPulsesPerSecond <= 400000,
+constexpr double kMaxFastEncoderPulsesPerSecond =
+ std::max({Values::kMaxControlPanelEncoderPulsesPerSecond(),
+ Values::kMaxFinisherEncoderPulsesPerSecond(),
+ Values::kMaxAcceleratorEncoderPulsesPerSecond()});
+static_assert(kMaxFastEncoderPulsesPerSecond <= 1000000.0,
+ "fast encoders are too fast");
+constexpr double kMaxMediumEncoderPulsesPerSecond =
+ std::max({Values::kMaxDrivetrainEncoderPulsesPerSecond(),
+ Values::kMaxHoodEncoderPulsesPerSecond(),
+ Values::kMaxIntakeEncoderPulsesPerSecond(),
+ Values::kMaxTurretEncoderPulsesPerSecond()});
+
+static_assert(kMaxMediumEncoderPulsesPerSecond <= 400000.0,
"medium encoders are too fast");
} // namespace
@@ -123,8 +124,74 @@
UpdateMediumEncoderFilterHz(kMaxMediumEncoderPulsesPerSecond);
}
+ // Hood
+
+ void set_hood_encoder(::std::unique_ptr<frc::Encoder> encoder) {
+ medium_encoder_filter_.Add(encoder.get());
+ hood_encoder_.set_encoder(::std::move(encoder));
+ }
+
+ void set_hood_absolute_pwm(
+ ::std::unique_ptr<frc::DigitalInput> absolute_pwm) {
+ hood_encoder_.set_absolute_pwm(::std::move(absolute_pwm));
+ }
+
+ // Intake
+
+ void set_intake_encoder(::std::unique_ptr<frc::Encoder> encoder) {
+ medium_encoder_filter_.Add(encoder.get());
+ intake_joint_encoder_.set_encoder(::std::move(encoder));
+ }
+
+ void set_intake_absolute_pwm(
+ ::std::unique_ptr<frc::DigitalInput> absolute_pwm) {
+ intake_joint_encoder_.set_absolute_pwm(::std::move(absolute_pwm));
+ }
+
+ // Turret
+
+ void set_turret_encoder(::std::unique_ptr<frc::Encoder> encoder) {
+ medium_encoder_filter_.Add(encoder.get());
+ turret_encoder_.set_encoder(::std::move(encoder));
+ }
+
+ void set_turret_absolute_pwm(
+ ::std::unique_ptr<frc::DigitalInput> absolute_pwm) {
+ turret_encoder_.set_absolute_pwm(::std::move(absolute_pwm));
+ }
+
+ void set_turret_potentiometer(
+ ::std::unique_ptr<frc::AnalogInput> potentiometer) {
+ turret_encoder_.set_potentiometer(::std::move(potentiometer));
+ }
+
+ // Shooter
+
+ void set_flywheel_encoder(::std::unique_ptr<frc::Encoder> encoder) {
+ fast_encoder_filter_.Add(encoder.get());
+ flywheel_encoder_ = ::std::move(encoder);
+ }
+
+ void set_left_kicker_encoder(::std::unique_ptr<frc::Encoder> encoder) {
+ fast_encoder_filter_.Add(encoder.get());
+ left_kicker_encoder_ = ::std::move(encoder);
+ }
+
+ void set_right_kicker_encoder(::std::unique_ptr<frc::Encoder> encoder) {
+ fast_encoder_filter_.Add(encoder.get());
+ right_kicker_encoder_ = ::std::move(encoder);
+ }
+
+ // Control Panel
+
+ void set_control_panel_encoder(::std::unique_ptr<frc::Encoder> encoder) {
+ fast_encoder_filter_.Add(encoder.get());
+ control_panel_encoder_ = ::std::move(encoder);
+ }
+
// Auto mode switches.
void set_autonomous_mode(int i, ::std::unique_ptr<frc::DigitalInput> sensor) {
+ medium_encoder_filter_.Add(sensor.get());
autonomous_modes_.at(i) = ::std::move(sensor);
}
@@ -149,11 +216,72 @@
builder.Send(drivetrain_builder.Finish());
}
+ const auto values = constants::GetValues();
{
auto builder = superstructure_position_sender_.MakeBuilder();
superstructure::Position::Builder position_builder =
builder.MakeBuilder<superstructure::Position>();
+ // TODO(alex): check new absolute encoder api.
+ // Hood
+ frc971::AbsolutePositionT hood;
+ CopyPosition(hood_encoder_, &hood,
+ Values::kHoodEncoderCountsPerRevolution(),
+ Values::kHoodEncoderRatio(), false);
+ flatbuffers::Offset<frc971::AbsolutePosition> hood_offset =
+ frc971::AbsolutePosition::Pack(*builder.fbb(), &hood);
+
+ // Intake
+ frc971::AbsolutePositionT intake_joint;
+ CopyPosition(intake_joint_encoder_, &intake_joint,
+ Values::kIntakeEncoderCountsPerRevolution(),
+ Values::kIntakeEncoderRatio(), false);
+ flatbuffers::Offset<frc971::AbsolutePosition> intake_joint_offset =
+ frc971::AbsolutePosition::Pack(*builder.fbb(), &intake_joint);
+
+ // Turret
+ frc971::PotAndAbsolutePositionT turret;
+ CopyPosition(turret_encoder_, &turret,
+ Values::kTurretEncoderCountsPerRevolution(),
+ Values::kTurretEncoderRatio(), turret_pot_translate, false,
+ values.turret.potentiometer_offset);
+ flatbuffers::Offset<frc971::PotAndAbsolutePosition> turret_offset =
+ frc971::PotAndAbsolutePosition::Pack(*builder.fbb(), &turret);
+
+ // Shooter
+ y2020::control_loops::superstructure::ShooterPositionT shooter;
+ shooter.theta_finisher =
+ encoder_translate(flywheel_encoder_->GetRaw(),
+ Values::kFinisherEncoderCountsPerRevolution(),
+ Values::kFinisherEncoderRatio());
+ // TODO; check sign
+ shooter.theta_accelerator_left =
+ encoder_translate(left_kicker_encoder_->GetRaw(),
+ Values::kAcceleratorEncoderCountsPerRevolution(),
+ Values::kAcceleratorEncoderRatio());
+ shooter.theta_accelerator_right =
+ encoder_translate(right_kicker_encoder_->GetRaw(),
+ Values::kAcceleratorEncoderCountsPerRevolution(),
+ Values::kAcceleratorEncoderRatio());
+ flatbuffers::Offset<y2020::control_loops::superstructure::ShooterPosition>
+ shooter_offset =
+ y2020::control_loops::superstructure::ShooterPosition::Pack(
+ *builder.fbb(), &shooter);
+
+ // Control Panel
+ frc971::RelativePositionT control_panel;
+ CopyPosition(*control_panel_encoder_, &control_panel,
+ Values::kControlPanelEncoderCountsPerRevolution(),
+ Values::kControlPanelEncoderRatio(), false);
+ flatbuffers::Offset<frc971::RelativePosition> control_panel_offset =
+ frc971::RelativePosition::Pack(*builder.fbb(), &control_panel);
+
+ position_builder.add_hood(hood_offset);
+ position_builder.add_intake_joint(intake_joint_offset);
+ position_builder.add_turret(turret_offset);
+ position_builder.add_shooter(shooter_offset);
+ position_builder.add_control_panel(control_panel_offset);
+
builder.Send(position_builder.Finish());
}
@@ -182,6 +310,13 @@
::aos::Sender<::frc971::control_loops::drivetrain::Position>
drivetrain_position_sender_;
+ ::frc971::wpilib::AbsoluteEncoderAndPotentiometer turret_encoder_;
+
+ ::frc971::wpilib::AbsoluteEncoder hood_encoder_, intake_joint_encoder_;
+
+ ::std::unique_ptr<::frc::Encoder> flywheel_encoder_, left_kicker_encoder_,
+ right_kicker_encoder_, control_panel_encoder_;
+
::std::array<::std::unique_ptr<frc::DigitalInput>, 2> autonomous_modes_;
frc971::wpilib::ADIS16470 *imu_ = nullptr;
@@ -194,17 +329,138 @@
: ::frc971::wpilib::LoopOutputHandler<superstructure::Output>(
event_loop, "/superstructure") {}
- private:
- void Write(const superstructure::Output & /*output*/) override {}
+ void set_hood_victor(::std::unique_ptr<::frc::VictorSP> t) {
+ hood_victor_ = ::std::move(t);
+ }
- void Stop() override { AOS_LOG(WARNING, "Superstructure output too old.\n"); }
+ void set_intake_joint_victor(::std::unique_ptr<::frc::VictorSP> t) {
+ intake_joint_victor_ = ::std::move(t);
+ }
+
+ void set_intake_roller_falcon(
+ ::std::unique_ptr<::ctre::phoenix::motorcontrol::can::TalonFX> t) {
+ intake_roller_falcon_ = ::std::move(t);
+ intake_roller_falcon_->ConfigSupplyCurrentLimit(
+ {true, Values::kIntakeRollerSupplyCurrentLimit(),
+ Values::kIntakeRollerSupplyCurrentLimit(), 0});
+ intake_roller_falcon_->ConfigStatorCurrentLimit(
+ {true, Values::kIntakeRollerStatorCurrentLimit(),
+ Values::kIntakeRollerStatorCurrentLimit(), 0});
+ }
+
+ void set_turret_victor(::std::unique_ptr<::frc::VictorSP> t) {
+ turret_victor_ = ::std::move(t);
+ }
+
+ void set_feeder_falcon(::std::unique_ptr<::frc::TalonFX> t) {
+ feeder_falcon_ = ::std::move(t);
+ }
+
+ void set_washing_machine_control_panel_victor(
+ ::std::unique_ptr<::frc::VictorSP> t) {
+ washing_machine_control_panel_victor_ = ::std::move(t);
+ }
+
+ void set_kicker_left_falcon(::std::unique_ptr<::frc::TalonFX> t) {
+ kicker_left_falcon_ = ::std::move(t);
+ }
+
+ void set_kicker_right_falcon(::std::unique_ptr<::frc::TalonFX> t) {
+ kicker_right_falcon_ = ::std::move(t);
+ }
+
+ void set_flywheel_falcon(::std::unique_ptr<::frc::TalonFX> t) {
+ flywheel_falcon_ = ::std::move(t);
+ }
+
+ void set_climber_falcon(
+ ::std::unique_ptr<::ctre::phoenix::motorcontrol::can::TalonFX> t) {
+ climber_falcon_ = ::std::move(t);
+ climber_falcon_->ConfigSupplyCurrentLimit(
+ {true, Values::kClimberSupplyCurrentLimit(),
+ Values::kClimberSupplyCurrentLimit(), 0});
+ }
+
+ private:
+ void Write(const superstructure::Output &output) override {
+ hood_victor_->SetSpeed(std::clamp(output.hood_voltage(), -kMaxBringupPower,
+ kMaxBringupPower) /
+ 12.0);
+
+ intake_joint_victor_->SetSpeed(std::clamp(output.intake_joint_voltage(),
+ -kMaxBringupPower,
+ kMaxBringupPower) /
+ 12.0);
+
+ intake_roller_falcon_->Set(
+ ctre::phoenix::motorcontrol::ControlMode::PercentOutput,
+ std::clamp(output.intake_roller_voltage(), -kMaxBringupPower,
+ kMaxBringupPower) /
+ 12.0);
+
+ turret_victor_->SetSpeed(std::clamp(output.turret_voltage(),
+ -kMaxBringupPower, kMaxBringupPower) /
+ 12.0);
+
+ feeder_falcon_->SetSpeed(std::clamp(output.feeder_voltage(),
+ -kMaxBringupPower, kMaxBringupPower) /
+ 12.0);
+
+ washing_machine_control_panel_victor_->SetSpeed(
+ std::clamp(output.washing_machine_spinner_voltage(), -kMaxBringupPower,
+ kMaxBringupPower) /
+ 12.0);
+
+ kicker_left_falcon_->SetSpeed(std::clamp(output.accelerator_left_voltage(),
+ -kMaxBringupPower,
+ kMaxBringupPower) /
+ 12.0);
+
+ kicker_right_falcon_->SetSpeed(
+ std::clamp(output.accelerator_right_voltage(), -kMaxBringupPower,
+ kMaxBringupPower) /
+ 12.0);
+
+ flywheel_falcon_->SetSpeed(std::clamp(output.finisher_voltage(),
+ -kMaxBringupPower,
+ kMaxBringupPower) /
+ 12.0);
+
+ climber_falcon_->Set(
+ ctre::phoenix::motorcontrol::ControlMode::PercentOutput,
+ std::clamp(output.climber_voltage(), -kMaxBringupPower,
+ kMaxBringupPower) /
+ 12.0);
+ }
+
+ void Stop() override {
+ AOS_LOG(WARNING, "Superstructure output too old.\n");
+ hood_victor_->SetDisabled();
+ intake_joint_victor_->SetDisabled();
+ turret_victor_->SetDisabled();
+ feeder_falcon_->SetDisabled();
+ washing_machine_control_panel_victor_->SetDisabled();
+ kicker_left_falcon_->SetDisabled();
+ kicker_right_falcon_->SetDisabled();
+ flywheel_falcon_->SetDisabled();
+ }
+
+ ::std::unique_ptr<::frc::VictorSP> hood_victor_, intake_joint_victor_,
+ turret_victor_, washing_machine_control_panel_victor_;
+
+ ::std::unique_ptr<::frc::TalonFX> feeder_falcon_, kicker_left_falcon_,
+ kicker_right_falcon_, flywheel_falcon_;
+
+ ::std::unique_ptr<::ctre::phoenix::motorcontrol::can::TalonFX>
+ intake_roller_falcon_, climber_falcon_;
};
class WPILibRobot : public ::frc971::wpilib::WPILibRobotBase {
public:
- ::std::unique_ptr<frc::Encoder> make_encoder(int index) {
+ ::std::unique_ptr<frc::Encoder> make_encoder(
+ int index, frc::Encoder::EncodingType encodingType = frc::Encoder::k4X) {
return make_unique<frc::Encoder>(10 + index * 2, 11 + index * 2, false,
- frc::Encoder::k4X);
+ encodingType);
}
void Run() override {
@@ -228,6 +484,23 @@
sensor_reader.set_drivetrain_left_encoder(make_encoder(0));
sensor_reader.set_drivetrain_right_encoder(make_encoder(1));
+ // TODO: pin numbers
+ sensor_reader.set_hood_encoder(make_encoder(2));
+ sensor_reader.set_hood_absolute_pwm(make_unique<frc::DigitalInput>(2));
+
+ sensor_reader.set_intake_encoder(make_encoder(3));
+ sensor_reader.set_intake_absolute_pwm(make_unique<frc::DigitalInput>(3));
+
+ sensor_reader.set_turret_encoder(make_encoder(4));
+ sensor_reader.set_turret_absolute_pwm(make_unique<frc::DigitalInput>(4));
+ sensor_reader.set_turret_potentiometer(make_unique<frc::AnalogInput>(4));
+
+ sensor_reader.set_flywheel_encoder(make_encoder(5));
+ sensor_reader.set_left_kicker_encoder(make_encoder(6));
+ sensor_reader.set_right_kicker_encoder(make_encoder(7));
+
+ sensor_reader.set_control_panel_encoder(make_encoder(8, frc::Encoder::k2X));
+
// Note: If ADIS16470 is plugged in directly to the roboRIO SPI port without
// the Spartan Board, then trigger is on 26, reset 27, and chip select is
// CS0.
@@ -250,6 +523,23 @@
::std::unique_ptr<::frc::VictorSP>(new ::frc::VictorSP(1)), false);
SuperstructureWriter superstructure_writer(&output_event_loop);
+ // TODO: check ports
+ superstructure_writer.set_hood_victor(make_unique<frc::VictorSP>(2));
+ superstructure_writer.set_intake_joint_victor(
+ make_unique<frc::VictorSP>(3));
+ superstructure_writer.set_intake_roller_falcon(
+ make_unique<::ctre::phoenix::motorcontrol::can::TalonFX>(4));
+ superstructure_writer.set_turret_victor(make_unique<frc::VictorSP>(5));
+ superstructure_writer.set_feeder_falcon(make_unique<frc::TalonFX>(6));
+ superstructure_writer.set_washing_machine_control_panel_victor(
+ make_unique<frc::VictorSP>(7));
+ superstructure_writer.set_kicker_left_falcon(
+ make_unique<::frc::TalonFX>(8));
+ superstructure_writer.set_kicker_right_falcon(
+ make_unique<::frc::TalonFX>(9));
+ superstructure_writer.set_flywheel_falcon(make_unique<::frc::TalonFX>(10));
+ superstructure_writer.set_climber_falcon(
+ make_unique<::ctre::phoenix::motorcontrol::can::TalonFX>(11));
AddLoop(&output_event_loop);