blob: 760c8f55759e5f2e3db9a767d9f5d7146555077d [file] [log] [blame]
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef __COMMAND_H__
#define __COMMAND_H__
#include "ErrorBase.h"
#include "SmartDashboard/NamedSendable.h"
#include <set>
#include <string>
class CommandGroup;
class Subsystem;
/**
* The Command class is at the very core of the entire command framework.
* Every command can be started with a call to {@link Command#Start() Start()}.
* Once a command is started it will call {@link Command#Initialize() Initialize()}, and then
* will repeatedly call {@link Command#Execute() Execute()} until the {@link Command#IsFinished() IsFinished()}
* returns true. Once it does, {@link Command#End() End()} will be called.
*
* <p>However, if at any point while it is running {@link Command#Cancel() Cancel()} is called, then
* the command will be stopped and {@link Command#Interrupted() Interrupted()} will be called.</p>
*
* <p>If a command uses a {@link Subsystem}, then it should specify that it does so by
* calling the {@link Command#Requires(Subsystem) Requires(...)} method
* in its constructor. Note that a Command may have multiple requirements, and
* {@link Command#Requires(Subsystem) Requires(...)} should be
* called for each one.</p>
*
* <p>If a command is running and a new command with shared requirements is started,
* then one of two things will happen. If the active command is interruptible,
* then {@link Command#Cancel() Cancel()} will be called and the command will be removed
* to make way for the new one. If the active command is not interruptible, the
* other one will not even be started, and the active one will continue functioning.</p>
*
* @see CommandGroup
* @see Subsystem
*/
class Command : public ErrorBase, public NamedSendable, public ITableListener
{
friend class CommandGroup;
friend class Scheduler;
public:
Command();
Command(const char *name);
Command(double timeout);
Command(const char *name, double timeout);
virtual ~Command();
double TimeSinceInitialized();
void Requires(Subsystem *s);
bool IsCanceled();
void Start();
bool Run();
void Cancel();
bool IsRunning();
bool IsInterruptible();
void SetInterruptible(bool interruptible);
bool DoesRequire(Subsystem *subsystem);
typedef std::set<Subsystem *> SubsystemSet;
SubsystemSet GetRequirements();
CommandGroup *GetGroup();
void SetRunWhenDisabled(bool run);
bool WillRunWhenDisabled();
protected:
void SetTimeout(double timeout);
bool IsTimedOut();
bool AssertUnlocked(const char *message);
void SetParent(CommandGroup *parent);
/**
* The initialize method is called the first time this Command is run after
* being started.
*/
virtual void Initialize() = 0;
/**
* The execute method is called repeatedly until this Command either finishes
* or is canceled.
*/
virtual void Execute() = 0;
/**
* Returns whether this command is finished.
* If it is, then the command will be removed
* and {@link Command#end() end()} will be called.
*
* <p>It may be useful for a team to reference the {@link Command#isTimedOut() isTimedOut()} method
* for time-sensitive commands.</p>
* @return whether this command is finished.
* @see Command#isTimedOut() isTimedOut()
*/
virtual bool IsFinished() = 0;
/**
* Called when the command ended peacefully. This is where you may want
* to wrap up loose ends, like shutting off a motor that was being used
* in the command.
*/
virtual void End() = 0;
/**
* Called when the command ends because somebody called {@link Command#cancel() cancel()}
* or another command shared the same requirements as this one, and booted
* it out.
*
* <p>This is where you may want
* to wrap up loose ends, like shutting off a motor that was being used
* in the command.</p>
*
* <p>Generally, it is useful to simply call the {@link Command#end() end()} method
* within this method</p>
*/
virtual void Interrupted() = 0;
virtual void _Initialize();
virtual void _Interrupted();
virtual void _Execute();
virtual void _End();
virtual void _Cancel();
private:
void InitCommand(const char *name, double timeout);
void LockChanges();
/*synchronized*/ void Removed();
void StartRunning();
void StartTiming();
/** The name of this command */
std::string m_name;
/** The time since this command was initialized */
double m_startTime;
/** The time (in seconds) before this command "times out" (or -1 if no timeout) */
double m_timeout;
/** Whether or not this command has been initialized */
bool m_initialized;
/** The requirements (or null if no requirements) */
SubsystemSet m_requirements;
/** Whether or not it is running */
bool m_running;
/** Whether or not it is interruptible*/
bool m_interruptible;
/** Whether or not it has been canceled */
bool m_canceled;
/** Whether or not it has been locked */
bool m_locked;
/** Whether this command should run when the robot is disabled */
bool m_runWhenDisabled;
/** The {@link CommandGroup} this is in */
CommandGroup *m_parent;
public:
virtual std::string GetName();
virtual void InitTable(ITable* table);
virtual ITable* GetTable();
virtual std::string GetSmartDashboardType();
virtual void ValueChanged(ITable* source, const std::string& key, EntryValue value, bool isNew);
protected:
ITable* m_table;
};
#endif