Skip to content

Commands

A command is a unit of robot behavior: drive forward, run the intake, follow a path, wait for a sensor. Commands are scheduled by the CommandScheduler and may control one or more Subsystems for some period of time.

All commands implement the Command interface from com.vcs.valleylib.core.command.

The lifecycle

Every command goes through the same four phases:

scheduled ──▶ initialize() ──▶ execute() ──▶ isFinished()? ── no ──▶ execute() ...
                                   │              │
                                   │             yes ──▶ end(false)
                                   │
                            canceled / preempted ──▶ end(true)
Method Called Purpose
initialize() Once, when the command is scheduled Reset timers, sensors, and internal state
execute() Every scheduler cycle while active Actuator control logic (must be fast and non-blocking)
isFinished() After each execute() Return true when the command has completed (defaults to false — runs forever until interrupted)
end(boolean interrupted) Once, when the command ends Stop motors safely; interrupted is true when canceled or preempted
getRequirements() On scheduling Declare exclusive subsystem access (defaults to none)

Only execute() is abstract — everything else has a sensible default, so the simplest possible command is:

Command spin = new Command() {
    @Override
    public void execute() {
        flywheel.spin();
    }
};

Requirements: preventing hardware conflicts

getRequirements() returns the set of subsystems a command needs exclusive access to. When a new command is scheduled that requires a subsystem already in use, the running command is interrupted (its end(true) is called) and the new one takes over.

@Override
public Set<Subsystem> getRequirements() {
    return Set.of(drive);
}

Warning

Forgetting requirements is the most common command-based bug: two commands silently fight over the same motors. Declare every subsystem your command touches.

Built-in commands

InstantCommand

Runs a single action once, then immediately finishes. Ideal for toggles, state changes, and one-shot hardware calls.

Command close = new InstantCommand(claw::close);

WaitCommand

Does nothing for a fixed duration (seconds). The backbone of timed autonomous sequences. Like every timed behavior in the library it reads RobotClock, so it is deterministic under a manual clock in tests.

Command pause = new WaitCommand(0.5);

WaitUntilCommand

Finishes once a condition becomes true.

Command untilLoaded = new WaitUntilCommand(sensor::hasGamePiece);

TimedCommand (valleyLib-ftc)

An abstract command that runs for a fixed duration with structured callbacks — preferred over raw timer bookkeeping in autos:

public class SpinUpCommand extends TimedCommand {
    public SpinUpCommand() { super(1.5); }               // run for 1.5 s

    @Override protected void onStart() { shooter.enable(); }
    @Override protected void onLoop(double elapsedSeconds) {
        shooter.setPower(Math.min(1.0, elapsedSeconds));  // ramp up
    }
    @Override protected void onEnd(boolean interrupted) { shooter.hold(); }
}

FunctionalCommand

A command assembled from lambdas for each lifecycle phase, with any number of requirements. It is the primitive behind the Commands and subsystem factories, and the right tool when a command needs a little logic in several phases and touches more than one subsystem — but is still too small for its own class:

Command handoff = new FunctionalCommand(
        () -> { intake.slow(); transfer.open(); },           // initialize
        () -> {},                                            // execute
        interrupted -> { intake.stop(); transfer.close(); }, // end
        transfer::hasGamePiece,                              // isFinished
        intake, transfer)                                    // requirements
    .withName("Handoff");

Command names

Every command has a getName() used by the command logger and useful in your own telemetry. Defaults are sensible — a class's simple name, Wait(0.5s), Intake.startEnd, StateMachine[SCORE] — and anonymous classes fall back to "Command". Name anything important explicitly:

Command score = lift.toHighCommand().andThen(claw::open).withName("Score");

Decorators report the wrapped command's name, so score.withTimeout(3) still logs as Score. FunctionalCommand.withName(...) sets the name in place (no wrapper); on any other command it returns a lightweight NamedCommand wrapper.

BaseCommand: a managed template

BaseCommand is an abstract convenience base that manages lifecycle state for you. Instead of overriding the interface methods directly, you override protected hooks:

Hook Replaces
onInitialize() initialize()
onExecute() execute()
onIsFinished() isFinished()
onEnd(boolean interrupted) end(...)

In exchange you get extra state queries:

  • justFinished() — true exactly once after the command ends (self-clearing), handy for chained state machines.
  • wasInterrupted() — whether the last run ended by interruption.

BaseCommand also guards against execute() running after the command has finished.

CommandWrapper: decorating existing commands

CommandWrapper extends BaseCommand and delegates the whole lifecycle to an inner command. It is the base used to build custom decorators — subclass it and override just the behavior you want to change:

public class LoggedCommand extends CommandWrapper {
    public LoggedCommand(Command inner) { super(inner); }

    @Override
    protected void onInitialize() {
        System.out.println("starting " + inner.getClass().getSimpleName());
        super.onInitialize();
    }
}

The wrapper automatically forwards getRequirements() from the inner command.

Composing commands

You rarely subclass anything for day-to-day robot code. Most behavior is assembled from small pieces using:

For writing your own commands from scratch, see the Custom Commands guide.