Merge "Pull down noise on turret camera"
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/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);