blob: 404425a2c76c6c2b58a2e1f7ef95f571ca8e3f41 [file] [log] [blame]
brians343bc112013-02-10 01:53:46 +00001#include <stddef.h>
2
3#include "aos/common/logging/logging.h"
Brian Silverman2ac0fbc2014-03-20 19:45:13 -07004#include "aos/common/messages/robot_state.q.h"
Brian Silvermand6974f42014-02-14 13:39:21 -08005#include "aos/common/logging/queue_logging.h"
Brian3afd6fc2014-04-02 20:41:49 -07006#include "aos/common/util/phased_loop.h"
brians343bc112013-02-10 01:53:46 +00007
Austin Schuh3d6e3df2014-02-17 01:51:03 -08008#include "bbb/sensor_generation.q.h"
Brian Silverman2704ecf2014-04-09 20:24:03 -07009#include "frc971/queues/output_check.q.h"
Austin Schuh3d6e3df2014-02-17 01:51:03 -080010
brians343bc112013-02-10 01:53:46 +000011namespace aos {
12namespace control_loops {
13
14// TODO(aschuh): Tests.
15
Brian Silverman71fbee02014-03-13 17:24:54 -070016template <class T, bool has_position, bool fail_no_position, bool fail_no_goal>
17constexpr ::aos::time::Time ControlLoop<T, has_position, fail_no_position,
18 fail_no_goal>::kStaleLogInterval;
Brian Silverman50a9d032014-02-16 17:20:57 -080019
Brian Silverman71fbee02014-03-13 17:24:54 -070020template <class T, bool has_position, bool fail_no_position, bool fail_no_goal>
21void
22ControlLoop<T, has_position, fail_no_position, fail_no_goal>::ZeroOutputs() {
brians343bc112013-02-10 01:53:46 +000023 aos::ScopedMessagePtr<OutputType> output =
24 control_loop_->output.MakeMessage();
25 Zero(output.get());
26 output.Send();
27}
28
Brian Silverman71fbee02014-03-13 17:24:54 -070029template <class T, bool has_position, bool fail_no_position, bool fail_no_goal>
30void ControlLoop<T, has_position, fail_no_position, fail_no_goal>::Iterate() {
Brian Silverman9c9ab422014-02-25 13:42:53 -080031 no_prior_goal_.Print();
32 no_sensor_generation_.Print();
33 very_stale_position_.Print();
34 no_prior_position_.Print();
35 driver_station_old_.Print();
36 no_driver_station_.Print();
Brian Silverman6a1cd212014-02-20 21:04:34 -080037
brians343bc112013-02-10 01:53:46 +000038 // Fetch the latest control loop goal and position. If there is no new
39 // goal, we will just reuse the old one.
40 // If there is no goal, we haven't started up fully. It isn't worth
41 // the added complexity for each loop implementation to handle that case.
42 control_loop_->goal.FetchLatest();
43 // TODO(aschuh): Check the age here if we want the loop to stop on old
44 // goals.
45 const GoalType *goal = control_loop_->goal.get();
46 if (goal == NULL) {
Brian Silverman9c9ab422014-02-25 13:42:53 -080047 LOG_INTERVAL(no_prior_goal_);
Brian Silverman71fbee02014-03-13 17:24:54 -070048 if (fail_no_goal) {
49 ZeroOutputs();
50 return;
51 }
brians343bc112013-02-10 01:53:46 +000052 }
Austin Schuh3d6e3df2014-02-17 01:51:03 -080053
54 ::bbb::sensor_generation.FetchLatest();
55 if (::bbb::sensor_generation.get() == nullptr) {
Brian Silverman9c9ab422014-02-25 13:42:53 -080056 LOG_INTERVAL(no_sensor_generation_);
Austin Schuh3d6e3df2014-02-17 01:51:03 -080057 ZeroOutputs();
58 return;
59 }
60 if (!has_sensor_reset_counters_ ||
61 ::bbb::sensor_generation->reader_pid != reader_pid_ ||
62 ::bbb::sensor_generation->cape_resets != cape_resets_) {
Brian Silverman71fbee02014-03-13 17:24:54 -070063 LOG_STRUCT(INFO, "new sensor_generation message",
64 *::bbb::sensor_generation.get());
Brian Silverman4910efb2014-02-17 11:10:10 -080065
Austin Schuh3d6e3df2014-02-17 01:51:03 -080066 reader_pid_ = ::bbb::sensor_generation->reader_pid;
67 cape_resets_ = ::bbb::sensor_generation->cape_resets;
68 has_sensor_reset_counters_ = true;
69 reset_ = true;
70 }
Brian Silverman71fbee02014-03-13 17:24:54 -070071
72 if (goal) {
73 LOG_STRUCT(DEBUG, "goal", *goal);
74 }
brians343bc112013-02-10 01:53:46 +000075
76 // Only pass in a position if we got one this cycle.
77 const PositionType *position = NULL;
78
79 // Only fetch the latest position if we have one.
80 if (has_position) {
81 // If the position is stale, this is really bad. Try fetching a position
82 // and check how fresh it is, and then take the appropriate action.
83 if (control_loop_->position.FetchLatest()) {
84 position = control_loop_->position.get();
85 } else {
Ben Fredrickson4283bb42014-02-22 08:31:50 +000086 if (control_loop_->position.get() && !reset_) {
brians343bc112013-02-10 01:53:46 +000087 int msec_age = control_loop_->position.Age().ToMSec();
88 if (!control_loop_->position.IsNewerThanMS(kPositionTimeoutMs)) {
Brian Silverman9c9ab422014-02-25 13:42:53 -080089 LOG_INTERVAL(very_stale_position_);
brians343bc112013-02-10 01:53:46 +000090 ZeroOutputs();
91 return;
92 } else {
Brian Silverman50a9d032014-02-16 17:20:57 -080093 LOG(ERROR, "Stale position. %d ms (< %d ms)\n", msec_age,
94 kPositionTimeoutMs);
brians343bc112013-02-10 01:53:46 +000095 }
96 } else {
Brian Silverman9c9ab422014-02-25 13:42:53 -080097 LOG_INTERVAL(no_prior_position_);
Brian Silverman10f997b2013-10-11 18:01:56 -070098 if (fail_no_position) {
99 ZeroOutputs();
100 return;
101 }
brians343bc112013-02-10 01:53:46 +0000102 }
103 }
Austin Schuhfa033692013-02-24 01:00:55 -0800104 if (position) {
Brian Silvermand6974f42014-02-14 13:39:21 -0800105 LOG_STRUCT(DEBUG, "position", *position);
Austin Schuhfa033692013-02-24 01:00:55 -0800106 }
brians343bc112013-02-10 01:53:46 +0000107 }
108
109 bool outputs_enabled = false;
110
111 // Check to see if we got a driver station packet recently.
Austin Schuh3d6e3df2014-02-17 01:51:03 -0800112 if (::aos::robot_state.FetchLatest()) {
brians343bc112013-02-10 01:53:46 +0000113 outputs_enabled = true;
Austin Schuh3d6e3df2014-02-17 01:51:03 -0800114 } else if (::aos::robot_state.IsNewerThanMS(kDSPacketTimeoutMs)) {
brians343bc112013-02-10 01:53:46 +0000115 outputs_enabled = true;
116 } else {
Austin Schuh3d6e3df2014-02-17 01:51:03 -0800117 if (::aos::robot_state.get()) {
Brian Silverman9c9ab422014-02-25 13:42:53 -0800118 LOG_INTERVAL(driver_station_old_);
brians343bc112013-02-10 01:53:46 +0000119 } else {
Brian Silverman9c9ab422014-02-25 13:42:53 -0800120 LOG_INTERVAL(no_driver_station_);
brians343bc112013-02-10 01:53:46 +0000121 }
122 }
123
Brian Silverman2704ecf2014-04-09 20:24:03 -0700124 ::frc971::output_check_received.FetchLatest();
125 // True if we're enabled but the motors aren't working.
126 // The 100ms is the result of disabling the robot while it's putting out a lot
127 // of power and looking at the time delay between the last PWM pulse and the
128 // battery voltage coming back up.
129 const bool motors_off = !::frc971::output_check_received.get() ||
130 !::frc971::output_check_received.IsNewerThanMS(100);
131 motors_off_log_.Print();
132 if (motors_off) {
133 if (!::aos::robot_state.get() || ::aos::robot_state->enabled) {
134 LOG_INTERVAL(motors_off_log_);
135 }
136 outputs_enabled = false;
137 }
138
brians343bc112013-02-10 01:53:46 +0000139 // Run the iteration.
140 aos::ScopedMessagePtr<StatusType> status =
141 control_loop_->status.MakeMessage();
142 if (status.get() == NULL) {
143 return;
144 }
145
146 if (outputs_enabled) {
147 aos::ScopedMessagePtr<OutputType> output =
148 control_loop_->output.MakeMessage();
149 RunIteration(goal, position, output.get(), status.get());
150
Brian Silvermand6974f42014-02-14 13:39:21 -0800151 LOG_STRUCT(DEBUG, "output", *output);
brians343bc112013-02-10 01:53:46 +0000152 output.Send();
153 } else {
154 // The outputs are disabled, so pass NULL in for the output.
Ben Fredrickson4283bb42014-02-22 08:31:50 +0000155 RunIteration(goal, position, nullptr, status.get());
brians343bc112013-02-10 01:53:46 +0000156 ZeroOutputs();
157 }
158
Brian Silvermand6974f42014-02-14 13:39:21 -0800159 LOG_STRUCT(DEBUG, "status", *status);
brians343bc112013-02-10 01:53:46 +0000160 status.Send();
161}
162
Brian Silverman71fbee02014-03-13 17:24:54 -0700163template <class T, bool has_position, bool fail_no_position, bool fail_no_goal>
164void ControlLoop<T, has_position, fail_no_position, fail_no_goal>::Run() {
Brian Silvermanb407c672014-04-09 11:58:37 -0700165 ::aos::time::Time::EnableMockTime();
brians343bc112013-02-10 01:53:46 +0000166 while (true) {
Brian Silvermanb407c672014-04-09 11:58:37 -0700167 ::aos::time::Time::UpdateMockTime();
168 const ::aos::time::Time next_loop = NextLoopTime();
169 time::SleepUntil(next_loop);
170 ::aos::time::Time::SetMockTime(next_loop);
brians343bc112013-02-10 01:53:46 +0000171 Iterate();
172 }
173}
174
175} // namespace control_loops
176} // namespace aos