Simple State Machine
This example shows how to create, configure and use a simple state machine. The full code for this example can be downloaded from here as one single file which is ready to be compiled and linked with the C1 Implementation code (available from here).
The example state machine has 2 states, 1 choice pseudo-state, 5 transitions, 4 actions and 2 guards configured as follows:
Hint: Model it with the free web-based modeling tool. The tool can be accessed from here.
A state machine is represented by its descriptor (the State Machine Descriptor or SMD). The SMD for the example state machine can be created as follows:
#define NSTATES 2 /* The number of states */
#define NCPS 1 /* The number of choice pseudo-states */
#define NTRANS 5 /* The number of transitions */
#define NACTIONS 4 /* The number of state and transition actions */
#define NGUARDS 2 /* The number of transition guards */
FwSmDesc_t smDesc; /* The state machine descriptor (SMD) */
smDesc = FwSmCreate(NSTATES, NCPS, NTRANS, NACTIONS, NGUARDS);
The SMD is configured in three steps. In the first step, the states are added to the SMD:
#define STATE_S1 1 /* The identifier of the first state */
#define STATE_S2 2 /* The identifier of the second state */
#define N_OUT_OF_S1 1 /* The number of transitions out of state S1 */
#define N_OUT_OF_S2 1 /* The number of transitions out of state S2 */
FwSmAddState(smDesc, STATE_S1, N_OUT_OF_S1, NULL, &Action2, NULL, NULL);
FwSmAddState(smDesc, STATE_S2, N_OUT_OF_S2, &Action4, NULL, &Action3, NULL);
Here, Action2 is the pointer to the function which implements the exit action of state S1 and Action3 and Action4 are the pointers to the functions implementing the entry and do actions of state S2. The last argument in the call to FwSmAddState could be used to embed a state machine within state S1 or S2.
In the second configuration step, the choice pseudo-state is added to the SMD:
#define CPS1 1 /* The identifier of the choice pseudo-state */
#define N_OUT_OF_CPS1 2 /* The number of transitions out of the choice-pseudo state */
FwSmAddChoicePseudoState(smDesc, CPS1, N_OUT_OF_CPS1);
Finally, the transitions between the states are defined. Different functions must be used depending on the type of the source and destination of each transition. Here, Guard1, Guard2 and Guard3 are the pointers to the functions which implement the transition guards and Action1 and Action5 are the pointers to the functions which implement the actions on the transition out of the initial pseudo-state and out of state S2.
#define TRIGGER1 1 /* The identifier of the first transition command */
#define TRIGGER2 2 /* The identifier of the second transition command */
FwSmAddTransIpsToSta(smDesc, STATE_S1, &Action1);
FwSmAddTransStaToCps(smDesc, TRIGGER1, STATE_S1, CPS1, NULL, NULL);
FwSmAddTransCpsToSta(smDesc, CPS1, STATE_S1, NULL, &Guard1);
FwSmAddTransCpsToSta(smDesc, CPS1, STATE_S2, NULL, &Guard2);
FwSmAddTransStaToFps(smDesc, TRIGGER2, STATE_S2, &Action2, &Guard2);
The configuration process is now complete. Optionally, a user can ask a state machine to verify the completeness of its own configuration as follows:
if (FwSmCheck(smDesc) != smSuccess)
... // State machine is not correctly configured!
After being configured, the state machine is ready to process transition commands. However, transition commands only have an effect on a state machine if the state machine has been started (a state machine can be started and stopped at any time: when it is stopped, it ignores transition commands). Thus, a commanding sequence for the state machine could be as follows:
FwSmStart(smDesc); /* Start the state machine */
FwSmMakeTrans(smDesc, TRIGGER1); /* Send command TRIGGER1 to the SM */
The Execute command is a pre-defined transition command which causes the do-action of the current state to be executed. Thus, if the state machine is in state S2, its do-action could be executed as follows:
FwSmExecute(smDesc); /* Send Execute command to the SM */
The pseudo-code above uses functions provided by the FW Profile. These functions must be included in the source code. For this example, the following files must be included:
#include "FwProfile/FwSmConstants.h" /* SM Types and Constants */
#include "FwProfile/FwSmDCreate.h" /* SM Creation Functions */
#include "FwProfile/FwSmConfig.h" /* SM Configuration Functions */
#include "FwProfile/FwSmCore.h" /* SM Execution Functions */
State Machine with Embedded State Machine
The previous example is expanded to show how to create, configure and use the state machine in the figure:
Hint: Model it with the free web-based modeling tool. The tool can be accessed from here.
The outer state machine is the one used in the previous example but now its state S2 holds an embedded state machine. The full code for this example can be downloaded from here as one single file which is ready to be compiled and linked with the C1 Implementation code (available from here).
The state machine embedded in state S2 is created as follows (recall that a state machine is represented by its "State Machine Descriptor" or SMD):
#define E_NSTATES 2 /* The number of states */
#define E_NCPS 0 /* The number of choice pseudo-states */
#define E_NTRANS 2 /* The number of transitions */
#define E_NACTIONS 2 /* The number of state and transition actions */
#define E_NGUARDS 0 /* The number of transition guards */
FwSmDesc_t esmDesc; /* The state machine descriptor (SMD) */
esmDesc = FwSmCreate(E_NSTATES, E_NCPS, E_NTRANS, E_NACTIONS, E_NGUARDS);
The SMD of the embedded state machine is configured by defining the states and the state transitions of the embedded state machine:
#define STATE_ES1 1 /* The identifier of the first state */
#define STATE_ES2 2 /* The identifier of the second state */
#define N_OUT_OF_ES1 1 /* The number of transitions out of state S1 */
#define N_OUT_OF_ES2 0 /* The number of transitions out of state S2 */
#define TRIGGER3 3 /* The identifier of the transition command */
FwSmAddState(esmDesc, STATE_ES1, N_OUT_OF_ES1, &Action5, NULL, NULL, NULL);
FwSmAddState(esmDesc, STATE_ES2, N_OUT_OF_ES2, &Action6, NULL, NULL, NULL);
FwSmAddTransIpsToSta(esmDesc, STATE_ES1, NULL);
FwSmAddTransStaToSta(esmDesc, TRIGGER3, STATE_ES1, STATE_ES2, NULL, NULL);
The descriptor esmDesc of the embedded SM is now ready to be embedded in state S2 in the outer state machine. The outer state machine is created and configured as in the first example:
#define NSTATES 2 /* The number of states */
#define NCPS 1 /* The number of choice pseudo-states */
#define NTRANS 5 /* The number of transitions */
#define NACTIONS 4 /* The number of state and transition actions */
#define NGUARDS 2 /* The number of transition guards */
#define STATE_S1 1 /* The identifier of the first state */
#define STATE_S2 2 /* The identifier of the second state */
#define N_OUT_OF_S1 1 /* The number of transitions out of state S1 */
#define N_OUT_OF_S2 1 /* The number of transitions out of state S2 */
#define CPS1 1 /* The identifier of the choice pseudo-state */
#define N_OUT_OF_CPS1 2 /* The number of transitions out of the choice-pseudo state */
#define TRIGGER1 1 /* The identifier of the first transition command */
#define TRIGGER2 2 /* The identifier of the second transition command */
FwSmDesc_t smDesc; /* The state machine descriptor (SMD) */
/* Create the SM descriptor */
smDesc = FwSmCreate(NSTATES, NCPS, NTRANS, NACTIONS, NGUARDS);
/* Configure the states of the SM */
FwSmAddState(smDesc, STATE_S1, N_OUT_OF_S1, NULL, &Action2, NULL, NULL);
FwSmAddState(smDesc, STATE_S2, N_OUT_OF_S2, &Action4, NULL, &Action3, esmDesc);
/* Configure the Choice Pseudo-State of the SM */
FwSmAddChoicePseudoState(smDesc, CPS1, N_OUT_OF_CPS1);
/* Configure the state transitions */
FwSmAddTransIpsToSta(smDesc, STATE_S1, &Action1);
FwSmAddTransStaToCps(smDesc, TRIGGER1, STATE_S1, CPS1, NULL, NULL);
FwSmAddTransCpsToSta(smDesc, CPS1, STATE_S1, NULL, &Guard1);
FwSmAddTransCpsToSta(smDesc, CPS1, STATE_S2, NULL, &Guard2);
FwSmAddTransStaToFps(smDesc, TRIGGER2, STATE_S2, &Action2, &Guard2);
The outer state machine is now ready to process transition commands. However, transition commands only have an effect on a state machine if the state machine has been started (a state machine can be started and stopped at any time: when it is stopped, it ignores transition commands). Thus, a commanding sequence for the state machine could be as follows:
FwSmStart(smDesc); /* Start the state machine */
FwSmMakeTrans(smDesc, TRIGGER1); /* Send command TRIGGER1 to the SM */
FwSmMakeTrans(smDesc, TRIGGER3); /* Send transition command TRIGGER3 to SM */
FwSmMakeTrans(smDesc, TRIGGER2); /* Send transition command TRIGGER2 to SM */
Note that, although transition commands are applied to the outer state machine, they are automatically propagated to the state machine embedded in its state S2.
State Machine Extension Mechanism
This example demonstrates the state machine extension mechanism of the FW Profile. First, a base state machine is created and configured and then a second state machine is derived from the first one. The derived state machine overrides one action of its base state machine but is otherwise a clone of its base. The figure shows a diagram of the two state machines:
The full code for this example can be downloaded from here as one single file which is ready to be compiled and linked with the C1 Implementation code (available from here).
The base state machine is created and configured as follows (recall that a state machine is represented by its "State Machine Descriptor" or SMD):
#define NSTATES 2 /* The number of states */
#define NCPS 0 /* The number of choice pseudo-states */
#define NTRANS 3 /* The number of transitions */
#define NACTIONS 2 /* The number of state and transition actions */
#define NGUARDS 1 /* The number of transition guards */
#define STATE_S1 1 /* The identifier of the first state */
#define STATE_S2 2 /* The identifier of the second state */
#define N_OUT_OF_S1 1 /* The number of transitions out of state S1 */
#define N_OUT_OF_S2 1 /* The number of transitions out of state S2 */
#define TRIGGER1 1 /* The identifier of the first transition command */
#define TRIGGER2 2 /* The identifier of the second transition command */
FwSmDesc_t smDesc; /* The descriptor of the base state machine */
smDesc = FwSmCreate(NSTATES, NCPS, NTRANS, NACTIONS, NGUARDS);
FwSmAddState(smDesc, STATE_S1, N_OUT_OF_S1, &Action1, NULL, NULL, NULL);
FwSmAddState(smDesc, STATE_S2, N_OUT_OF_S2, NULL, &Action2, NULL, NULL);
FwSmAddTransIpsToSta(smDesc, STATE_S1, NULL);
FwSmAddTransStaToSta(smDesc, TRIGGER1, STATE_S1, STATE_S2, NULL, &Guard1);
FwSmAddTransStaToSta(smDesc, TRIGGER2, STATE_S2, STATE_S1, NULL, NULL);
The SMD of the derived state machine is created as follows:
FwSmDesc_t smDescDer; /* The descriptor of the derived state machine */
smDescDer = FwSmCreateDer(smDesc); /* Create derived SMD */
At this point, smDescDer holds the descriptor of a state machine which is a clone of the base state machine smDesc. In this example, we want to override the exit action of state S2 (Action2) with a new action (Action2_Der). This is done as follows:
FwSmOverrideAction(smDescDer, &Action2, &Action2_Der);
Now, the derived state machine smDescDer is identical to its base with the exception of the exit action of state S2 (which is now Action2_Der instead of Action2). Note that guards, as well as actions, can be overridden in a derived state machine (using function FwSmOverrideGuard).