/*----------------------------------------------------------------------------*/ | |
/* Copyright (c) FIRST 2011. All Rights Reserved. */ | |
/* Open Source Software - may be modified and shared by FRC teams. The code */ | |
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */ | |
/*----------------------------------------------------------------------------*/ | |
#include "Commands/CommandGroup.h" | |
#include "WPIErrors.h" | |
/** | |
* Creates a new {@link CommandGroup CommandGroup}. | |
*/ | |
CommandGroup::CommandGroup() | |
{ | |
m_currentCommandIndex = -1; | |
} | |
/** | |
* Creates a new {@link CommandGroup CommandGroup} with the given name. | |
* @param name the name for this command group | |
*/ | |
CommandGroup::CommandGroup(const char *name) : | |
Command(name) | |
{ | |
m_currentCommandIndex = -1; | |
} | |
CommandGroup::~CommandGroup() | |
{ | |
} | |
/** | |
* Adds a new {@link Command Command} to the group. The {@link Command Command} will be started after | |
* all the previously added {@link Command Commands}. | |
* | |
* <p>Note that any requirements the given {@link Command Command} has will be added to the | |
* group. For this reason, a {@link Command Command's} requirements can not be changed after | |
* being added to a group.</p> | |
* | |
* <p>It is recommended that this method be called in the constructor.</p> | |
* | |
* @param command The {@link Command Command} to be added | |
*/ | |
void CommandGroup::AddSequential(Command *command) | |
{ | |
if (command == NULL) | |
{ | |
wpi_setWPIErrorWithContext(NullParameter, "command"); | |
return; | |
} | |
if (!AssertUnlocked("Cannot add new command to command group")) | |
return; | |
command->SetParent(this); | |
m_commands.push_back(CommandGroupEntry(command, CommandGroupEntry::kSequence_InSequence)); | |
// Iterate through command->GetRequirements() and call Requires() on each required subsystem | |
Command::SubsystemSet requirements = command->GetRequirements(); | |
Command::SubsystemSet::iterator iter = requirements.begin(); | |
for (; iter != requirements.end(); iter++) | |
Requires(*iter); | |
} | |
/** | |
* Adds a new {@link Command Command} to the group with a given timeout. | |
* The {@link Command Command} will be started after all the previously added commands. | |
* | |
* <p>Once the {@link Command Command} is started, it will be run until it finishes or the time | |
* expires, whichever is sooner. Note that the given {@link Command Command} will have no | |
* knowledge that it is on a timer.</p> | |
* | |
* <p>Note that any requirements the given {@link Command Command} has will be added to the | |
* group. For this reason, a {@link Command Command's} requirements can not be changed after | |
* being added to a group.</p> | |
* | |
* <p>It is recommended that this method be called in the constructor.</p> | |
* | |
* @param command The {@link Command Command} to be added | |
* @param timeout The timeout (in seconds) | |
*/ | |
void CommandGroup::AddSequential(Command *command, double timeout) | |
{ | |
if (command == NULL) | |
{ | |
wpi_setWPIErrorWithContext(NullParameter, "command"); | |
return; | |
} | |
if (!AssertUnlocked("Cannot add new command to command group")) | |
return; | |
if (timeout < 0.0) | |
{ | |
wpi_setWPIErrorWithContext(ParameterOutOfRange, "timeout < 0.0"); | |
return; | |
} | |
command->SetParent(this); | |
m_commands.push_back(CommandGroupEntry(command, CommandGroupEntry::kSequence_InSequence, timeout)); | |
// Iterate through command->GetRequirements() and call Requires() on each required subsystem | |
Command::SubsystemSet requirements = command->GetRequirements(); | |
Command::SubsystemSet::iterator iter = requirements.begin(); | |
for (; iter != requirements.end(); iter++) | |
Requires(*iter); | |
} | |
/** | |
* Adds a new child {@link Command} to the group. The {@link Command} will be started after | |
* all the previously added {@link Command Commands}. | |
* | |
* <p>Instead of waiting for the child to finish, a {@link CommandGroup} will have it | |
* run at the same time as the subsequent {@link Command Commands}. The child will run until either | |
* it finishes, a new child with conflicting requirements is started, or | |
* the main sequence runs a {@link Command} with conflicting requirements. In the latter | |
* two cases, the child will be canceled even if it says it can't be | |
* interrupted.</p> | |
* | |
* <p>Note that any requirements the given {@link Command Command} has will be added to the | |
* group. For this reason, a {@link Command Command's} requirements can not be changed after | |
* being added to a group.</p> | |
* | |
* <p>It is recommended that this method be called in the constructor.</p> | |
* | |
* @param command The command to be added | |
*/ | |
void CommandGroup::AddParallel(Command *command) | |
{ | |
if (command == NULL) | |
{ | |
wpi_setWPIErrorWithContext(NullParameter, "command"); | |
return; | |
} | |
if (!AssertUnlocked("Cannot add new command to command group")) | |
return; | |
command->SetParent(this); | |
m_commands.push_back(CommandGroupEntry(command, CommandGroupEntry::kSequence_BranchChild)); | |
// Iterate through command->GetRequirements() and call Requires() on each required subsystem | |
Command::SubsystemSet requirements = command->GetRequirements(); | |
Command::SubsystemSet::iterator iter = requirements.begin(); | |
for (; iter != requirements.end(); iter++) | |
Requires(*iter); | |
} | |
/** | |
* Adds a new child {@link Command} to the group with the given timeout. The {@link Command} will be started after | |
* all the previously added {@link Command Commands}. | |
* | |
* <p>Once the {@link Command Command} is started, it will run until it finishes, is interrupted, | |
* or the time expires, whichever is sooner. Note that the given {@link Command Command} will have no | |
* knowledge that it is on a timer.</p> | |
* | |
* <p>Instead of waiting for the child to finish, a {@link CommandGroup} will have it | |
* run at the same time as the subsequent {@link Command Commands}. The child will run until either | |
* it finishes, the timeout expires, a new child with conflicting requirements is started, or | |
* the main sequence runs a {@link Command} with conflicting requirements. In the latter | |
* two cases, the child will be canceled even if it says it can't be | |
* interrupted.</p> | |
* | |
* <p>Note that any requirements the given {@link Command Command} has will be added to the | |
* group. For this reason, a {@link Command Command's} requirements can not be changed after | |
* being added to a group.</p> | |
* | |
* <p>It is recommended that this method be called in the constructor.</p> | |
* | |
* @param command The command to be added | |
* @param timeout The timeout (in seconds) | |
*/ | |
void CommandGroup::AddParallel(Command *command, double timeout) | |
{ | |
if (command == NULL) | |
{ | |
wpi_setWPIErrorWithContext(NullParameter, "command"); | |
return; | |
} | |
if (!AssertUnlocked("Cannot add new command to command group")) | |
return; | |
if (timeout < 0.0) | |
{ | |
wpi_setWPIErrorWithContext(ParameterOutOfRange, "timeout < 0.0"); | |
return; | |
} | |
command->SetParent(this); | |
m_commands.push_back(CommandGroupEntry(command, CommandGroupEntry::kSequence_BranchChild, timeout)); | |
// Iterate through command->GetRequirements() and call Requires() on each required subsystem | |
Command::SubsystemSet requirements = command->GetRequirements(); | |
Command::SubsystemSet::iterator iter = requirements.begin(); | |
for (; iter != requirements.end(); iter++) | |
Requires(*iter); | |
} | |
void CommandGroup::_Initialize() | |
{ | |
m_currentCommandIndex = -1; | |
} | |
void CommandGroup::_Execute() | |
{ | |
CommandGroupEntry entry; | |
Command *cmd = NULL; | |
bool firstRun = false; | |
if (m_currentCommandIndex == -1) | |
{ | |
firstRun = true; | |
m_currentCommandIndex = 0; | |
} | |
while ((unsigned)m_currentCommandIndex < m_commands.size()) | |
{ | |
if (cmd != NULL) | |
{ | |
if (entry.IsTimedOut()) | |
cmd->_Cancel(); | |
if (cmd->Run()) | |
{ | |
break; | |
} | |
else | |
{ | |
cmd->Removed(); | |
m_currentCommandIndex++; | |
firstRun = true; | |
cmd = NULL; | |
continue; | |
} | |
} | |
entry = m_commands[m_currentCommandIndex]; | |
cmd = NULL; | |
switch (entry.m_state) | |
{ | |
case CommandGroupEntry::kSequence_InSequence: | |
cmd = entry.m_command; | |
if (firstRun) | |
{ | |
cmd->StartRunning(); | |
CancelConflicts(cmd); | |
firstRun = false; | |
} | |
break; | |
case CommandGroupEntry::kSequence_BranchPeer: | |
m_currentCommandIndex++; | |
entry.m_command->Start(); | |
break; | |
case CommandGroupEntry::kSequence_BranchChild: | |
m_currentCommandIndex++; | |
CancelConflicts(entry.m_command); | |
entry.m_command->StartRunning(); | |
m_children.push_back(entry); | |
break; | |
} | |
} | |
// Run Children | |
CommandList::iterator iter = m_children.begin(); | |
for (; iter != m_children.end();) | |
{ | |
entry = *iter; | |
Command *child = entry.m_command; | |
if (entry.IsTimedOut()) | |
child->_Cancel(); | |
if (!child->Run()) | |
{ | |
child->Removed(); | |
iter = m_children.erase(iter); | |
} | |
else | |
{ | |
iter++; | |
} | |
} | |
} | |
void CommandGroup::_End() | |
{ | |
// Theoretically, we don't have to check this, but we do if teams override the IsFinished method | |
if (m_currentCommandIndex != -1 && (unsigned)m_currentCommandIndex < m_commands.size()) | |
{ | |
Command *cmd = m_commands[m_currentCommandIndex].m_command; | |
cmd->_Cancel(); | |
cmd->Removed(); | |
} | |
CommandList::iterator iter = m_children.begin(); | |
for (; iter != m_children.end(); iter++) | |
{ | |
Command *cmd = iter->m_command; | |
cmd->_Cancel(); | |
cmd->Removed(); | |
} | |
m_children.clear(); | |
} | |
void CommandGroup::_Interrupted() | |
{ | |
_End(); | |
} | |
// Can be overwritten by teams | |
void CommandGroup::Initialize() | |
{ | |
} | |
// Can be overwritten by teams | |
void CommandGroup::Execute() | |
{ | |
} | |
// Can be overwritten by teams | |
void CommandGroup::End() | |
{ | |
} | |
// Can be overwritten by teams | |
void CommandGroup::Interrupted() | |
{ | |
} | |
bool CommandGroup::IsFinished() | |
{ | |
return (unsigned)m_currentCommandIndex >= m_commands.size() && m_children.empty(); | |
} | |
bool CommandGroup::IsInterruptible() | |
{ | |
if (!Command::IsInterruptible()) | |
return false; | |
if (m_currentCommandIndex != -1 && (unsigned)m_currentCommandIndex < m_commands.size()) | |
{ | |
Command *cmd = m_commands[m_currentCommandIndex].m_command; | |
if (!cmd->IsInterruptible()) | |
return false; | |
} | |
CommandList::iterator iter = m_children.begin(); | |
for (; iter != m_children.end(); iter++) | |
{ | |
if (!iter->m_command->IsInterruptible()) | |
return false; | |
} | |
return true; | |
} | |
void CommandGroup::CancelConflicts(Command *command) | |
{ | |
CommandList::iterator childIter = m_children.begin(); | |
for (; childIter != m_children.end();) | |
{ | |
Command *child = childIter->m_command; | |
bool erased = false; | |
Command::SubsystemSet requirements = command->GetRequirements(); | |
Command::SubsystemSet::iterator requirementIter = requirements.begin(); | |
for (; requirementIter != requirements.end(); requirementIter++) | |
{ | |
if (child->DoesRequire(*requirementIter)) | |
{ | |
child->_Cancel(); | |
child->Removed(); | |
childIter = m_children.erase(childIter); | |
erased = true; | |
break; | |
} | |
} | |
if (!erased) | |
childIter++; | |
} | |
} | |
int CommandGroup::GetSize() | |
{ | |
return m_children.size(); | |
} |