2.4.6 - Command Decorators

Because all commands inherit from Command in WPILib, there is a lot of prebuilt methods that we can call on commands to modify them. We call these decorators because they modify the behavior of a command with some new feature or change, but don’t actually change the command’s execution.

This page does not go over all of the methods on the Command object, but only the most important ones from experience.

Common decorators

unless

Command.unless(BooleanSupplier condition) returns a new command that does the exact same thing as the original command, however it will do nothing and end instantly if condition evaluates to true when the command is scheduled. In simple words, it runs the regular command unless the condition is true when the command is scheduled.

Note

The condition is evaluated when the command is scheduled, not when the command object is created.

Calling command.unless(() -> false) will always run the command, and command.unless(() -> true) will never run the command.

until

Command.until(BooleanSupplier condition) is similar to unless() however it doesn’t just evaluate the condition at the start of the command - it only allows the command to continue if the condition stays false.

This means that if the condition is false, and the command begins, the original command will run. However, if the condition turns to true, then the command is interrupted.

This is the same thing as Commands.race(Commands.waitUntil(condition), command).

onlyIf

Command.onlyIf(BooleanSupplier condition) is the opposite to unless(). The condition is evaluated once - at the time when the command is scheduled - and if the condition is false, the command will not run.

onlyWhile

Command.onlyWhile(BooleanSupplier condition) is the opposite of until() because it will allow the command to run as long as the condition continues to evaluate to true. If at any point the condition evaluates to false, the command will be interrupted and end.

finallyDo

This is a very helpful command decorator. This has two forms: finallyDo(Runnable end) and finallyDo(BooleanConsumer end).

If the second option is used, the value of the boolean passed to the consumer will be the same as the boolean in Command.end(boolean interrupted). It is true if the command was interrupted, and false otherwise.

If a Runnable is passed in, then that runnable (obviously) cannot determine if the command was interrupted.

Whatever the argument is, the function that it represents will be called when the command ends. Just like Command.end(), this will always be called, even if the command was interrupted.

Tip

You can have multiple calls to .finallyDo() chained, and they will all run. .finallyDo() always runs the previous command’s end() method first, so you can chain calls to .finallyDo() as many times as you would like.

A note about requirements

Let’s consider a command that scores a gamepiece. It may look something like this:

Command fullScoreCommand = scoreCommand.onlyIf(intake::hasPiece);

A question will naturally arise about the requirements of fullScoreCommand. Even if the condition is false, does the command still demand requirements on the subsystems it won’t use? Yes. This is what happens.

This means that if you have another command running that shares requirements with scoreCommand, and then fullScoreCommand is scheduled, the other command will be interrupted regardless of whether scoreCommand actually runs.