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