Skip to content

Oscar Software Framework Manual Hierarchical State Machine

Beat Küng edited this page Jul 16, 2012 · 3 revisions

Go to Table of Contents.

Table of Contents

Hierarchical State Machine (hsm)

Target Hardware Resource

Not hardware resource related.

Description

The module provides a hierarchical state machine (hsm) framework. HSM is a well know software design pattern published by Miro Samek.

A stop watch example code is provided as shown is the diagram below. See watch.h/watch.c files in hsm source directory.

Dependencies

None.

Usage

 enum StateMachineEvents {		/* (2) */
 	EVENT1,   
 	EVENT2,
 	EVENT3
 };
 
 const Msg stateMachineMsg[] = {
 	{ EVENT1 },
 	{ EVENT2 },
 	{ EVENT3 }
 };
  1. Declare the main state machine structure. This contains, apart from the member 'Hsm super', the declaration of all states that are going to be used. Optionally, variables that should be accessible from all states can also be declared here.
  2. Enumerate all Events that the state machine is going to handle and put them into a message array so they can be referenced (a pointer to them can be created ).
 HsmCtor((Hsm *)&SM,             /* (1) */
         "StateMachine", 
        (EvtHndlr)StateMachine_top);
 
 StateCtor(&SM.state1,		 /* (2) */
           "State 1",
           &((Hsm *)&SM)->top, 
           (EvtHndlr)StateMachine_State1);
 
 StateCtor(&SM.state2, 
           "State 2",
           &((Hsm *)&SM)->top, 
           (EvtHndlr)StateMachine_State2);
 
 StateCtor(&SM.state2_1,	/* (3) */
           "State 2, Substate 1", 
           &(&SM)->state2, 
           (EvtHndlr)StateMachine_State2_1);
 
 StateCtor(&SM.state2_2, 
           "State 2, Substate 2",
           &(&SM)->state2, 
           (EvtHndlr)StateMachine_State2_2);
 
 HsmOnStart((Hsm *)&SM);       /* (4) */
  
 while(1)	 	       /* (5) */
 {
 	if(<TRIGGER CONDITION FOR EVENT1>)
 		HsmOnEvent(&SM, &stateMachineMsg[EVENT1]);
 	...
 	if(<TRIGGER_CONDITION_FOR_EVENT2>)
 		HsmOnEvent(&SM, &stateMachineMsg[EVENT2]);
 	...
 	if(<TRIGGER_CONDITION_FOR_EVENT3>)
 	 	HsmOnEvent(&SM, &stateMachineMsg[EVENT3]);
 	...
 }
  1. Create the hierarchic state machine and its top (root) state. Also, supply a name for it and the event-handler for the top state (see below).
  2. Register the event handler and names of the first-level states of the state machine. Their parent state is the top state. Each state must supply an event handler function (see below) to which events can be dispatched.
  3. Register the event handler and names of the second-level states of the state machine to their parent state. In our case, we add two substates to state2 (state2_1 and state2_2). In the same way, the state machine can be extended to contain an arbitrary number of hierarchy levels.
  4. Initiate the state machine. This causes the built-in START_EVT to be dispatched.
  5. Trigger events for the state machine depending on external occurences or internal logic in a main loop. Which reactions these events cause is determined by their current state. An event that is not handled by the current state is propagated up to its parent until it reaches the top state.
 Msg const *StateMachine_state1(StateMachine *me, Msg *msg)
 {
 	switch (msg->evt)
 	{
 	case EVENT1:
 		....
 		STATE_TRAN(me, &me->state2_1);		/* (2) */
 		return 0;
 	case EVENT3:
 		....
 		return 0;
 	}
 	return msg;
 }
 
 Msg const *StateMachine_state2(StateMachine *me, Msg *msg)
 {
 	switch (msg->evt)
 	{
 	case EVENT2:
 		....
 		return 0;
 	case EVENT3:
 		....
 		return 0;
 	}
 	return msg;
 }
 
 Msg const *StateMachine_state2_1(StateMachine *me, Msg *msg)
 {
 	switch (msg->evt)			       /* (3) */
 	{
 	case EVENT1:
 		....
 		STATE_TRAN(me, &me->state2_2);
 		return 0;
 	}
 	return msg;
 }
 
 Msg const *StateMachine_state2_2(StateMachine *me, Msg *msg)
 {
 	switch (msg->evt)
 	{
 	case EVENT1:
 		....
 		STATE_TRAN(me, &me->state2_1);
  		return 0;
 	case EVENT2:
 		...
 		STATE_TRAN(me, &me->stage1);
                return 0;
 	}
 	return msg;
 }
  1. Implement the top state event handler. All events that are not handled in another state should be handled here. This includes the START_EVT, which is issued when first starting the state machine. Therefore, a state transition to the actual starting state of the state machine should be initiated here. This is done with the STATE_TRAN macro.
  2. Implement the states, very similar to the top state. You do not have to handle every event in every state; unhandled events are just passed up the chain (to the top). An event is marked as handled by returning 0 in the event handler. Typically, at least some events cause state transitions. Here we are transitioning to the state2_1, which is a substate of stage2, on EVENT1. It is possible to 'switch levels' in the state machine. In our case the state machine will afterwards implicitely be in state2 additionally to state2_1.
  3. Implement the sub-(and/or sub-sub-...-)state event handlers. The only difference here is that unhandled events pass at least another layer of states (state2), where they have a chance to be handled, before reaching the top.
Clone this wiki locally