Page cover

Essential Usage

This page covers all essential syntax that's necessary to use the State Factory library. As you read through the page, you can reference the full, basic class example at the bottom of this page.


State Factory organizes each state using enum constants. These are created by you to make it easier to organize code and relate each state to a name. Here's an example of what you might use:

enum LinearStates {
    IDLING,
    EXTENDING,
    DEPOSITING,
    RETRACTING,
    RELEASE
}

enum FallbackStates {
    PUSH_FALLBACK,
    DETECTION_FAIL
} 

After defining the individual states, it's time to get into the actual state machine. Defining the StateMachine object goes like this:

StateMachine machine = new StateMachineBuilder()
    .build();

The actual code of the state machine goes between the object instantiation and the .build(). This .build() marks the end of the state machine.


States

Before putting everything together, it's important to get to know the methods that will be used. The first one is the simplest - the .state() method. This defines the state and connects it to the enum constant specified in the parameter. It is possible to replace this enum constant with a string, for example .state("IDLING") however, this is not recommended.

.state(LinearStates.IDLING) 

Next, you define your enter actions which will be executed when the state is entered. These are placed within a lambda function. Here's an example:

.onEnter( () -> System.out.println("Entering the state!") )

Similarly, there are exit actions that execute when a transition case becomes true. The exit command follows the same rules as the enter actions. For example:

.onExit( () -> System.out.println("Exiting the state!") )

To execute an action after a certain amount of time, you can use the afterTime() method which takes in the amount of seconds and executes the given action after that time. Here's an example:

.afterTime(0.5, () -> servo.setPosition(CLOSE))

To execute actions repeatedly during the loop, you can put them in the loop command. This could be used if you want to update a servo position so it is always updating when a value is changed in FTCDashboard. This follows the same rules as the previous actions, here is an example:

.loop( () -> servo.setPosition(DASH_CONSTANT)

To add multiple lines of code that should be executed within the lambda, use curly braces.

.onEnter( () -> {
    System.out.println("Entering the state");
    System.out.println("A second line");
})

Finally, the part that pulls it together: transitions. The most basic transitions go from one state to the next in linear order. This can be specified by indicating a boolean statement. For example:

// The transition case occurs when the slide position is greater than 700. 
// This will go to the state that is defined right after it.
.transition( () -> slides.getPosition() > 700)

In addition to this, there is also functionality for timed based transitions. With this transition, after the specified amount of time the current state will transition to the next. This timer will start as soon as the state is entered.

// will transition after 5.5 seconds
.transitionTimed(5.5)

To easily make a state that only has a specified timed transition, you can use the waitState(time) method. This creates a state that has a transitionTimed attached to it by default which allows it to transition from one state to the next in the specified time. Similar to any other state, you can add enter, exit, and loop actions to this state if preferred.

// will move to the next state after 3 seconds
.waitState(3)

For a more advanced look at transitions, check out the Advanced Usage page.


Running Your State Machines

Before putting it into the actual code, it is important to understand how to control the state machine externally. This includes starting, resetting, and stopping the state machine. This part is the simplest, but the most important for the state machine to actually work.

To start the state machine the command is:

machine.start(); // where machine is the StateMachine object

To reset the state machine, the command is:

machine.reset();

To stop the state machine, the command is:

machine.stop();

To update the machine, the command is:

machine.update();

To get the current state, the command is:

machine.getState();

To set the current state, which is not intended to be used often, the command is:

machine.setState(Enum/String);

To check if the machine is currently running, the command is:

machine.isRunning();

Basic Example

Now for putting it all together:

SFTest.java
import com.sfdev.assembly.state.*;

// @Autonomous or @TeleOp
public class SFTest extends LinearOpMode() {
    enum States {
        FIRST,
        SECOND,
        THIRD
    }
    
    @Override
    public void runOpMode() throws InterruptedException {
    
        StateMachine machine = new StateMachineBuilder() 
            .state(States.FIRST)
            .onEnter( () -> {
                System.out.println( "Entering the first state" );
            })
            .transition( () ->  gamepad1.x ) // transition when gamepad1.x is clicked
            .onExit( () -> Sysetm.out.println("Exiting!") ) // setting check2 to false
            
            .state(States.SECOND)           
            .onEnter( () -> System.out.println( "Entering the second state" ) ) 
            .transition( () -> gamepad1.b) // if check2 is false transition
            
            .state(States.THIRD)
            .onEnter( () -> System.out.println( "In the third state " ) )
            .build();
            
        waitForStart();

        machine.start();
        
        while(opModeIsActive()) { // autonomous loop
            machine.update()
        }
    }
}

This is a sample program to provide you with an example of how the entire class can be put together. A more thorough example with Roadrunner can be seen on the Examples page. If you have more questions or have some recommendations that should be added, feel free to reach out to @.vvk. on Discord.

Last updated