How to construct a finite state machine for a Linux Application

Source: Internet
Author: User
Tags fsm
The method for constructing a finite state machine for Linux applications-general Linux technology-Linux programming and kernel information. The following is a detailed description. Finite Automation Machine is an important cornerstone of computer science. It is usually called a Finite State Machine (Finite State Machine) in the field of software development ), it is a Design Pattern that is widely used ). This article describes how to build a software system based on the state machine and how to use tools in Linux to automatically generate a practical state machine framework.
  
   1. What is a state machine?

A finite state machine is a tool used to model object behavior. Its role is to describe the sequence of states that an object has experienced during its lifecycle, and how to respond to various events from outside. In an object-oriented software system, no matter how simple or complex an object is, it will inevitably go through a complete process from initial creation to final extinction, this is usually called the lifecycle of an object. Generally, an object cannot be completely isolated during its life cycle. It must affect other objects by sending messages, or change itself by accepting messages. In most cases, these messages are just simple and synchronous method calls. For example, in the bank Customer management system, when an instance of the Customer class is required, the getBalance () method defined in the Account class may be called. In this simple case, the Class Customer does not need a finite state machine to describe its own behavior, mainly because its current behavior does not depend on a previous state.
  
Unfortunately, not all cases will be so simple. In fact, many practical software systems must maintain one or two very critical objects, which usually have very complex state conversion relationships, in addition, it is necessary to respond to various asynchronous events from external sources. For example, in a VoIP Phone System, Telephone instances must be able to respond to random calls from the other party, key events from the user, and signaling from the network. When processing these messages, Telephone-like behaviors depend entirely on the current status. Therefore, using a state machine is a good choice.
  
The game engine is one of the most successful applications of finite state machines. Because a well-designed state machine can be used to replace some artificial intelligence algorithms, therefore, each role or device in the game may be embedded with a state machine. Consider a simple object such as the gate in RPG games. It has four states: open, Closed, Locked, and Unlocked, as shown in 1. When a player reaches a Locked door, if he has found the key to open the door, he can use it to change the current status of the door to Unlocked, you can also switch the door knob to Opened to successfully enter the city.
  
State machine controlling the city gate
Javascript: if (this. width> screen. width-600) this. style. width = screen. width-600; ">
When describing finite state machines, States, events, transformations, and actions are common basic concepts.
  
A State is a condition in the lifecycle of an object. an object in a specific State must meet certain conditions, perform certain actions, or wait for certain events. "
Events refer to things that occupy a certain position in time and space and are meaningful to the state machine. Events usually cause state changes, prompting the state machine to switch from one state to another.
Transition refers to a relationship between two States, indicating that the object will perform certain actions in the first State, the second state is entered when a specific condition is met when an event occurs.
Action refers to the atomic operations that can be performed in the state machine. The so-called Atomic operations refer to the operations that cannot be interrupted by other messages during the running process and must be executed continuously.
  
   2. Manually write a State Machine

Unlike other commonly used design patterns, when programmers want to add state machines to their software systems, they must write an additional part of the code for logical control. If the system is complex enough, this part of code is still quite difficult to implement and maintain. When implementing a finite state machine, the switch statement is the simplest and most direct method. The basic idea is to set a case Branch for each State in the state machine, it is used to control the status. The following code demonstrates how to use the switch statement to implement the state machine shown in:
  
Switch (state ){
  
// Process the branch of Opened
Case (Opened ):{
// Execute the Open action
Open ();
// Check for CloseDoor events
If (closeDoor ()){
// Switch the current status to Closed
ChangeState (Closed)
}
Break;
}
  
// Handle the branch of the Closed state
Case (Closed ):{
// Execute the Close action
Close ();
// Check whether OpenDoor events exist
If (openDoor ()){
// Convert the current status to Opened
ChangeState (Opened );
}
// Check for LockDoor events
If (lockDoor ()){
// Switch the current status to Locked
ChangeState (Locked );
}
Break;
}
  
// Process the Locked Branch
Case (Locked ):{
// Execute the Lock action
Lock ();
// Check for UnlockDoor events
If (unlockDoor ()){
// Switch the current status to Unlocked
ChangeState (Unlocked );
}
Break;
}
  
// Handle Unlocked branches
Case (Unlocked ):{
// Execute the action Unlock
Unlock ();
// Check for LockDoor events
If (lockDoor ()){
// Switch the current status to Locked
ChangeState (Locked)
}
// Check whether OpenDoor events exist
If (openDoor ()){
// Convert the current status to Opened
ChangeSate (Opened );
}
Break;
}
  
}
The Finite State Machine implemented by the switch statement does work well, but the code is not very readable, mainly because the conversion between States is realized, check the conversion conditions and perform state conversion in the current state. For example, when the city gate is in the Opened State, you need to call the closeDoor () function in the corresponding case to check whether status conversion is necessary. If yes, you also need to call changeState () the function switches the current status to Closed. Obviously, if multiple different conversion conditions need to be checked in each State, and the state machine needs to switch to different States according to the check results, such code will be boring and difficult to understand. From the perspective of code refactoring, it is better to introduce the checkStateChange () and commit mstatechange () functions to check the conversion conditions, and the various actions that need to be performed to activate the conversion. In this way, the program structure will become clearer:
Switch (state ){
  
// Process the branch of Opened
Case (Opened ):{
// Execute the Open action
Open ();
// Check whether there are events that trigger status conversion.
If (checkStateChange ()){
// Convert the state machine
Optional mstatechange ();
}
Break;
}
  
// Handle the branch of the Closed state
Case (Closed ):{
// Execute the Close action
Close ();
// Check whether there are events that trigger status conversion.
If (checkStateChange ()){
// Convert the state machine
Optional mstatechange ();
}
Break;
}
  
// Process the Locked Branch
Case (Locked ):{
// Execute the Lock action
Lock ();
// Check whether there are events that trigger status conversion.
If (checkStateChange ()){
// Convert the state machine
Optional mstatechange ();
}
Break;
}
  
// Handle Unlocked branches
Case (Unlocked ):{
// Execute the Lock action
Unlock ();
// Check whether there are events that trigger status conversion.
If (checkStateChange ()){
// Convert the state machine
Optional mstatechange ();
}
Break;
}
  
}
But the checkStateChange () and performStateChange () functions will become very bloated and even difficult to implement internal logic in the face of complicated state machines.
  
For a long period of time, the use of the switch statement has always been the only way to implement a finite state machine, and most of the complex software systems such as compilers directly adopt this implementation method. However, as the state machine application deepens, the constructed state machine becomes more and more complex. This method also begins to face various severe challenges. The biggest headache is that if there are many states in the state machine, or the Transition Relationship between States is very complex. The state machine constructed by using the switch statement is not maintained.
  
   Iii. Automatic Generation of State Machine

Writing state machines for practical software systems is not very easy, especially when the state machines are complex, many programmers with similar experiences often refer to it as "uncreative" because they need to devote a lot of time and energy to how to manage various states in the state machine, rather than the running logic of the program itself. As a general software design model, the state machines of various software systems certainly have some commonalities. Therefore, people begin to develop some tools to automatically generate framework code of finite state machines, in Linux, there is a good choice -- FSME (Finite State Machine Editor ).
  
Visual FSME
Javascript: if (this. width> screen. width-600) this. style. width = screen. width-600; ">
FSME is a Finite State Machine Tool Based on Qt. It allows users to model the state machine required by the program in a graphical manner, it can also automatically generate the state machine Framework Code implemented in C ++ or Python. The following describes how to use FSME to automatically generate the state machine code required by the program.
  
3.1 state machine Modeling
Run the fsme command to start the state machine editor, and then click "New" on the toolbar to create a New state machine. In FSME, there are five basic elements used to build a State machine: Event, Input, Output, State, and Transition ), you can find four of them in the tree list on the left of the interface.
  
State Modeling
Select "States" from the tree list on the left of the FSME interface, and press the Insert key on the keyboard to Insert a new state, enter the Name of the status in the "Name" text box in the lower-right corner, and click the position of the status in the drawing area in the upper-right corner. A new status is created. You can add all the States required by the state machine in the same way, as shown in figure 3.
  
State Modeling
Javascript: if (this. width> screen. width-600) this. style. width = screen. width-600; ">
Event Modeling
  
Select "Events" in the tree list on the left of the FSME interface, and press the Insert key on the keyboard to add a new event, enter the event Name in the "Name" text box in the lower-right corner and click "Apply". A new event is created. You can add all the events required by the state machine in the same way, as shown in figure 4.
  
Event Modeling
Javascript: if (this. width> screen. width-600) this. style. width = screen. width-600; ">
Conversion Modeling
  
State transition is the most important part in the modeling process. It defines how a State in a finite state machine switches to another State. For example, if a Close event is generated when the state machine used to control the city gate is in the Opened State, the current state of the state machine is switched to the Closed state, such a complete process can be described in the state machine model using a closeDoor conversion.
  
To add such a conversion in FSME, you must first select the "Opened" item under "States" in the tree list on the left of the interface, press the Insert key on the keyboard to add a new conversion, and then enter the converted Name "closeDoor" in the "Name" text box in the lower right corner ", enter "Close" in the "Condition" text box to indicate that the Condition for triggering the conversion is that the event Close is generated, in the "Target" drop-down box, select "Closed" to indicate that the state machine will be switched to the Closed state after the conversion, and then click "Apply, A new state transition relationship is defined, as shown in Figure 5. You can add all the transformations required by the state machine in the same way.
  
Conversion Modeling
Javascript: if (this. width> screen. width-600) this. style. width = screen. width-600; ">
3.2 "generate the state machine framework
Using FSME not only enables visual state machine modeling, but also automatically generates a state machine framework implemented in C ++ or Python Based on the obtained model. First, select the "Root" item in the tree list on the left of the FSME interface, and then enter the Name of the state machine "DoorFSM" in the "Name" text box in the lower right corner ", select "Opened" as the initialization State of the State machine from the "Initial State" drop-down list, as shown in figure 6.
  
Set initial attributes
Javascript: if (this. width> screen. width-600) this. style. width = screen. width-600; ">
After saving the state machine model as the door. fsm file, run the following command to generate a header file containing the state machine definition:
[Xiaowp @ linuxgam "code] $ fsmc door. fsm-d-o DoorFSM.
You can also generate the Framework Code that includes the state machine implementation:
[Xiaowp @ linuxgam code] $ fsmc door. fsm-d-impl DoorFSM. h-o DoorFSM. cpp
  
To verify the generated state machine, you only need to manually write a piece of code for testing:
/*
* TestFSM. cpp
* Test the generated state machine framework.
*/
  
# Include "DoorFSM. h"
  
Int main ()
{
DoorFSM door;
Door. A (DoorFSM: Close );
Door. A (DoorFSM: Lock );
Door. A (DoorFSM: Unlock );
Door. A (DoorFSM: Open );
}
The finite state machine is driven by events. In the state machine framework code generated by FSME, method A () can be used to send corresponding events to the state machine, this provides the "power" required for the normal operation of the state machine ". The state machine is responsible for maintaining an event queue within it. All arriving events will be placed in the event queue for waiting, so that they will be processed in order of arrival. When processing each arriving event, the state machine checks whether the conversion conditions corresponding to the status are met based on the current status, if yes, the corresponding state conversion process is activated.
  
Use the following command to compile the generated state machine framework and test code into an executable file:
[Xiaowp @ linuxgam code] $ g ++ DoorFSM. cpp TestFSM. cpp-o fsm
  
Since the-d option was used to generate the state machine code using the fsmc command, the generated state machine framework contains some debugging information, this includes the activation events during each State Transition in the state machine, the status before the transition, the status after the transition, and so on, as shown below:
  
[Xiaowp @ linuxgam code] $./fsm
DoorFSM: event: 'close'
DoorFSM: state: 'opened'
DoorFSM: transition: 'closedoor'
DoorFSM: new state: 'closed'
DoorFSM: event: 'lock'
DoorFSM: state: 'closed'
DoorFSM: transition: 'lockdoor'
DoorFSM: new state: 'locked'
DoorFSM: event: 'unlock'
DoorFSM: state: 'locked'
DoorFSM: transition: 'unlockdoor'
DoorFSM: new state: 'unlocked'
DoorFSM: event: 'Open'
DoorFSM: state: 'unlocked'
DoorFSM: transition: 'opendoor'
DoorFSM: new state: 'opened'
  
3.3 custom state machine
The current state machine has been able to respond to various external events and adjust its current state as appropriate, that is, it has implemented the function of the state machine engine, the next step is to customize according to the specific needs of the application and add the processing logic related to the software system to the state machine. In FSME, operations related to specific applications are called outputs. They are actually virtual functions that require specific implementation, the automatically generated state machine engine is responsible for calling them when they enter or exit a certain state.
  
Take the state machine that controls the city gate as an example. Suppose we want to add some processing logic when entering every State. First select the "Outputs" item in the tree list on the left of the FSME interface, and then press the Insert key on the keyboard to add a new output, enter a Name in the "Name" text box in the lower-right corner, and click "Apply". A new output is created, as shown in figure 7. You can add all the output required by the state machine in the same way.
  
Add output
Javascript: if (this. width> screen. width-600) this. style. width = screen. width-600; ">
After all outputs are defined, You can bind the corresponding output for each State in the state machine. First, select the corresponding status from the "States" item on the left of the FSME interface, and then select the output corresponding to the status from the "Available" list box in the lower right corner, click <to add it to the In list, as shown In figure 8. The same method can be used to set the corresponding output for all States In the state machine. The same State can have multiple outputs. The output In the In list will be called when it enters this state, the output In the Out list is called when you exit this status. The order of output calls is the same as that In or Out list.
  
Figure 8 Status setting output
Javascript: if (this. width> screen. width-600) this. style. width = screen. width-600; ">
Because the state machine model has been modified, we need to re-generate the frame code of the state machine, but this time we do not need to add the-d parameter:
  
[Xiaowp @ linuxgam "code] $ fsmc door. fsm-o DoorFSM. h
[Xiaowp @ linuxgam code] $ fsmc door. fsm-d-impl DoorFSM. h-o DoorFSM. cpp
  
We have added four outputs in the new State Machine Model: enterOpend, enterClosed, enterLocked, and enterUnlocked. Therefore, the generated class DoorFSM contains the following pure virtual functions:
  
Virtual void enterOpened () = 0;
Virtual void enterLocked () = 0;
Virtual void enterUnlocked () = 0;
Virtual void enterClosed () = 0;
Obviously, the generated state machine framework cannot be directly compiled at this time. We must derive a subclass from the class DoorFSM and provide specific implementation of these pure virtual functions:
/*
* DoorFSMLogic. h
* State machine control logic header file
*/
# Include "DoorFSM. h"
  
Class DoorFSMLogic: public DoorFSM
{
  
Protected:
Virtual void enterOpened ();
Virtual void enterLocked ();
Virtual void enterUnlocked ();
Virtual void enterClosed ();
};
  
As mentioned above, these functions actually represent the processing logic of the application system. As an example, we simply output some tips:
/*
* DoorFSMLogic. cpp
* State machine control logic implementation file
*/
# Include "DoorFSMLogic. h"
# Include
  
Void DoorFSMLogic: enterOpened ()
{
Std: cout <"Enter Opened state." <std: endl;
}
  
Void DoorFSMLogic: enterClosed ()
{
Std: cout <"Enter Closed state." <std: endl;
}
  
Void DoorFSMLogic: enterLocked ()
{
Std: cout <"Enter Locked state." <std: endl;
}
  
Void DoorFSMLogic: enterUnlocked ()
{
Std: cout <"Enter Unlocked state." <std: endl;
}
Similarly, to verify the generated state machine, we also need to manually write a test code:
/*
* TestFSM. cpp
* Test the state machine logic.
*/
# Include "DoorFSMLogic. h"
  
Int main ()
{
DoorFSMLogic door;
Door. A (DoorFSM: Close );
Door. A (DoorFSM: Lock );
Door. A (DoorFSM: Unlock );
Door. A (DoorFSM: Open );
}
Use the following command to compile the generated state machine framework and test code into an executable file:
[Xiaowp @ linuxgam code] $ g ++ DoorFSM. cpp DoorFSMLogic. cpp TestLogic. cpp-o logic
  
The running result is as follows:
[Xiaowp @ linuxgam code] $./logic
Enter Closed state.
Enter Locked state.
Enter Unlocked state.
Enter Opened state.
  
   Iv. Summary

In an object-oriented software system, some objects have very complex lifecycles. Using finite state machines is the best way to describe such objects. As a software design model, although the concept of a finite state machine is not complex, it is not difficult to implement it. However, when the model of a state machine is complex to a certain extent, it brings difficulties in implementation and maintenance. In Linux, FSME is a visual Finite State Machine modeling tool that supports automatic generation of state machine framework code. With it, you can build an application system based on finite state machine more easily.
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.