Command Groups & Factories¶
Command groups combine multiple commands into one. A group is itself a command — it can be scheduled, decorated, bound to buttons, and nested inside other groups. Groups aggregate the subsystem requirements of all their children, so scheduler conflict enforcement works through arbitrarily deep compositions.
The group types¶
All groups live in com.vcs.valleylib.core.command.decorators.
SequentialCommandGroup¶
Runs commands one after another, in order. Finishes when the last command finishes. If the group is interrupted, the currently running child receives end(true).
Command auto = new SequentialCommandGroup(
new InstantCommand(claw::close),
new WaitCommand(0.2),
new DriveDistanceCommand(drive, 24)
);
ParallelCommandGroup¶
Starts all commands at once. Finishes when all of them have finished.
Command deploy = new ParallelCommandGroup(
new LiftToHeightCommand(lift, HIGH),
new ExtendArmCommand(arm)
);
RaceCommand¶
Starts all commands at once. Finishes as soon as any one finishes; the rest are interrupted.
Command driveOrGiveUp = new RaceCommand(
new FollowPathCommand(drive, path, 1.0),
new WaitCommand(5.0) // safety timeout
);
DeadlineCommand¶
Starts all commands at once. Finishes when the designated deadline command finishes; the others are interrupted at that moment.
Command cycle = new DeadlineCommand(
followPath, // the deadline
runIntake // interrupted when the path ends
);
ConditionalCommand¶
Chooses one of two commands at initialization time based on a condition — the command-based if/else.
Command scorePosition = new ConditionalCommand(
vision::seesLeftProp,
driveLeft, // when true
driveCenter // when false
);
Groups are reusable
All group types reset their internal state in initialize(), so the same group instance can be scheduled again after it finishes — important for commands bound to buttons.
The Commands factory class¶
com.vcs.valleylib.core.command.Commands provides static factories so you can build common commands without new noise. It is the idiomatic entry point for inline commands.
Requirements
The plain forms carry no subsystem requirements — use them for glue (waits, markers, grouping). For anything that actuates a mechanism, either use the subsystem factories (intake.startEnd(...)) for a single subsystem, or pass requirements explicitly for several: Commands.run(() -> ..., intake, transfer). Both participate in scheduler conflict resolution.
| Factory | Returns |
|---|---|
Commands.none() |
A command that does nothing and finishes instantly |
Commands.runOnce(action[, requirements...]) |
Runs the action once, then finishes |
Commands.run(action[, requirements...]) |
Runs the action every cycle, never finishes on its own (great for default commands) |
Commands.startEnd(onStart, onEnd[, requirements...]) |
Runs onStart when scheduled, onEnd when it ends — perfect for whileTrue bindings |
Commands.runEnd(action, onEnd[, requirements...]) |
Runs action every cycle and onEnd when it ends |
Commands.waitSeconds(seconds) |
WaitCommand |
Commands.waitUntil(condition) |
WaitUntilCommand |
Commands.sequence(cmds...) |
SequentialCommandGroup |
Commands.parallel(cmds...) |
ParallelCommandGroup |
Commands.race(cmds...) |
RaceCommand |
Commands.deadline(deadline, others...) |
DeadlineCommand |
Commands.either(onTrue, onFalse, condition) |
ConditionalCommand |
Examples¶
// Hold-to-run intake: start on press, stop on release
// (subsystem factory → the command requires the intake)
driver.a().whileTrue(intake.startEnd(intake::in, intake::stop));
// Default joystick drive (requires the drivetrain)
drive.setDefaultCommand(drive.run(() ->
drive.arcadeDrive(driver.leftY(), driver.rightX())));
// A timed pulse
Command pulse = Commands.sequence(
intake.runOnce(intake::in),
Commands.waitSeconds(0.5),
intake.runOnce(intake::stop)
);
// Vision-dependent auto branch
Command branch = Commands.either(leftAuto, centerAuto, vision::seesLeft);
Factories vs. decorators vs. constructors¶
These three snippets are equivalent — pick whichever reads best:
// Decorator style
Command a = closeClaw.andThen(Commands.waitSeconds(0.2)).andThen(driveOut);
// Factory style
Command b = Commands.sequence(closeClaw, Commands.waitSeconds(0.2), driveOut);
// Constructor style
Command c = new SequentialCommandGroup(closeClaw, new WaitCommand(0.2), driveOut);
For longer autonomous routines, consider the AutoDsl, which layers a readable builder on top of these groups.