Understanding and Implementation of FSM in Linux Programming

Posted by quark76 on Thu, 20 Jun 2019 02:19:35 +0200

finite state machine (FSM) is a mathematical model that represents finite states and their transitions and actions. It has been widely used in computer field. FSM is an efficient programming method within logic unit. In server programming, the server can process the corresponding logic according to different states or message types, which makes the program logic clear and easy to understand.

Where is the finite state machine usually used?

tokenizer, a processing language or natural language, parser s that parse grammar from the bottom up.
All kinds of communication protocols, such as sender and receiver, transmit data to process messages, game AI and so on, have application scenarios.

There are several implementations of state machines, and I will describe their advantages and disadvantages one by one.

1. FSM implemented with if/else if statement

The use of if/else if statements is the simplest and easiest way to implement FSM. We only need a large number of if/else if statements to judge the state value to perform the corresponding logical processing.

Looking at the following examples, we use a large number of if/else if statements to implement a simple state machine, which can perform corresponding operations according to different states, and realize the state jump.

//For example, we define the state of Xiao Ming's day as follows
enum
{
    GET_UP,
    GO_TO_SCHOOL,
    HAVE_LUNCH,
    GO_HOME,
    DO_HOMEWORK,
    SLEEP,
};


int main()
{
    int state = GET_UP;
    //Xiao Ming's Day
    while (1)
    {
        if (state == GET_UP)
        {
            GetUp(); //Specifically called functions
            state = GO_TO_SCHOOL;  //Transfer of state
        }
        else if (state == GO_TO_SCHOOL)
        {
            Go2School();
            state = HAVE_LUNCH;
        }
        else if (state == HAVE_LUNCH)
        {
            HaveLunch();
        }
        ...
        else if (state == SLEEP)
        {
            Go2Bed();
            state = GET_UP;
        }
    }

    return 0;
}

After reading the above examples, how do you feel? Is it true that although the program is simple and easy to understand, it uses a large number of if judgment statements, which makes the code very low-end, while the code is more inflated. The state machine has only a few states, and the code expansion is not obvious, but if there are dozens of states we need to deal with, the state machine's code is not readable.

2. Implementing FSM with switch

The structure of FSM implemented by switch statement becomes clearer, and its shortcomings are obvious: although this design method is simple and can be handled by a large number of judgments, it is suitable for small-scale state switching process, but it is difficult to expand and maintain if the scale is enlarged.

int main()
{
    int state = GET_UP;
    //Xiao Ming's Day
    while (1)
    {

        switch(state)
        {
        case GET_UP:
            GetUp(); //Specifically called functions
            state = GO_TO_SCHOOL;  //Transfer of state
            break;
        case GO_TO_SCHOOL:
            Go2School();
            state = HAVE_LUNCH;
            break;
        case HAVE_LUNCH:
            HaveLunch();
            state = GO_HOME;
            break;
            ...
        default:
            break;
        }
    }

    return 0;
}

3. Implementing FSM with Function Pointer

The idea of using function pointer to realize FSM is to establish the corresponding state table and action query table, locate the corresponding action processing function according to the state table, event and action table, and switch the state after execution.

Of course, the process of using function pointer to implement FSM is time-consuming and laborious, but all of this is worthwhile, because when your program is large, the state machine based on this table structure is also handy to maintain the program.

Following is a framework for implementing FSM using function pointers:

We also take Xiaoming's Day as an example to design the FSM.

Firstly, the state transition diagram of the FSM is given.

Here's the key part of the code implementation

First of all, we define Xiao Ming's activity state for the day.

//For example, we define the state of Xiao Ming's day as follows
enum
{
    GET_UP,
    GO_TO_SCHOOL,
    HAVE_LUNCH,
    DO_HOMEWORK,
    SLEEP,
};

We also define what happens.

enum
{
    EVENT1 = 1,
    EVENT2,
    EVENT3,
};

Define the data structure of the state table

typedef struct FsmTable_s
{
    int event;   //Event
    int CurState;  //current state
    void (*eventActFun)();  //Function pointer
    int NextState;  //Next state
}FsmTable_t;

Next, we define the state table of the most important FSM, and our entire FSM operates according to this defined table.

FsmTable_t XiaoMingTable[] =
{
    //{The coming event, the current state, the function to be executed, the next state}
    { EVENT1,  SLEEP,           GetUp,        GET_UP },
    { EVENT2,  GET_UP,          Go2School,    GO_TO_SCHOOL },
    { EVENT3,  GO_TO_SCHOOL,    HaveLunch,    HAVE_LUNCH },
    { EVENT1,  HAVE_LUNCH,      DoHomework,   DO_HOMEWORK },
    { EVENT2,  DO_HOMEWORK,     Go2Bed,       SLEEP },

    //add your codes here
};

Actions Implementation of State Machine Registration, State Transition and Event Processing

/*State Machine Registration*/
void FSM_Regist(FSM_t* pFsm, FsmTable_t* pTable)
{
    pFsm->FsmTable = pTable;
}

/*State migration*/
void FSM_StateTransfer(FSM_t* pFsm, int state)
{
    pFsm->curState = state;
}

/*event processing*/
void FSM_EventHandle(FSM_t* pFsm, int event)
{
    FsmTable_t* pActTable = pFsm->FsmTable;
    void (*eventActFun)() = NULL;  //Function pointer initialized to null
    int NextState;
    int CurState = pFsm->curState;
    int flag = 0; //Does the logo satisfy the criteria
    int i;

    /*Get the current action function*/
    for (i = 0; i<g_max_num; i++)
    {
        //If and only if there is a specified event in the current state, I will execute it.
        if (event == pActTable[i].event && CurState == pActTable[i].CurState)
        {
            flag = 1;
            eventActFun = pActTable[i].eventActFun;
            NextState = pActTable[i].NextState;
            break;
        }
    }


    if (flag) //If the conditions are met
    {
        /*Action execution*/
        if (eventActFun)
        {
            eventActFun();
        }

        //Jump to the next state
        FSM_StateTransfer(pFsm, NextState);
    }
    else
    {
        // do nothing
    }
}

Let's write the main function like this, and then observe how the state machine works.

int main()
{
    FSM_t fsm;
    InitFsm(&fsm);
    int event = EVENT1; 
    //Xiao Ming's day, day after day, is doing the same activities.
    while (1)
    {
        printf("event %d is coming...\n", event);
        FSM_EventHandle(&fsm, event);
        printf("fsm current state %d\n", fsm.curState);
        test(&event); 
        sleep(1);  //Sleep for 1 second for easy observation
    }

    return 0;
}

Look at the state transition when the state machine runs up:

As can be seen from the figure above, if and only when the specified event comes in the specified state, the execution of the function and the state transition will occur, otherwise the state jump will not occur. This mechanism enables the state machine to run automatically and systematically.

Compared with the former two methods, using function pointer to implement FSM can be used in large-scale switching process very well. As long as we set up the FSM framework, it will be very simple to expand later (as long as we add a line to the state table to write a new state processing).

For children's shoes requiring complete FSM code, please visit My github

Topics: Linux Programming github