blob: a19acbeb6dd6da01a60d4f2e79558789e64b4b21 [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2011-2017. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "Commands/CommandGroup.h"
9
10#include "WPIErrors.h"
11
12using namespace frc;
13
14/**
15 * Creates a new {@link CommandGroup CommandGroup} with the given name.
16 * @param name the name for this command group
17 */
18CommandGroup::CommandGroup(const std::string& name) : Command(name) {}
19
20/**
21 * Adds a new {@link Command Command} to the group. The {@link Command Command}
22 * will be started after all the previously added {@link Command Commands}.
23 *
24 * <p>Note that any requirements the given {@link Command Command} has will be
25 * added to the group. For this reason, a {@link Command Command's}
26 * requirements can not be changed after being added to a group.</p>
27 *
28 * <p>It is recommended that this method be called in the constructor.</p>
29 *
30 * @param command The {@link Command Command} to be added
31 */
32void CommandGroup::AddSequential(Command* command) {
33 if (command == nullptr) {
34 wpi_setWPIErrorWithContext(NullParameter, "command");
35 return;
36 }
37 if (!AssertUnlocked("Cannot add new command to command group")) return;
38
39 command->SetParent(this);
40
41 m_commands.push_back(
42 CommandGroupEntry(command, CommandGroupEntry::kSequence_InSequence));
43 // Iterate through command->GetRequirements() and call Requires() on each
44 // required subsystem
45 Command::SubsystemSet requirements = command->GetRequirements();
46 auto iter = requirements.begin();
47 for (; iter != requirements.end(); iter++) Requires(*iter);
48}
49
50/**
51 * Adds a new {@link Command Command} to the group with a given timeout.
52 * The {@link Command Command} will be started after all the previously added
53 * commands.
54 *
55 * <p>Once the {@link Command Command} is started, it will be run until it
56 * finishes or the time expires, whichever is sooner. Note that the given
57 * {@link Command Command} will have no knowledge that it is on a timer.</p>
58 *
59 * <p>Note that any requirements the given {@link Command Command} has will be
60 * added to the group. For this reason, a {@link Command Command's}
61 * requirements can not be changed after being added to a group.</p>
62 *
63 * <p>It is recommended that this method be called in the constructor.</p>
64 *
65 * @param command The {@link Command Command} to be added
66 * @param timeout The timeout (in seconds)
67 */
68void CommandGroup::AddSequential(Command* command, double timeout) {
69 if (command == nullptr) {
70 wpi_setWPIErrorWithContext(NullParameter, "command");
71 return;
72 }
73 if (!AssertUnlocked("Cannot add new command to command group")) return;
74 if (timeout < 0.0) {
75 wpi_setWPIErrorWithContext(ParameterOutOfRange, "timeout < 0.0");
76 return;
77 }
78
79 command->SetParent(this);
80
81 m_commands.push_back(CommandGroupEntry(
82 command, CommandGroupEntry::kSequence_InSequence, timeout));
83 // Iterate through command->GetRequirements() and call Requires() on each
84 // required subsystem
85 Command::SubsystemSet requirements = command->GetRequirements();
86 auto iter = requirements.begin();
87 for (; iter != requirements.end(); iter++) Requires(*iter);
88}
89
90/**
91 * Adds a new child {@link Command} to the group. The {@link Command} will be
92 * started after all the previously added {@link Command Commands}.
93 *
94 * <p>Instead of waiting for the child to finish, a {@link CommandGroup} will
95 * have it run at the same time as the subsequent {@link Command Commands}.
96 * The child will run until either it finishes, a new child with conflicting
97 * requirements is started, or the main sequence runs a {@link Command} with
98 * conflicting requirements. In the latter two cases, the child will be
99 * canceled even if it says it can't be interrupted.</p>
100 *
101 * <p>Note that any requirements the given {@link Command Command} has will be
102 * added to the group. For this reason, a {@link Command Command's}
103 * requirements can not be changed after being added to a group.</p>
104 *
105 * <p>It is recommended that this method be called in the constructor.</p>
106 *
107 * @param command The command to be added
108 */
109void CommandGroup::AddParallel(Command* command) {
110 if (command == nullptr) {
111 wpi_setWPIErrorWithContext(NullParameter, "command");
112 return;
113 }
114 if (!AssertUnlocked("Cannot add new command to command group")) return;
115
116 command->SetParent(this);
117
118 m_commands.push_back(
119 CommandGroupEntry(command, CommandGroupEntry::kSequence_BranchChild));
120 // Iterate through command->GetRequirements() and call Requires() on each
121 // required subsystem
122 Command::SubsystemSet requirements = command->GetRequirements();
123 auto iter = requirements.begin();
124 for (; iter != requirements.end(); iter++) Requires(*iter);
125}
126
127/**
128 * Adds a new child {@link Command} to the group with the given timeout. The
129 * {@link Command} will be started after all the previously added
130 * {@link Command Commands}.
131 *
132 * <p>Once the {@link Command Command} is started, it will run until it
133 * finishes, is interrupted, or the time expires, whichever is sooner. Note
134 * that the given {@link Command Command} will have no knowledge that it is on
135 * a timer.</p>
136 *
137 * <p>Instead of waiting for the child to finish, a {@link CommandGroup} will
138 * have it run at the same time as the subsequent {@link Command Commands}.
139 * The child will run until either it finishes, the timeout expires, a new
140 * child with conflicting requirements is started, or the main sequence runs a
141 * {@link Command} with conflicting requirements. In the latter two cases, the
142 * child will be canceled even if it says it can't be interrupted.</p>
143 *
144 * <p>Note that any requirements the given {@link Command Command} has will be
145 * added to the group. For this reason, a {@link Command Command's}
146 * requirements can not be changed after being added to a group.</p>
147 *
148 * <p>It is recommended that this method be called in the constructor.</p>
149 *
150 * @param command The command to be added
151 * @param timeout The timeout (in seconds)
152 */
153void CommandGroup::AddParallel(Command* command, double timeout) {
154 if (command == nullptr) {
155 wpi_setWPIErrorWithContext(NullParameter, "command");
156 return;
157 }
158 if (!AssertUnlocked("Cannot add new command to command group")) return;
159 if (timeout < 0.0) {
160 wpi_setWPIErrorWithContext(ParameterOutOfRange, "timeout < 0.0");
161 return;
162 }
163
164 command->SetParent(this);
165
166 m_commands.push_back(CommandGroupEntry(
167 command, CommandGroupEntry::kSequence_BranchChild, timeout));
168 // Iterate through command->GetRequirements() and call Requires() on each
169 // required subsystem
170 Command::SubsystemSet requirements = command->GetRequirements();
171 auto iter = requirements.begin();
172 for (; iter != requirements.end(); iter++) Requires(*iter);
173}
174
175void CommandGroup::_Initialize() { m_currentCommandIndex = -1; }
176
177void CommandGroup::_Execute() {
178 CommandGroupEntry entry;
179 Command* cmd = nullptr;
180 bool firstRun = false;
181
182 if (m_currentCommandIndex == -1) {
183 firstRun = true;
184 m_currentCommandIndex = 0;
185 }
186
187 while (static_cast<size_t>(m_currentCommandIndex) < m_commands.size()) {
188 if (cmd != nullptr) {
189 if (entry.IsTimedOut()) cmd->_Cancel();
190
191 if (cmd->Run()) {
192 break;
193 } else {
194 cmd->Removed();
195 m_currentCommandIndex++;
196 firstRun = true;
197 cmd = nullptr;
198 continue;
199 }
200 }
201
202 entry = m_commands[m_currentCommandIndex];
203 cmd = nullptr;
204
205 switch (entry.m_state) {
206 case CommandGroupEntry::kSequence_InSequence:
207 cmd = entry.m_command;
208 if (firstRun) {
209 cmd->StartRunning();
210 CancelConflicts(cmd);
211 firstRun = false;
212 }
213 break;
214
215 case CommandGroupEntry::kSequence_BranchPeer:
216 m_currentCommandIndex++;
217 entry.m_command->Start();
218 break;
219
220 case CommandGroupEntry::kSequence_BranchChild:
221 m_currentCommandIndex++;
222 CancelConflicts(entry.m_command);
223 entry.m_command->StartRunning();
224 m_children.push_back(entry);
225 break;
226 }
227 }
228
229 // Run Children
230 auto iter = m_children.begin();
231 for (; iter != m_children.end();) {
232 entry = *iter;
233 Command* child = entry.m_command;
234 if (entry.IsTimedOut()) child->_Cancel();
235
236 if (!child->Run()) {
237 child->Removed();
238 iter = m_children.erase(iter);
239 } else {
240 iter++;
241 }
242 }
243}
244
245void CommandGroup::_End() {
246 // Theoretically, we don't have to check this, but we do if teams override the
247 // IsFinished method
248 if (m_currentCommandIndex != -1 &&
249 static_cast<size_t>(m_currentCommandIndex) < m_commands.size()) {
250 Command* cmd = m_commands[m_currentCommandIndex].m_command;
251 cmd->_Cancel();
252 cmd->Removed();
253 }
254
255 auto iter = m_children.begin();
256 for (; iter != m_children.end(); iter++) {
257 Command* cmd = iter->m_command;
258 cmd->_Cancel();
259 cmd->Removed();
260 }
261 m_children.clear();
262}
263
264void CommandGroup::_Interrupted() { _End(); }
265
266// Can be overwritten by teams
267void CommandGroup::Initialize() {}
268
269// Can be overwritten by teams
270void CommandGroup::Execute() {}
271
272// Can be overwritten by teams
273void CommandGroup::End() {}
274
275// Can be overwritten by teams
276void CommandGroup::Interrupted() {}
277
278bool CommandGroup::IsFinished() {
279 return static_cast<size_t>(m_currentCommandIndex) >= m_commands.size() &&
280 m_children.empty();
281}
282
283bool CommandGroup::IsInterruptible() const {
284 if (!Command::IsInterruptible()) return false;
285
286 if (m_currentCommandIndex != -1 &&
287 static_cast<size_t>(m_currentCommandIndex) < m_commands.size()) {
288 Command* cmd = m_commands[m_currentCommandIndex].m_command;
289 if (!cmd->IsInterruptible()) return false;
290 }
291
292 auto iter = m_children.cbegin();
293 for (; iter != m_children.cend(); iter++) {
294 if (!iter->m_command->IsInterruptible()) return false;
295 }
296
297 return true;
298}
299
300void CommandGroup::CancelConflicts(Command* command) {
301 auto childIter = m_children.begin();
302 for (; childIter != m_children.end();) {
303 Command* child = childIter->m_command;
304 bool erased = false;
305
306 Command::SubsystemSet requirements = command->GetRequirements();
307 auto requirementIter = requirements.begin();
308 for (; requirementIter != requirements.end(); requirementIter++) {
309 if (child->DoesRequire(*requirementIter)) {
310 child->_Cancel();
311 child->Removed();
312 childIter = m_children.erase(childIter);
313 erased = true;
314 break;
315 }
316 }
317 if (!erased) childIter++;
318 }
319}
320
321int CommandGroup::GetSize() const { return m_children.size(); }