Commands & Mechanisms, Part 1
Defining Mechanisms
Section titled “Defining Mechanisms”Mechanisms are simply classes that have the implements Mechanism keyword:
class Intake implements Mechanism { // Store any motors specific to the mechanism as private members. // This can include TalonFX, SparkMax and/or SparkFlex instances. private final ExampleMotor motor = new ExampleMotor();}In general, you should create one instance of each mechanism, and that instance should be stored in your robot class:
class Robot { private final Intake intake = new Intake();}Ignore this if you haven’t used Commands V2! Mechanisms in Commands V3
are identical to Subsystems in Commands V2. The difference is that you have
to define your own periodic() method while calling it manually in
robotPeriodic(), instead of overriding a method from SubsystemBase.
Defining Commands
Section titled “Defining Commands”Most commands are defined inside of the file containing the class of a mechanism
(like Intake.java or Shooter.java).
The code below defines a new Command:
run(coroutine -> { System.out.println("Full Speed Baby!"); while (true) { motor.setThrottle(1.0); coroutine.yield(); } }) .named("Set to Full Throttle");Notice that:
- Every line of code between
coroutine -> {and}will be run when the command runs, like a method body. - This command will print “Full Speed Baby!” once before commanding the motor to spin at max speed.
- The
named("Set to Full Throttle")call is required to create aCommand. This allows coders to distinguish this command from others when debugging their code.
To change the priority of the command, simply add a withPriority(int) call before
you call named():
run(coroutine -> { System.out.println("Full Speed Baby!"); while (true) { motor.setThrottle(1.0); coroutine.yield(); } }) .withPriority(1) // recall that the default priority is 0. .named("Set to Full Throttle");Ignore this if you haven’t used Commands V2! Unlike in Commands V2, the
code inside of a run() method won’t run repeatedly! Use while loops inside
of the run() method to run code repeatedly.
Combining Commands and Mechanisms
Section titled “Combining Commands and Mechanisms”Here’s an example of a command defined inside of a mechanism class:
class Intake implements Mechanism { private final ExampleMotor motor = new ExampleMotor();
public Command fullThrottle() { return run(coroutine -> { System.out.println("Full Speed Baby!"); while (true) { motor.setThrottle(1.0); coroutine.yield(); } }) .named("Set to Full Throttle"); }}Commands created with run() will automatically require the mechanism that they are defined inside.
Here, the full throttle command requires the Intake.
Also, notice that the fullThrottle method creates and returns a command. All mechanism-specific commands should be defined this way, for 2 reasons:
- It allows for commands to define parameters, just like a method.
- It gives the
Robotclass easy access to these commands.
// This command can now set the intake at any throttle level:public Command runAtThrottle(double throttle) { return run(coroutine -> { while (true) { motor.setThrottle(throttle); coroutine.yield(); } }) .named("Set Throttle to " + throttle);}However, this approach requires caution. Observe the snippet below:
public Command printHiThenFullThrottle() { return run(coroutine -> { System.out.println("Hi!"); runAtThrottle(1.0); // Error: Commands must be used! Did you mean to fork it or bind it to a trigger? }) .named("Set to Full Throttle");}Calling the fullThrottle()
method, as shown, doesn’t spin the motor, but instead creates a Command object that must be run separately.
The java
compiler will catch this and throw a compile-time error.
Can you figure out why the error message says “bind it to a trigger”? This is left as an exercise to the reader.
JK, we’ll give you the solution
Remember that in the command-based overview section, we mentioned how
Triggers are the stimuli that cause actions, or commands, to run.
Note that “forking”, in this error message, means to schedule a command. It’s not the same as the “git fork” terminal command.
Command Sequences
Section titled “Command Sequences”If you want to run the fullThrottle command inside of another command, you must
call coroutine.await(Command), like so:
public Command printHiThenFullThrottle() { return run(coroutine -> { // "Hi" is printed before the runAtThrottle Command is run System.out.println("Hi!"); coroutine.await(runAtThrottle(1.0)); }) .named("Set to Full Throttle & Print Hi");}That means you can run multiple commands in sequence:
public Command deployPivot() { return null; // placeholder for actual command}
public Command runIntakeRollers() { return null; // placeholder for actual command}
public Command commandSequence() { return run(coroutine -> { System.out.println("Hi!"); // step 1 coroutine.await(deployPivot()); // step 2 coroutine.await(runIntakeRollers()); // step 3 }) .named("Command Sequence");}