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