While looking at the 2910’s 2022 robot code, I noticed an interesting thing they did. They added commands to the Sequential Command Group on the initialize method to be able to change the command group with relation the some conditionals.
I guess this is easier than creating all possible combinations of command groups and running them on a conditional command. However, it seems like this implantation will cause some issues on the Scheduler side. As far as I understand at first this will prevent subsystem requirements to be defined since they are read before the initialize is called.
What other problems could this implementation cause and also what other methods can be used to create a similar code structure?
package org.frcteam2910.c2022.commands;
import edu.wpi.first.wpilibj2.command.*;
import org.frcteam2910.c2022.subsystems.ClimberSubsystem;
import org.frcteam2910.c2022.subsystems.ShooterSubsystem;
import org.frcteam2910.c2022.util.ClimbChooser;
public class AutoClimbCommand extends SequentialCommandGroup {
private final ClimberSubsystem climber;
private final ShooterSubsystem shooter;
private final ClimbTypeSupplier climbTypeSupplier;
private boolean hasAddedCommands;
@FunctionalInterface
public interface ClimbTypeSupplier {
ClimbChooser.ClimbType getClimbType();
}
public AutoClimbCommand(ClimberSubsystem climber, ShooterSubsystem shooter, ClimbTypeSupplier climbTypeSupplier) {
this.climber = climber;
this.shooter = shooter;
this.climbTypeSupplier = climbTypeSupplier;
}
@Override
public void initialize() {
if (!hasAddedCommands) {
hasAddedCommands = true;
ClimbChooser.ClimbType climbType = climbTypeSupplier.getClimbType();
// Climber is currently hooked on the mid rung, but the robot is on the ground
addCommands(new InstantCommand(shooter::disableFlywheel));
// Prepare to transfer mid rung to hood
addCommands(new ClimberToPointCommand(climber, ClimberSubsystem.HOOD_TRANSFER_HEIGHT, true, false)
.alongWith(new SetHoodAngleCommand(shooter, ShooterSubsystem.HOOD_PREPARE_TRANSFER_ANGLE)));
// Transfer mid rung to hood
addCommands(transferToHood(climber, shooter));
if (climbType != ClimbChooser.ClimbType.MID) {
// Move from mid rung to high rung
if (climbType == ClimbChooser.ClimbType.HIGH_PARTWAY) {
addCommands(traverseToNextRung(climber, shooter, false, true));
addCommands(new SetHoodAngleCommand(shooter, ShooterSubsystem.HOOD_MIN_ANGLE));
} else {
addCommands(traverseToNextRung(climber, shooter));
}
if (climbType != ClimbChooser.ClimbType.HIGH_PARTWAY) {
// Transfer high rung to hood
addCommands(transferToHood(climber, shooter));
if (climbType != ClimbChooser.ClimbType.HIGH_HOOK) {
// Move from high rung to traverse rung
if (climbType == ClimbChooser.ClimbType.TRAVERSAL_PARTWAY) {
addCommands(traverseToNextRung(climber, shooter, true, true));
} else {
addCommands(traverseToNextRung(climber, shooter, true, false));
}
if (climbType != ClimbChooser.ClimbType.TRAVERSAL_PARTWAY) {
addCommands(new ClimberToPointCommand(climber, ClimberSubsystem.HOOD_PASSAGE_HEIGHT, false,
false));
// Transfer traverse rung to hood
addCommands(transferToHood(climber, shooter));
}
}
}
}
addCommands(new InstantCommand(() -> climber.setTargetVoltage(0.0)));
addCommands(new WaitCommand(5).perpetually());
super.initialize();
}
}
private static Command transferToHood(ClimberSubsystem climber, ShooterSubsystem shooter) {
SequentialCommandGroup group = new SequentialCommandGroup();
// Move the climber to the transfer height
group.addCommands(new ClimberToPointCommand(climber, ClimberSubsystem.HOOD_TRANSFER_HEIGHT));
// Grab the rung with the hood
group.addCommands(new SetHoodAngleCommand(shooter, ShooterSubsystem.HOOD_TRANSFER_ANGLE));
// Release the rung with the climber so the hood can move freely
group.addCommands(new ClimberToPointCommand(climber, ClimberSubsystem.HOOD_PASSAGE_HEIGHT));
return group;
}
private static Command traverseToNextRung(ClimberSubsystem climber, ShooterSubsystem shooter) {
return traverseToNextRung(climber, shooter, false, false);
}
private static Command traverseToNextRung(ClimberSubsystem climber, ShooterSubsystem shooter, boolean traversal,
boolean partway) {
SequentialCommandGroup group = new SequentialCommandGroup();
if (!traversal) {
group.addCommands(
new SetHoodAngleCommand(shooter, ShooterSubsystem.HOOD_TRAVERSE_EXTEND_ANGLE_HIGH, true, true)
.alongWith(new ClimberToPointCommand(climber, ClimberSubsystem.TRAVERSE_EXTEND_HEIGHT)));
} else {
// Angle the robot to extend the climber
group.addCommands(new SetHoodAngleCommand(shooter, ShooterSubsystem.HOOD_TRAVERSE_EXTEND_ANGLE_TRAVERSE,
false, true));
// Extend the climber slightly past the next rung
group.addCommands(new ClimberToPointCommand(climber, ClimberSubsystem.TRAVERSE_EXTEND_HEIGHT));
}
// Angle the robot so the climber hooks will grab the next rung
group.addCommands(new SetHoodAngleCommand(shooter, ShooterSubsystem.HOOD_TRAVERSE_RETRACT_ANGLE, false, true));
if (traversal) {
if (partway) {
group.addCommands(
new ClimberToPointCommand(climber, ClimberSubsystem.TRAVERSE_RUNG_PARTWAY_HEIGHT, false, false)
.alongWith(new SetHoodAngleCommand(shooter,
ShooterSubsystem.HOOD_PREPARE_TRANSFER_ANGLE, true, true)));
group.addCommands(new SetHoodAngleCommand(shooter, ShooterSubsystem.HOOD_MIN_ANGLE));
} else {
group.addCommands(new ClimberToPointCommand(climber, ClimberSubsystem.HOOD_PASSAGE_HEIGHT, false, false)
.alongWith(new SetHoodAngleCommand(shooter, ShooterSubsystem.HOOD_PREPARE_TRANSFER_ANGLE, true,
true)));
}
} else {
// Retract the climber, and move the hood to the transfer position after the
// climber grabs onto the next rung
if (!partway) {
group.addCommands(new ClimberToPointCommand(climber, ClimberSubsystem.HOOD_PASSAGE_HEIGHT, false, false)
.alongWith(new SetHoodAngleCommand(shooter, ShooterSubsystem.HOOD_PREPARE_TRANSFER_ANGLE, true,
true)));
} else {
group.addCommands(
new ClimberToPointCommand(climber, ClimberSubsystem.TRAVERSE_RUNG_PARTWAY_HEIGHT, false, false)
.alongWith(new SetHoodAngleCommand(shooter,
ShooterSubsystem.HOOD_PREPARE_TRANSFER_ANGLE, true, true)));
}
}
return group;
}
}