How to use state machines for your modeling (Part 3): The big switch statement

Having read part 1 and part 2 of this series, we should now have a better understanding of state machines, basic elements of the graphical modeling language and time-controlled state transitionsIn the following sections we examine how model code is created from modeled state machines. 

Whoever models the blind control system not only needs it as an example, but will want to use it to create a real control unit, and so needs executable code. We will look at different implementation approaches for state automata and preview the automatic code generation abilities of YAKINDU Statechart Tools.

  Download YAKINDU Statechart Tools

The big switch statement

Various approaches are possible for implementing an automatic state machine in software. The most common is using a switch statement. Developers often implement state-of-the-art switches with switch instructions, even if they are not aware that they are state machines. Each state of the machine corresponds to one of the case clauses of the switch statement. The program jumps to the case clause that corresponds to the active state. Within this case clause, it executes state-specific statements, checks whether and/or which events are present, and activates a different state depending on them.

In principle two variants are standard:

  • In the event-driven approach, the state machine executes the switch instruction as soon as an event arrives.
  • In the cycle-based approach, this is done at regular intervals.

A combination of both approaches is also possible. Executing the switch statement and processing the current events is called run-to-completion-step (RTC).

The basic structure of the implementation can be sketched very simply using the example of a traffic light, because each state of a traffic light is always assigned exactly one following state: red follows red-yellow, then green, then yellow and then red again. This state machine does currently not need any events, which results in simple and clear source code.

We choose Java as an implementation language here, but of course this principle can also be implemented analogously in other programming languages. First we define an enumeration with all states of the state automaton, i.e. with the traffic light phases.

enum State {
    RED, RED_YELLOW, GREEN, YELLOW
}

The variable activeState contains the current status; this is initialized to red.

State activeState = State.RED;

The stateMachine method implements the actual state automata. Its core is a switch statement, which, depending on the current state, activates the next state by assigning it to activeState. In this example this occurs cyclically in an endless loop.

public void stateMachine() {
    while (true) {
       switch (activeState) {

            case RED: {
                activeState = State.RED_YELLOW;
                break;
            }
    
            case RED_YELLOW: {
                activeState = State.GREEN;
                break;
            }
    
           case GREEN: {
                activeState = State.YELLOW;
                break;
            }
    
            case YELLOW: {
                activeState = State.RED;
                break;
            }

        }
    }
}

The next example goes one step further and takes events into account. For this purpose we will return to the blind control, as shown in the figure below.

vbc_02.png


The implementation in Java summarizes the states of the machine in the enum State:

enum State {
    INITIAL, IDLE, MOVING_UP, MOVING_DOWN
}

The events can also be represented in an enum:

enum Event {
    USER_UP, USER_DOWN, POSSENSOR_UPPER_POSITION, POSSENSOR_LOWER_POSITION
}

The variable activeState is preset with the initial state of the machine:

State activeState = State.INITIAL;

The Collection<Event> events records the incoming events:

Collection events = new ArrayList();

For developers who are unfamiliar with Java, Collection<Event> is a data type that manages a group of event objects. The example uses an ArrayList<Event> as a concrete implementation, a list of event objects that is implemented internally using an array.

The following idea lies behind this. As we have seen, the state machine continuously executes the switch statement in an endless loop. The state machine should only do something if there is actually something to do, for example to save power, or to prevent a multitasking system from unnecessarily hogging CPU capacity. The endless loop therefore does not run at the highest possible speed, but at a suitable, lower rate. That is, the machine executes one cycle (run-to-completion step) and then pauses for a while. This power saving effect is particularly important for embedded devices without external power supplies. In this example the waiting time between two cycles is 100 milliseconds:

long clockPulse = 100;

Events can arrive within this waiting period: we leave how this happens outside this discussion. In any case, these events enter the collection defined above and can be evaluated in the next processing cycle. If the blind control is in the Idle state and the User.up event is present, the state machine switches to the Moving up state. The event User.down is a transition to Moving down. The Java code that implements this function looks like this:

case IDLE: {
    if (events.contains(Event.USER_UP))
        activeState = State.MOVING_UP;

    else if (events.contains(Event.USER_DOWN))
        activeState = State.MOVING_DOWN;

    break;
}

If any other events arrive while Idle is the active state they are ignored: a state machine should respond by definition only to those events for which the respective active state is "sensitive".

Several events may be present simultaneously. The lower the clock rate, the greater the probability is that a user will press both the [↑] and [↓] keys between two processing cycles. In this case, in the above implementation the [↑] key always "wins". If you want to give priority to the [↓] key, you must adapt the implementation accordingly.

Here is the complete method stateMachine, which implements the behaviour of the automatic state machine:

public void stateMachine() {
    while (true) {

        /* Act upon the active state: */
        switch (activeState) {
    
            case INITIAL: {
                activeState = State.IDLE;
                break;
            }
            
            case IDLE: {
                if (events.contains(Event.USER_UP))
                    activeState = State.MOVING_UP;
            
                else if (events.contains(Event.USER_DOWN))
                    activeState = State.MOVING_DOWN;
            
                break;
            }
            
            case MOVING_UP: {
                if (events.contains(Event.POSSENSOR_UPPER_POSITION) || events.contains(Event.USER_DOWN))
                    activeState = State.IDLE;
            
                break;
            }
            
            case MOVING_DOWN: {
                if (events.contains(Event.POSSENSOR_LOWER_POSITION) || events.contains(Event.USER_UP))            
                    activeState = State.IDLE;

                break;            
            }
            
            default: {
                /* Should never happen. */
                throw (new IllegalStateException());
            }
        }

We have already seen that at most one event leads to a state transition within a single clock cycle. This will have already happened at this point if there was a suitable event. Whether we are in a new state or still in the same state as before, the events that arrived during the last cycle have no further relevance. For the next cycle we must clear them:

       /* Clear events: */
        events.clear();

The current processing cycle is thus completed. The state machine now pauses clockPulse milliseconds:

       /* Pause until the next run to completion step is due: */
        try {
            Thread.sleep(clockPulse);

        } catch (InterruptedException e) {
           // Ignore
        }
    }
}

In the following parts of this series, we will learn about two other implementation approaches: the representation of state machines as a table and – as an object-oriented variant – the “State” software design pattern.

In addition, we will take a brief look at YAKINDU Statechart Tools’ automatic code generation feature.

To read the whole “How to use state machines for your modeling” series, download our whitepaper for free.

Download free whitepaper now

About Rainer Klute

Rainer Klute works as a software architect and engineer at itemis AG in Lünen, Germany. He is a member of the YAKINDU Statechart Tools team where he oversees the documentation.