blob: e397cefbede5e0f5566d8e5b3f887d3707e4cab4 [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"
6
7namespace aos {
8namespace control_loops {
9
10// TODO(aschuh): Tests.
11
Daniel Petti96c1b572013-11-12 05:47:16 +000012template <class T, bool has_position, bool fail_no_position, bool ignore_stale>
13void ControlLoop<T, has_position, fail_no_position, ignore_stale>::ZeroOutputs() {
brians343bc112013-02-10 01:53:46 +000014 aos::ScopedMessagePtr<OutputType> output =
15 control_loop_->output.MakeMessage();
16 Zero(output.get());
17 output.Send();
18}
19
Daniel Petti96c1b572013-11-12 05:47:16 +000020template <class T, bool has_position, bool fail_no_position, bool ignore_stale>
21void ControlLoop<T, has_position, fail_no_position, ignore_stale>::Iterate() {
22 bool use_model = false;
brians343bc112013-02-10 01:53:46 +000023 // Temporary storage for printing out inputs and outputs.
24 char state[1024];
25
26 // Fetch the latest control loop goal and position. If there is no new
27 // goal, we will just reuse the old one.
28 // If there is no goal, we haven't started up fully. It isn't worth
29 // the added complexity for each loop implementation to handle that case.
30 control_loop_->goal.FetchLatest();
31 // TODO(aschuh): Check the age here if we want the loop to stop on old
32 // goals.
33 const GoalType *goal = control_loop_->goal.get();
34 if (goal == NULL) {
35 LOG(ERROR, "No prior control loop goal.\n");
36 ZeroOutputs();
37 return;
38 }
39 goal->Print(state, sizeof(state));
40 LOG(DEBUG, "goal={%s}\n", state);
41
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 {
Daniel Petti96c1b572013-11-12 05:47:16 +000052 if (ignore_stale) {
53 use_model = true;
54 }
brians343bc112013-02-10 01:53:46 +000055 if (control_loop_->position.get()) {
56 int msec_age = control_loop_->position.Age().ToMSec();
57 if (!control_loop_->position.IsNewerThanMS(kPositionTimeoutMs)) {
58 LOG(ERROR, "Stale position. %d ms > %d ms. Outputs disabled.\n",
59 msec_age, kPositionTimeoutMs);
Daniel Petti96c1b572013-11-12 05:47:16 +000060 if (!ignore_stale) {
61 ZeroOutputs();
62 return;
63 }
brians343bc112013-02-10 01:53:46 +000064 } else {
65 LOG(ERROR, "Stale position. %d ms\n", msec_age);
66 }
67 } else {
68 LOG(ERROR, "Never had a position.\n");
Brian Silverman10f997b2013-10-11 18:01:56 -070069 if (fail_no_position) {
Daniel Petti96c1b572013-11-12 05:47:16 +000070 ZeroOutputs();
71 return;
Brian Silverman10f997b2013-10-11 18:01:56 -070072 }
brians343bc112013-02-10 01:53:46 +000073 }
74 }
Austin Schuhfa033692013-02-24 01:00:55 -080075 if (position) {
76 position->Print(state, sizeof(state));
77 LOG(DEBUG, "position={%s}\n", state);
78 }
brians343bc112013-02-10 01:53:46 +000079 }
80
81 bool outputs_enabled = false;
82
83 // Check to see if we got a driver station packet recently.
84 if (aos::robot_state.FetchLatest()) {
85 outputs_enabled = true;
86 } else if (aos::robot_state.IsNewerThanMS(kDSPacketTimeoutMs)) {
87 outputs_enabled = true;
88 } else {
89 if (aos::robot_state.get()) {
90 int msec_age = aos::robot_state.Age().ToMSec();
91 LOG(ERROR, "Driver Station packet is too old (%d ms).\n", msec_age);
92 } else {
93 LOG(ERROR, "No Driver Station packet.\n");
94 }
95 }
96
97 // Run the iteration.
98 aos::ScopedMessagePtr<StatusType> status =
99 control_loop_->status.MakeMessage();
100 if (status.get() == NULL) {
101 return;
102 }
103
Daniel Petti96c1b572013-11-12 05:47:16 +0000104 LOG(DEBUG, "Outputs enabled: %d\n", outputs_enabled);
brians343bc112013-02-10 01:53:46 +0000105 if (outputs_enabled) {
Daniel Petti96c1b572013-11-12 05:47:16 +0000106 if (use_model) {
107 position = NULL;
108 }
brians343bc112013-02-10 01:53:46 +0000109 aos::ScopedMessagePtr<OutputType> output =
110 control_loop_->output.MakeMessage();
111 RunIteration(goal, position, output.get(), status.get());
112
113 output->Print(state, sizeof(state));
114 LOG(DEBUG, "output={%s}\n", state);
115 output.Send();
116 } else {
117 // The outputs are disabled, so pass NULL in for the output.
118 RunIteration(goal, position, NULL, status.get());
119 ZeroOutputs();
120 }
121
122 status->Print(state, sizeof(state));
123 LOG(DEBUG, "status={%s}\n", state);
124 status.Send();
125}
126
Daniel Petti96c1b572013-11-12 05:47:16 +0000127template <class T, bool has_position, bool fail_no_position, bool ignore_stale>
128void ControlLoop<T, has_position, fail_no_position, ignore_stale>::Run() {
brians343bc112013-02-10 01:53:46 +0000129 while (true) {
Brian Silverman15ca9852013-03-17 18:24:15 -0700130 time::SleepUntil(NextLoopTime());
brians343bc112013-02-10 01:53:46 +0000131 Iterate();
132 }
133}
134
135} // namespace control_loops
136} // namespace aos