Class Coroutine

java.lang.Object
org.wpilib.command3.Coroutine

public final class Coroutine extends Object
A coroutine object is injected into command's Command.run(Coroutine) method to allow commands to yield and compositions to run other commands. Commands are considered bound to a coroutine while they're scheduled; attempting to use a coroutine outside the command bound to it will result in an IllegalStateException being thrown.
  • Method Details

    • setCancelOnForkFailure

      public void setCancelOnForkFailure(boolean cancelOnForkFailure)
      Configures the coroutine to cancel itself if a child command couldn't be forked. This setting defaults to true, but can be changed to allow the coroutine to continue running after a fork failure.

      This setting only affects commands forked by using the coroutine methods fork, await, awaitAll, or awaitAny. Commands scheduled directly with Scheduler.schedule(Command) or via inner trigger bindings will not be affected.

      Parameters:
      cancelOnForkFailure - true to make the coroutine cancel itself if a child command couldn't be forked, false to allow the coroutine to continue running after a fork failure
      See Also:
    • isCancelOnForkFailure

      public boolean isCancelOnForkFailure()
      Checks if the coroutine is currently configured to cancel itself if a child command couldn't be forked.
      Returns:
      true if the coroutine will cancel itself if a child command couldn't be forked, false otherwise
      See Also:
    • requestCancellation

      public void requestCancellation()
      Requests early cancellation of the coroutine and the command bound to it. Any child commands will be canceled as well, as commands can never outlive their parent, but the parent command will not be canceled.

      Team code shouldn't need to use this; instead, use a return statement in your command's function body to exit early. This method is primarily to be used by internal APIs that don't have access to the command's function body.

    • yield

      public boolean yield()
      Yields control back to the scheduler to allow other commands to execute. This can be thought of as "pausing" the currently executing command.
      Returns:
      true
      Throws:
      IllegalStateException - if called anywhere other than the coroutine's running command
    • park

      public void park()
      Parks the current command. No code in a command declared after calling park() will be executed. A parked command will never complete naturally and must be interrupted or canceled.
      Throws:
      IllegalStateException - if called anywhere other than the coroutine's running command
    • fork

      public Coroutine.ForkResult fork(Command... commands)
      Schedules child commands and then immediately returns. The child commands will run until their natural completion, the parent command exits, or the parent command cancels it.

      If any child command is unable to be scheduled per the contract of Scheduler.isSchedulable(Command), then none of the commands will be scheduled and the method call will return a Coroutine.ForkResult containing the unschedulable commands. If the coroutine is configured to cancel on fork failure, then the coroutine will be canceled and user code will not get a chance to handle the failure result.

      This is a nonblocking operation. To fork and then wait for the child command to complete, use await(Command).

      The parent command will continue executing while the child command runs, and can resync with the child command using await(Command).

      Command example() {
        return Command.noRequirements(coroutine -> {
          Command child = ...;
          ForkResult childTask = coroutine.fork(child);
          // ... do more things
          // then sync back up with the child command
          if (childTask.successful()) {
            childTask.awaitCompletion();
          }
        }).named("Example");
      }
      
      Parameters:
      commands - The commands to fork.
      Returns:
      a result indicating whether the fork was successful or if the commands could not be forked
      Throws:
      IllegalStateException - if called anywhere other than the coroutine's running command
      See Also:
    • fork

      public Coroutine.ForkResult fork(Collection<? extends Command> commands)
      Forks off some commands. Each command will run until its natural completion, the parent command exits, or the parent command cancels it. The parent command will continue executing while the forked commands run, and can resync with the forked commands using awaitAll(Collection).

      If any child command is unable to be scheduled per the contract of Scheduler.isSchedulable(Command), then none of the commands will be scheduled and the method call will return a Coroutine.ForkResult containing the unschedulable commands. If the coroutine is configured to cancel on fork failure, then the coroutine will be canceled and user code will not get a chance to handle the failure result.

      Command example() {
        return Command.noRequirements(coroutine -> {
          Collection<Command> innerCommands = ...;
          ForkResult childTask = coroutine.fork(innerCommands);
          // ... do more things
          // then sync back up with the inner commands
          if (childTask.successful()) {
            childTask.awaitCompletion();
          }
        }).named("Example");
      }
      
      Parameters:
      commands - The commands to fork.
      Returns:
      a result indicating whether the fork was successful or if the commands could not be forked
      Throws:
      IllegalStateException - if called anywhere other than the coroutine's running command
      See Also:
    • await

      public Coroutine.ForkResult await(Command command)
      Awaits completion of a command. If the command is not currently scheduled or running, it will be scheduled automatically. This is a blocking operation and will not return until the command completes or has been interrupted by another command scheduled by the same parent.

      If the child command is unable to be scheduled per the contract of Scheduler.isSchedulable(Command), then it will not be scheduled and the method call will return a Coroutine.ForkResult containing the command. If the coroutine is configured to cancel on fork failure, then the coroutine will be canceled and user code will not get a chance to handle the failure result.

      Parameters:
      command - the command to await
      Returns:
      a result indicating whether the await was successful or if the commands could not be awaited
      Throws:
      IllegalStateException - if called anywhere other than the coroutine's running command
      See Also:
    • awaitAll

      public Coroutine.ForkResult awaitAll(Collection<? extends Command> commands)
      Awaits completion of all given commands. If any command is not currently scheduled or running, it will be scheduled.

      If any child command is unable to be scheduled per the contract of Scheduler.isSchedulable(Command), then none of the commands will be scheduled and the method call will return a Coroutine.ForkResult containing the unschedulable commands. If the coroutine is configured to cancel on fork failure, then the coroutine will be canceled and user code will not get a chance to handle the failure result.

      Parameters:
      commands - the commands to await
      Returns:
      a result indicating whether the await was successful or if the commands could not be awaited
      Throws:
      IllegalArgumentException - if any of the commands conflict with each other
      IllegalStateException - if called anywhere other than the coroutine's running command
      See Also:
    • awaitAll

      public Coroutine.ForkResult awaitAll(Command... commands)
      Awaits completion of all given commands. If any command is not currently scheduled or running, it will be scheduled.

      If any child command is unable to be scheduled per the contract of Scheduler.isSchedulable(Command), then none of the commands will be scheduled and the method call will return a Coroutine.ForkResult containing the unschedulable commands. If the coroutine is configured to cancel on fork failure, then the coroutine will be canceled and user code will not get a chance to handle the failure result.

      Parameters:
      commands - the commands to await
      Returns:
      a result indicating whether the await was successful or if the commands could not be awaited
      Throws:
      IllegalArgumentException - if any of the commands conflict with each other
      IllegalStateException - if called anywhere other than the coroutine's running command
      See Also:
    • awaitAny

      public Coroutine.ForkResult awaitAny(Collection<? extends Command> commands)
      Awaits completion of any given commands. Any command that's not already scheduled or running will be scheduled. After any of the given commands complete, the rest will be canceled.

      If any child command is unable to be scheduled per the contract of Scheduler.isSchedulable(Command), then none of the commands will be scheduled and the method call will return a Coroutine.ForkResult containing the unschedulable commands. If the coroutine is configured to cancel on fork failure, then the coroutine will be canceled and user code will not get a chance to handle the failure result.

      Parameters:
      commands - the commands to await
      Returns:
      a result indicating whether the await was successful or if the commands could not be awaited
      Throws:
      IllegalArgumentException - if any of the commands conflict with each other
      IllegalStateException - if called anywhere other than the coroutine's running command
      See Also:
    • awaitAny

      public Coroutine.ForkResult awaitAny(Command... commands)
      Awaits completion of any given commands. Any command that's not already scheduled or running will be scheduled. After any of the given commands completes, the rest will be canceled.

      If any child command is unable to be scheduled per the contract of Scheduler.isSchedulable(Command), then none of the commands will be scheduled and the method call will return a Coroutine.ForkResult containing the unschedulable commands. If the coroutine is configured to cancel on fork failure, then the coroutine will be canceled and user code will not get a chance to handle the failure result.

      Parameters:
      commands - the commands to await
      Returns:
      a result indicating whether the await was successful or if the commands could not be awaited
      Throws:
      IllegalArgumentException - if any of the commands conflict with each other
      IllegalStateException - if called anywhere other than the coroutine's running command
      See Also:
    • wait

      public void wait(Time duration)
      Waits for some duration of time to elapse. Returns immediately if the given duration is zero or negative. Call this within a command or command composition to introduce a simple delay.

      For example, a basic autonomous routine that drives straight for 5 seconds:

      Command timedDrive() {
        return drivebase.run(coroutine -> {
          drivebase.tankDrive(1, 1);
          coroutine.wait(Seconds.of(5));
          drivebase.stop();
        }).named("Timed Drive");
      }
      

      Note that the resolution of the wait period is equal to the period at which Scheduler.run() is called by the robot program. If using a 20 millisecond update period, the wait will be rounded up to the nearest 20 millisecond interval; in this scenario, calling wait(Milliseconds.of(1)) and wait(Milliseconds.of(19)) would have identical effects.

      Very small loop times near the loop period are sensitive to the order in which commands are executed. If a command waits for 10 ms at the end of a scheduler cycle, and then all the commands that ran before it complete or exit, and then the next cycle starts immediately, the wait will be evaluated at the start of that next cycle, which occurred less than 10 ms later. Therefore, the wait will see not enough time has passed and only exit after an additional cycle elapses, adding an unexpected extra 20 ms to the wait time. This becomes less of a problem with smaller loop periods, as the extra 1-loop delay becomes smaller.

      Parameters:
      duration - the duration of time to wait
      Throws:
      IllegalStateException - if called anywhere other than the coroutine's running command
    • waitUntil

      public Coroutine.WaitResult waitUntil(BooleanSupplier condition)
      Yields until a condition is met. This method will only return once the condition is met; if the condition never becomes true or is delayed longer than you expect, this method will block indefinitely. waitUntil(BooleanSupplier, Time) is an alternative that will only wait up until a maximum duration before exiting.
      Parameters:
      condition - The condition to wait for
      Returns:
      Coroutine.WaitResult.CONDITION_MET
      Throws:
      IllegalStateException - if called anywhere other than the coroutine's running command
      See Also:
    • waitUntil

      public Coroutine.WaitResult waitUntil(BooleanSupplier condition, Time timeout)
      Yields until a condition is met, waiting for up to a specified duration. If the condition is not met before the timeout duration elapses, this method stops waiting and returns Coroutine.WaitResult.TIMED_OUT.

      Teams may use this method for waiting for conditions in autonomous or automated routines to be resilient to hardware faults (e.g., a mechanism not reaching a target state or a faulty sensor).

      Command.noRequirements(coroutine -> {
        coroutine.fork(elevator.up());
        Coroutine.WaitResult upResult = coroutine.waitUntil(elevator::atTop, Seconds.of(1.25));
        if (upResult.timedOut()) {
          // We've waited 1.25 seconds and the elevator still has not reached the top. It may have
          // jammed or there may be a fault with the sensors. In this example, we set a driverstation
          // alert and exit early - team code may want to take other approaches like retrying the
          // command or falling back to a secondary behavior that doesn't need the elevator to be up.
          elevator.setJamAlert();
          return;
        }
      
        // The elevator reached the top within 1.25 seconds. Clear the alert and continue.
        elevator.clearJamAlert();
      
        // ... more commands ...
      })
      
      Parameters:
      condition - The condition to wait for
      timeout - The maximum duration to wait
      Returns:
      Coroutine.WaitResult.CONDITION_MET if the condition was met within the specified timeout, or Coroutine.WaitResult.TIMED_OUT if the timeout duration elapsed before the condition was met.
      See Also:
    • scheduler

      public Scheduler scheduler()
      Advanced users only: this permits access to the backing command scheduler to run custom logic not provided by the standard coroutine methods. Any commands manually scheduled through this will be canceled when the parent command exits - it's not possible to "fork" a command that lives longer than the command that scheduled it.
      Returns:
      the command scheduler backing this coroutine
      Throws:
      IllegalStateException - if called anywhere other than the coroutine's running command