blob: 19d1e7a0a148c799e31c0115126ad24b7cd49330 [file] [log] [blame]
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2011-2017. 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. */
/*----------------------------------------------------------------------------*/
#pragma once
#include <memory>
#include <set>
#include <string>
#include "ErrorBase.h"
#include "SmartDashboard/NamedSendable.h"
#include "tables/ITableListener.h"
namespace frc {
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();
explicit Command(const std::string& name);
explicit 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<Subsystem*> 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);
void ClearRequirements();
virtual void Initialize();
virtual void Execute();
/**
* 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>
*
* <p>Returning false will result in the command never ending automatically.
* It may still be cancelled manually or interrupted by another command.
* Returning true will result in the command executing once and finishing
* immediately. We recommend using {@link InstantCommand} for this.</p>
*
* @return whether this command is finished.
* @see Command#isTimedOut() isTimedOut()
*/
virtual bool IsFinished() = 0;
virtual void End();
virtual void Interrupted();
virtual void _Initialize();
virtual void _Interrupted();
virtual void _Execute();
virtual void _End();
virtual void _Cancel();
friend class ConditionalCommand;
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:
std::string GetName() const override;
void InitTable(std::shared_ptr<ITable> subtable) override;
std::shared_ptr<ITable> GetTable() const override;
std::string GetSmartDashboardType() const override;
void ValueChanged(ITable* source, llvm::StringRef key,
std::shared_ptr<nt::Value> value, bool isNew) override;
protected:
std::shared_ptr<ITable> m_table;
};
} // namespace frc