Files
allwpilib/wpilibc/lib/Commands/Scheduler.cpp
Brad Miller 69d9ad70ab CMake Changes
This is the changes made by Patrick Plenefisch converting the native
code to use CMake and the CMake Maven Plugin, as opposed to the
native Maven plugin. This is to allow for compatibility with newer
versions of the GCC toolchain. All the cpp sources were moved from
maven style directories to cpp style directories for CMake.

Change-Id: I67f5e3608948f37c83b0990d232105a3784f8593
2014-04-01 11:18:29 -04:00

286 lines
7.5 KiB
C++

/*----------------------------------------------------------------------------*/
/* 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/Scheduler.h"
#include "Buttons/ButtonScheduler.h"
#include "Commands/Subsystem.h"
//#include "NetworkCommunication/UsageReporting.h"
#include "HAL/cpp/Synchronized.h"
#include "WPIErrors.h"
#include <iostream>
#include <set>
#include <algorithm>
Scheduler *Scheduler::_instance = NULL;
Scheduler::Scheduler() :
m_buttonsLock(NULL), m_additionsLock(NULL), m_adding(false) {
m_buttonsLock = initializeMutexNormal();
m_additionsLock = initializeMutexNormal();
HALReport(HALUsageReporting::kResourceType_Command,
HALUsageReporting::kCommand_Scheduler);
m_table = NULL;
m_enabled = true;
}
Scheduler::~Scheduler() {
takeMutex(m_additionsLock);
deleteMutex(m_additionsLock);
takeMutex(m_buttonsLock);
deleteMutex(m_buttonsLock);
}
/**
* Returns the {@link Scheduler}, creating it if one does not exist.
* @return the {@link Scheduler}
*/
Scheduler *Scheduler::GetInstance() {
if (_instance == NULL)
_instance = new Scheduler();
return _instance;
}
void Scheduler::SetEnabled(bool enabled) {
m_enabled = enabled;
}
/**
* Add a command to be scheduled later.
* In any pass through the scheduler, all commands are added to the additions list, then
* at the end of the pass, they are all scheduled.
* @param command The command to be scheduled
*/
void Scheduler::AddCommand(Command *command) {
Synchronized sync(m_additionsLock);
if (std::find(m_additions.begin(), m_additions.end(), command)
!= m_additions.end())
return;
m_additions.push_back(command);
}
void Scheduler::AddButton(ButtonScheduler *button) {
Synchronized sync(m_buttonsLock);
m_buttons.push_back(button);
}
void Scheduler::ProcessCommandAddition(Command *command) {
if (command == NULL)
return;
// Check to make sure no adding during adding
if (m_adding) {
wpi_setWPIErrorWithContext(IncompatibleState, "Can not start command from cancel method");
return;
}
// Only add if not already in
CommandSet::iterator found = m_commands.find(command);
if (found == m_commands.end()) {
// Check that the requirements can be had
Command::SubsystemSet requirements = command->GetRequirements();
Command::SubsystemSet::iterator iter;
for (iter = requirements.begin(); iter != requirements.end(); iter++) {
Subsystem *lock = *iter;
if (lock->GetCurrentCommand() != NULL
&& !lock->GetCurrentCommand()->IsInterruptible())
return;
}
// Give it the requirements
m_adding = true;
for (iter = requirements.begin(); iter != requirements.end(); iter++) {
Subsystem *lock = *iter;
if (lock->GetCurrentCommand() != NULL) {
lock->GetCurrentCommand()->Cancel();
Remove(lock->GetCurrentCommand());
}
lock->SetCurrentCommand(command);
}
m_adding = false;
m_commands.insert(command);
command->StartRunning();
m_runningCommandsChanged = true;
}
}
/**
* Runs a single iteration of the loop. This method should be called often in order to have a functioning
* {@link Command} system. The loop has five stages:
*
* <ol>
* <li> Poll the Buttons </li>
* <li> Execute/Remove the Commands </li>
* <li> Send values to SmartDashboard </li>
* <li> Add Commands </li>
* <li> Add Defaults </li>
* </ol>
*/
void Scheduler::Run() {
// Get button input (going backwards preserves button priority)
{
if (!m_enabled)
return;
Synchronized sync(m_buttonsLock);
ButtonVector::reverse_iterator rButtonIter = m_buttons.rbegin();
for (; rButtonIter != m_buttons.rend(); rButtonIter++) {
(*rButtonIter)->Execute();
}
}
m_runningCommandsChanged = false;
// Loop through the commands
CommandSet::iterator commandIter = m_commands.begin();
for (; commandIter != m_commands.end();) {
Command *command = *commandIter;
// Increment before potentially removing to keep the iterator valid
commandIter++;
if (!command->Run()) {
Remove(command);
m_runningCommandsChanged = true;
}
}
// Add the new things
{
Synchronized sync(m_additionsLock);
CommandVector::iterator additionsIter = m_additions.begin();
for (; additionsIter != m_additions.end(); additionsIter++) {
ProcessCommandAddition(*additionsIter);
}
m_additions.clear();
}
// Add in the defaults
Command::SubsystemSet::iterator subsystemIter = m_subsystems.begin();
for (; subsystemIter != m_subsystems.end(); subsystemIter++) {
Subsystem *lock = *subsystemIter;
if (lock->GetCurrentCommand() == NULL) {
ProcessCommandAddition(lock->GetDefaultCommand());
}
lock->ConfirmCommand();
}
UpdateTable();
}
/**
* Registers a {@link Subsystem} to this {@link Scheduler}, so that the {@link Scheduler} might know
* if a default {@link Command} needs to be run. All {@link Subsystem Subsystems} should call this.
* @param system the system
*/
void Scheduler::RegisterSubsystem(Subsystem *subsystem) {
if (subsystem == NULL) {
wpi_setWPIErrorWithContext(NullParameter, "subsystem");
return;
}
m_subsystems.insert(subsystem);
}
/**
* Removes the {@link Command} from the {@link Scheduler}.
* @param command the command to remove
*/
void Scheduler::Remove(Command *command) {
if (command == NULL) {
wpi_setWPIErrorWithContext(NullParameter, "command");
return;
}
if (!m_commands.erase(command))
return;
Command::SubsystemSet requirements = command->GetRequirements();
Command::SubsystemSet::iterator iter = requirements.begin();
for (; iter != requirements.end(); iter++) {
Subsystem *lock = *iter;
lock->SetCurrentCommand(NULL);
}
command->Removed();
}
void Scheduler::RemoveAll() {
while (m_commands.size() > 0) {
Remove(*m_commands.begin());
}
}
/**
* Update the network tables associated with the Scheduler object on the SmartDashboard
*/
void Scheduler::UpdateTable() {
CommandSet::iterator commandIter;
if (m_table != NULL) {
// Get the list of possible commands to cancel
m_table->RetrieveValue("Cancel", *toCancel);
// m_table->RetrieveValue("Ids", *ids);
// cancel commands that have had the cancel buttons pressed
// on the SmartDashboad
if (toCancel->size() > 0) {
for (commandIter = m_commands.begin(); commandIter
!= m_commands.end(); ++commandIter) {
for (unsigned i = 0; i < toCancel->size(); i++) {
Command *c = *commandIter;
if (c->GetID() == toCancel->get(i)) {
c->Cancel();
}
}
}
toCancel->setSize(0);
m_table->PutValue("Cancel", *toCancel);
}
// Set the running commands
if (m_runningCommandsChanged) {
commands->setSize(0);
ids->setSize(0);
for (commandIter = m_commands.begin(); commandIter != m_commands.end(); ++commandIter) {
Command *c = *commandIter;
commands->add(c->GetName());
ids->add(c->GetID());
}
m_table->PutValue("Names", *commands);
m_table->PutValue("Ids", *ids);
}
}
}
std::string Scheduler::GetName() {
return "Scheduler";
}
std::string Scheduler::GetType() {
return "Scheduler";
}
std::string Scheduler::GetSmartDashboardType() {
return "Scheduler";
}
void Scheduler::InitTable(ITable *subTable) {
m_table = subTable;
commands = new StringArray();
ids = new NumberArray();
toCancel = new NumberArray();
m_table->PutValue("Names", *commands);
m_table->PutValue("Ids", *ids);
m_table->PutValue("Cancel", *toCancel);
}
ITable * Scheduler::GetTable() {
return m_table;
}