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