[Home]

State Machine Objects - SMO
For Microsoft Visual C++
SMO Documentation 1.0
©1998 KG Computer Services

  1. Introduction

State Machine Objects is a very simple and powerful C++ class library that assists in the creation of user defined state machines. There are two parts to SMO:

  1. The SMO class library
  2. The SMO Generator (a simple CASE tool).

The CASE tool is simply a compiler that takes a state machine definition source file and outputs a C++ include file with the "hpp" extension. This file in turn is included in the C++ source file that contains the user defined state machine classes and member functions. Note that the generator and language of SMO have never been fully developed or tested, but the SMO class library has.

This language makes it very easy to maintain and also to document the behavior of a state machine.

The SMO Class Library consists of two files: 1) smo.cpp and 2) smo.hpp. The user links with the object code of smo.cpp and includes smo.hpp in their own source code.

  1. SMO Definition Language

In the syntax that follows, anything in between square brackets [ ] is optional.

SMO Definition Syntax
MACHINE machine name

{

STATE state name

{

[ENTER_INIT]

[ENTER]

[EXIT]

[SUPERSTATE machine name]

event [ --> state name | SAME | EOM [,NoAction] ]

etc.

DEFAULT [ --> state name | SAME | EOM [,NoAction] ]

}

}

 

State Machine Objects Definition Language

MACHINE <machine name> { }

Syntax Explanation
CONSTRUCT

(optional)

Causes the constructor for the machine to be executed upon creating this machine object. The name of the constructor will be the machine name provided by the user.
DESTRUCT

(optional)

Causes the destructor for the machine to be executed upon deleting this machine object. The name of the destructor will be the machine name provided by the user.

 

State Machine Objects Definition Language

STATE <state name> { }

Syntax Explanation
ENTER_INIT

(optional)

Execute the member function enter_init() of the state upon transitioning into this state for the very first time for the life of the machine object. The generator declares enter_init() in the state declaration within the generated header file. If the user does not specify ENTER_INIT then the generator will not create the member function declaration for the state in the header file. In this case a default enter_init() virtual function in the base class SmoState will be executed, which has no effect.
ENTER

(optional)

Execute the member function enter() of the state upon transitioning into this state from any other state except for this state. The generator declares enter() in the state declaration within the generated header file. If the user does not specify ENTER then the generator will not create the member function declaration for the state in the header file. In this case a default enter() virtual function in the base class SmoState will be executed, which has no effect.
EXIT

(optional)

Execute the member function exit() of the state upon transitioning out of this state to any other state except for this state. The generator declares exit() in the state declaration within the generated header file. If the user does not specify EXIT then the generator will not create the member function declaration for the state in the header file. In this case a default exit() virtual function in the base class SmoState will be executed, which has no effect.
SUPERSTATE machine name This directive causes the state to become a "Super State", which is a state that upon entering it from any state, calls a machine object, executing it as a machine within a state. This type of state does not have a ‘process_input()’ method associated with it. The input is all handled in the machine that is called from the state. Upon returning from calling the machine, the input for the super state is taken from the return value of the called machine. The return value is a standard smo event object type. It is the last event that occurred in the called machine before it ended.
Event[[ --> state name | SAME | EOM [,NoAction] ] event’ is the message that is de-queued from the state machine input queue as a message, which may contain parameter data. The event name is used to define in the generated hpp file a member function of the same name with Action appended to it. For example, if the event is called "ClassKey" then the member action function will be generated as "ClassKeyAction".

-->’ indicates the transition that the event on the left side is driving. ‘state name‘ is the name of the state which is being transition to. Optionally, this can be the predefined state name ‘SAME’, which means to transition into the same (this current) state. The predefined state ‘EOM’ means to exit this state machine, and return to the caller the last event to occur.

The optional [,NoAction] can be used to prevent the SMO compiler from generating a corresponding action member function for this state class. In this case the virtual base class action member SmoTrans::action() is called, which does nothing.

Note that everything after the event name is optional. If no state name is provided to transition to then it means that the user’s action method will have a return value that indicates the state to goto next. This provides for a very powerful self modifying state machine.

You must define at least one event in the state, and you may define as many events as you need.

DEFAULT [ --> state name | SAME | EOM [,NoAction] ] Predefined event DEFAULT. The DEFAULT event will be generated for all events that may occur in this state that are not defined in the state class.

-->’ indicates the transition that the event on the left side is driving. ‘state name‘ is the name of the state which is being transition to. Optionally this can be the predefined keyword ‘SAME’, which means to transition into the same (this current) state.

The optional [,NoAction] can be used to prevent the SMO compiler from generating a corresponding action member in the state class.

Note that everything after the event name is optional. If no state name is provided to transition to then it means that the user’s action method will have a return value that indicates the state to goto next. This provides for a very powerful self modifying state machine.

DEFAULT must be included at the end of each state definition.

 

 

Multi-Mode Lane (subset) Example:

MACHINE vehicle

{

CONSTRUCTOR

DESTRUCTOR

STATE open_idle

{

ANY_CLASSIFICATION-->classed

AVI_PAID -->avi

AUTO_AVI_PAID -->paid

COIN -->avi

ENTRY_LOOP_ON -->SAME

EXIT_LOOP_OFF -->SAME

DEFAULT -->SAME

}

STATE classed

{

ANY_MOP -->paid

AVI_PAID -->avi

AUTO_AVI_PAID -->paid

COIN -->coin

ENTRY_LOOP_ON -->SAME

EXIT_LOOP_OFF -->open_idle

CANCEL_KEY -->open_idle

DEFAULT -->SAME

}

STATE paid

{

ENTER // start trx timer

EXIT // stop trx timer

EXIT_LOOP_OFF -->open_idle

TIMEOUT -->open_idle

RESET_KEY -->open_idle

RECEIPT -->receipt

CANCEL_KEY -->open_idle

ENTRY_LOOP_ON -->SAME

DEFAULT -->SAME

}

}

 

 

This high level file will be processed by the state machine objects generator and the output will be an hpp include file (e.g., vehicle.hpp). This include file is then used in combination with a corresponding user created cpp source file for the creation of a state machine. Even without the generator, programmers can create the hpp file themselves. The smo.cpp and smo.hpp files contain the base classes and methods that are generic for all state machines and which are encapsulated within the generated user classes. The hpp file inherits the classes and base data and methods from the smo.cpp and smo.hpp files. This is a very simple, powerful and safe way to implement a state machine system.

Super State Machine model

wpe1.jpg (24744 bytes)

 

Walk Through

Event 1 is an event produced while machine X is in state A, and this event drives the machine into the super state D. Super state D contains a machine within it, machine Y. So, when machine X transitions from state A to state D, and machine X is fed another event from the user code, the event is passed to machine Y. Machine Y must already exist and have been initialized. Machine Y is just a regular state machine, nothing special about it. The active state of machine Y will receive the input event from the user code, not state D. State D in this sense is acting like a broker for machine Y, passing the user events into the active state of machine Y. This is how input events are passed from one machine to another. It is handled automatically by the base classes of SMO. All the user code does is call the process_input() method for the state machine they are working with. The process_input() method in class SmoMachine is not virtual and can not be overridden by the user sub-classing it. The process input method is the heart of the SMO class library; it is the brains, the engine of SMO.

When the user called process_input() for machine X while in super state D, the event passed into the function will go directly into machine Y, since machine Y is defined as the super state machine within state D. So the active state of machine Y will be fed the event passed in from the user code instead being fed to state D directly. This interception occurs right at the top of the SmoMachine::process_input() method. The instance of process_input()for machine X calls the instance of process_input() for machine Y.

Machine Y now handles the event on behalf of machine X, and transitions into whatever states it may have. Eventually machine Y must give up control of the event handling and return to the calling machine, X. This is illustrated by events 2 and 3. In machine Y these two events occurring in certain states will cause the machine to end it’s reign and give control back to machine Y. Events 2 and 3 lead out of machine Y and back to states B and C within machine X. The events are defined in machine Y as such: event2à EOM and event3à EOM respectively. The syntax, as explained earlier in this document, means that event 2 and 3 signal the End Of Machine (EOM). Ending machine Y with event 2 causes event 2 to bring machine X to state B, and ending with event 3 transitions out of machine Y and into state C of machine X. It is quite simple, elegant, and very powerful.

Event 4 in the super state shows a very interesting transition. It means that event 4 in a certain state within machine Y caused the machine to end, but the "goto" state for the transition in state D of machine X is defined to go back to the super state D.

What is even more interesting is the fact that you can define nested super state machines. For example, machine Y could itself have defined within it a super state that invokes machine Z.

And going one step further, or backward as it may be, machine Z could potentially have within it a super state that encapsulates machine X. This would be considered a recursive super state machine. It is possible, but care must be taken when designing and implementing just a state machine system.

 

To show that state machines are used in the real world, here is an example from the C++ documentation for streams:

Stream States

Each of the circles denotes a stable state. Each of the lines denotes a transition that can occur as the result of a function call that operates on the stream. Five groups of functions can cause state transitions.

Functions in the first three groups are declared in <stdio.h>: