blob: 46178f10bc52ede6839c1a422e71eb6e355d740c [file] [log] [blame]
brians343bc112013-02-10 01:53:46 +00001#include <stddef.h>
2
3#include "aos/common/logging/logging.h"
4#include "aos/common/control_loop/Timing.h"
5#include "aos/common/messages/RobotState.q.h"
Brian Silvermand6974f42014-02-14 13:39:21 -08006#include "aos/common/logging/queue_logging.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 Silverman10f997b2013-10-11 18:01:56 -070015template <class T, bool has_position, bool fail_no_position>
Brian Silverman50a9d032014-02-16 17:20:57 -080016constexpr ::aos::time::Time
17 ControlLoop<T, has_position, fail_no_position>::kStaleLogInterval;
18
19template <class T, bool has_position, bool fail_no_position>
Brian Silverman10f997b2013-10-11 18:01:56 -070020void ControlLoop<T, has_position, fail_no_position>::ZeroOutputs() {
brians343bc112013-02-10 01:53:46 +000021 aos::ScopedMessagePtr<OutputType> output =
22 control_loop_->output.MakeMessage();
23 Zero(output.get());
24 output.Send();
25}
26
Brian Silverman10f997b2013-10-11 18:01:56 -070027template <class T, bool has_position, bool fail_no_position>
28void ControlLoop<T, has_position, fail_no_position>::Iterate() {
Brian Silverman6a1cd212014-02-20 21:04:34 -080029 LOG_INTERVAL(no_prior_goal_);
30 LOG_INTERVAL(no_sensor_generation_);
31 LOG_INTERVAL(very_stale_position_);
32 LOG_INTERVAL(no_prior_position_);
33 LOG_INTERVAL(driver_station_old_);
34 LOG_INTERVAL(no_driver_station_);
35
brians343bc112013-02-10 01:53:46 +000036 // Fetch the latest control loop goal and position. If there is no new
37 // goal, we will just reuse the old one.
38 // If there is no goal, we haven't started up fully. It isn't worth
39 // the added complexity for each loop implementation to handle that case.
40 control_loop_->goal.FetchLatest();
41 // TODO(aschuh): Check the age here if we want the loop to stop on old
42 // goals.
43 const GoalType *goal = control_loop_->goal.get();
44 if (goal == NULL) {
Brian Silverman6a1cd212014-02-20 21:04:34 -080045 no_prior_goal_.WantToLog();
brians343bc112013-02-10 01:53:46 +000046 ZeroOutputs();
47 return;
48 }
Austin Schuh3d6e3df2014-02-17 01:51:03 -080049
50 ::bbb::sensor_generation.FetchLatest();
51 if (::bbb::sensor_generation.get() == nullptr) {
Brian Silverman6a1cd212014-02-20 21:04:34 -080052 no_sensor_generation_.WantToLog();
Austin Schuh3d6e3df2014-02-17 01:51:03 -080053 ZeroOutputs();
54 return;
55 }
56 if (!has_sensor_reset_counters_ ||
57 ::bbb::sensor_generation->reader_pid != reader_pid_ ||
58 ::bbb::sensor_generation->cape_resets != cape_resets_) {
Brian Silverman4910efb2014-02-17 11:10:10 -080059 char buffer[128];
60 size_t characters = ::bbb::sensor_generation->Print(buffer, sizeof(buffer));
61 LOG(INFO, "new sensor_generation message %.*s\n",
62 static_cast<int>(characters), buffer);
63
Austin Schuh3d6e3df2014-02-17 01:51:03 -080064 reader_pid_ = ::bbb::sensor_generation->reader_pid;
65 cape_resets_ = ::bbb::sensor_generation->cape_resets;
66 has_sensor_reset_counters_ = true;
67 reset_ = true;
68 }
69
Brian Silvermand6974f42014-02-14 13:39:21 -080070 LOG_STRUCT(DEBUG, "goal", *goal);
brians343bc112013-02-10 01:53:46 +000071
72 // Only pass in a position if we got one this cycle.
73 const PositionType *position = NULL;
74
75 // Only fetch the latest position if we have one.
76 if (has_position) {
77 // If the position is stale, this is really bad. Try fetching a position
78 // and check how fresh it is, and then take the appropriate action.
79 if (control_loop_->position.FetchLatest()) {
80 position = control_loop_->position.get();
81 } else {
Ben Fredrickson4283bb42014-02-22 08:31:50 +000082 if (control_loop_->position.get() && !reset_) {
brians343bc112013-02-10 01:53:46 +000083 int msec_age = control_loop_->position.Age().ToMSec();
84 if (!control_loop_->position.IsNewerThanMS(kPositionTimeoutMs)) {
Brian Silverman6a1cd212014-02-20 21:04:34 -080085 very_stale_position_.WantToLog();
brians343bc112013-02-10 01:53:46 +000086 ZeroOutputs();
87 return;
88 } else {
Brian Silverman50a9d032014-02-16 17:20:57 -080089 LOG(ERROR, "Stale position. %d ms (< %d ms)\n", msec_age,
90 kPositionTimeoutMs);
brians343bc112013-02-10 01:53:46 +000091 }
92 } else {
Brian Silverman6a1cd212014-02-20 21:04:34 -080093 no_prior_position_.WantToLog();
Brian Silverman10f997b2013-10-11 18:01:56 -070094 if (fail_no_position) {
95 ZeroOutputs();
96 return;
97 }
brians343bc112013-02-10 01:53:46 +000098 }
99 }
Austin Schuhfa033692013-02-24 01:00:55 -0800100 if (position) {
Brian Silvermand6974f42014-02-14 13:39:21 -0800101 LOG_STRUCT(DEBUG, "position", *position);
Austin Schuhfa033692013-02-24 01:00:55 -0800102 }
brians343bc112013-02-10 01:53:46 +0000103 }
104
105 bool outputs_enabled = false;
106
107 // Check to see if we got a driver station packet recently.
Austin Schuh3d6e3df2014-02-17 01:51:03 -0800108 if (::aos::robot_state.FetchLatest()) {
brians343bc112013-02-10 01:53:46 +0000109 outputs_enabled = true;
Austin Schuh3d6e3df2014-02-17 01:51:03 -0800110 } else if (::aos::robot_state.IsNewerThanMS(kDSPacketTimeoutMs)) {
brians343bc112013-02-10 01:53:46 +0000111 outputs_enabled = true;
112 } else {
Austin Schuh3d6e3df2014-02-17 01:51:03 -0800113 if (::aos::robot_state.get()) {
Brian Silverman6a1cd212014-02-20 21:04:34 -0800114 driver_station_old_.WantToLog();
brians343bc112013-02-10 01:53:46 +0000115 } else {
Brian Silverman6a1cd212014-02-20 21:04:34 -0800116 no_driver_station_.WantToLog();
brians343bc112013-02-10 01:53:46 +0000117 }
118 }
119
120 // Run the iteration.
121 aos::ScopedMessagePtr<StatusType> status =
122 control_loop_->status.MakeMessage();
123 if (status.get() == NULL) {
124 return;
125 }
126
127 if (outputs_enabled) {
128 aos::ScopedMessagePtr<OutputType> output =
129 control_loop_->output.MakeMessage();
130 RunIteration(goal, position, output.get(), status.get());
131
Brian Silvermand6974f42014-02-14 13:39:21 -0800132 LOG_STRUCT(DEBUG, "output", *output);
brians343bc112013-02-10 01:53:46 +0000133 output.Send();
134 } else {
135 // The outputs are disabled, so pass NULL in for the output.
Ben Fredrickson4283bb42014-02-22 08:31:50 +0000136 RunIteration(goal, position, nullptr, status.get());
brians343bc112013-02-10 01:53:46 +0000137 ZeroOutputs();
138 }
139
Brian Silvermand6974f42014-02-14 13:39:21 -0800140 LOG_STRUCT(DEBUG, "status", *status);
brians343bc112013-02-10 01:53:46 +0000141 status.Send();
142}
143
Brian Silverman10f997b2013-10-11 18:01:56 -0700144template <class T, bool has_position, bool fail_no_position>
145void ControlLoop<T, has_position, fail_no_position>::Run() {
brians343bc112013-02-10 01:53:46 +0000146 while (true) {
Brian Silverman15ca9852013-03-17 18:24:15 -0700147 time::SleepUntil(NextLoopTime());
brians343bc112013-02-10 01:53:46 +0000148 Iterate();
149 }
150}
151
152} // namespace control_loops
153} // namespace aos