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    
008    package edu.wpi.first.wpilibj.command;
009    
010    import edu.wpi.first.wpilibj.DriverStation;
011    import edu.wpi.first.wpilibj.NamedSendable;
012    import edu.wpi.first.wpilibj.Timer;
013    import edu.wpi.first.wpilibj.tables.ITable;
014    import edu.wpi.first.wpilibj.tables.ITableListener;
015    import java.util.Enumeration;
016    import 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     */
046    public 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 && DriverStation.getInstance().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    }