blob: 032d5fc26f28cbe01fa2293ea2b3f066da73e9d5 [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() {
brians343bc112013-02-10 01:53:46 +000029 // Fetch the latest control loop goal and position. If there is no new
30 // goal, we will just reuse the old one.
31 // If there is no goal, we haven't started up fully. It isn't worth
32 // the added complexity for each loop implementation to handle that case.
33 control_loop_->goal.FetchLatest();
34 // TODO(aschuh): Check the age here if we want the loop to stop on old
35 // goals.
36 const GoalType *goal = control_loop_->goal.get();
37 if (goal == NULL) {
Brian Silverman50a9d032014-02-16 17:20:57 -080038 LOG_INTERVAL(no_prior_goal_);
brians343bc112013-02-10 01:53:46 +000039 ZeroOutputs();
40 return;
41 }
Austin Schuh3d6e3df2014-02-17 01:51:03 -080042
43 ::bbb::sensor_generation.FetchLatest();
44 if (::bbb::sensor_generation.get() == nullptr) {
Brian Silverman4910efb2014-02-17 11:10:10 -080045 LOG_INTERVAL(no_sensor_generation_);
Austin Schuh3d6e3df2014-02-17 01:51:03 -080046 ZeroOutputs();
47 return;
48 }
49 if (!has_sensor_reset_counters_ ||
50 ::bbb::sensor_generation->reader_pid != reader_pid_ ||
51 ::bbb::sensor_generation->cape_resets != cape_resets_) {
Brian Silverman4910efb2014-02-17 11:10:10 -080052 char buffer[128];
53 size_t characters = ::bbb::sensor_generation->Print(buffer, sizeof(buffer));
54 LOG(INFO, "new sensor_generation message %.*s\n",
55 static_cast<int>(characters), buffer);
56
Austin Schuh3d6e3df2014-02-17 01:51:03 -080057 reader_pid_ = ::bbb::sensor_generation->reader_pid;
58 cape_resets_ = ::bbb::sensor_generation->cape_resets;
59 has_sensor_reset_counters_ = true;
60 reset_ = true;
61 }
62
Brian Silvermand6974f42014-02-14 13:39:21 -080063 LOG_STRUCT(DEBUG, "goal", *goal);
brians343bc112013-02-10 01:53:46 +000064
65 // Only pass in a position if we got one this cycle.
66 const PositionType *position = NULL;
67
68 // Only fetch the latest position if we have one.
69 if (has_position) {
70 // If the position is stale, this is really bad. Try fetching a position
71 // and check how fresh it is, and then take the appropriate action.
72 if (control_loop_->position.FetchLatest()) {
73 position = control_loop_->position.get();
74 } else {
75 if (control_loop_->position.get()) {
76 int msec_age = control_loop_->position.Age().ToMSec();
77 if (!control_loop_->position.IsNewerThanMS(kPositionTimeoutMs)) {
Brian Silverman50a9d032014-02-16 17:20:57 -080078 LOG_INTERVAL(very_stale_position_);
brians343bc112013-02-10 01:53:46 +000079 ZeroOutputs();
80 return;
81 } else {
Brian Silverman50a9d032014-02-16 17:20:57 -080082 LOG(ERROR, "Stale position. %d ms (< %d ms)\n", msec_age,
83 kPositionTimeoutMs);
brians343bc112013-02-10 01:53:46 +000084 }
85 } else {
Brian Silverman50a9d032014-02-16 17:20:57 -080086 LOG_INTERVAL(no_prior_position_);
Brian Silverman10f997b2013-10-11 18:01:56 -070087 if (fail_no_position) {
88 ZeroOutputs();
89 return;
90 }
brians343bc112013-02-10 01:53:46 +000091 }
92 }
Austin Schuhfa033692013-02-24 01:00:55 -080093 if (position) {
Brian Silvermand6974f42014-02-14 13:39:21 -080094 LOG_STRUCT(DEBUG, "position", *position);
Austin Schuhfa033692013-02-24 01:00:55 -080095 }
brians343bc112013-02-10 01:53:46 +000096 }
97
98 bool outputs_enabled = false;
99
100 // Check to see if we got a driver station packet recently.
Austin Schuh3d6e3df2014-02-17 01:51:03 -0800101 if (::aos::robot_state.FetchLatest()) {
brians343bc112013-02-10 01:53:46 +0000102 outputs_enabled = true;
Austin Schuh3d6e3df2014-02-17 01:51:03 -0800103 } else if (::aos::robot_state.IsNewerThanMS(kDSPacketTimeoutMs)) {
brians343bc112013-02-10 01:53:46 +0000104 outputs_enabled = true;
105 } else {
Austin Schuh3d6e3df2014-02-17 01:51:03 -0800106 if (::aos::robot_state.get()) {
Brian Silverman50a9d032014-02-16 17:20:57 -0800107 LOG_INTERVAL(driver_station_old_);
brians343bc112013-02-10 01:53:46 +0000108 } else {
Brian Silverman50a9d032014-02-16 17:20:57 -0800109 LOG_INTERVAL(no_driver_station_);
brians343bc112013-02-10 01:53:46 +0000110 }
111 }
112
113 // Run the iteration.
114 aos::ScopedMessagePtr<StatusType> status =
115 control_loop_->status.MakeMessage();
116 if (status.get() == NULL) {
117 return;
118 }
119
120 if (outputs_enabled) {
121 aos::ScopedMessagePtr<OutputType> output =
122 control_loop_->output.MakeMessage();
123 RunIteration(goal, position, output.get(), status.get());
124
Brian Silvermand6974f42014-02-14 13:39:21 -0800125 LOG_STRUCT(DEBUG, "output", *output);
brians343bc112013-02-10 01:53:46 +0000126 output.Send();
127 } else {
128 // The outputs are disabled, so pass NULL in for the output.
129 RunIteration(goal, position, NULL, status.get());
130 ZeroOutputs();
131 }
132
Brian Silvermand6974f42014-02-14 13:39:21 -0800133 LOG_STRUCT(DEBUG, "status", *status);
brians343bc112013-02-10 01:53:46 +0000134 status.Send();
135}
136
Brian Silverman10f997b2013-10-11 18:01:56 -0700137template <class T, bool has_position, bool fail_no_position>
138void ControlLoop<T, has_position, fail_no_position>::Run() {
brians343bc112013-02-10 01:53:46 +0000139 while (true) {
Brian Silverman15ca9852013-03-17 18:24:15 -0700140 time::SleepUntil(NextLoopTime());
brians343bc112013-02-10 01:53:46 +0000141 Iterate();
142 }
143}
144
145} // namespace control_loops
146} // namespace aos