blob: 4e1999c6c5af9a42516e69d7eaab19cfd7cb015a [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) {
45 ZeroOutputs();
46 return;
47 }
48 if (!has_sensor_reset_counters_ ||
49 ::bbb::sensor_generation->reader_pid != reader_pid_ ||
50 ::bbb::sensor_generation->cape_resets != cape_resets_) {
51 reader_pid_ = ::bbb::sensor_generation->reader_pid;
52 cape_resets_ = ::bbb::sensor_generation->cape_resets;
53 has_sensor_reset_counters_ = true;
54 reset_ = true;
55 }
56
Brian Silvermand6974f42014-02-14 13:39:21 -080057 LOG_STRUCT(DEBUG, "goal", *goal);
brians343bc112013-02-10 01:53:46 +000058
59 // Only pass in a position if we got one this cycle.
60 const PositionType *position = NULL;
61
62 // Only fetch the latest position if we have one.
63 if (has_position) {
64 // If the position is stale, this is really bad. Try fetching a position
65 // and check how fresh it is, and then take the appropriate action.
66 if (control_loop_->position.FetchLatest()) {
67 position = control_loop_->position.get();
68 } else {
69 if (control_loop_->position.get()) {
70 int msec_age = control_loop_->position.Age().ToMSec();
71 if (!control_loop_->position.IsNewerThanMS(kPositionTimeoutMs)) {
Brian Silverman50a9d032014-02-16 17:20:57 -080072 LOG_INTERVAL(very_stale_position_);
brians343bc112013-02-10 01:53:46 +000073 ZeroOutputs();
74 return;
75 } else {
Brian Silverman50a9d032014-02-16 17:20:57 -080076 LOG(ERROR, "Stale position. %d ms (< %d ms)\n", msec_age,
77 kPositionTimeoutMs);
brians343bc112013-02-10 01:53:46 +000078 }
79 } else {
Brian Silverman50a9d032014-02-16 17:20:57 -080080 LOG_INTERVAL(no_prior_position_);
Brian Silverman10f997b2013-10-11 18:01:56 -070081 if (fail_no_position) {
82 ZeroOutputs();
83 return;
84 }
brians343bc112013-02-10 01:53:46 +000085 }
86 }
Austin Schuhfa033692013-02-24 01:00:55 -080087 if (position) {
Brian Silvermand6974f42014-02-14 13:39:21 -080088 LOG_STRUCT(DEBUG, "position", *position);
Austin Schuhfa033692013-02-24 01:00:55 -080089 }
brians343bc112013-02-10 01:53:46 +000090 }
91
92 bool outputs_enabled = false;
93
94 // Check to see if we got a driver station packet recently.
Austin Schuh3d6e3df2014-02-17 01:51:03 -080095 if (::aos::robot_state.FetchLatest()) {
brians343bc112013-02-10 01:53:46 +000096 outputs_enabled = true;
Austin Schuh3d6e3df2014-02-17 01:51:03 -080097 } else if (::aos::robot_state.IsNewerThanMS(kDSPacketTimeoutMs)) {
brians343bc112013-02-10 01:53:46 +000098 outputs_enabled = true;
99 } else {
Austin Schuh3d6e3df2014-02-17 01:51:03 -0800100 if (::aos::robot_state.get()) {
Brian Silverman50a9d032014-02-16 17:20:57 -0800101 LOG_INTERVAL(driver_station_old_);
brians343bc112013-02-10 01:53:46 +0000102 } else {
Brian Silverman50a9d032014-02-16 17:20:57 -0800103 LOG_INTERVAL(no_driver_station_);
brians343bc112013-02-10 01:53:46 +0000104 }
105 }
106
107 // Run the iteration.
108 aos::ScopedMessagePtr<StatusType> status =
109 control_loop_->status.MakeMessage();
110 if (status.get() == NULL) {
111 return;
112 }
113
114 if (outputs_enabled) {
115 aos::ScopedMessagePtr<OutputType> output =
116 control_loop_->output.MakeMessage();
117 RunIteration(goal, position, output.get(), status.get());
118
Brian Silvermand6974f42014-02-14 13:39:21 -0800119 LOG_STRUCT(DEBUG, "output", *output);
brians343bc112013-02-10 01:53:46 +0000120 output.Send();
121 } else {
122 // The outputs are disabled, so pass NULL in for the output.
123 RunIteration(goal, position, NULL, status.get());
124 ZeroOutputs();
125 }
126
Brian Silvermand6974f42014-02-14 13:39:21 -0800127 LOG_STRUCT(DEBUG, "status", *status);
brians343bc112013-02-10 01:53:46 +0000128 status.Send();
129}
130
Brian Silverman10f997b2013-10-11 18:01:56 -0700131template <class T, bool has_position, bool fail_no_position>
132void ControlLoop<T, has_position, fail_no_position>::Run() {
brians343bc112013-02-10 01:53:46 +0000133 while (true) {
Brian Silverman15ca9852013-03-17 18:24:15 -0700134 time::SleepUntil(NextLoopTime());
brians343bc112013-02-10 01:53:46 +0000135 Iterate();
136 }
137}
138
139} // namespace control_loops
140} // namespace aos