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