blob: 2b57a0beb4005480cf4437888f0cbe02e63eda4c [file] [log] [blame]
brians343bc112013-02-10 01:53:46 +00001#include <stddef.h>
Brian Silverman699f0cb2015-02-05 19:45:01 -05002#include <inttypes.h>
brians343bc112013-02-10 01:53:46 +00003
John Park33858a32018-09-28 23:05:48 -07004#include "aos/logging/logging.h"
5#include "aos/logging/queue_logging.h"
Austin Schuh3d6e3df2014-02-17 01:51:03 -08006
brians343bc112013-02-10 01:53:46 +00007namespace aos {
Brian Silverman38111502014-04-10 12:36:26 -07008namespace controls {
brians343bc112013-02-10 01:53:46 +00009
10// TODO(aschuh): Tests.
11
Brian Silverman089f5812015-02-15 01:58:19 -050012template <class T>
Austin Schuh61bdc602016-12-04 19:10:10 -080013constexpr ::std::chrono::milliseconds ControlLoop<T>::kStaleLogInterval;
Brian Silverman089f5812015-02-15 01:58:19 -050014template <class T>
Austin Schuh61bdc602016-12-04 19:10:10 -080015constexpr ::std::chrono::milliseconds ControlLoop<T>::kPwmDisableTime;
Brian Silverman50a9d032014-02-16 17:20:57 -080016
Brian Silverman089f5812015-02-15 01:58:19 -050017template <class T>
18void ControlLoop<T>::ZeroOutputs() {
Austin Schuha1654ed2019-01-27 17:24:54 -080019 typename ::aos::Sender<OutputType>::Message output =
20 output_sender_.MakeMessage();
brians343bc112013-02-10 01:53:46 +000021 Zero(output.get());
22 output.Send();
23}
24
Brian Silverman089f5812015-02-15 01:58:19 -050025template <class T>
26void ControlLoop<T>::Iterate() {
Austin Schuheeec74a2019-01-27 20:58:59 -080027 if (!has_iterate_fetcher_) {
28 iterate_position_fetcher_ =
29 event_loop_->MakeFetcher<PositionType>(name_ + ".position");
30 has_iterate_fetcher_ = true;
31 }
32 const bool did_fetch = iterate_position_fetcher_.Fetch();
33 if (!did_fetch) {
34 LOG(FATAL, "Failed to fetch from position queue\n");
35 }
36 IteratePosition(*iterate_position_fetcher_);
Austin Schuha1654ed2019-01-27 17:24:54 -080037}
38
39template <class T>
40void ControlLoop<T>::IteratePosition(const PositionType &position) {
41 // Since Exit() isn't async safe, we want to call Exit from the periodic
42 // handler.
43 if (!run_) {
44 event_loop_->Exit();
45 }
Brian Silverman699f0cb2015-02-05 19:45:01 -050046 no_goal_.Print();
Brian Silverman699f0cb2015-02-05 19:45:01 -050047 no_sensor_state_.Print();
Brian Silverman699f0cb2015-02-05 19:45:01 -050048 motors_off_log_.Print();
Brian Silverman6a1cd212014-02-20 21:04:34 -080049
Austin Schuha1654ed2019-01-27 17:24:54 -080050 LOG_STRUCT(DEBUG, "position", position);
Brian Silvermand8f403a2014-12-13 19:12:04 -050051
52 // Fetch the latest control loop goal. If there is no new
brians343bc112013-02-10 01:53:46 +000053 // goal, we will just reuse the old one.
Austin Schuha1654ed2019-01-27 17:24:54 -080054 goal_fetcher_.Fetch();
55 const GoalType *goal = goal_fetcher_.get();
Brian Silverman699f0cb2015-02-05 19:45:01 -050056 if (goal) {
57 LOG_STRUCT(DEBUG, "goal", *goal);
58 } else {
59 LOG_INTERVAL(no_goal_);
brians343bc112013-02-10 01:53:46 +000060 }
Austin Schuh3d6e3df2014-02-17 01:51:03 -080061
Austin Schuheeec74a2019-01-27 20:58:59 -080062 const bool new_robot_state = robot_state_fetcher_.Fetch();
63 if (!robot_state_fetcher_.get()) {
Brian Silverman699f0cb2015-02-05 19:45:01 -050064 LOG_INTERVAL(no_sensor_state_);
Austin Schuh3d6e3df2014-02-17 01:51:03 -080065 return;
66 }
Austin Schuheeec74a2019-01-27 20:58:59 -080067 if (sensor_reader_pid_ != robot_state_fetcher_->reader_pid) {
Brian Silverman699f0cb2015-02-05 19:45:01 -050068 LOG(INFO, "new sensor reader PID %" PRId32 ", old was %" PRId32 "\n",
Austin Schuheeec74a2019-01-27 20:58:59 -080069 robot_state_fetcher_->reader_pid, sensor_reader_pid_);
Austin Schuh3d6e3df2014-02-17 01:51:03 -080070 reset_ = true;
Austin Schuheeec74a2019-01-27 20:58:59 -080071 sensor_reader_pid_ = robot_state_fetcher_->reader_pid;
Brian Silverman71fbee02014-03-13 17:24:54 -070072 }
brians343bc112013-02-10 01:53:46 +000073
Austin Schuheeec74a2019-01-27 20:58:59 -080074 bool outputs_enabled = robot_state_fetcher_->outputs_enabled;
brians343bc112013-02-10 01:53:46 +000075
76 // Check to see if we got a driver station packet recently.
Austin Schuh61bdc602016-12-04 19:10:10 -080077 if (new_robot_state) {
Austin Schuheeec74a2019-01-27 20:58:59 -080078 if (robot_state_fetcher_->outputs_enabled) {
Brian Silverman295f74b2015-02-14 23:00:47 -050079 // If the driver's station reports being disabled, we're probably not
80 // actually going to send motor values regardless of what the FPGA
81 // reports.
Austin Schuheeec74a2019-01-27 20:58:59 -080082 last_pwm_sent_ = robot_state_fetcher_->sent_time;
brians343bc112013-02-10 01:53:46 +000083 }
84 }
85
Austin Schuh19845272019-07-07 20:45:22 -070086 const ::aos::monotonic_clock::time_point monotonic_now =
87 event_loop_->monotonic_now();
88 const bool motors_off = monotonic_now >= kPwmDisableTime + last_pwm_sent_;
Austin Schuheeec74a2019-01-27 20:58:59 -080089 joystick_state_fetcher_.Fetch();
Brian Silverman2704ecf2014-04-09 20:24:03 -070090 if (motors_off) {
Austin Schuheeec74a2019-01-27 20:58:59 -080091 if (joystick_state_fetcher_.get() && joystick_state_fetcher_->enabled) {
Brian Silverman2704ecf2014-04-09 20:24:03 -070092 LOG_INTERVAL(motors_off_log_);
93 }
94 outputs_enabled = false;
95 }
96
Austin Schuha1654ed2019-01-27 17:24:54 -080097 typename ::aos::Sender<StatusType>::Message status =
98 status_sender_.MakeMessage();
Brian Silverman699f0cb2015-02-05 19:45:01 -050099 if (status.get() == nullptr) {
brians343bc112013-02-10 01:53:46 +0000100 return;
101 }
102
103 if (outputs_enabled) {
Austin Schuha1654ed2019-01-27 17:24:54 -0800104 typename ::aos::Sender<OutputType>::Message output =
105 output_sender_.MakeMessage();
106 RunIteration(goal, &position, output.get(), status.get());
brians343bc112013-02-10 01:53:46 +0000107
Austin Schuhf907f2e2018-01-02 01:15:27 -0800108 output->SetTimeToNow();
Brian Silvermand6974f42014-02-14 13:39:21 -0800109 LOG_STRUCT(DEBUG, "output", *output);
brians343bc112013-02-10 01:53:46 +0000110 output.Send();
111 } else {
Brian Silverman699f0cb2015-02-05 19:45:01 -0500112 // The outputs are disabled, so pass nullptr in for the output.
Austin Schuha1654ed2019-01-27 17:24:54 -0800113 RunIteration(goal, &position, nullptr, status.get());
brians343bc112013-02-10 01:53:46 +0000114 ZeroOutputs();
115 }
116
Austin Schuhf907f2e2018-01-02 01:15:27 -0800117 status->SetTimeToNow();
Brian Silvermand6974f42014-02-14 13:39:21 -0800118 LOG_STRUCT(DEBUG, "status", *status);
brians343bc112013-02-10 01:53:46 +0000119 status.Send();
120}
121
Brian Silverman089f5812015-02-15 01:58:19 -0500122template <class T>
123void ControlLoop<T>::Run() {
Austin Schuhb58ceb62017-02-05 14:21:57 -0800124 struct sigaction action;
125 action.sa_handler = &ControlLoop<T>::Quit;
126 sigemptyset(&action.sa_mask);
127 action.sa_flags = SA_RESETHAND;
128
129 PCHECK(sigaction(SIGTERM, &action, nullptr));
130 PCHECK(sigaction(SIGQUIT, &action, nullptr));
131 PCHECK(sigaction(SIGINT, &action, nullptr));
132
Austin Schuheeec74a2019-01-27 20:58:59 -0800133 event_loop_->MakeWatcher(name_ + ".position",
Austin Schuha1654ed2019-01-27 17:24:54 -0800134 [this](const PositionType &position) {
135 this->IteratePosition(position);
136 });
137
138 event_loop_->Run();
Austin Schuhb58ceb62017-02-05 14:21:57 -0800139 LOG(INFO, "Shutting down\n");
brians343bc112013-02-10 01:53:46 +0000140}
141
Austin Schuhb58ceb62017-02-05 14:21:57 -0800142template <class T>
143::std::atomic<bool> ControlLoop<T>::run_{true};
144
Brian Silverman38111502014-04-10 12:36:26 -0700145} // namespace controls
brians343bc112013-02-10 01:53:46 +0000146} // namespace aos