blob: 19d1e7a0a148c799e31c0115126ad24b7cd49330 [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2011-2017. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#pragma once
9
10#include <memory>
11#include <set>
12#include <string>
13
14#include "ErrorBase.h"
15#include "SmartDashboard/NamedSendable.h"
16#include "tables/ITableListener.h"
17
18namespace frc {
19
20class CommandGroup;
21class Subsystem;
22
23/**
24 * The Command class is at the very core of the entire command framework.
25 * Every command can be started with a call to {@link Command#Start() Start()}.
26 * Once a command is started it will call {@link Command#Initialize()
27 * Initialize()}, and then will repeatedly call
28 * {@link Command#Execute() Execute()} until the
29 * {@link Command#IsFinished() IsFinished()} returns true. Once it does,
30 * {@link Command#End() End()} will be called.
31 *
32 * <p>However, if at any point while it is running {@link Command#Cancel()
33 * Cancel()} is called, then the command will be stopped and
34 * {@link Command#Interrupted() Interrupted()} will be called.</p>
35 *
36 * <p>If a command uses a {@link Subsystem}, then it should specify that it does
37 * so by calling the {@link Command#Requires(Subsystem) Requires(...)} method
38 * in its constructor. Note that a Command may have multiple requirements, and
39 * {@link Command#Requires(Subsystem) Requires(...)} should be called for each
40 * one.</p>
41 *
42 * <p>If a command is running and a new command with shared requirements is
43 * started, then one of two things will happen. If the active command is
44 * interruptible, then {@link Command#Cancel() Cancel()} will be called and the
45 * command will be removed to make way for the new one. If the active command
46 * is not interruptible, the other one will not even be started, and the active
47 * one will continue functioning.</p>
48 *
49 * @see CommandGroup
50 * @see Subsystem
51 */
52class Command : public ErrorBase, public NamedSendable, public ITableListener {
53 friend class CommandGroup;
54 friend class Scheduler;
55
56 public:
57 Command();
58 explicit Command(const std::string& name);
59 explicit Command(double timeout);
60 Command(const std::string& name, double timeout);
61 virtual ~Command();
62 double TimeSinceInitialized() const;
63 void Requires(Subsystem* s);
64 bool IsCanceled() const;
65 void Start();
66 bool Run();
67 void Cancel();
68 bool IsRunning() const;
69 bool IsInterruptible() const;
70 void SetInterruptible(bool interruptible);
71 bool DoesRequire(Subsystem* subsystem) const;
72 typedef std::set<Subsystem*> SubsystemSet;
73 SubsystemSet GetRequirements() const;
74 CommandGroup* GetGroup() const;
75 void SetRunWhenDisabled(bool run);
76 bool WillRunWhenDisabled() const;
77 int GetID() const;
78
79 protected:
80 void SetTimeout(double timeout);
81 bool IsTimedOut() const;
82 bool AssertUnlocked(const std::string& message);
83 void SetParent(CommandGroup* parent);
84 void ClearRequirements();
85
86 virtual void Initialize();
87 virtual void Execute();
88
89 /**
90 * Returns whether this command is finished.
91 * If it is, then the command will be removed and {@link Command#end() end()}
92 * will be called.
93 *
94 * <p>It may be useful for a team to reference the {@link Command#isTimedOut()
95 * isTimedOut()} method for time-sensitive commands.</p>
96 *
97 * <p>Returning false will result in the command never ending automatically.
98 * It may still be cancelled manually or interrupted by another command.
99 * Returning true will result in the command executing once and finishing
100 * immediately. We recommend using {@link InstantCommand} for this.</p>
101 *
102 * @return whether this command is finished.
103 * @see Command#isTimedOut() isTimedOut()
104 */
105 virtual bool IsFinished() = 0;
106
107 virtual void End();
108 virtual void Interrupted();
109
110 virtual void _Initialize();
111 virtual void _Interrupted();
112 virtual void _Execute();
113 virtual void _End();
114 virtual void _Cancel();
115
116 friend class ConditionalCommand;
117
118 private:
119 void LockChanges();
120 /*synchronized*/ void Removed();
121 void StartRunning();
122 void StartTiming();
123
124 /** The name of this command */
125 std::string m_name;
126
127 /** The time since this command was initialized */
128 double m_startTime = -1;
129
130 /** The time (in seconds) before this command "times out" (or -1 if no
131 * timeout) */
132 double m_timeout;
133
134 /** Whether or not this command has been initialized */
135 bool m_initialized = false;
136
137 /** The requirements (or null if no requirements) */
138 SubsystemSet m_requirements;
139
140 /** Whether or not it is running */
141 bool m_running = false;
142
143 /** Whether or not it is interruptible*/
144 bool m_interruptible = true;
145
146 /** Whether or not it has been canceled */
147 bool m_canceled = false;
148
149 /** Whether or not it has been locked */
150 bool m_locked = false;
151
152 /** Whether this command should run when the robot is disabled */
153 bool m_runWhenDisabled = false;
154
155 /** The {@link CommandGroup} this is in */
156 CommandGroup* m_parent = nullptr;
157
158 int m_commandID = m_commandCounter++;
159 static int m_commandCounter;
160
161 public:
162 std::string GetName() const override;
163 void InitTable(std::shared_ptr<ITable> subtable) override;
164 std::shared_ptr<ITable> GetTable() const override;
165 std::string GetSmartDashboardType() const override;
166 void ValueChanged(ITable* source, llvm::StringRef key,
167 std::shared_ptr<nt::Value> value, bool isNew) override;
168
169 protected:
170 std::shared_ptr<ITable> m_table;
171};
172
173} // namespace frc