Merge "Fix for problems with using multiple targets"
diff --git a/.clang-format b/.clang-format
index 510c427..fa1936b 100644
--- a/.clang-format
+++ b/.clang-format
@@ -6,3 +6,8 @@
DerivePointerAlignment: false
PointerAlignment: Right
Standard: Cpp11
+
+---
+Language: JavaScript
+BasedOnStyle: Google
+ColumnLimit: 80
diff --git a/aos/configuration.cc b/aos/configuration.cc
index 046f7c1..e9c7160 100644
--- a/aos/configuration.cc
+++ b/aos/configuration.cc
@@ -60,7 +60,7 @@
flatbuffers::DetachedBuffer buffer = JsonToFlatbuffer(
util::ReadFileToStringOrDie(path), ConfigurationTypeTable());
- CHECK_GT(buffer.size(), 0u) << ": Failed to parse JSON file";
+ CHECK_GT(buffer.size(), 0u) << ": Failed to parse JSON file: " << path;
return FlatbufferDetachedBuffer<Configuration>(std::move(buffer));
}
diff --git a/aos/events/event_loop.cc b/aos/events/event_loop.cc
index 68cc923..644d998 100644
--- a/aos/events/event_loop.cc
+++ b/aos/events/event_loop.cc
@@ -202,6 +202,11 @@
}
void EventLoop::SendTimingReport() {
+ if (!timing_report_sender_) {
+ // Timing reports are disabled, so nothing for us to do.
+ return;
+ }
+
// We need to do a fancy dance here to get all the accounting to work right.
// We want to copy the memory here, but then send after resetting. Otherwise
// the send for the timing report won't be counted in the timing report.
diff --git a/aos/events/event_loop.h b/aos/events/event_loop.h
index 0e7b66a..6f51fb0 100644
--- a/aos/events/event_loop.h
+++ b/aos/events/event_loop.h
@@ -734,7 +734,9 @@
std::vector<std::unique_ptr<PhasedLoopHandler>> phased_loops_;
std::vector<std::unique_ptr<WatcherState>> watchers_;
+ // Does nothing if timing reports are disabled.
void SendTimingReport();
+
void UpdateTimingReport();
void MaybeScheduleTimingReports();
diff --git a/aos/events/shm_event_loop_test.cc b/aos/events/shm_event_loop_test.cc
index 4c12213..7e1444b 100644
--- a/aos/events/shm_event_loop_test.cc
+++ b/aos/events/shm_event_loop_test.cc
@@ -77,10 +77,10 @@
}
INSTANTIATE_TEST_SUITE_P(ShmEventLoopCommonTest, AbstractEventLoopTest,
- CommonParameters());
+ CommonParameters());
-INSTANTIATE_TEST_SUITE_P(ShmEventLoopCommonDeathTest, AbstractEventLoopDeathTest,
- CommonParameters());
+INSTANTIATE_TEST_SUITE_P(ShmEventLoopCommonDeathTest,
+ AbstractEventLoopDeathTest, CommonParameters());
} // namespace
@@ -348,16 +348,104 @@
}
}
+// Tests that the next message not being available prints a helpful error in the
+// normal case.
+TEST_P(ShmEventLoopDeathTest, NextMessageNotAvailable) {
+ auto loop1 = factory()->MakePrimary("loop1");
+ auto fetcher = loop1->MakeFetcher<TestMessage>("/test");
+ auto loop2 = factory()->Make("loop2");
+ auto sender = loop2->MakeSender<TestMessage>("/test");
+ bool ran = false;
+ loop1->OnRun([this, &sender, &fetcher, &ran]() {
+ for (int i = 0; i < 2000; ++i) {
+ auto builder = sender.MakeBuilder();
+ TestMessage::Builder test_builder(*builder.fbb());
+ test_builder.add_value(0);
+ CHECK(builder.Send(test_builder.Finish()));
+ }
+ EXPECT_DEATH(fetcher.FetchNext(),
+ "The next message is no longer "
+ "available.*\"/test\".*\"aos\\.TestMessage\"");
+ factory()->Exit();
+ ran = true;
+ });
+ factory()->Run();
+ EXPECT_TRUE(ran);
+}
+
+// Tests that the next message not being available prints a helpful error with
+// timing reports disabled.
+TEST_P(ShmEventLoopDeathTest, NextMessageNotAvailableNoTimingReports) {
+ auto loop1 = factory()->MakePrimary("loop1");
+ loop1->SkipTimingReport();
+ auto fetcher = loop1->MakeFetcher<TestMessage>("/test");
+ auto loop2 = factory()->Make("loop2");
+ auto sender = loop2->MakeSender<TestMessage>("/test");
+ bool ran = false;
+ loop1->OnRun([this, &sender, &fetcher, &ran]() {
+ for (int i = 0; i < 2000; ++i) {
+ auto builder = sender.MakeBuilder();
+ TestMessage::Builder test_builder(*builder.fbb());
+ test_builder.add_value(0);
+ CHECK(builder.Send(test_builder.Finish()));
+ }
+ EXPECT_DEATH(fetcher.FetchNext(),
+ "The next message is no longer "
+ "available.*\"/test\".*\"aos\\.TestMessage\"");
+ factory()->Exit();
+ ran = true;
+ });
+ factory()->Run();
+ EXPECT_TRUE(ran);
+}
+
+// Tests that the next message not being available prints a helpful error even
+// when Run is never called.
+TEST_P(ShmEventLoopDeathTest, NextMessageNotAvailableNoRun) {
+ auto loop1 = factory()->MakePrimary("loop1");
+ auto fetcher = loop1->MakeFetcher<TestMessage>("/test");
+ auto loop2 = factory()->Make("loop2");
+ auto sender = loop2->MakeSender<TestMessage>("/test");
+ for (int i = 0; i < 2000; ++i) {
+ auto builder = sender.MakeBuilder();
+ TestMessage::Builder test_builder(*builder.fbb());
+ test_builder.add_value(0);
+ CHECK(builder.Send(test_builder.Finish()));
+ }
+ EXPECT_DEATH(fetcher.FetchNext(),
+ "The next message is no longer "
+ "available.*\"/test\".*\"aos\\.TestMessage\"");
+}
+
+// Tests that the next message not being available prints a helpful error even
+// when Run is never called without timing reports.
+TEST_P(ShmEventLoopDeathTest, NextMessageNotAvailableNoRunNoTimingReports) {
+ auto loop1 = factory()->MakePrimary("loop1");
+ loop1->SkipTimingReport();
+ auto fetcher = loop1->MakeFetcher<TestMessage>("/test");
+ auto loop2 = factory()->Make("loop2");
+ auto sender = loop2->MakeSender<TestMessage>("/test");
+ for (int i = 0; i < 2000; ++i) {
+ auto builder = sender.MakeBuilder();
+ TestMessage::Builder test_builder(*builder.fbb());
+ test_builder.add_value(0);
+ CHECK(builder.Send(test_builder.Finish()));
+ }
+ EXPECT_DEATH(fetcher.FetchNext(),
+ "The next message is no longer "
+ "available.*\"/test\".*\"aos\\.TestMessage\"");
+}
+
// TODO(austin): Test that missing a deadline with a timer recovers as expected.
INSTANTIATE_TEST_SUITE_P(ShmEventLoopCopyTest, ShmEventLoopTest,
- ::testing::Values(ReadMethod::COPY));
+ ::testing::Values(ReadMethod::COPY));
INSTANTIATE_TEST_SUITE_P(ShmEventLoopPinTest, ShmEventLoopTest,
- ::testing::Values(ReadMethod::PIN));
+ ::testing::Values(ReadMethod::PIN));
INSTANTIATE_TEST_SUITE_P(ShmEventLoopCopyDeathTest, ShmEventLoopDeathTest,
- ::testing::Values(ReadMethod::COPY));
+ ::testing::Values(ReadMethod::COPY));
INSTANTIATE_TEST_SUITE_P(ShmEventLoopPinDeathTest, ShmEventLoopDeathTest,
- ::testing::Values(ReadMethod::PIN));
+ ::testing::Values(ReadMethod::PIN));
} // namespace testing
} // namespace aos
diff --git a/aos/network/message_bridge_server_lib.cc b/aos/network/message_bridge_server_lib.cc
index da925f5..4f32e95 100644
--- a/aos/network/message_bridge_server_lib.cc
+++ b/aos/network/message_bridge_server_lib.cc
@@ -244,7 +244,8 @@
MessageBridgeServer::MessageBridgeServer(aos::ShmEventLoop *event_loop)
: event_loop_(event_loop),
timestamp_loggers_(event_loop_),
- server_("::", event_loop->node()->port()),
+ server_(max_channels() + kControlStreams(),
+ "::", event_loop->node()->port()),
server_status_(event_loop, [this](const Context &context) {
timestamp_state_->SendData(&server_, context);
}) {
@@ -448,6 +449,10 @@
}
VLOG(1) << FlatbufferToJson(connect);
+ CHECK_LE(connect->channels_to_transfer()->size(),
+ static_cast<size_t>(max_channels()))
+ << ": Client has more channels than we do";
+
// Account for the control channel and delivery times channel.
size_t channel_index = kControlStreams();
int node_index = -1;
diff --git a/aos/network/message_bridge_server_lib.h b/aos/network/message_bridge_server_lib.h
index db6e6d9..6f96c02 100644
--- a/aos/network/message_bridge_server_lib.h
+++ b/aos/network/message_bridge_server_lib.h
@@ -120,6 +120,13 @@
// received.
void HandleData(const Message *message);
+ // The maximum number of channels we support on a single connection. We need
+ // to configure the SCTP socket with this before any clients connect, so we
+ // need an upper bound on the number of channels any of them will use.
+ int max_channels() const {
+ return event_loop_->configuration()->channels()->size();
+ }
+
// Event loop to schedule everything on.
aos::ShmEventLoop *event_loop_;
diff --git a/aos/network/sctp_client.cc b/aos/network/sctp_client.cc
index 33115be..273cff8 100644
--- a/aos/network/sctp_client.cc
+++ b/aos/network/sctp_client.cc
@@ -32,9 +32,9 @@
}
{
- // Servers send promptly. Clients don't.
- // TODO(austin): Revisit this assumption when we have time sync.
- int on = 0;
+ // Turn off the NAGLE algorithm so the timestamps heading back across the
+ // network arrive promptly.
+ int on = 1;
PCHECK(setsockopt(fd(), IPPROTO_SCTP, SCTP_NODELAY, &on, sizeof(int)) == 0);
}
diff --git a/aos/network/sctp_server.cc b/aos/network/sctp_server.cc
index 894a1f1..33046d0 100644
--- a/aos/network/sctp_server.cc
+++ b/aos/network/sctp_server.cc
@@ -20,12 +20,21 @@
namespace aos {
namespace message_bridge {
-SctpServer::SctpServer(std::string_view local_host, int local_port)
+SctpServer::SctpServer(int streams, std::string_view local_host, int local_port)
: sockaddr_local_(ResolveSocket(local_host, local_port)) {
while (true) {
sctp_.OpenSocket(sockaddr_local_);
{
+ struct sctp_initmsg initmsg;
+ memset(&initmsg, 0, sizeof(struct sctp_initmsg));
+ initmsg.sinit_num_ostreams = streams;
+ initmsg.sinit_max_instreams = streams;
+ PCHECK(setsockopt(fd(), IPPROTO_SCTP, SCTP_INITMSG, &initmsg,
+ sizeof(struct sctp_initmsg)) == 0);
+ }
+
+ {
// Turn off the NAGLE algorithm.
int on = 1;
PCHECK(setsockopt(fd(), IPPROTO_SCTP, SCTP_NODELAY, &on, sizeof(int)) ==
diff --git a/aos/network/sctp_server.h b/aos/network/sctp_server.h
index c6737d6..6ffe93e 100644
--- a/aos/network/sctp_server.h
+++ b/aos/network/sctp_server.h
@@ -22,7 +22,8 @@
class SctpServer {
public:
- SctpServer(std::string_view local_host = "0.0.0.0", int local_port = 9971);
+ SctpServer(int streams, std::string_view local_host = "0.0.0.0",
+ int local_port = 9971);
~SctpServer() {}
diff --git a/aos/starter/starter.sh b/aos/starter/starter.sh
index 2db812a..7a25fc7 100755
--- a/aos/starter/starter.sh
+++ b/aos/starter/starter.sh
@@ -10,6 +10,7 @@
elif [[ "$(hostname)" == "pi-"* ]]; then
# We have systemd configured to handle restarting, so just exec.
export PATH="${PATH}:/home/pi/robot_code"
+ rm -rf /dev/shm/aos
exec starterd
else
ROBOT_CODE="${HOME}/robot_code"
@@ -18,5 +19,6 @@
cd "${ROBOT_CODE}"
export PATH="${PATH}:${ROBOT_CODE}"
while true; do
- starterd 2>&1
+ rm -rf /dev/shm/aos
+ starterd 2>&1
done
diff --git a/frc971/config/setup_roborio.sh b/frc971/config/setup_roborio.sh
index 2cd4bd5..0a70e27 100755
--- a/frc971/config/setup_roborio.sh
+++ b/frc971/config/setup_roborio.sh
@@ -34,6 +34,7 @@
ssh "admin@${ROBOT_HOSTNAME}" ln -s /media/sda1/aos_log-current robot_code/aos_log-current
echo "Adding aos_dump autocomplete to profile"
ssh "admin@${ROBOT_HOSTNAME}" 'echo "if [ -f /home/admin/robot_code/aos_dump_autocomplete.sh ]; then source /home/admin/robot_code/aos_dump_autocomplete.sh; fi;" >> /etc/profile'
+ ssh "admin@${ROBOT_HOSTNAME}" 'echo "export PATH=\"\${PATH}:/home/admin/robot_code:/home/admin/bin\"" >> /etc/profile'
fi
if [[ "$(ssh admin@${ROBOT_HOSTNAME} uname -r)" != "4.14.87-rt49-cg-7.0.0f0-xilinx-zynq-189" ]]; then
diff --git a/tools/cherry-pick-merge.sh b/tools/cherry-pick-merge.sh
new file mode 100755
index 0000000..547fecf
--- /dev/null
+++ b/tools/cherry-pick-merge.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# The script replays a merge based on its first parent.
+# This will create a new merge with the same message and second parent, but with
+# its first parent as the current commit.
+#
+# It is advised for now to backup your repo before running this command and
+# make sure the git history is what you want until we use it more.
+
+set -e
+set -o pipefail
+
+COMMIT=$1
+
+MESSAGE=$(git show -s --format=%B ${COMMIT})
+
+git cherry-pick -m1 ${COMMIT}
+git reset --soft HEAD~
+echo ${COMMIT}^2 > .git/MERGE_HEAD
+git commit --message="${MESSAGE}"
diff --git a/y2020/control_loops/drivetrain/localizer.cc b/y2020/control_loops/drivetrain/localizer.cc
index 9314aba..d4d5f72 100644
--- a/y2020/control_loops/drivetrain/localizer.cc
+++ b/y2020/control_loops/drivetrain/localizer.cc
@@ -394,6 +394,8 @@
// are less useful when it comes to actually making shots.
if (!is_turret) {
noises *= 3.0;
+ } else {
+ noises /= 5.0;
}
Eigen::Matrix3f R = Eigen::Matrix3f::Zero();
diff --git a/y2020/control_loops/superstructure/turret/aiming.cc b/y2020/control_loops/superstructure/turret/aiming.cc
index c5daf22..cdc6181 100644
--- a/y2020/control_loops/superstructure/turret/aiming.cc
+++ b/y2020/control_loops/superstructure/turret/aiming.cc
@@ -53,7 +53,7 @@
// Distance (in meters) from the edge of the field to the port, with some
// compensation to ensure that our definition of where the target is matches
// that reported by the cameras.
-constexpr double kEdgeOfFieldToPort = 2.404 + .0034;
+constexpr double kEdgeOfFieldToPort = 2.347;
// The amount (in meters) that the inner port is set back from the outer port.
constexpr double kInnerPortBackset = 0.743;
diff --git a/y2020/vision/rootfs/logind.conf b/y2020/vision/rootfs/logind.conf
new file mode 100644
index 0000000..17ced30
--- /dev/null
+++ b/y2020/vision/rootfs/logind.conf
@@ -0,0 +1,36 @@
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# See logind.conf(5) for details.
+
+[Login]
+#NAutoVTs=6
+#ReserveVT=6
+#KillUserProcesses=no
+#KillOnlyUsers=
+#KillExcludeUsers=root
+#InhibitDelayMaxSec=5
+#HandlePowerKey=poweroff
+#HandleSuspendKey=suspend
+#HandleHibernateKey=hibernate
+#HandleLidSwitch=suspend
+#HandleLidSwitchExternalPower=suspend
+#HandleLidSwitchDocked=ignore
+#PowerKeyIgnoreInhibited=no
+#SuspendKeyIgnoreInhibited=no
+#HibernateKeyIgnoreInhibited=no
+#LidSwitchIgnoreInhibited=yes
+#HoldoffTimeoutSec=30s
+#IdleAction=ignore
+#IdleActionSec=30min
+#RuntimeDirectorySize=10%
+
+# We don't want systemd removing IPC. This makes it so nothing new can talk to
+# existing channels.
+RemoveIPC=no
+#InhibitorsMax=8192
+#SessionsMax=8192
diff --git a/y2020/vision/rootfs/modify_rootfs.sh b/y2020/vision/rootfs/modify_rootfs.sh
index ae1e493..eb87113 100755
--- a/y2020/vision/rootfs/modify_rootfs.sh
+++ b/y2020/vision/rootfs/modify_rootfs.sh
@@ -76,6 +76,7 @@
sudo cp target_configure.sh "${PARTITION}/tmp/"
sudo cp dhcpcd.conf "${PARTITION}/tmp/dhcpcd.conf"
sudo cp sctp.conf "${PARTITION}/etc/sysctl.d/sctp.conf"
+sudo cp logind.conf "${PARTITION}/etc/systemd/logind.conf"
sudo cp change_hostname.sh "${PARTITION}/tmp/change_hostname.sh"
sudo cp frc971.service "${PARTITION}/etc/systemd/system/frc971.service"
sudo cp rt.conf "${PARTITION}/etc/security/limits.d/rt.conf"
diff --git a/y2021_bot3/constants.h b/y2021_bot3/constants.h
index fd6ab72..f0cbacb 100644
--- a/y2021_bot3/constants.h
+++ b/y2021_bot3/constants.h
@@ -29,6 +29,9 @@
}
static constexpr double kRollerSupplyCurrentLimit() { return 30.0; }
static constexpr double kRollerStatorCurrentLimit() { return 40.0; }
+
+ // Climber
+ static constexpr double kClimberSupplyCurrentLimit() { return 60.0; }
};
// Creates (once) a Values instance for ::aos::network::GetTeamNumber() and
diff --git a/y2021_bot3/wpilib_interface.cc b/y2021_bot3/wpilib_interface.cc
index 1ad2f52..1808846 100644
--- a/y2021_bot3/wpilib_interface.cc
+++ b/y2021_bot3/wpilib_interface.cc
@@ -196,17 +196,26 @@
void set_intake_falcon(
::std::unique_ptr<::ctre::phoenix::motorcontrol::can::TalonFX> t) {
intake_falcon_ = ::std::move(t);
- ConfigureFalcon(intake_falcon_.get());
+ ConfigureRollerFalcon(intake_falcon_.get());
}
void set_outtake_falcon(
::std::unique_ptr<::ctre::phoenix::motorcontrol::can::TalonFX> t) {
outtake_falcon_ = ::std::move(t);
- ConfigureFalcon(outtake_falcon_.get());
+ ConfigureRollerFalcon(outtake_falcon_.get());
+ }
+
+ 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 ConfigureFalcon(::ctre::phoenix::motorcontrol::can::TalonFX *falcon) {
+ void ConfigureRollerFalcon(
+ ::ctre::phoenix::motorcontrol::can::TalonFX *falcon) {
falcon->ConfigSupplyCurrentLimit({true, Values::kRollerSupplyCurrentLimit(),
Values::kRollerSupplyCurrentLimit(), 0});
falcon->ConfigStatorCurrentLimit({true, Values::kRollerStatorCurrentLimit(),
@@ -223,12 +232,22 @@
void Write(const superstructure::Output &output) override {
WriteToFalcon(output.intake_volts(), intake_falcon_.get());
WriteToFalcon(output.outtake_volts(), outtake_falcon_.get());
+
+ WriteToFalcon(-output.climber_volts(), climber_falcon_.get());
}
- void Stop() override { AOS_LOG(WARNING, "Superstructure output too old.\n"); }
+ void Stop() override {
+ AOS_LOG(WARNING, "Superstructure output too old.\n");
+ climber_falcon_->Set(ctre::phoenix::motorcontrol::ControlMode::Disabled, 0);
+ intake_falcon_->Set(ctre::phoenix::motorcontrol::ControlMode::Disabled, 0);
+ outtake_falcon_->Set(ctre::phoenix::motorcontrol::ControlMode::Disabled, 0);
+ }
::std::unique_ptr<::ctre::phoenix::motorcontrol::can::TalonFX> intake_falcon_,
outtake_falcon_;
+
+ ::std::unique_ptr<::ctre::phoenix::motorcontrol::can::TalonFX>
+ climber_falcon_;
};
class WPILibRobot : public ::frc971::wpilib::WPILibRobotBase {
@@ -274,6 +293,8 @@
make_unique<::ctre::phoenix::motorcontrol::can::TalonFX>(0));
superstructure_writer.set_outtake_falcon(
make_unique<::ctre::phoenix::motorcontrol::can::TalonFX>(1));
+ superstructure_writer.set_climber_falcon(
+ make_unique<::ctre::phoenix::motorcontrol::can::TalonFX>(2));
AddLoop(&output_event_loop);