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++)

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++):

 99
100
101
102
103
104
    new JoystickButton(m_driverController, Button.kB.value)
        .whenPressed(new InstantCommand(m_hatchSubsystem::releaseHatch, m_hatchSubsystem));
    // While holding the shoulder button, drive at half speed
    new JoystickButton(m_driverController, Button.kBumperRight.value)
        .whenPressed(() -> m_robotDrive.setMaxOutput(0.5))
        .whenReleased(() -> m_robotDrive.setMaxOutput(1));
63
64
65
  frc2::InstantCommand m_grabHatch{[this] { m_hatch.GrabHatch(); }, {&m_hatch}};
  frc2::InstantCommand m_releaseHatch{[this] { m_hatch.ReleaseHatch(); },
                                      {&m_hatch}};
39
40
41
42
  // Grab the hatch when the 'A' button is pressed.
  frc2::JoystickButton(&m_driverController, 1).WhenPressed(&m_grabHatch);
  // Release the hatch when the 'B' button is pressed.
  frc2::JoystickButton(&m_driverController, 2).WhenPressed(&m_releaseHatch);

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)
// Runs either commandOnTrue or commandOnFalse depending on the value of m_limitSwitch.get()
frc2::ConditionalCommand(commandOnTrue, commandOnFalse, [&m_limitSwitch] { return 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++):

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  // The enum used as keys for selecting the command to run.
  private enum CommandSelector {
    ONE, TWO, THREE
  }

  // An example selector method for the selectcommand.  Returns the selector that will select
  // which command to run.  Can base this choice on logical conditions evaluated at runtime.
  private CommandSelector select() {
    return CommandSelector.ONE;
  }

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

  // An example selector method for the selectcommand.  Returns the selector
  // that will select which command to run.  Can base this choice on logical
  // conditions evaluated at runtime.
  CommandSelector Select() { return ONE; }

  // The robot's subsystems and commands are defined here...

  // An example selectcommand.  Will select from the three commands based on the
  // value returned by the selector method at runtime.  Note that selectcommand
  // takes a generic type, so the selector does not have to be an enum; it could
  // be any desired type (string, integer, boolean, double...)
  frc2::SelectCommand<CommandSelector> m_exampleSelectCommand{
      [this] { return Select(); },
      // Maps selector values to commands
      std::pair{ONE, frc2::PrintCommand{"Command one was selected!"}},
      std::pair{TWO, frc2::PrintCommand{"Command two was selected!"}},
      std::pair{THREE, frc2::PrintCommand{"Command three was selected!"}}};

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)
// Actuates the hatch subsystem to grab the hatch
frc2::InstantCommand([&m_hatchSubsystem] { 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)
// A split-stick arcade command, with forward/backward controlled by the left
// hand, and turning controlled by the right.
frc2::RunCommand(
  [this] {
    m_drive.ArcadeDrive(
        m_driverController.GetY(frc::GenericHID::kLeftHand),
        m_driverController.GetX(frc::GenericHID::kRightHand));
  },
  {&m_drive}))

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 driving forward at the start of the command
    () -> m_robotDrive.arcadeDrive(kAutoDriveSpeed, 0),
    // Stop driving at the end of the command
    () -> m_robotDrive.arcadeDrive(0, 0),
    // Requires the drive subsystem
    m_robotDrive
)
frc2::StartEndCommand(
  // Start driving forward at the start of the command
  [this] { m_drive.ArcadeDrive(ac::kAutoDriveSpeed, 0); },
  // Stop driving at the end of the command
  [this] { m_drive.ArcadeDrive(0, 0); },
  // Requires the drive subsystem
  {&m_drive}
)

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
    () -> 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
)
frc2::FunctionalCommand(
  // Reset encoders on command start
  [this] { m_drive.ResetEncoders(); },
  // Start driving forward at the start of the command
  [this] { m_drive.ArcadeDrive(ac::kAutoDriveSpeed, 0); },
  // Stop driving at the end of the command
  [this] { m_drive.ArcadeDrive(0, 0); },
  // End the command when the robot's driven distance exceeds the desired value
  [this] { return m_drive.GetAverageEncoderDistance() >= kAutoDriveDistanceInches; },
  // Requires the drive subsystem
  {&m_drive}
)

PrintCommand

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

new PrintCommand("This message will be printed!")
frc2::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)
// Schedules commandToSchedule when run
frc2::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)
// Schedules commandToSchedule when run, does not end until commandToSchedule is no longer scheduled
frc2::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)
// Ends 5 seconds after being scheduled
frc2::WaitCommand(5.0_s)

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

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)
// Ends after the 60-second mark of the current match
frc2::WaitUntilCommand(60.0_s)

// Ends after m_limitSwitch.Get() returns true
frc2::WaitUntilCommand([&m_limitSwitch] { return 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)
// Will run commandToRunForever perpetually, even if its isFinished() method returns true
frc2::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));
// Will time out 5 seconds after being scheduled, and be interrupted
button.WhenPressed(command.WithTimeout(5.0_s));

interruptOn

The interruptOn() (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.interruptOn(m_limitSwitch::get));
// Will be interrupted if m_limitSwitch.get() returns true
button.WhenPressed(command.InterruptOn([&m_limitSwitch] { return m_limitSwitch.Get(); }));

whenFinished

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

// Will print "hello" after ending
button.whenPressed(command.whenFinished(() -> System.out.println("hello")));
// Will print "hello" after ending
button.WhenPressed(command.WhenFinished([] { std::cout << "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")));
// Will print "hello" before starting
button.WhenPressed(command.BeforeStarting([] { std::cout << "hello"; }));

andThen (Java only)

Note

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

The andThen() decorator returns a sequential command group containing the command, followed by the list of commands passed as arguments:

// Will be the sequence fooCommand -> barCommand -> bazCommand
button.whenPressed(fooCommand.andThen(barCommand, bazCommand));

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 containing the command, along with all the other commands passed in as arguments:

// Will be a parallel command group containing fooCommand, barCommand, and bazCommand
button.whenPressed(fooCommand.alongWith(barCommand, bazCommand));

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 command race containing the command, along with all the other commands passed in as arguments:

// Will be a parallel command race containing fooCommand, barCommand, and bazCommand
button.whenPressed(fooCommand.raceWith(barCommand, bazCommand));

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 containing the command, along with all the other commands passed in as arguments:

// Will be a parallel deadline group containing fooCommand, barCommand, and bazCommand; fooCommand is the deadline
button.whenPressed(fooCommand.deadlineWith(barCommand, bazCommand));

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());
// 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()
        )
    );
  }

}