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