Self-built http://www.ibm.com/developerworks/cn/linux/l-fsmachine/index.html
Author: Xiao wenpeng(Xiaowp@263.net), Free Software enthusiasts
Introduction: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 well-designed state machines can be used to replace part of artificial intelligence.AlgorithmTherefore, 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.
Figure 1 state machine controlling the city gate
When describing finite state machines, States, events, transformations, and actions are common basic concepts.
- Status)An object is a condition in its lifecycle. an object in a specific State must meet certain conditions, execute certain actions, or wait for certain events.
- Event)It refers to things that occupy a certain position in time and space and make sense for the state machine. Events usually cause state changes, prompting the state machine to switch from one state to another.
- Transition)It 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)It refers to the atomic operations that can be executed in the state machine. The so-called Atomic operations mean that they cannot be interrupted by other messages during the running process and must be executed continuously.
Back to Top
2. Manually write a State Machine
Unlike other common design patterns,ProgramWhen you want to add a state machine to your software system, you must write an additional part for logical control.CodeIf the system is complex enough, it is still quite difficult to implement and maintain this part of code. 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 Figure 1:
switch (state) {// Branch case (opened) of the processing status opened: {// execute open (); // check whether there is a closedoor event if (closedoor () {// The current state is converted to closed changestate (closed)} break;} // The Branch case (closed) that processes the closedoor event): {// execute close (); // check whether there is an opendoor event if (opendoor () {// The current status is changed to opened changestate (opened );} // check whether there is a lockdoor event if (lockdoor () {// The current status is converted to locked changestate (locked);} break ;} // handle the status of the locked Branch case (locked): {// execute the lock () action; // check whether there is an unlockdoor event if (unlockdoor ()) {// switch the current status to unlocked changestate (unlocked);} break;} // handle the status of the unlocked Branch case (unlocked): {// execute the action unlock (); // check whether there is a lockdoor event if (lockdoor () {// The current status is converted to locked changestate (locked)} // check whether there is an opendoor event if (opendoor ()) {// The current status is converted 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) {// Branch case (opened) of the opened processing status: {// open (); // check whether there are events that trigger state conversion to generate if (checkstatechange () {// convert the state machine into mstatechange ();} break ;} // handle the Branch case (closed) of the Status closed: {// execute the close () action; // check whether any event that triggers the status transition generates if (checkstatechange ()) {// convert the status of the state machine into mstatechange ();} break;} // handle the status locked Branch case (locked): {// execute the action lock (); // check whether there are events that trigger state conversion to generate if (checkstatechange () {// convert the state machine into mstatechange ();} break ;} // handle the status of the unlocked Branch case (unlocked): {// execute the action Lock unlock (); // check whether there are events that trigger status conversion to generate if (checkstatechange ()) {// convert the status of the state machine into 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.
Back to Top
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 ).
Figure 2 visual fsme
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 in the program by taking the state machine at the Gate 1 as an example.
3.1 state machine Modeling
first 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, press the Insert key on the keyboard to insert a new status, and then enter the status name in the "name" text box in the lower right corner, in the drawing area in the upper-right corner, click the position where the state is to be placed. A new state is created. You can add all the States required by the state machine in the same way, as shown in figure 3.
Figure 3 state modeling
- event modeling
select "events" from the tree list on the left of the fsme interface, 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, and then 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.
figure 4 event modeling
- transformation 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.
Figure 5 conversion modeling
3.2 generate a 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.
Figure 6 set initial attributes
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. h |
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.
Figure 7 add output
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
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 <iostream> 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. |
Download the Code involved in this article: code.zip
Back to Top
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.
References
- Wikipedia.
- state machine is an important component of UML, Robert C. in his Article UML Tutorial: finite state machines, Martin describes how to use the UML language to model a state machine, you can find the break at http://www.objectmentor.com/resources/articles/umlfsm.htm.
- fsme is the next QT-based state machine modeling tool in Linux. It can automatically generate state machine framework code and support both C ++ and Python languages.
- mongosm is also a state machine modeling tool running in Linux. It not only provides a visual state machine editor, but also simulates the generated state machine in real time. You can use the website http://mongosm.sourceforge.net/to obtain more information about mongosm.