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}