001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package org.wpilib.command2; 006 007import static org.wpilib.units.Units.Seconds; 008import static org.wpilib.util.ErrorMessages.requireNonNullParam; 009 010import java.util.Collection; 011import java.util.HashSet; 012import java.util.Set; 013import java.util.function.BooleanSupplier; 014import org.wpilib.annotation.NoDiscard; 015import org.wpilib.units.measure.Time; 016import org.wpilib.util.function.BooleanConsumer; 017import org.wpilib.util.sendable.Sendable; 018import org.wpilib.util.sendable.SendableBuilder; 019import org.wpilib.util.sendable.SendableRegistry; 020 021/** 022 * A state machine representing a complete action to be performed by the robot. Commands are run by 023 * the {@link CommandScheduler}, and can be composed into CommandGroups to allow users to build 024 * complicated multistep actions without the need to roll the state machine logic themselves. 025 * 026 * <p>Commands are run synchronously from the main robot loop; no multithreading is used, unless 027 * specified explicitly from the command implementation. 028 * 029 * <p>This class is provided by the Commands v2 VendorDep 030 */ 031@NoDiscard("Commands must be used! Did you mean to bind it to a trigger?") 032public abstract class Command implements Sendable { 033 /** Requirements set. */ 034 private final Set<Subsystem> m_requirements = new HashSet<>(); 035 036 /** Default constructor. */ 037 @SuppressWarnings("this-escape") 038 protected Command() { 039 String name = getClass().getName(); 040 SendableRegistry.add(this, name.substring(name.lastIndexOf('.') + 1)); 041 } 042 043 /** The initial subroutine of a command. Called once when the command is initially scheduled. */ 044 public void initialize() {} 045 046 /** The main body of a command. Called repeatedly while the command is scheduled. */ 047 public void execute() {} 048 049 /** 050 * The action to take when the command ends. Called when either the command finishes normally, or 051 * when it interrupted/canceled. 052 * 053 * <p>Do not schedule commands here that share requirements with this command. Use {@link 054 * #andThen(Command...)} instead. 055 * 056 * @param interrupted whether the command was interrupted/canceled 057 */ 058 public void end(boolean interrupted) {} 059 060 /** 061 * Whether the command has finished. Once a command finishes, the scheduler will call its end() 062 * method and un-schedule it. 063 * 064 * @return whether the command has finished. 065 */ 066 public boolean isFinished() { 067 return false; 068 } 069 070 /** 071 * Specifies the set of subsystems used by this command. Two commands cannot use the same 072 * subsystem at the same time. If another command is scheduled that shares a requirement, {@link 073 * #getInterruptionBehavior()} will be checked and followed. If no subsystems are required, return 074 * an empty set. 075 * 076 * <p>Note: it is recommended that user implementations contain the requirements as a field, and 077 * return that field here, rather than allocating a new set every time this is called. 078 * 079 * @return the set of subsystems that are required 080 * @see InterruptionBehavior 081 */ 082 public Set<Subsystem> getRequirements() { 083 return m_requirements; 084 } 085 086 /** 087 * Adds the specified subsystems to the requirements of the command. The scheduler will prevent 088 * two commands that require the same subsystem from being scheduled simultaneously. 089 * 090 * <p>Note that the scheduler determines the requirements of a command when it is scheduled, so 091 * this method should normally be called from the command's constructor. 092 * 093 * @param requirements the requirements to add 094 */ 095 public final void addRequirements(Subsystem... requirements) { 096 for (Subsystem requirement : requirements) { 097 m_requirements.add(requireNonNullParam(requirement, "requirement", "addRequirements")); 098 } 099 } 100 101 /** 102 * Adds the specified subsystems to the requirements of the command. The scheduler will prevent 103 * two commands that require the same subsystem from being scheduled simultaneously. 104 * 105 * <p>Note that the scheduler determines the requirements of a command when it is scheduled, so 106 * this method should normally be called from the command's constructor. 107 * 108 * @param requirements the requirements to add 109 */ 110 public final void addRequirements(Collection<Subsystem> requirements) { 111 m_requirements.addAll(requirements); 112 } 113 114 /** 115 * Gets the name of this Command. 116 * 117 * <p>By default, the simple class name is used. This can be changed with {@link 118 * #setName(String)}. 119 * 120 * @return The display name of the Command 121 */ 122 public String getName() { 123 return SendableRegistry.getName(this); 124 } 125 126 /** 127 * Sets the name of this Command. 128 * 129 * @param name The display name of the Command. 130 */ 131 public void setName(String name) { 132 SendableRegistry.setName(this, name); 133 } 134 135 /** 136 * Gets the subsystem name of this Command. 137 * 138 * @return Subsystem name 139 */ 140 public String getSubsystem() { 141 return SendableRegistry.getSubsystem(this); 142 } 143 144 /** 145 * Sets the subsystem name of this Command. 146 * 147 * @param subsystem subsystem name 148 */ 149 public void setSubsystem(String subsystem) { 150 SendableRegistry.setSubsystem(this, subsystem); 151 } 152 153 /** 154 * Decorates this command with a timeout. If the specified timeout is exceeded before the command 155 * finishes normally, the command will be interrupted and un-scheduled. 156 * 157 * <p>Note: This decorator works by adding this command to a composition. The command the 158 * decorator was called on cannot be scheduled independently or be added to a different 159 * composition (namely, decorators), unless it is manually cleared from the list of composed 160 * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition 161 * returned from this method can be further decorated without issue. 162 * 163 * @param seconds the timeout duration 164 * @return the command with the timeout added 165 */ 166 public ParallelRaceGroup withTimeout(double seconds) { 167 return raceWith(new WaitCommand(seconds)); 168 } 169 170 /** 171 * Decorates this command with a timeout. If the specified timeout is exceeded before the command 172 * finishes normally, the command will be interrupted and un-scheduled. 173 * 174 * <p>Note: This decorator works by adding this command to a composition. The command the 175 * decorator was called on cannot be scheduled independently or be added to a different 176 * composition (namely, decorators), unless it is manually cleared from the list of composed 177 * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition 178 * returned from this method can be further decorated without issue. 179 * 180 * @param time the timeout duration 181 * @return the command with the timeout added 182 */ 183 public ParallelRaceGroup withTimeout(Time time) { 184 return withTimeout(time.in(Seconds)); 185 } 186 187 /** 188 * Decorates this command with an interrupt condition. If the specified condition becomes true 189 * before the command finishes normally, the command will be interrupted and un-scheduled. 190 * 191 * <p>Note: This decorator works by adding this command to a composition. The command the 192 * decorator was called on cannot be scheduled independently or be added to a different 193 * composition (namely, decorators), unless it is manually cleared from the list of composed 194 * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition 195 * returned from this method can be further decorated without issue. 196 * 197 * @param condition the interrupt condition 198 * @return the command with the interrupt condition added 199 * @see #onlyWhile(BooleanSupplier) 200 */ 201 public ParallelRaceGroup until(BooleanSupplier condition) { 202 return raceWith(new WaitUntilCommand(condition)); 203 } 204 205 /** 206 * Decorates this command with a run condition. If the specified condition becomes false before 207 * the command finishes normally, the command will be interrupted and un-scheduled. 208 * 209 * <p>Note: This decorator works by adding this command to a composition. The command the 210 * decorator was called on cannot be scheduled independently or be added to a different 211 * composition (namely, decorators), unless it is manually cleared from the list of composed 212 * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition 213 * returned from this method can be further decorated without issue. 214 * 215 * @param condition the run condition 216 * @return the command with the run condition added 217 * @see #until(BooleanSupplier) 218 */ 219 public ParallelRaceGroup onlyWhile(BooleanSupplier condition) { 220 return until(() -> !condition.getAsBoolean()); 221 } 222 223 /** 224 * Decorates this command with a runnable to run before this command starts. 225 * 226 * <p>Note: This decorator works by adding this command to a composition. The command the 227 * decorator was called on cannot be scheduled independently or be added to a different 228 * composition (namely, decorators), unless it is manually cleared from the list of composed 229 * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition 230 * returned from this method can be further decorated without issue. 231 * 232 * @param toRun the Runnable to run 233 * @param requirements the required subsystems 234 * @return the decorated command 235 */ 236 public SequentialCommandGroup beforeStarting(Runnable toRun, Subsystem... requirements) { 237 return beforeStarting(new InstantCommand(toRun, requirements)); 238 } 239 240 /** 241 * Decorates this command with another command to run before this command starts. 242 * 243 * <p>Note: This decorator works by adding this command to a composition. The command the 244 * decorator was called on cannot be scheduled independently or be added to a different 245 * composition (namely, decorators), unless it is manually cleared from the list of composed 246 * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition 247 * returned from this method can be further decorated without issue. 248 * 249 * @param before the command to run before this one 250 * @return the decorated command 251 */ 252 public SequentialCommandGroup beforeStarting(Command before) { 253 return new SequentialCommandGroup(before, this); 254 } 255 256 /** 257 * Decorates this command with a runnable to run after the command finishes. 258 * 259 * <p>Note: This decorator works by adding this command to a composition. The command the 260 * decorator was called on cannot be scheduled independently or be added to a different 261 * composition (namely, decorators), unless it is manually cleared from the list of composed 262 * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition 263 * returned from this method can be further decorated without issue. 264 * 265 * @param toRun the Runnable to run 266 * @param requirements the required subsystems 267 * @return the decorated command 268 */ 269 public SequentialCommandGroup andThen(Runnable toRun, Subsystem... requirements) { 270 return andThen(new InstantCommand(toRun, requirements)); 271 } 272 273 /** 274 * Decorates this command with a set of commands to run after it in sequence. Often more 275 * convenient/less-verbose than constructing a new {@link SequentialCommandGroup} explicitly. 276 * 277 * <p>Note: This decorator works by adding this command to a composition. The command the 278 * decorator was called on cannot be scheduled independently or be added to a different 279 * composition (namely, decorators), unless it is manually cleared from the list of composed 280 * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition 281 * returned from this method can be further decorated without issue. 282 * 283 * @param next the commands to run next 284 * @return the decorated command 285 */ 286 public SequentialCommandGroup andThen(Command... next) { 287 SequentialCommandGroup group = new SequentialCommandGroup(this); 288 group.addCommands(next); 289 return group; 290 } 291 292 /** 293 * Creates a new command that runs this command and the deadline in parallel, finishing (and 294 * interrupting this command) when the deadline finishes. 295 * 296 * <p>Note: This decorator works by adding this command to a composition. The command the 297 * decorator was called on cannot be scheduled independently or be added to a different 298 * composition (namely, decorators), unless it is manually cleared from the list of composed 299 * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition 300 * returned from this method can be further decorated without issue. 301 * 302 * @param deadline the deadline of the command group 303 * @return the decorated command 304 * @see Command#deadlineFor 305 */ 306 public ParallelDeadlineGroup withDeadline(Command deadline) { 307 return new ParallelDeadlineGroup(deadline, this); 308 } 309 310 /** 311 * Decorates this command with a set of commands to run parallel to it, ending when the calling 312 * command ends and interrupting all the others. Often more convenient/less-verbose than 313 * constructing a new {@link ParallelDeadlineGroup} explicitly. 314 * 315 * <p>Note: This decorator works by adding this command to a composition. The command the 316 * decorator was called on cannot be scheduled independently or be added to a different 317 * composition (namely, decorators), unless it is manually cleared from the list of composed 318 * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition 319 * returned from this method can be further decorated without issue. 320 * 321 * @param parallel the commands to run in parallel. Note the parallel commands will be interrupted 322 * when the deadline command ends 323 * @return the decorated command 324 * @see Command#withDeadline 325 */ 326 public ParallelDeadlineGroup deadlineFor(Command... parallel) { 327 return new ParallelDeadlineGroup(this, parallel); 328 } 329 330 /** 331 * Decorates this command with a set of commands to run parallel to it, ending when the last 332 * command ends. Often more convenient/less-verbose than constructing a new {@link 333 * ParallelCommandGroup} explicitly. 334 * 335 * <p>Note: This decorator works by adding this command to a composition. The command the 336 * decorator was called on cannot be scheduled independently or be added to a different 337 * composition (namely, decorators), unless it is manually cleared from the list of composed 338 * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition 339 * returned from this method can be further decorated without issue. 340 * 341 * @param parallel the commands to run in parallel 342 * @return the decorated command 343 */ 344 public ParallelCommandGroup alongWith(Command... parallel) { 345 ParallelCommandGroup group = new ParallelCommandGroup(this); 346 group.addCommands(parallel); 347 return group; 348 } 349 350 /** 351 * Decorates this command with a set of commands to run parallel to it, ending when the first 352 * command ends. Often more convenient/less-verbose than constructing a new {@link 353 * ParallelRaceGroup} explicitly. 354 * 355 * <p>Note: This decorator works by adding this command to a composition. The command the 356 * decorator was called on cannot be scheduled independently or be added to a different 357 * composition (namely, decorators), unless it is manually cleared from the list of composed 358 * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition 359 * returned from this method can be further decorated without issue. 360 * 361 * @param parallel the commands to run in parallel 362 * @return the decorated command 363 */ 364 public ParallelRaceGroup raceWith(Command... parallel) { 365 ParallelRaceGroup group = new ParallelRaceGroup(this); 366 group.addCommands(parallel); 367 return group; 368 } 369 370 /** 371 * Decorates this command to run repeatedly, restarting it when it ends, until this command is 372 * interrupted. The decorated command can still be canceled. 373 * 374 * <p>Note: This decorator works by adding this command to a composition. The command the 375 * decorator was called on cannot be scheduled independently or be added to a different 376 * composition (namely, decorators), unless it is manually cleared from the list of composed 377 * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition 378 * returned from this method can be further decorated without issue. 379 * 380 * @return the decorated command 381 */ 382 public RepeatCommand repeatedly() { 383 return new RepeatCommand(this); 384 } 385 386 /** 387 * Decorates this command to run "by proxy" by wrapping it in a {@link ProxyCommand}. Use this for 388 * "forking off" from command compositions when the user does not wish to extend the command's 389 * requirements to the entire command composition. ProxyCommand has unique implications and 390 * semantics, see the WPILib docs for a full explanation. 391 * 392 * @return the decorated command 393 * @see ProxyCommand 394 * @see <a 395 * href="https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html#scheduling-other-commands">WPILib 396 * docs</a> 397 */ 398 public ProxyCommand asProxy() { 399 return new ProxyCommand(this); 400 } 401 402 /** 403 * Decorates this command to only run if this condition is not met. If the command is already 404 * running and the condition changes to true, the command will not stop running. The requirements 405 * of this command will be kept for the new conditional command. 406 * 407 * <p>Note: This decorator works by adding this command to a composition. The command the 408 * decorator was called on cannot be scheduled independently or be added to a different 409 * composition (namely, decorators), unless it is manually cleared from the list of composed 410 * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition 411 * returned from this method can be further decorated without issue. 412 * 413 * @param condition the condition that will prevent the command from running 414 * @return the decorated command 415 * @see #onlyIf(BooleanSupplier) 416 */ 417 public ConditionalCommand unless(BooleanSupplier condition) { 418 return new ConditionalCommand(new InstantCommand(), this, condition); 419 } 420 421 /** 422 * Decorates this command to only run if this condition is met. If the command is already running 423 * and the condition changes to false, the command will not stop running. The requirements of this 424 * command will be kept for the new conditional command. 425 * 426 * <p>Note: This decorator works by adding this command to a composition. The command the 427 * decorator was called on cannot be scheduled independently or be added to a different 428 * composition (namely, decorators), unless it is manually cleared from the list of composed 429 * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition 430 * returned from this method can be further decorated without issue. 431 * 432 * @param condition the condition that will allow the command to run 433 * @return the decorated command 434 * @see #unless(BooleanSupplier) 435 */ 436 public ConditionalCommand onlyIf(BooleanSupplier condition) { 437 return unless(() -> !condition.getAsBoolean()); 438 } 439 440 /** 441 * Decorates this command to run or stop when disabled. 442 * 443 * @param doesRunWhenDisabled true to run when disabled. 444 * @return the decorated command 445 */ 446 public WrapperCommand ignoringDisable(boolean doesRunWhenDisabled) { 447 return new WrapperCommand(this) { 448 @Override 449 public boolean runsWhenDisabled() { 450 return doesRunWhenDisabled; 451 } 452 }; 453 } 454 455 /** 456 * Decorates this command to have a different {@link InterruptionBehavior interruption behavior}. 457 * 458 * @param interruptBehavior the desired interrupt behavior 459 * @return the decorated command 460 */ 461 public WrapperCommand withInterruptBehavior(InterruptionBehavior interruptBehavior) { 462 return new WrapperCommand(this) { 463 @Override 464 public InterruptionBehavior getInterruptionBehavior() { 465 return interruptBehavior; 466 } 467 }; 468 } 469 470 /** 471 * Decorates this command with a lambda to call on interrupt or end, following the command's 472 * inherent {@link #end(boolean)} method. 473 * 474 * @param end a lambda accepting a boolean parameter specifying whether the command was 475 * interrupted. 476 * @return the decorated command 477 */ 478 public WrapperCommand finallyDo(BooleanConsumer end) { 479 requireNonNullParam(end, "end", "Command.finallyDo()"); 480 return new WrapperCommand(this) { 481 @Override 482 public void end(boolean interrupted) { 483 super.end(interrupted); 484 end.accept(interrupted); 485 } 486 }; 487 } 488 489 /** 490 * Decorates this command with a lambda to call on interrupt or end, following the command's 491 * inherent {@link #end(boolean)} method. The provided lambda will run identically in both 492 * interrupt and end cases. 493 * 494 * @param end a lambda to run when the command ends, whether or not it was interrupted. 495 * @return the decorated command 496 */ 497 public WrapperCommand finallyDo(Runnable end) { 498 return finallyDo(interrupted -> end.run()); 499 } 500 501 /** 502 * Decorates this command with a lambda to call on interrupt, following the command's inherent 503 * {@link #end(boolean)} method. 504 * 505 * @param handler a lambda to run when the command is interrupted 506 * @return the decorated command 507 */ 508 public WrapperCommand handleInterrupt(Runnable handler) { 509 requireNonNullParam(handler, "handler", "Command.handleInterrupt()"); 510 return finallyDo( 511 interrupted -> { 512 if (interrupted) { 513 handler.run(); 514 } 515 }); 516 } 517 518 /** 519 * Cancels this command. Will call {@link #end(boolean) end(true)}. Commands will be canceled 520 * regardless of {@link InterruptionBehavior interruption behavior}. 521 * 522 * @see CommandScheduler#cancel(Command...) 523 */ 524 public void cancel() { 525 CommandScheduler.getInstance().cancel(this); 526 } 527 528 /** 529 * Whether the command is currently scheduled. Note that this does not detect whether the command 530 * is in a composition, only whether it is directly being run by the scheduler. 531 * 532 * @return Whether the command is scheduled. 533 */ 534 public boolean isScheduled() { 535 return CommandScheduler.getInstance().isScheduled(this); 536 } 537 538 /** 539 * Whether the command requires a given subsystem. 540 * 541 * @param requirement the subsystem to inquire about 542 * @return whether the subsystem is required 543 */ 544 public boolean hasRequirement(Subsystem requirement) { 545 return getRequirements().contains(requirement); 546 } 547 548 /** 549 * How the command behaves when another command with a shared requirement is scheduled. 550 * 551 * @return a variant of {@link InterruptionBehavior}, defaulting to {@link 552 * InterruptionBehavior#kCancelSelf kCancelSelf}. 553 */ 554 public InterruptionBehavior getInterruptionBehavior() { 555 return InterruptionBehavior.kCancelSelf; 556 } 557 558 /** 559 * Whether the given command should run when the robot is disabled. Override to return true if the 560 * command should run when disabled. 561 * 562 * @return whether the command should run when the robot is disabled 563 */ 564 public boolean runsWhenDisabled() { 565 return false; 566 } 567 568 /** 569 * Decorates this Command with a name. 570 * 571 * @param name name 572 * @return the decorated Command 573 */ 574 public WrapperCommand withName(String name) { 575 WrapperCommand wrapper = new WrapperCommand(Command.this) {}; 576 wrapper.setName(name); 577 return wrapper; 578 } 579 580 @Override 581 public void initSendable(SendableBuilder builder) { 582 builder.setSmartDashboardType("Command"); 583 builder.addStringProperty(".name", this::getName, null); 584 builder.addBooleanProperty( 585 "running", 586 this::isScheduled, 587 value -> { 588 if (value) { 589 if (!isScheduled()) { 590 CommandScheduler.getInstance().schedule(this); 591 } 592 } else { 593 if (isScheduled()) { 594 cancel(); 595 } 596 } 597 }); 598 builder.addBooleanProperty( 599 ".isParented", () -> CommandScheduler.getInstance().isComposed(this), null); 600 builder.addStringProperty( 601 "interruptBehavior", () -> getInterruptionBehavior().toString(), null); 602 builder.addBooleanProperty("runsWhenDisabled", this::runsWhenDisabled, null); 603 } 604 605 /** 606 * An enum describing the command's behavior when another command with a shared requirement is 607 * scheduled. 608 */ 609 public enum InterruptionBehavior { 610 /** 611 * This command ends, {@link #end(boolean) end(true)} is called, and the incoming command is 612 * scheduled normally. 613 * 614 * <p>This is the default behavior. 615 */ 616 kCancelSelf, 617 /** This command continues, and the incoming command is not scheduled. */ 618 kCancelIncoming 619 } 620}