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