blob: b5714d5d14f56f2b8f888f4667e4d86e18404a6a [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
8namespace aos {
9namespace control_loops {
10
11// TODO(aschuh): Tests.
12
Brian Silverman10f997b2013-10-11 18:01:56 -070013template <class T, bool has_position, bool fail_no_position>
Brian Silverman50a9d032014-02-16 17:20:57 -080014constexpr ::aos::time::Time
15 ControlLoop<T, has_position, fail_no_position>::kStaleLogInterval;
16
17template <class T, bool has_position, bool fail_no_position>
Brian Silverman10f997b2013-10-11 18:01:56 -070018void ControlLoop<T, has_position, fail_no_position>::ZeroOutputs() {
brians343bc112013-02-10 01:53:46 +000019 aos::ScopedMessagePtr<OutputType> output =
20 control_loop_->output.MakeMessage();
21 Zero(output.get());
22 output.Send();
23}
24
Brian Silverman10f997b2013-10-11 18:01:56 -070025template <class T, bool has_position, bool fail_no_position>
26void ControlLoop<T, has_position, fail_no_position>::Iterate() {
brians343bc112013-02-10 01:53:46 +000027 // Fetch the latest control loop goal and position. If there is no new
28 // goal, we will just reuse the old one.
29 // If there is no goal, we haven't started up fully. It isn't worth
30 // the added complexity for each loop implementation to handle that case.
31 control_loop_->goal.FetchLatest();
32 // TODO(aschuh): Check the age here if we want the loop to stop on old
33 // goals.
34 const GoalType *goal = control_loop_->goal.get();
35 if (goal == NULL) {
Brian Silverman50a9d032014-02-16 17:20:57 -080036 LOG_INTERVAL(no_prior_goal_);
brians343bc112013-02-10 01:53:46 +000037 ZeroOutputs();
38 return;
39 }
Brian Silvermand6974f42014-02-14 13:39:21 -080040 LOG_STRUCT(DEBUG, "goal", *goal);
brians343bc112013-02-10 01:53:46 +000041
42 // Only pass in a position if we got one this cycle.
43 const PositionType *position = NULL;
44
45 // Only fetch the latest position if we have one.
46 if (has_position) {
47 // If the position is stale, this is really bad. Try fetching a position
48 // and check how fresh it is, and then take the appropriate action.
49 if (control_loop_->position.FetchLatest()) {
50 position = control_loop_->position.get();
51 } else {
52 if (control_loop_->position.get()) {
53 int msec_age = control_loop_->position.Age().ToMSec();
54 if (!control_loop_->position.IsNewerThanMS(kPositionTimeoutMs)) {
Brian Silverman50a9d032014-02-16 17:20:57 -080055 LOG_INTERVAL(very_stale_position_);
brians343bc112013-02-10 01:53:46 +000056 ZeroOutputs();
57 return;
58 } else {
Brian Silverman50a9d032014-02-16 17:20:57 -080059 LOG(ERROR, "Stale position. %d ms (< %d ms)\n", msec_age,
60 kPositionTimeoutMs);
brians343bc112013-02-10 01:53:46 +000061 }
62 } else {
Brian Silverman50a9d032014-02-16 17:20:57 -080063 LOG_INTERVAL(no_prior_position_);
Brian Silverman10f997b2013-10-11 18:01:56 -070064 if (fail_no_position) {
65 ZeroOutputs();
66 return;
67 }
brians343bc112013-02-10 01:53:46 +000068 }
69 }
Austin Schuhfa033692013-02-24 01:00:55 -080070 if (position) {
Brian Silvermand6974f42014-02-14 13:39:21 -080071 LOG_STRUCT(DEBUG, "position", *position);
Austin Schuhfa033692013-02-24 01:00:55 -080072 }
brians343bc112013-02-10 01:53:46 +000073 }
74
75 bool outputs_enabled = false;
76
77 // Check to see if we got a driver station packet recently.
78 if (aos::robot_state.FetchLatest()) {
79 outputs_enabled = true;
80 } else if (aos::robot_state.IsNewerThanMS(kDSPacketTimeoutMs)) {
81 outputs_enabled = true;
82 } else {
83 if (aos::robot_state.get()) {
Brian Silverman50a9d032014-02-16 17:20:57 -080084 LOG_INTERVAL(driver_station_old_);
brians343bc112013-02-10 01:53:46 +000085 } else {
Brian Silverman50a9d032014-02-16 17:20:57 -080086 LOG_INTERVAL(no_driver_station_);
brians343bc112013-02-10 01:53:46 +000087 }
88 }
89
90 // Run the iteration.
91 aos::ScopedMessagePtr<StatusType> status =
92 control_loop_->status.MakeMessage();
93 if (status.get() == NULL) {
94 return;
95 }
96
97 if (outputs_enabled) {
98 aos::ScopedMessagePtr<OutputType> output =
99 control_loop_->output.MakeMessage();
100 RunIteration(goal, position, output.get(), status.get());
101
Brian Silvermand6974f42014-02-14 13:39:21 -0800102 LOG_STRUCT(DEBUG, "output", *output);
brians343bc112013-02-10 01:53:46 +0000103 output.Send();
104 } else {
105 // The outputs are disabled, so pass NULL in for the output.
106 RunIteration(goal, position, NULL, status.get());
107 ZeroOutputs();
108 }
109
Brian Silvermand6974f42014-02-14 13:39:21 -0800110 LOG_STRUCT(DEBUG, "status", *status);
brians343bc112013-02-10 01:53:46 +0000111 status.Send();
112}
113
Brian Silverman10f997b2013-10-11 18:01:56 -0700114template <class T, bool has_position, bool fail_no_position>
115void ControlLoop<T, has_position, fail_no_position>::Run() {
brians343bc112013-02-10 01:53:46 +0000116 while (true) {
Brian Silverman15ca9852013-03-17 18:24:15 -0700117 time::SleepUntil(NextLoopTime());
brians343bc112013-02-10 01:53:46 +0000118 Iterate();
119 }
120}
121
122} // namespace control_loops
123} // namespace aos