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