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}