Convenience Features

While the previously-described methodologies will work fine for writing command-based robot code, the command-based libraries contain several convenience features for more-advanced users that can greatly reduce the verbosity/complexity of command-based code. It is highly recommended that users familiarize themselves with these features to maximize the value they get out of the command-based libraries.

Inline Command Definitions

While users are able to create commands by explicitly writing command classes (either by subclassing CommandBase or implementing Command), for many commands (such as those that simply call a single subsystem method) this involves a lot of wasteful boilerplate code. To help alleviate this, many of the prewritten commands included in the command-based library may be inlined - that is, the command body can be defined in a single line of code at command construction.

Passing Subroutines As Parameters

In order to inline a command definition, users require some way to specify what code the commands will run as constructor parameters. Fortunately, both Java and C++ offer users the ability to pass subroutines as parameters.

Method References (Java)

In Java, a reference to a subroutine that can be passed as a parameter is called a method reference. The general syntax for a method reference is object::method. Note that no method parameters are included, since the method itself is the parameter. The method is not being called - it is being passed to another piece of code (in this case, a command) so that that code can call it when needed. For further information on method references, see the official Oracle documentation.

Lambda Expressions (Java)

While method references work well for passing a subroutine that has already been written, often it is inconvenient/wasteful to write a subroutine solely for the purpose of sending as a method reference, if that subroutine will never be used elsewhere. To avoid this, Java also supports a feature called “lambda expressions.” A lambda expression is an inline method definition - it allows a subroutine to be defined inside of a parameter list. For specifics on how to write Java lambda expressions, see this tutorial.

Lambda Expressions (C++)

Warning

Due to complications in C++ semantics, capturing this in a C++ lambda can cause a null pointer exception if done from a component command of a command group. Whenever possible, C++ users should capture relevant command members explicitly and by value. For more details, see here.

C++ lacks a close equivalent to Java method references - pointers to member functions are generally not directly useable as parameters due to the presence of the implicit this parameter. However, C++ does offer lambda expressions - in addition, the lambda expressions offered by C++ are in many ways more powerful than those in Java. For specifics on how to write C++ lambda expressions, see cppreference.

Inlined Command Example

So, what does an inlined command definition look like in practice?

The InstantCommand class provides an example of a type of command that benefits greatly from inlining. Consider the following from the HatchBotInlined example project (Java, C++):

90    // Grab the hatch when the Circle button is pressed.
91    new JoystickButton(m_driverController, Button.kCircle.value)
92        .whenPressed(new InstantCommand(m_hatchSubsystem::grabHatch, m_hatchSubsystem));
93    // Release the hatch when the Square button is pressed.
94    new JoystickButton(m_driverController, Button.kSquare.value)
95        .whenPressed(new InstantCommand(m_hatchSubsystem::releaseHatch, m_hatchSubsystem));

Instead of wastefully writing separate GrabHatch and ReleaseHatch commands which call only one method before ending, both can be accomplished with a simple inline definition by passing appropriate subsystem method.

Included Command Types

The command-based library includes a variety of pre-written commands for commonly-encountered use cases. Many of these commands are intended to be used “out-of-the-box” via inlining, however they may be subclassed, as well. A list of the included pre-made commands can be found below, along with brief examples of each - for more rigorous documentation, see the API docs (Java, C++).

ConditionalCommand

The ConditionalCommand class (Java, C++) runs one of two commands when executed, depending on a user-specified true-or-false condition:

// Runs either commandOnTrue or commandOnFalse depending on the value of m_limitSwitch.get()
new ConditionalCommand(commandOnTrue, commandOnFalse, m_limitSwitch::get)

SelectCommand

Note

While the Java version of SelectCommand simply uses an Object as a key, the C++ version is templated on the key type.

Note

An alternate version of SelectCommand simply takes a method that supplies the command to be run - this can be very succinct, but makes inferring the command’s requirements impossible, and so leaves the user responsible for manually adding the requirements to the SelectCommand.

The SelectCommand class (Java, C++) is a generalization of the ConditionalCommand class that runs one of a selection of commands based on the value of a user-specified selector. The following example code is taken from the SelectCommand example project (Java, C++):

20public class RobotContainer {
21  // The enum used as keys for selecting the command to run.
22  private enum CommandSelector {
23    ONE,
24    TWO,
25    THREE
26  }
27
28  // An example selector method for the selectcommand.  Returns the selector that will select
29  // which command to run.  Can base this choice on logical conditions evaluated at runtime.
30  private CommandSelector select() {
31    return CommandSelector.ONE;
32  }
33
34  // An example selectcommand.  Will select from the three commands based on the value returned
35  // by the selector method at runtime.  Note that selectcommand works on Object(), so the
36  // selector does not have to be an enum; it could be any desired type (string, integer,
37  // boolean, double...)
38  private final Command m_exampleSelectCommand =
39      new SelectCommand(
40          // Maps selector values to commands
41          Map.ofEntries(
42              Map.entry(CommandSelector.ONE, new PrintCommand("Command one was selected!")),
43              Map.entry(CommandSelector.TWO, new PrintCommand("Command two was selected!")),
44              Map.entry(CommandSelector.THREE, new PrintCommand("Command three was selected!"))),
45          this::select);

InstantCommand

The InstantCommand class (Java, C++) executes a single action on initialization, and then ends immediately:

// Actuates the hatch subsystem to grab the hatch
new InstantCommand(m_hatchSubsystem::grabHatch, m_hatchSubsystem)

RunCommand

The RunCommand class (Java, C++) runs a specified method repeatedly in its execute() block. It does not have end conditions by default; users can either subclass it, or decorate it to add them.

// A split-stick arcade command, with forward/backward controlled by the left
// hand, and turning controlled by the right.
new RunCommand(() -> m_robotDrive.arcadeDrive(
    -driverController.getY(GenericHID.Hand.kLeft),
    driverController.getX(GenericHID.Hand.kRight)),
    m_robotDrive)

StartEndCommand

The StartEndCommand class (Java, C++) executes an action when starting, and a second one when ending. It does not have end conditions by default; users can either subclass it, or decorate an inlined command to add them.

new StartEndCommand(
    // Start a flywheel spinning at 50% power
    () -> m_shooter.shooterSpeed(0.5),
    // Stop the flywheel at the end of the command
    () -> m_shooter.shooterSpeed(0.0),
    // Requires the shooter subsystem
    m_shooter
)

FunctionalCommand

The FunctionalCommand class (Java, C++) allows all four Command methods to be passed in as method references or lambdas:

new FunctionalCommand(
    // Reset encoders on command start
    m_robotDrive::resetEncoders,
    // Start driving forward at the start of the command
    () -> m_robotDrive.arcadeDrive(kAutoDriveSpeed, 0),
    // Stop driving at the end of the command
    interrupted -> m_robotDrive.arcadeDrive(0, 0),
    // End the command when the robot's driven distance exceeds the desired value
    () -> m_robotDrive.getAverageEncoderDistance() >= kAutoDriveDistanceInches,
    // Require the drive subsystem
    m_robotDrive
)

PrintCommand

The PrintCommand class (Java, C++) prints a given string.

new PrintCommand("This message will be printed!")

ScheduleCommand

The ScheduleCommand class (Java, C++) schedules a specified command, and ends instantly:

// Schedules commandToSchedule when run
new ScheduleCommand(commandToSchedule)

This is often useful for “forking off” from command groups: by default, commands in command groups are run through the command group, and are never themselves seen by the scheduler. Accordingly, their requirements are added to the group’s requirements. While this is usually fine, sometimes it is undesirable for the entire command group to gain the requirements of a single command - a good solution is to “fork off” from the command group and schedule that command separately.

ProxyScheduleCommand

The ProxyScheduleCommand class (Java, C++) schedules a specified command, and does not end until that command ends:

// Schedules commandToSchedule when run, does not end until commandToSchedule is no longer scheduled
new ProxyScheduleCommand(commandToSchedule)

This is often useful for “forking off” from command groups: by default, commands in command groups are run through the command group, and are never themselves seen by the scheduler. Accordingly, their requirements are added to the group’s requirements. While this is usually fine, sometimes it is undesirable for the entire command group to gain the requirements of a single command - a good solution is to “fork off” from the command group and schedule the command separately.

WaitCommand

The WaitCommand class (Java, C++) does nothing, and ends after a specified period of time elapses after its initial scheduling:

// Ends 5 seconds after being scheduled
new WaitCommand(5)

This is often useful as a component of a command group.

WaitCommand can also be subclassed to create a more complicated command that runs for a period of time. If WaitCommand is used in this method, the user must ensure that the WaitCommand’s Initialize, End, and IsFinished methods are still called in order for the WaitCommand’s timer to work.

WaitUntilCommand

Warning

The match timer used by WaitUntilCommand does not provide an official match time! While it is fairly accurate, use of this timer can not guarantee the legality of your robot’s actions.

The WaitUntilCommand class (Java, C++) does nothing, and ends once a specified condition becomes true, or until a specified match time passes.

// Ends after the 60-second mark of the current match
new WaitUntilCommand(60)

// Ends after m_limitSwitch.get() returns true
new WaitUntilCommand(m_limitSwitch::get)

PerpetualCommand

The PerpetualCommand class (Java, C++) runs a given command with its end condition removed, so that it runs forever (unless externally interrupted):

// Will run commandToRunForever perpetually, even if its isFinished() method returns true
new PerpetualCommand(commandToRunForever)

Command Decorator Methods

The Command interface contains a number of defaulted “decorator” methods which can be used to add additional functionality to existing commands. A “decorator” method is a method that takes an object (in this case, a command) and returns an object of the same type (i.e. a command) with some additional functionality added to it. A list of the included decorator methods with brief examples is included below - for rigorous documentation, see the API docs (Java, C++).

withTimeout

The withTimeout() decorator (Java, C++) adds a timeout to a command. The decorated command will be interrupted if the timeout expires:

// Will time out 5 seconds after being scheduled, and be interrupted
button.whenPressed(command.withTimeout(5));

until/withInterrupt

The until() (Java, C++) decorator adds a condition on which the command will be interrupted:

// Will be interrupted if m_limitSwitch.get() returns true
button.whenPressed(command.until(m_limitSwitch::get));

withInterrupt() is an alias for until().

andThen

The andThen() decorator (Java, C++) adds a method to be executed after the command ends:

// Will print "hello" after ending
button.whenPressed(command.andThen(() -> System.out.println("hello")));

beforeStarting

The beforeStarting() decorator (Java, C++) adds a method to be executed before the command starts:

// Will print "hello" before starting
button.whenPressed(command.beforeStarting(() -> System.out.println("hello")));

alongWith (Java only)

Note

This decorator is not supported in C++ due to technical constraints - users should simply construct a parallel command group the ordinary way instead.

The alongWith() decorator returns a parallel command group. All commands will execute at the same time and each will end independently of each other:

// Will be a parallel command group that ends after three seconds with all three commands running their full duration.
button.whenPressed(oneSecCommand.alongWith(twoSecCommand, threeSecCommand));

raceWith (Java only)

Note

This decorator is not supported in C++ due to technical constraints - users should simply construct a parallel race group the ordinary way instead.

The raceWith() decorator returns a parallel race group that ends as soon as the first command ends. At this point all others are interrupted. It doesn’t matter which command is the calling command:

// Will be a parallel race group that ends after one second with the two and three second commands getting interrupted.
button.whenPressed(twoSecCommand.raceWith(oneSecCommand, threeSecCommand));

deadlineWith (Java only)

Note

This decorator is not supported in C++ due to technical constraints - users should simply construct a parallel deadline group the ordinary way instead.

The deadlineWith() decorator returns a parallel deadline group with the calling command being the deadline. When this deadline command ends it will interrupt any others that are not finished:

// Will be a parallel deadline group that ends after two seconds (the deadline) with the three second command getting interrupted (one second command already finished).
button.whenPressed(twoSecCommand.deadlineWith(oneSecCommand, threeSecCommand));

withName (Java only)

Note

This decorator is not supported in C++ due to technical constraints - users should set the name of the command inside their command class instead.

The withName() decorator adds a name to a command. This name will appear on a dashboard when the command is sent via the sendable interface.

// This command will be called "My Command".
var command = new PrintCommand("Hello robot!").withName("My Command");

perpetually

The perpetually() decorator (Java, C++) removes the end condition of a command, so that it runs forever.

// Will run forever unless externally interrupted, regardless of command.isFinished()
button.whenPressed(command.perpetually());

Composing Decorators

Remember that decorators, like all command groups, can be composed! This allows very powerful and concise inline expressions:

// Will run fooCommand, and then a race between barCommand and bazCommand
button.whenPressed(fooCommand.andThen(barCommand.raceWith(bazCommand)));

Static Factory Methods for Command Groups (Java only)

Note

These factory methods are not included in the C++ command library, as the reduction in verbosity would be minimal - C++ commands should be stack-allocated, removing the need for the new keyword.

If users do not wish to use the andThen, alongWith, raceWith, and deadlineWith decorators for declaring command groups, but still wish to reduce verbosity compared to calling the constructors, the CommandGroupBase class contains four static factory methods for declaring command groups: sequence(), parallel(), race(), and deadline(). When used from within a command group subclass or in combination with import static, these become extremely concise and greatly aid in command composition:

public class ExampleSequence extends SequentialCommandGroup {

  // Will run a FooCommand, and then a race between a BarCommand and a BazCommand
  public ExampleSequence() {
    addCommands(
        new FooCommand(),
        race(
            new BarCommand(),
            new BazCommand()
        )
    );
  }

}