blob: dde33c073749ed0eacb5fd83da1b24fdbb01bfbb [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"
9
brians343bc112013-02-10 01:53:46 +000010namespace aos {
11namespace control_loops {
12
13// TODO(aschuh): Tests.
14
Brian Silverman71fbee02014-03-13 17:24:54 -070015template <class T, bool has_position, bool fail_no_position, bool fail_no_goal>
16constexpr ::aos::time::Time ControlLoop<T, has_position, fail_no_position,
17 fail_no_goal>::kStaleLogInterval;
Brian Silverman50a9d032014-02-16 17:20:57 -080018
Brian Silverman71fbee02014-03-13 17:24:54 -070019template <class T, bool has_position, bool fail_no_position, bool fail_no_goal>
20void
21ControlLoop<T, has_position, fail_no_position, fail_no_goal>::ZeroOutputs() {
brians343bc112013-02-10 01:53:46 +000022 aos::ScopedMessagePtr<OutputType> output =
23 control_loop_->output.MakeMessage();
24 Zero(output.get());
25 output.Send();
26}
27
Brian Silverman71fbee02014-03-13 17:24:54 -070028template <class T, bool has_position, bool fail_no_position, bool fail_no_goal>
29void ControlLoop<T, has_position, fail_no_position, fail_no_goal>::Iterate() {
Brian Silverman9c9ab422014-02-25 13:42:53 -080030 no_prior_goal_.Print();
31 no_sensor_generation_.Print();
32 very_stale_position_.Print();
33 no_prior_position_.Print();
34 driver_station_old_.Print();
35 no_driver_station_.Print();
Brian Silverman6a1cd212014-02-20 21:04:34 -080036
brians343bc112013-02-10 01:53:46 +000037 // Fetch the latest control loop goal and position. If there is no new
38 // goal, we will just reuse the old one.
39 // If there is no goal, we haven't started up fully. It isn't worth
40 // the added complexity for each loop implementation to handle that case.
41 control_loop_->goal.FetchLatest();
42 // TODO(aschuh): Check the age here if we want the loop to stop on old
43 // goals.
44 const GoalType *goal = control_loop_->goal.get();
45 if (goal == NULL) {
Brian Silverman9c9ab422014-02-25 13:42:53 -080046 LOG_INTERVAL(no_prior_goal_);
Brian Silverman71fbee02014-03-13 17:24:54 -070047 if (fail_no_goal) {
48 ZeroOutputs();
49 return;
50 }
brians343bc112013-02-10 01:53:46 +000051 }
Austin Schuh3d6e3df2014-02-17 01:51:03 -080052
53 ::bbb::sensor_generation.FetchLatest();
54 if (::bbb::sensor_generation.get() == nullptr) {
Brian Silverman9c9ab422014-02-25 13:42:53 -080055 LOG_INTERVAL(no_sensor_generation_);
Austin Schuh3d6e3df2014-02-17 01:51:03 -080056 ZeroOutputs();
57 return;
58 }
59 if (!has_sensor_reset_counters_ ||
60 ::bbb::sensor_generation->reader_pid != reader_pid_ ||
61 ::bbb::sensor_generation->cape_resets != cape_resets_) {
Brian Silverman71fbee02014-03-13 17:24:54 -070062 LOG_STRUCT(INFO, "new sensor_generation message",
63 *::bbb::sensor_generation.get());
Brian Silverman4910efb2014-02-17 11:10:10 -080064
Austin Schuh3d6e3df2014-02-17 01:51:03 -080065 reader_pid_ = ::bbb::sensor_generation->reader_pid;
66 cape_resets_ = ::bbb::sensor_generation->cape_resets;
67 has_sensor_reset_counters_ = true;
68 reset_ = true;
69 }
Brian Silverman71fbee02014-03-13 17:24:54 -070070
71 if (goal) {
72 LOG_STRUCT(DEBUG, "goal", *goal);
73 }
brians343bc112013-02-10 01:53:46 +000074
75 // Only pass in a position if we got one this cycle.
76 const PositionType *position = NULL;
77
78 // Only fetch the latest position if we have one.
79 if (has_position) {
80 // If the position is stale, this is really bad. Try fetching a position
81 // and check how fresh it is, and then take the appropriate action.
82 if (control_loop_->position.FetchLatest()) {
83 position = control_loop_->position.get();
84 } else {
Ben Fredrickson4283bb42014-02-22 08:31:50 +000085 if (control_loop_->position.get() && !reset_) {
brians343bc112013-02-10 01:53:46 +000086 int msec_age = control_loop_->position.Age().ToMSec();
87 if (!control_loop_->position.IsNewerThanMS(kPositionTimeoutMs)) {
Brian Silverman9c9ab422014-02-25 13:42:53 -080088 LOG_INTERVAL(very_stale_position_);
brians343bc112013-02-10 01:53:46 +000089 ZeroOutputs();
90 return;
91 } else {
Brian Silverman50a9d032014-02-16 17:20:57 -080092 LOG(ERROR, "Stale position. %d ms (< %d ms)\n", msec_age,
93 kPositionTimeoutMs);
brians343bc112013-02-10 01:53:46 +000094 }
95 } else {
Brian Silverman9c9ab422014-02-25 13:42:53 -080096 LOG_INTERVAL(no_prior_position_);
Brian Silverman10f997b2013-10-11 18:01:56 -070097 if (fail_no_position) {
98 ZeroOutputs();
99 return;
100 }
brians343bc112013-02-10 01:53:46 +0000101 }
102 }
Austin Schuhfa033692013-02-24 01:00:55 -0800103 if (position) {
Brian Silvermand6974f42014-02-14 13:39:21 -0800104 LOG_STRUCT(DEBUG, "position", *position);
Austin Schuhfa033692013-02-24 01:00:55 -0800105 }
brians343bc112013-02-10 01:53:46 +0000106 }
107
108 bool outputs_enabled = false;
109
110 // Check to see if we got a driver station packet recently.
Austin Schuh3d6e3df2014-02-17 01:51:03 -0800111 if (::aos::robot_state.FetchLatest()) {
brians343bc112013-02-10 01:53:46 +0000112 outputs_enabled = true;
Austin Schuh3d6e3df2014-02-17 01:51:03 -0800113 } else if (::aos::robot_state.IsNewerThanMS(kDSPacketTimeoutMs)) {
brians343bc112013-02-10 01:53:46 +0000114 outputs_enabled = true;
115 } else {
Austin Schuh3d6e3df2014-02-17 01:51:03 -0800116 if (::aos::robot_state.get()) {
Brian Silverman9c9ab422014-02-25 13:42:53 -0800117 LOG_INTERVAL(driver_station_old_);
brians343bc112013-02-10 01:53:46 +0000118 } else {
Brian Silverman9c9ab422014-02-25 13:42:53 -0800119 LOG_INTERVAL(no_driver_station_);
brians343bc112013-02-10 01:53:46 +0000120 }
121 }
122
123 // Run the iteration.
124 aos::ScopedMessagePtr<StatusType> status =
125 control_loop_->status.MakeMessage();
126 if (status.get() == NULL) {
127 return;
128 }
129
130 if (outputs_enabled) {
131 aos::ScopedMessagePtr<OutputType> output =
132 control_loop_->output.MakeMessage();
133 RunIteration(goal, position, output.get(), status.get());
134
Brian Silvermand6974f42014-02-14 13:39:21 -0800135 LOG_STRUCT(DEBUG, "output", *output);
brians343bc112013-02-10 01:53:46 +0000136 output.Send();
137 } else {
138 // The outputs are disabled, so pass NULL in for the output.
Ben Fredrickson4283bb42014-02-22 08:31:50 +0000139 RunIteration(goal, position, nullptr, status.get());
brians343bc112013-02-10 01:53:46 +0000140 ZeroOutputs();
141 }
142
Brian Silvermand6974f42014-02-14 13:39:21 -0800143 LOG_STRUCT(DEBUG, "status", *status);
brians343bc112013-02-10 01:53:46 +0000144 status.Send();
145}
146
Brian Silverman71fbee02014-03-13 17:24:54 -0700147template <class T, bool has_position, bool fail_no_position, bool fail_no_goal>
148void ControlLoop<T, has_position, fail_no_position, fail_no_goal>::Run() {
brians343bc112013-02-10 01:53:46 +0000149 while (true) {
Brian Silverman15ca9852013-03-17 18:24:15 -0700150 time::SleepUntil(NextLoopTime());
brians343bc112013-02-10 01:53:46 +0000151 Iterate();
152 }
153}
154
155} // namespace control_loops
156} // namespace aos