001/*----------------------------------------------------------------------------*/
002/* Copyright (c) 2008-2018 FIRST. All Rights Reserved.                        */
003/* Open Source Software - may be modified and shared by FRC teams. The code   */
004/* must be accompanied by the FIRST BSD license file in the root directory of */
005/* the project.                                                               */
006/*----------------------------------------------------------------------------*/
007
008package edu.wpi.first.wpilibj.command;
009
010import java.util.Enumeration;
011
012import edu.wpi.first.wpilibj.RobotState;
013import edu.wpi.first.wpilibj.Sendable;
014import edu.wpi.first.wpilibj.SendableBase;
015import edu.wpi.first.wpilibj.Timer;
016import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder;
017
018/**
019 * The Command class is at the very core of the entire command framework. Every command can be
020 * started with a call to {@link Command#start() start()}. Once a command is started it will call
021 * {@link Command#initialize() initialize()}, and then will repeatedly call {@link Command#execute()
022 * execute()} until the {@link Command#isFinished() isFinished()} returns true. Once it does, {@link
023 * Command#end() end()} will be called.
024 *
025 * <p>However, if at any point while it is running {@link Command#cancel() cancel()} is called,
026 * then the command will be stopped and {@link Command#interrupted() interrupted()} will be called.
027 *
028 * <p>If a command uses a {@link Subsystem}, then it should specify that it does so by calling the
029 * {@link Command#requires(Subsystem) requires(...)} method in its constructor. Note that a Command
030 * may have multiple requirements, and {@link Command#requires(Subsystem) requires(...)} should be
031 * called for each one.
032 *
033 * <p>If a command is running and a new command with shared requirements is started, then one of
034 * two things will happen. If the active command is interruptible, then {@link Command#cancel()
035 * cancel()} will be called and the command will be removed to make way for the new one. If the
036 * active command is not interruptible, the other one will not even be started, and the active one
037 * will continue functioning.
038 *
039 * @see Subsystem
040 * @see CommandGroup
041 * @see IllegalUseOfCommandException
042 */
043public abstract class Command extends SendableBase implements Sendable {
044  /**
045   * The time since this command was initialized.
046   */
047  private double m_startTime = -1;
048
049  /**
050   * The time (in seconds) before this command "times out" (or -1 if no timeout).
051   */
052  private double m_timeout = -1;
053
054  /**
055   * Whether or not this command has been initialized.
056   */
057  private boolean m_initialized = false;
058
059  /**
060   * The required subsystems.
061   */
062  private final Set m_requirements = new Set();
063
064  /**
065   * Whether or not it is running.
066   */
067  private boolean m_running = false;
068
069  /**
070   * Whether or not it is interruptible.
071   */
072  private boolean m_interruptible = true;
073
074  /**
075   * Whether or not it has been canceled.
076   */
077  private boolean m_canceled = false;
078
079  /**
080   * Whether or not it has been locked.
081   */
082  private boolean m_locked = false;
083
084  /**
085   * Whether this command should run when the robot is disabled.
086   */
087  private boolean m_runWhenDisabled = false;
088
089  /**
090   * Whether or not this command has completed running.
091   */
092  private boolean m_completed = false;
093
094  /**
095   * The {@link CommandGroup} this is in.
096   */
097  private CommandGroup m_parent;
098
099  /**
100   * Creates a new command. The name of this command will be set to its class name.
101   */
102  public Command() {
103    super(false);
104    String name = getClass().getName();
105    setName(name.substring(name.lastIndexOf('.') + 1));
106  }
107
108  /**
109   * Creates a new command with the given name.
110   *
111   * @param name the name for this command
112   * @throws IllegalArgumentException if name is null
113   */
114  public Command(String name) {
115    super(false);
116    if (name == null) {
117      throw new IllegalArgumentException("Name must not be null.");
118    }
119    setName(name);
120  }
121
122  /**
123   * Creates a new command with the given timeout and a default name. The default name is the name
124   * of the class.
125   *
126   * @param timeout the time (in seconds) before this command "times out"
127   * @throws IllegalArgumentException if given a negative timeout
128   * @see Command#isTimedOut() isTimedOut()
129   */
130  public Command(double timeout) {
131    this();
132    if (timeout < 0) {
133      throw new IllegalArgumentException("Timeout must not be negative.  Given:" + timeout);
134    }
135    m_timeout = timeout;
136  }
137
138  /**
139   * Creates a new command with the given name and timeout.
140   *
141   * @param name    the name of the command
142   * @param timeout the time (in seconds) before this command "times out"
143   * @throws IllegalArgumentException if given a negative timeout or name was null.
144   * @see Command#isTimedOut() isTimedOut()
145   */
146  public Command(String name, double timeout) {
147    this(name);
148    if (timeout < 0) {
149      throw new IllegalArgumentException("Timeout must not be negative.  Given:" + timeout);
150    }
151    m_timeout = timeout;
152  }
153
154  /**
155   * Sets the timeout of this command.
156   *
157   * @param seconds the timeout (in seconds)
158   * @throws IllegalArgumentException if seconds is negative
159   * @see Command#isTimedOut() isTimedOut()
160   */
161  protected final synchronized void setTimeout(double seconds) {
162    if (seconds < 0) {
163      throw new IllegalArgumentException("Seconds must be positive.  Given:" + seconds);
164    }
165    m_timeout = seconds;
166  }
167
168  /**
169   * Returns the time since this command was initialized (in seconds). This function will work even
170   * if there is no specified timeout.
171   *
172   * @return the time since this command was initialized (in seconds).
173   */
174  public final synchronized double timeSinceInitialized() {
175    return m_startTime < 0 ? 0 : Timer.getFPGATimestamp() - m_startTime;
176  }
177
178  /**
179   * This method specifies that the given {@link Subsystem} is used by this command. This method is
180   * crucial to the functioning of the Command System in general.
181   *
182   * <p>Note that the recommended way to call this method is in the constructor.
183   *
184   * @param subsystem the {@link Subsystem} required
185   * @throws IllegalArgumentException     if subsystem is null
186   * @throws IllegalUseOfCommandException if this command has started before or if it has been given
187   *                                      to a {@link CommandGroup}
188   * @see Subsystem
189   */
190  protected synchronized void requires(Subsystem subsystem) {
191    validate("Can not add new requirement to command");
192    if (subsystem != null) {
193      m_requirements.add(subsystem);
194    } else {
195      throw new IllegalArgumentException("Subsystem must not be null.");
196    }
197  }
198
199  /**
200   * Called when the command has been removed. This will call {@link Command#interrupted()
201   * interrupted()} or {@link Command#end() end()}.
202   */
203  synchronized void removed() {
204    if (m_initialized) {
205      if (isCanceled()) {
206        interrupted();
207        _interrupted();
208      } else {
209        end();
210        _end();
211      }
212    }
213    m_initialized = false;
214    m_canceled = false;
215    m_running = false;
216    m_completed = true;
217  }
218
219  /**
220   * The run method is used internally to actually run the commands.
221   *
222   * @return whether or not the command should stay within the {@link Scheduler}.
223   */
224  synchronized boolean run() {
225    if (!m_runWhenDisabled && m_parent == null && RobotState.isDisabled()) {
226      cancel();
227    }
228    if (isCanceled()) {
229      return false;
230    }
231    if (!m_initialized) {
232      m_initialized = true;
233      startTiming();
234      _initialize();
235      initialize();
236    }
237    _execute();
238    execute();
239    return !isFinished();
240  }
241
242  /**
243   * The initialize method is called the first time this Command is run after being started.
244   */
245  protected void initialize() {}
246
247  /**
248   * A shadow method called before {@link Command#initialize() initialize()}.
249   */
250  @SuppressWarnings("MethodName")
251  void _initialize() {
252  }
253
254  /**
255   * The execute method is called repeatedly until this Command either finishes or is canceled.
256   */
257  @SuppressWarnings("MethodName")
258  protected void execute() {}
259
260  /**
261   * A shadow method called before {@link Command#execute() execute()}.
262   */
263  @SuppressWarnings("MethodName")
264  void _execute() {
265  }
266
267  /**
268   * Returns whether this command is finished. If it is, then the command will be removed and {@link
269   * Command#end() end()} will be called.
270   *
271   * <p>It may be useful for a team to reference the {@link Command#isTimedOut() isTimedOut()}
272   * method for time-sensitive commands.
273   *
274   * <p>Returning false will result in the command never ending automatically. It may still be
275   * cancelled manually or interrupted by another command. Returning true will result in the
276   * command executing once and finishing immediately. We recommend using {@link InstantCommand}
277   * for this.
278   *
279   * @return whether this command is finished.
280   * @see Command#isTimedOut() isTimedOut()
281   */
282  protected abstract boolean isFinished();
283
284  /**
285   * Called when the command ended peacefully. This is where you may want to wrap up loose ends,
286   * like shutting off a motor that was being used in the command.
287   */
288  protected void end() {}
289
290  /**
291   * A shadow method called after {@link Command#end() end()}.
292   */
293  @SuppressWarnings("MethodName")
294  void _end() {
295  }
296
297  /**
298   * Called when the command ends because somebody called {@link Command#cancel() cancel()} or
299   * another command shared the same requirements as this one, and booted it out.
300   *
301   * <p>This is where you may want to wrap up loose ends, like shutting off a motor that was being
302   * used in the command.
303   *
304   * <p>Generally, it is useful to simply call the {@link Command#end() end()} method within this
305   * method, as done here.
306   */
307  protected void interrupted() {
308    end();
309  }
310
311  /**
312   * A shadow method called after {@link Command#interrupted() interrupted()}.
313   */
314  @SuppressWarnings("MethodName")
315  void _interrupted() {}
316
317  /**
318   * Called to indicate that the timer should start. This is called right before {@link
319   * Command#initialize() initialize()} is, inside the {@link Command#run() run()} method.
320   */
321  private void startTiming() {
322    m_startTime = Timer.getFPGATimestamp();
323  }
324
325  /**
326   * Returns whether or not the {@link Command#timeSinceInitialized() timeSinceInitialized()} method
327   * returns a number which is greater than or equal to the timeout for the command. If there is no
328   * timeout, this will always return false.
329   *
330   * @return whether the time has expired
331   */
332  protected synchronized boolean isTimedOut() {
333    return m_timeout != -1 && timeSinceInitialized() >= m_timeout;
334  }
335
336  /**
337   * Returns the requirements (as an {@link Enumeration Enumeration} of {@link Subsystem
338   * Subsystems}) of this command.
339   *
340   * @return the requirements (as an {@link Enumeration Enumeration} of {@link Subsystem
341   * Subsystems}) of this command
342   */
343  synchronized Enumeration getRequirements() {
344    return m_requirements.getElements();
345  }
346
347  /**
348   * Prevents further changes from being made.
349   */
350  synchronized void lockChanges() {
351    m_locked = true;
352  }
353
354  /**
355   * If changes are locked, then this will throw an {@link IllegalUseOfCommandException}.
356   *
357   * @param message the message to say (it is appended by a default message)
358   */
359  synchronized void validate(String message) {
360    if (m_locked) {
361      throw new IllegalUseOfCommandException(message
362          + " after being started or being added to a command group");
363    }
364  }
365
366  /**
367   * Sets the parent of this command. No actual change is made to the group.
368   *
369   * @param parent the parent
370   * @throws IllegalUseOfCommandException if this {@link Command} already is already in a group
371   */
372  synchronized void setParent(CommandGroup parent) {
373    if (m_parent != null) {
374      throw new IllegalUseOfCommandException(
375          "Can not give command to a command group after already being put in a command group");
376    }
377    lockChanges();
378    m_parent = parent;
379  }
380
381  /**
382   * Returns whether the command has a parent.
383   *
384   * @param True if the command has a parent.
385   */
386  synchronized boolean isParented() {
387    return m_parent != null;
388  }
389
390  /**
391   * Clears list of subsystem requirements. This is only used by
392   * {@link ConditionalCommand} so cancelling the chosen command works properly
393   * in {@link CommandGroup}.
394   */
395  protected void clearRequirements() {
396    m_requirements.clear();
397  }
398
399  /**
400   * Starts up the command. Gets the command ready to start. <p> Note that the command will
401   * eventually start, however it will not necessarily do so immediately, and may in fact be
402   * canceled before initialize is even called. </p>
403   *
404   * @throws IllegalUseOfCommandException if the command is a part of a CommandGroup
405   */
406  public synchronized void start() {
407    lockChanges();
408    if (m_parent != null) {
409      throw new IllegalUseOfCommandException(
410          "Can not start a command that is a part of a command group");
411    }
412    Scheduler.getInstance().add(this);
413    m_completed = false;
414  }
415
416  /**
417   * This is used internally to mark that the command has been started. The lifecycle of a command
418   * is:
419   *
420   * <p>startRunning() is called. run() is called (multiple times potentially) removed() is called.
421   *
422   * <p>It is very important that startRunning and removed be called in order or some assumptions of
423   * the code will be broken.
424   */
425  synchronized void startRunning() {
426    m_running = true;
427    m_startTime = -1;
428  }
429
430  /**
431   * Returns whether or not the command is running. This may return true even if the command has
432   * just been canceled, as it may not have yet called {@link Command#interrupted()}.
433   *
434   * @return whether or not the command is running
435   */
436  public synchronized boolean isRunning() {
437    return m_running;
438  }
439
440  /**
441   * This will cancel the current command. <p> This will cancel the current command eventually. It
442   * can be called multiple times. And it can be called when the command is not running. If the
443   * command is running though, then the command will be marked as canceled and eventually removed.
444   * </p> <p> A command can not be canceled if it is a part of a command group, you must cancel the
445   * command group instead. </p>
446   *
447   * @throws IllegalUseOfCommandException if this command is a part of a command group
448   */
449  public synchronized void cancel() {
450    if (m_parent != null) {
451      throw new IllegalUseOfCommandException("Can not manually cancel a command in a command "
452          + "group");
453    }
454    _cancel();
455  }
456
457  /**
458   * This works like cancel(), except that it doesn't throw an exception if it is a part of a
459   * command group. Should only be called by the parent command group.
460   */
461  @SuppressWarnings("MethodName")
462  synchronized void _cancel() {
463    if (isRunning()) {
464      m_canceled = true;
465    }
466  }
467
468  /**
469   * Returns whether or not this has been canceled.
470   *
471   * @return whether or not this has been canceled
472   */
473  public synchronized boolean isCanceled() {
474    return m_canceled;
475  }
476
477  /**
478   * Whether or not this command has completed running.
479   *
480   * @return whether or not this command has completed running.
481   */
482  public synchronized boolean isCompleted() {
483    return m_completed;
484  }
485
486  /**
487   * Returns whether or not this command can be interrupted.
488   *
489   * @return whether or not this command can be interrupted
490   */
491  public synchronized boolean isInterruptible() {
492    return m_interruptible;
493  }
494
495  /**
496   * Sets whether or not this command can be interrupted.
497   *
498   * @param interruptible whether or not this command can be interrupted
499   */
500  protected synchronized void setInterruptible(boolean interruptible) {
501    m_interruptible = interruptible;
502  }
503
504  /**
505   * Checks if the command requires the given {@link Subsystem}.
506   *
507   * @param system the system
508   * @return whether or not the subsystem is required, or false if given null
509   */
510  public synchronized boolean doesRequire(Subsystem system) {
511    return m_requirements.contains(system);
512  }
513
514  /**
515   * Returns the {@link CommandGroup} that this command is a part of. Will return null if this
516   * {@link Command} is not in a group.
517   *
518   * @return the {@link CommandGroup} that this command is a part of (or null if not in group)
519   */
520  public synchronized CommandGroup getGroup() {
521    return m_parent;
522  }
523
524  /**
525   * Sets whether or not this {@link Command} should run when the robot is disabled.
526   *
527   * <p>By default a command will not run when the robot is disabled, and will in fact be canceled.
528   *
529   * @param run whether or not this command should run when the robot is disabled
530   */
531  public void setRunWhenDisabled(boolean run) {
532    m_runWhenDisabled = run;
533  }
534
535  /**
536   * Returns whether or not this {@link Command} will run when the robot is disabled, or if it will
537   * cancel itself.
538   *
539   * @return True if this command will run when the robot is disabled.
540   */
541  public boolean willRunWhenDisabled() {
542    return m_runWhenDisabled;
543  }
544
545  /**
546   * The string representation for a {@link Command} is by default its name.
547   *
548   * @return the string representation of this object
549   */
550  @Override
551  public String toString() {
552    return getName();
553  }
554
555  @Override
556  public void initSendable(SendableBuilder builder) {
557    builder.setSmartDashboardType("Command");
558    builder.addStringProperty(".name", this::getName, null);
559    builder.addBooleanProperty("running", this::isRunning, (value) -> {
560      if (value) {
561        if (!isRunning()) {
562          start();
563        }
564      } else {
565        if (isRunning()) {
566          cancel();
567        }
568      }
569    });
570    builder.addBooleanProperty(".isParented", this::isParented, null);
571  }
572}