Suppliers in Command-Based
A Small Riddle
Section titled “A Small Riddle”Let’s say that we define an Intake mechanism:
class Intake implements Mechanism { // Placeholder for TalonFX, SparkMax or SparkFlex private final ExampleMotor motor = new ExampleMotor();
public Command runAtThrottle(double throttle) { return run(coroutine -> { while (true) { motor.setThrottle(throttle); coroutine.yield(); } }) .named("Run Intake"); }}And the following code in Robot.java:
class Robot { public Robot() { var xbox = new CommandXboxController(0); var intake = new Intake(); double controllerOutput = xbox.getLeftY(); intake.setDefaultCommand(intake.runAtThrottle(controllerOutput)); }}In case you forgot, getLeftY() returns a value between -1 and 1. If the
left joystick isn’t pushed at all, getLeftY() returns 0; if it’s pushed
all the way up, getLeftY() returns 1; and if it’s pushed all the way down,
getLeftY() returns -1.
When you run the code above, what happens?
- The intake does a little dance before rejoicing your coding skills.
- The intake runs at a speed dependent on how far up or down the joystick on the left is pushed.
- The intake does nothing.
Solution
#3: The intake does nothing.
But Why?
Section titled “But Why?”Recall that the constructor of the Robot class is called when the
robot code is first loaded, not when the robot is enabled.
At this point,
your hands aren’t even on the controller - so, the value of controllerOutput would be 0.
When the robot is enabled, the value of getLeftY() will change; however, the value of the throttle
parameter passed into the runAtThrottle command cannot, because it’s value has already been determined
before the command has even began.
Fixing the problem
Section titled “Fixing the problem”In java, we use a DoubleSupplier to represent a value of type double that is constantly changing.
We call getAsDouble() to fetch its current value.
public Command runAtThrottle(DoubleSupplier throttleSupplier) { return run(coroutine -> { while (true) { double throttle = throttleSupplier.getAsDouble(); motor.setThrottle(throttle); coroutine.yield(); } }) .named("Run Intake");}DoubleSupplier instances are created with the syntax of () -> expression, with the caveat that
the expression must return a double.
Calling getAsDouble() will evaluate that expression.
DoubleSupplier supplier = () -> xbox.getLeftY();double controllerOutput = supplier.getAsDouble();The () -> ... syntax for defining a DoubleSupplier is also called a lambda expression.
Here is how we would define and use a DoubleSupplier in our previous example:
class Robot { public Robot() { var xbox = new CommandXboxController(0); var intake = new Intake(); intake.setDefaultCommand(intake.runAtThrottle(() -> xbox.getLeftY())); }}BooleanSuppliers and the until() Command Modifier
Section titled “BooleanSuppliers and the until() Command Modifier”You might recognize the () -> syntax used to define DoubleSuppliers.
As it turns out,
we define Triggers with that syntax:
new Trigger(() -> motor.speed() > 60);Notice, however, that the expression to the left of the () -> statement is
a boolean instead of a double.
As it turns out, the constructor for a Trigger
takes a BooleanSupplier.
To fetch the latest value of a BooleanSupplier, you would call
getAsBoolean() instead.
BooleanSuppliers are used in one other place: adding stop conditions to commands.
You can use the until(BooleanSupplier) method to add an arbitrary stop condition
to a command:
// From the Intake class mentioned earlierpublic Command fullThrottleUntilRobotDisabled() { return fullThrottle().until(() -> RobotState.isDisabled()).named("Full Throttle Until Disable");}
private Command fullThrottle() { return null; // placeholder for actual command}