Under the Hood

The files that drive FTCLib

Before looking under the hood of our code, programmers should study the Command pattern's goals of organized, non-blocking code. We've done that ✅. You don't need to go past this point. We hardly ever need to touch these files. Programmers can contribute to a team by knowing how Command and Subsystem work together and how to run their code. Proceeding on means you want the next level. You're comfortable and open-minded about how this all works.

Robot.java

The secret to understanding how we turned FTC's code into a Command pattern is how the following two files connect. They sit in our utils folder, working their magic--we may never need to touch them. Let's start with Robot.java.

public abstract class Robot {

    public static boolean isDisabled = false;

    /**
     * Cancels all previous commands
     */
    public void reset() {
        CommandScheduler.getInstance().reset();
    }

    /**
     * Runs the {@link CommandScheduler} instance
     */
    public void run() {
        CommandScheduler.getInstance().run();
    }

    /**
     * Schedules {@link com.arcrobotics.ftclib.command.Command} objects to the scheduler
     */
    public void schedule(Command... commands) {
        CommandScheduler.getInstance().schedule(commands);
    }

    /**
     * Registers {@link com.arcrobotics.ftclib.command.Subsystem} objects to the scheduler
     */
    public void register(Subsystem... subsystems) {
        CommandScheduler.getInstance().registerSubsystem(subsystems);
    }

    public static void disable() {
        isDisabled = true;
    }

    public static void enable() {
        isDisabled = false;
    }

}

This file acts as an invisible layer, providing essential functionalities:

  • Shared State: The isDisabled static variable is a central flag accessible throughout your code, indicating robot state (enabled/disabled). The disable() and enable() methods allow external control over the robot's state during transitions between autonomous and teleop periods.

  • Command Control Center: Methods like reset(), run(), schedule(), and register() grant access to the CommandScheduler, the mastermind behind command execution and resource management. Think of it as the conductor of your robot's actions.

Beyond isDisabled: Expanding State Variables

Remember, the way the isDisabled variable controls state is not how we need to track our robot's current status. It doesn't need a static variable; we'll have easy access to the instance of our robot. We're not restricted to booleans. We'll use custom state variables of all sorts--here's an example that uses enums to represent various robot conditions:

public class MyRobot extends Robot {
    // ... other code
    public enum AprilTagToAlign {
        LEFT, CENTER, RIGHT, NONE
    }
    public AprilTagToAlign target;

    // ... any other subsystem will have access to check which target we want
}

CommandOpMode

Here's the most straightforward connection point between Command and FTC. This file is our translator.

public abstract class CommandOpMode extends LinearOpMode {
    /**
     * Cancels all previous commands
     */
    public void reset() {
        CommandScheduler.getInstance().reset();
    }

    /**
     * Runs the {@link CommandScheduler} instance
     */
    public void run() {
        CommandScheduler.getInstance().run();
    }

    /**
     * Schedules {@link com.arcrobotics.ftclib.command.Command} objects to the scheduler
     */
    public void schedule(Command... commands) {
        CommandScheduler.getInstance().schedule(commands);
    }

    /**
     * Registers {@link com.arcrobotics.ftclib.command.Subsystem} objects to the scheduler
     */
    public void register(Subsystem... subsystems) {
        CommandScheduler.getInstance().registerSubsystem(subsystems);
    }

    @Override
    public void runOpMode() throws InterruptedException {
        telemetry = new MultipleTelemetry(telemetry, FtcDashboard.getInstance().getTelemetry());
        initialize();

        waitForStart();

        // run the scheduler
        while (!isStopRequested() && opModeIsActive()) {
            run();
            telemetry.addData("hi", "hi");
            telemetry.update();
        }
        reset();
    }

    public abstract void initialize();

    public static void disable() {
        Robot.disable();
    }

    public static void enable() {
        Robot.enable();
    }

}

Lifecycle Integration

CommandOpMode runs LinearOpMode's critical method runOpMode()method. That's it 🤯. Sure, we can also post telemetry updates, but the real essential bit is just that stupid run() call. That keeps our CommandScheduler working while satisfying the requirements for FTC gameplay.

Reset

The reset() method within both Robot and CommandOpMode plays a crucial role in managing Commands during various FTC gameplay scenarios. Here are some practical examples of when you might utilize it:

1. Autonomous Period Transitions:

  • Switching Strategies: During autonomous, imagine your robot first performs a pre-determined path using a pre-scheduled set of Commands. If external sensors detect an unexpected obstacle, you can call reset() in Robot or CommandOpMode to clear all running Commands and initiate a new set of Commands for obstacle avoidance or adaptation.

  • Recovering from Errors: If a sensor reading is deemed unreliable or a Command encounters an error, resetting can clear the current action and allow you to initiate a new Command for recovery or safe shutdown.

2. Teleop Period:

  • Button-Triggered Resets: Consider a button press on your gamepad that triggers reset() in CommandOpMode. This could be used to:

    • Cancel an ongoing movement Command if the driver wants to stop abruptly.

    • Reset arm or claw positions to known starting points for precise manipulation.

    • Restart a specific Command sequence for a repeated action.

3. Testing and Debugging:

  • Isolating Command Behavior: During testing, you might use reset() after each test run to isolate specific Commands and verify their functionality without interference from previous actions.

  • Reproducing Issues: If your robot exhibits unexpected behavior, strategically placing reset() calls can help reproduce the issue for easier debugging and troubleshooting.

Last updated