2.4.5 - Command Factories
We can create command compositions from some helpful static factories like
Commands.sequence() and Commands.parallel(). But there are more
factories that help us create new commands instead of just combining
preexisting commands.
The command factories that we will be explaining are quite different from the
process of subclassing the Command class itself. Firstly, they are much
shorter to write, and they allow us to write commands using a much more
declarative style.
Common factories
Tip
Refer to the documentation in addition to this section; not all factories are listed here and these are certainly not the only “helpful” ones, only the ones I found myself using the most commonly.
run()
The factory Commands.run(Runnable runnable) runs the given runable
repeatedly, without end.
For example, the following command would simply print out “Hello, world!” to the console repeatedly every tick.
Command printCommand = Commands.run(() -> System.out.println("Hello, world!"));
This isn’t really helpful until you learn more about decorators in 2.4.6 - Command Decorators, because commands normally should end at some point.
runOnce()
The factory Commands.runOnce(Runnable runnable) creates a command that runs
the given runnable exactly once, and then the command is done.
The following command would set a motor voltage and then finish.
Command motorCommand = Commands.runOnce(() -> motor.setVoltage(5.0));
idle()
This is a helper command - the factory Commands.idle() simply is a command
that does nothing and never ends.
none()
This is just like idle() because it does nothing - however, this command
ends instantly, so it’s non-blocking.
waitUntil()
The factory Commands.waitUntil(BooleanSupplier condition) is a particularly
helpful factory because it creates a command that polls the condition each
cycle and only ends when the condition is true.
either()
The factory Commands.either(Command onTrue, Command onFalse, BooleanSupplier
selector) can be used to run one of two commands depending on the value of a
given condition, evaluated at runtime.
select()
The factory Commands.select(Map<K, Command> commands, Supplier<? extends K>
selector) is used in place of Commands.either() when there are more
options than just two. When the command is initialized, it calls get() on
selector to get a “key” to use. If the key is found in the map of keys to
commands, the command corresponding to the given key is scheduled. If there is
no corresponding key in the map, a command that simply prints a message to the
console is scheduled instead. This is a rarely used factory, but when you find
a use for it, it is incredibly powerful.
Commands with requirements
Often, we still want commands to hold requirements on subsystems as they execute. We need some way to specify which subsystems are required for commands that we generate from factories.
There are two ways to do this.
Manually passing in subsystems
In any of the above factories that don’t take in commands themselves (except
Commands.none()), we can also pass subsystems into the factories to make
those commands require those subsystems.
Note
The reason that this doesn’t work with Commands.none() is because
that command does nothing and ends instantly.
What this means is that, if we want a command to wait on an Intake
subsystem to reach a setpoint, we can write this:
Command command = Commands.waitUntil(intake::atSetpoint, intake);
Notice how we pass the subsystem in as an argument to add that subsystem as a requirement.
This command will wait until the method intake.atSetpoint() returns
true, and it will hold requirements on the intake subsystem while it runs.
We can also pass in multiple subsystems to these factories, such as:
Command command = Commands.waitUntil(
() -> (intake.atSetpoint() && elevator.atSetpoint()),
intake,
elevator);
The above command waits for both elevator and intake to be at their respective setpoints, and also holds requirements on both.
Using the SubsystemBase class
In cases where the factory would normally only have requirements on exactly
one subsystem, there’s a handy way to create those commands. The
SubsystemBase class (which subsystems inherit from) comes with the exact
same factories (except Commands.none() and the factories that accept
commands) but with one difference: the commands automatically hold requirements
on the subsystem.
So, the following two commands are equivalent:
// This command:
Command command = Commands.run(intake::update, intake);
// Is the same as this:
Command command = intake.run(intake::update);
Because we call .run() on intake, the SubsystemBase implementation
of run() automatically adds requirements on itself.