Advanced State Machines with Enums Lesson

Advanced State Machines with "Smart" Enums

You know how to build reliable state machines with enums and `switch` statements. Now, it's time to learn an advanced technique that makes our state machine code even cleaner, more readable, and less prone to errors.

The Problem: When States Have Data

Consider a robot arm that moves to predefined positions. With a basic `switch` statement, the state names are in the `enum`, but the data associated with them (the angles) is located somewhere else. This separation can lead to bugs if you add a new state but forget to update the `switch` statement.

The Solution: Enums Can Have Fields and Constructors!

The key insight is that in Java, an enum is a special kind of class. And just like any other class, it can have its own fields, constructor, and methods. We can store the data for a state directly inside the enum itself.

Anatomy of a "Smart" Enum

Let's refactor our `ArmState` enum to be "smart" by giving it a field for the target angle and a constructor to initialize it.

public enum ArmState {
    // 1. Define the constants, passing arguments to the constructor
    STOW(0.0),
    INTAKE(95.0),
    AMP_SCORE(110.0);

    // 2. Add a field to hold the data for each state
    public final double angle;

    // 3. Create a constructor to initialize the field
    private ArmState(double angle) {
        this.angle = angle;
    }
}
    

The Result: Cleaner, Safer Subsystem Code

Now, our `ArmSubsystem` code becomes dramatically simpler and more robust. The `switch` statement is completely gone. To get the target angle, we just ask the state itself!

public class ArmSubsystem extends SubsystemBase {
    private final PIDController m_pidController;
    private ArmState m_targetState;

    // ... constructor and other methods ...

    public void setTargetState(ArmState state) {
        this.m_targetState = state;
    }

    @Override
    public void periodic() {
        if (m_targetState != null) {
            // Get the angle directly from the enum state! No switch needed.
            double setpoint = m_targetState.angle; 
            m_pidController.setSetpoint(setpoint);
            
            double pidOutput = m_pidController.calculate(getCurrentAngle());
            m_motor.set(pidOutput);
        }
    }
}
    

The 2910 Philosophy: Single Source of Truth

This "smart enum" pattern is a core part of our coding standard because it follows a critical software principle: the Single Source of Truth. All information about the `INTAKE` state—its name and its target angle—is located in one single place. This reduces bugs, improves readability, and makes the code much easier to maintain.

Test Your Knowledge

Question: What is the main advantage of storing state-specific data (like a target angle) inside the enum itself?