wiki:ControlSystems/SoftwareTeam/Training/GettingStarted/StateMachines1

Version 3 (modified by David Albert, 6 years ago) (diff)

--

Consider a program that must drive a robot forward some distance and then turn the robot 90-degrees to face a target. As the program runs, it spends time in one of these states:

  • Initial (startup)
  • Driving Forward
  • Turning
  • Finished

It may remain in a state for a considerable amount of time. When it finishes one state, it transitions to another. At any given time, the program is only in one state

We can model this behavior using a software construct called a State Machine.

As the name implies, a State Machine is a program that has stateful behavior, meaning that what the program does at any given moment depends on its current state. We could model the above sequence of states with this program:

public class StateMachine {
    private enum State { START, FORWARD, TURN, STOP };

    public static void main(String args[]) {
        State state = State.START;
        while (true) {
            switch (state) {
                case START:
                    System.out.println("START->FORWARD...");
                    state = State.FORWARD;
                    break;
                case FORWARD:
                    System.out.println("FORWARD->TURN...");
                    state = State.TURN;
                    break;
                case TURN:
                    System.out.println("TURN->STOP...");
                    state = State.STOP;
                    break;
                case STOP:
                default:
                    // finished..nothing to do                
            }
        }
    }
}

This program introduces a new type of variable called an enumeration (enum). An enum is a variable that can hold one of the values you specify when you declare it. For example:

enum Color { RED, GREEN, BLUE };

In this example, we've enumerated the states the robot can be in: starting, driving forward, turning, and stopped.

The program also introduces the switch statement which is another type of conditional like if ,else if, else. With a switch statement, the condition is the value of a variable; execution jumps to the case that matches the variable's value and proceeds forward from there until a break statement is encountered which exits the switch block. If there is no case statement matching the variable's value, the default case is executed.

Run the program in the debugger and observe how the state variable and switch statement interact.

Notice that:

  • The current state is stored in a state variable named state in this program.
  • The case matching the current state is run each time we go through the loop
  • The program can stay in any state for as long as needed and explicitly advances to the next state when it's ready.

In computer science, we often draw state diagrams that represent the states and the transitions between states. Read about them here

Java is object oriented so its natural to model a state as an object. Consider (and run) the following program (from here) that models a vending machine; it's a complex program that will take some time to understand; try running it in the debugger:

import java.util.*;
 
public class StateMachine {
 
    private enum State {
        // each state and its transitions (actions)
        Ready(true, "Deposit", "Quit"),
        Waiting(true, "Select", "Refund"),
        Dispensing(true, "Remove"),
        Refunding(false, "Refunding"),
        Exiting(false, "Quiting");
 
        State(boolean is_explicit, String... in) {
            inputs = Arrays.asList(in);
            explicit = is_explicit;
        }
 
        State nextState(String input, State current) {
            if (inputs.contains(input)) {
                return map.getOrDefault(input, current);
            }
            return current;
        }
 
        final List<String> inputs;
        final static Map<String, State> map = new HashMap<>();
        final boolean explicit;
 
        // Map contains transitions, next state
        static {
            map.put("Deposit", State.Waiting);
            map.put("Quit", State.Exiting);
            map.put("Select", State.Dispensing);
            map.put("Refund", State.Refunding);
            map.put("Remove", State.Ready);
            map.put("Refunding", State.Ready);
        }
    }
 
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        State state = State.Ready;
 
        while (state != State.Exiting) {
            System.out.println(state.inputs);
            if (state.explicit){
                System.out.print("> ");
                state = state.nextState(sc.nextLine().trim(), state);
            } else {
                state = state.nextState(state.inputs.get(0), state);
            }
        }
        sc.close();
    }
}

Read more about implementing state machines in Java in these advanced tutorials here and here