/*----------------------------------------------------------------------------*/ /* Copyright (c) FIRST 2011-2016. 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 the root directory of */ /* the project. */ /*----------------------------------------------------------------------------*/ #ifndef __COMMAND_H__ #define __COMMAND_H__ #include "ErrorBase.h" #include "SmartDashboard/NamedSendable.h" #include "tables/ITableListener.h" #include #include #include 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. * *

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.

* *

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.

* *

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.

* * @see CommandGroup * @see Subsystem */ class Command : public ErrorBase, public NamedSendable, public ITableListener { friend class CommandGroup; friend class Scheduler; public: Command(); Command(const std::string &name); Command(double timeout); Command(const std::string &name, double timeout); virtual ~Command(); double TimeSinceInitialized() const; void Requires(Subsystem *s); bool IsCanceled() const; void Start(); bool Run(); void Cancel(); bool IsRunning() const; bool IsInterruptible() const; void SetInterruptible(bool interruptible); bool DoesRequire(Subsystem *subsystem) const; typedef std::set SubsystemSet; SubsystemSet GetRequirements() const; CommandGroup *GetGroup() const; void SetRunWhenDisabled(bool run); bool WillRunWhenDisabled() const; int GetID() const; protected: void SetTimeout(double timeout); bool IsTimedOut() const; bool AssertUnlocked(const std::string &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. * *

It may be useful for a team to reference the {@link Command#isTimedOut() * isTimedOut()} method * for time-sensitive commands.

* @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. * *

This is where you may want * to wrap up loose ends, like shutting off a motor that was being used * in the command.

* *

Generally, it is useful to simply call the {@link Command#end() end()} * method * within this method

*/ virtual void Interrupted() = 0; virtual void _Initialize(); virtual void _Interrupted(); virtual void _Execute(); virtual void _End(); virtual void _Cancel(); private: 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 = -1; /** 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 = false; /** The requirements (or null if no requirements) */ SubsystemSet m_requirements; /** Whether or not it is running */ bool m_running = false; /** Whether or not it is interruptible*/ bool m_interruptible = true; /** Whether or not it has been canceled */ bool m_canceled = false; /** Whether or not it has been locked */ bool m_locked = false; /** Whether this command should run when the robot is disabled */ bool m_runWhenDisabled = false; /** The {@link CommandGroup} this is in */ CommandGroup *m_parent = nullptr; int m_commandID = m_commandCounter++; static int m_commandCounter; public: virtual std::string GetName() const; virtual void InitTable(std::shared_ptr table); virtual std::shared_ptr GetTable() const; virtual std::string GetSmartDashboardType() const; virtual void ValueChanged(ITable* source, llvm::StringRef key, std::shared_ptr value, bool isNew); protected: std::shared_ptr m_table; }; #endif