Overview
The state machine is one of the fundamental programming patterns that are most commonly used. This approach breaks down the design into a series of finite steps called "states" that perform some narrowly defined actions. Every state can change to another as a consequence of incoming stimuli also called events or signals. This elemental mechanism allows designers to solve complex engineering problems in a very straightforward way. Knowing the importance of this approach in the development of embedded applications, the OS adopts this design pattern as a kernel extension.
In an effort to maximize efficiency and minimize complexity, the extension implements the basic features of the Harel statecharts to represent hierarchical state machines. These features form a proper subset that approaches in a very minimalist way, some of the specifications of the UML statecharts, including:
- Nested states with proper handling of group transitions and group reactions.
- Guaranteed execution of entry/exit actions upon entering/exiting states.
- Straightforward transitions and guards.
In addition to this, the provided implementation also features a powerful coding abstraction including transition tables and timeout signals, allowing to build scalable solutions from simple flat state-machines to complex statecharts.
The provided approach
In QuarkTS, a state machine must be instantiated with an object of type qSM_t. States are represented as instances of the qSM_State_t object.
fsmdesign
FSM module design One important attribute of the qSM_State_t object is the callback function, which is used to describe the behavior specific to the state. Also, there is a pointer to the parent state to define the nesting of the state and its place in the hierarchical topology. As shown in figure above, a state machine consists of a least one state, the "top-level" state. So concrete state machine are built by adding an arbitrary number of states and defining callback functions. The only purpose of the top state is to provide the root of the hierarchy, so that the highest level can return to the top as their parent state.
Setting up a state machine
Like any other OS object, a Finite State Machine (FSM) must be explicitly initialized before it can be used. The qStateMachine_Setup() API initializes the instance, sets the callback for the top state, sets the initial state and the surrounding callback function.
Subscribing states and defining callbacks
State machines are constructed by composition, therefore, the topology of a state machine is determined upon construction. In this FSM implementation, there is no distinction between composite states(states containing substates) and leaf states. All states are potentially composite. The API qStateMachine_StateSubscribe() should be used to initialize the state and define its position in the topology.
A state callback-functions takes a qSM_Handler_t object as input argument and returns a qSM_Status_t value. An example is shown in the following code snippet:
}
qSM_Status_t
This enumeration defines the built-in state-execution status values that can be used as return value ...
Definition qfsm.h:175
@ qSM_STATUS_EXIT_SUCCESS
Definition qfsm.h:179
The callback argument to handle the state-machine dynamics and provide execution information....
Definition qfsm.h:232
The state callback handler: performing transitions and retrieving data
Because callback functions are methods derived from the state-machine object, they have direct access to some attributes via the qSM_Handler_t argument. The usage of this object it's required to make the FSM moves between states and additionally get extra data. The provided attributes are:
- qSM_Handler_t::NextState : Desired next state. The application writer should change this field to another state to produce a state transition in the next FSM's cycle. Changing this field will only take effect when the state is executed under user custom-defined signals or in the absence of signals QSM_SIGNAL_NONE.
- qSM_Handler_t::StartState : Desired nested initial state (substate). The application writer should change this field to set the initial transition if the current state is a parent(or composite state). Changing this field attribute only takes effect when the state is executed under the QSM_SIGNAL_START signal.
- qSM_Handler_t::Signal (read-only) : Received signal. Can have any of the following values:
- qSM_Handler_t::SignalData (read-only) : Pointer to the data associated with the signal. For internal signals, including timeout signals, its value will be
NULL
.
- qSM_Handler_t::TransitionHistory : Use this option if the transition is to a composite state. This attribute defines how the story should be handled. If this field is not established, qSM_TRANSITION_NO_HISTORY is assumed. The possible values for this attribute are:
- qSM_TRANSITION_NO_HISTORY : History is not preserved. Composite states will start according to their default transition.
- qSM_TRANSITION_SHALLOW_HISTORY : History will be kept to allow the return to only the top-most sub-state of the most recent state configuration, which is entered using the default entry rule.
- qSM_TRANSITION_DEEP_HISTORY : History will be kept to allow full state configuration of the most recent visit to the containing region.
- qSM_Handler_t::Status (read-only) : The exit(or return) status of the last state. Should be used in the Surrounding callback to perform the corresponding actions for every value. On states callback will take the value qSM_STATUS_NULL
- qSM_Handler_t::machine (read-only) : A generic pointer to the container state machine.
- qSM_Handler_t::Data (read-only) : State-machine associated data. If the FSM is running as a task, the associated event data can be queried through this field. (here, a cast to qEvent_t is mandatory).
- qSM_Handler_t::StateData (read-only) : State associated data. Storage-pointer.
Within the callback function of every state, only one level of dispatching (based on the signal) is necessary. Typically this is archived using a single-level switch statement. Callback functions communicate with the state machine engine through the qSM_Handler_t and the return value of type qSM_Status_t.
The semantic is simple, if a signal is processed, the callback functions returns the status value qSM_STATUS_SIGNAL_HANDLED. Otherwise, it throws the signal for further processing by higher-level states. Also, this returning mechanism can be used to handle exceptions by using the surrounding callback.
Entry/Exit actions and default transitions are also implemented inside the callback function in the response to pre-defined signals. QSM_SIGNAL_ENTRY, QSM_SIGNAL_EXIT and QSM_SIGNAL_START. The state machine generates and dispatches these signals to appropriate handlers upon state transitions.
The example below shows what a status callback should look like including the use of the handler.
break;
break;
break;
case USER_DEFINED_SIGNAL :
break;
default:
break;
}
}
#define QSM_SIGNAL_ENTRY
Built-in signal to indicate if the current state has just entered from another state.
Definition qfsm.h:82
#define QSM_SIGNAL_EXIT
Built-in signal to indicate if the current state has just exit to another state.
Definition qfsm.h:76
#define QSM_SIGNAL_START
Built-in signal that can be used to set a nested initial-transition (aka default transition) by writi...
Definition qfsm.h:70
const qSM_SigId_t Signal
Definition qfsm.h:240
void * NextState
Definition qfsm.h:234
As shown above, the return value represents the exit status of the state, and it can be handled with an additional surrounding callback function \( S_u \) established at the moment of the FSM setup. The values allowed to return are listed below.
To code initial transitions, the application writer should catch the QSM_SIGNAL_START, perform the required actions and then designate the target sub-state by assigning the StartState
attribute of the qSM_Handler_t argument. Regular transitions are coded in a very similar way, except that here, you catch the custom-defined signal and then assign the NextState
attribute of the qSM_Handler_t argument. The developer is free to write and control state transitions. Transitions are only allowed under the availability of user custom-defined signals. Regular transitions are not allowed at an entry point (QSM_SIGNAL_ENTRY), exit point (QSM_SIGNAL_EXIT), or a start point (QSM_SIGNAL_START).
- Note
- User should not target the top state in a transition and use it as transition source either. The only customizable aspect of the top state is the initial transition.
The surrounding callback
It is a checkpoint before and after each state executes its activities through its state callback. The behavior of this surrounding callback must be defined by the programmer.
surrounding
Surrounding callback invocation after and before the current state The surrounding callback \( S_u \) invocation occurs after and before the current state \( P \). When the surrounding callback is executed, indicates its own checkpoint through the Status
attribute of the qSM_Handler_t argument. Unlike a state callback, the surrounding callback should not return anything, thus, the callback should be written as:
break;
break;
break;
break;
case 5:
break;
default:
break
}
}
@ qSM_STATUS_BEFORE_ANY
Definition qfsm.h:176
@ qSM_STATUS_EXIT_FAILURE
Definition qfsm.h:178
@ qSM_STATUS_SIGNAL_HANDLED
Definition qfsm.h:180
const qSM_Status_t Status
Definition qfsm.h:241
As you can see in the example below, the surrounding execution case its verified through the FSM handle by reading the Status
field.
Adding a state machine as a task
The best strategy to run a FSM is delegating it to a task. For this, the qOS_Add_StateMachineTask() API should be used. Here, the task does not have a specific callback, instead, it will evaluate the active state of the FSM, and later, all the other possible states in response to events that mark their own transition. The task will be scheduled to run every t
seconds in qPeriodic mode.
By using this API, the kernel will take care of the FSM by itself, so the usage of qStateMachine_Run() can be omitted.
Now that a task is running a dedicated state machine, the specific task event-information can be obtained in every state callback through the Data
field of the qSM_Handler_t argument.
Check the example below:
break;
break;
default:
break;
break;
break;
default: break;
}
break;
}
}
@ byQueueCount
When the element-count of the attached queue reaches the specified value. A pointer to the queue will...
Definition qtasks.h:83
@ byNotificationSimple
When the execution chain does, according to a requirement of asynchronous notification event prompted...
Definition qtasks.h:65
@ byTimeElapsed
When the time specified for the task elapsed.
Definition qtasks.h:51
The task argument with all the regarding information of the task execution.
Definition qtasks.h:162
qTrigger_t Trigger
This member indicates the event source that triggers the task execution. Possible values are describe...
Definition qtasks.h:180
const void * Data
Definition qfsm.h:237
A demonstrative example for a FSM
In this example, one press of the button turns on the LED, a second push of the button will make the LED blink and if the button is pressed again, the LED will turn off. Also, our system must turn off the LED after a period of inactivity. If the button hasn't been pressed in the last 10 seconds, the LED will turn off.
fsmled
Flat FSM example with three states To start the implementation, let's define the necessary global variables...
A state object.
Definition qfsm.h:346
A FSM(Finite State Machine) object.
Definition qfsm.h:387
A task node object.
Definition qtasks.h:268
Then, we define our states as the flow diagram shown in the figure above.
BSP_LED_OFF();
break;
break;
default:
if ( BUTTON_PRESSED ) {
}
break;
}
}
BSP_LED_ON();
break;
break;
default:
}
if ( BUTTON_PRESSED ) {
}
break;
}
}
break;
break;
default:
}
BSP_LED_TOGGLE();
}
break;
}
}
qBool_t qSTimer_Expired(const qSTimer_t *const t)
Non-Blocking STimer check.
Definition qstimers.c:55
qBool_t qSTimer_Set(qSTimer_t *const t, const qTime_t tTime)
Set the expiration time for a STimer. On success, the STimer gets armed immediately.
Definition qstimers.c:22
qBool_t qSTimer_FreeRun(qSTimer_t *const t, const qTime_t tTime)
Non-Blocking STimer check with automatic arming.
Definition qstimers.c:36
A STimer(Software Timer) object.
Definition qstimers.h:32
Finally, we add the task to the scheduling scheme running the dedicated state machine. Remember that you must set up the scheduler before adding a task to the scheduling scheme.
State_LEDOff_Callback , NULL , NULL );
State_LEDOn_Callback , NULL , NULL );
State_LEDBlink_Callback , NULL , NULL );
qBool_t qStateMachine_StateSubscribe(qSM_t *const m, qSM_State_t *const s, qSM_State_t *const parent, qSM_StateCallback_t sFcn, qSM_State_t *const init, void *pData)
This function subscribes the FSM instance to a specific state with an associated callback function.
Definition qfsm.c:565
qBool_t qStateMachine_Setup(qSM_t *const m, qSM_StateCallback_t topFcn, qSM_State_t *const init, qSM_SurroundingCallback_t sFcn, void *pData)
Initializes a Finite State Machine (FSM).
Definition qfsm.c:525
#define qHigh_Priority
A macro directive to indicate the highest priority level.
Definition qkernel.h:77
qBool_t qOS_Add_StateMachineTask(qTask_t *const Task, qSM_t *m, const qPriority_t p, const qTime_t t, const qState_t init, void *arg)
Add a task to the scheduling scheme running a dedicated state-machine. The task is scheduled to run e...
Definition qkernel.c:490
#define qEnabled
A state value that enables a task.
Definition qtypes.h:167
Sending signals
To communicate within and between state machines or even other contexts, use signals. A signal is a simple value that can be used to abstract an incoming event. In the receiving state machine, a queue or an exclusion variable receives the signal and holds it until the state machine can evaluate it.
When coding state machines, the application writer can benefit from this simple event-abstraction mechanism. On the one hand, there would be a more uniform programming when writing state callbacks and on the other hand, the communication of the state machine from other contexts becomes easier.
To send a signal to a state machine, use the qStateMachine_SendSignal() API. This API can manage their delivery to one of these possible destinations: an exclusion variable or a signal queue:
- An exclusion variable its a variable with an important distinction, it can only be written if it is empty. The empty situation only happens, if the engine has already propagated the signal within the state machine. If the signal has not yet propagated, the signal sending cannot be carried out.
- When a signal queue is used, the signal is put into a FIFO structure and the engine takes care of dispatching the signal in an orderly manner. The only situation where the signal cannot be delivered is if the queue is full. This is the preferred destination, as long as there is a previously installed signal queue.
- Note
- If the signal-queue its available, the qStateMachine_SendSignal() will always select it as destination.
- Warning
- If a state machine, a task, or another context sends a signal to a full queue, a queue overflow occurs. The result of the queue overflow it that the state machine drops the new signal.
Installing a signal queue
A state machine can have a FIFO queue to allow the delivery of signals from another contexts. If the signal queue is installed, the state-machine engine constantly monitors the queue for available signals. The engine then propagates the signal through the hierarchy until it is processed. To enable this functionality in your state machine, the queue must be installed by using the qStateMachine_InstallSignalQueue() API.
The install operation should be performed after both, the queue and the FSM are correctly initialized by using qQueue_Setup() and qStateMachine_Setup() respectively.
- Note
- Make sure that queues are enabled in the
qconfig.h
header file
- Attention
- When configuring a signal queue with qQueue_Setup(), remember to size it based on the type qSM_Signal_t.
- Note
- If the state machines its delegated to a task, make sure to install the queue prior to setting up the task. In this way, a kernel connection can be performed between the FSM signal queue and the FSM-task, allowing the OS to catch signals to produce a task event, this prevents the wait of the task for the specified period, resulting in faster handling of incoming signals.
Using a transition table
In this approach, the FSM is coded in tables with the outgoing transitions of every state, where each entry relates signals, actions and the target state. This is an elegant method to translate the FSM to actual implementation as the handling for every state and event combination is encapsulated in the table.
Here, the application writer gets a quick picture of the FSM and the embedded software maintenance is also much more under control. A transition table should be explicitly installed in the target state with the corresponding entries, an n-sized array of qSM_Transition_t elements following the layout described below:
The API qStateMachine_Set_StateTransitions(), should be used to perform the transition table installation to a specific state.
Transition table layout for a state
Signal Id | Signal action/guard | Target state | History mode | Signal data |
Signal1 | NULL | StateB | 0 | NULL |
Signal3 | DoOnSignal3 | StateD | 0 | &sig3data |
... | ... | ... | ... | ... |
Signal6 | NULL | StateA | 0 | NULL |
Caveats:
- State transitions are not limited to the specification of the transition table. A state callback owns the higher precedence to change a state. The application writer can use both, a transition table and direct
NextState
field manipulation in state callbacks to perform a transition to the FSM.
- Special care is required when the table grows very large, that is, when there are many invalid state/event combinations, leading to a waste of memory. There is also a memory penalty as the number of states and events grows. The application writer needs to accurately account for this during the initial design. A statechart pattern can be used to improve the design and reduce the number of transition entries.
- The user is responsible for defining the transitions according to the topology of the state machine. Undefined behaviors can occur if the topology is broken with poorly defined transitions.
Signal actions and guards
Transition tables allow the usage of this feature. When an event-signal is received from the queue, the signal-action, if available, is evaluated before the transition is triggered. This action is user-defined and should be coded as a function that takes a qSM_Handler_t object and returns a value of type qBool_t.
}
qUINT8_t qBool_t
A type to instantiate an OS boolean variable.
Definition qtypes.h:139
#define qTrue
A boolean value that represents true/success/On or High.
Definition qtypes.h:162
The return value is checked after to allow or reject the state transition. The application writer can code a boolean expression to implement statechart guards or perform some pre-transition procedure.
- Note
- When a transition entry is defined. the signal-action should be located as the third parameter of the entry. Please see the transition layout. A
NULL
value will act as a NOT-defined, always allowing the state-transition.
FSM Timeout specification
A timeout specification is a mechanism to simplify the notion of time passage inside states. The basic usage model of the timeout signals is as follows:
A timeout specification allocates one or more timer objects. The user relates in a table each specific timeout operation within the state where are they going to operate. So, according to the table, when a state needs to arrange for a timeout, the engine can set or reset the given timer. When the FSM engine detects that the appropriate moment has arrived (a timer expiration occurs), it inserts the timeout signal directly into the recipient's event queue. The recipient then processes the timeout signal just like any other signal.
Given the above explanation, it is evident that for its operation, the state machine requires an installed signal queue. A timeout specification is referenced by an object of type qSM_TimeoutSpec_t and must be installed inside the state machine using the API qStateMachine_InstallTimeoutSpec(). Then, timeout operations can be defined in a table for each state by using the qStateMachine_Set_StateTimeouts().
A timeout specification element is defined as a structure of type qSM_TimeoutStateDefinition_t and should follow this layout:
Timeout specification layout
Timeout value | Options |
The options for every timeout its a bitwise value that indicates which timeout should be used and the operations than should be performed internally by the state-machine engine. These options can be combined with a bitwise OR and are detailed as follows:
- Note
- Data associated with timeout signals should be set to
NULL
. Any other value will be ignored and will be passed as NULL
to the FSM handler.
- Attention
- The user is responsible for writing timeout specifications correctly. Care must be taken that the specifications do not collide between hierarchical states to avoid overwriting operations.
- Note
- You can increase the number of available timeouts instances by changing the
Q_FSM_MAX_TIMEOUTS
configuration macro inside qconfig.h
.
Demonstrative example using transition tables
The following example shows the implementation of the led-button FSM presented above by using the transition table approach with signal-queue and a timeout specification.
Before getting started, the required variables should be defined:
#define SIGNAL_BUTTON_PRESSED ( (qSM_SigId_t)1 )
#define SIGNAL_TIMEOUT ( QSM_SIGNAL_TIMEOUT( 0 ) )
#define SIGNAL_BLINK ( QSM_SIGNAL_TIMEOUT( 1 ) )
{ SIGNAL_BUTTON_PRESSED, NULL, &State_LEDOn , 0, NULL}
};
{ SIGNAL_TIMEOUT, NULL, &State_LEDOff , 0, NULL},
{ SIGNAL_BUTTON_PRESSED, NULL, &State_LEDBlink , 0, NULL}
};
{ SIGNAL_TIMEOUT, NULL, &State_LEDOff , 0, NULL},
{ SIGNAL_BUTTON_PRESSED, NULL, &State_LEDOff , 0, NULL}
};
};
};
#define QSM_TSOPT_RST_EXIT
This timeout-specification option its used to specify that the engine should reset the timeout when t...
Definition qfsm.h:126
#define QSM_TSOPT_INDEX(index)
Timeout-specification option. Should be used to specify the timeout index.
Definition qfsm.h:100
#define QSM_TSOPT_PERIODIC
This timeout-specification option its used setup the timeout in periodic mode.
Definition qfsm.h:138
#define QSM_TSOPT_SET_ENTRY
This timeout-specification option its used to specify that the engine should set the timeout when the...
Definition qfsm.h:108
A Queue object.
Definition qqueues.h:53
The type to be used as a container variable for a signal.
Definition qfsm.h:166
A FSM Timeout-specification object.
Definition qfsm.h:368
This structure should be used to define an item for a timeout-specification table.
Definition qfsm.h:319
This structure should be used to define an item for a state transition table.
Definition qfsm.h:413
Then, we define the callback for the states.
BSP_LED_OFF();
break;
default:
break;
}
}
BSP_LED_ON();
break;
default:
break;
}
}
case SIGNAL_BLINK:
BSP_LED_TOGGLE();
break;
default:
break;
}
}
In the previous code snippet, we assumed that SIGNAL_BUTTON_PRESSED
can be delivered from either the interrupt context or another task.
To finish the setup, a task is added to handle the FSM and then, the transition table can be installed with the other required objects.
#define qFLM_ArraySize(x)
Returns the number of elements in an array x.
Definition qflm.h:234
#define QSM_STATE_TOP
This macro can be used to reference the top state when using the qStateMachine_StateSubscribe() API.
Definition qfsm.h:57
qBool_t qStateMachine_InstallSignalQueue(qSM_t *const m, qQueue_t *q)
Install a signal queue to the provided Finite State Machine (FSM).
Definition qfsm.c:607
qBool_t qStateMachine_Set_StateTransitions(qSM_State_t *const s, qSM_Transition_t *const table, const size_t n)
Installs a table with the outgoing transitions for the supplied state.
Definition qfsm.c:592
qBool_t qStateMachine_InstallTimeoutSpec(qSM_t *const m, qSM_TimeoutSpec_t *const ts)
Install the Timeout-specification object to target FSM to allow timed signals within states ( See the...
Definition qfsm.c:799
qBool_t qStateMachine_Set_StateTimeouts(qSM_State_t *const s, qSM_TimeoutStateDefinition_t *tdef, const size_t n)
Setup fixed timeouts for the specified state using a lookup-table.
Definition qfsm.c:818
qBool_t qQueue_Setup(qQueue_t *const q, void *pData, const size_t itemSize, const size_t itemsCount)
Configures a Queue. Here, the RAM used to hold the queue data pData is statically allocated at compil...
Definition qqueues.c:32
#define qMedium_Priority
A macro directive to indicate the medium priority level.
Definition qkernel.h:74
Using the hierarchical approach
In conventional state machine designs, all states are considered at the same level. The design does not capture the commonality that exists among states. In real life, many states handle most transitions in similar fashion and differ only in a few key components. Even when the actual handling differs, there is still some commonality. It is in these situations where the hierarchical designs make the most sense.
A hierarchical state-machine is characterized by having compound states. A composite state is defined as state that has inner states and can be used as a decomposition mechanism that allows factoring of common behaviors and their reuse. And this is the biggest advantage of this design, because it captures the commonality by organizing the states as a hierarchy. The states at the higher level in the hierarchy perform the common handling, while the lower level states inherit the commonality from higher level ones and perform the state specific functions.
Example using a hierarchical FSM
This example takes the "Cruise Control" study case, a real-time system that manages the speed of an automobile based on inputs from the driver.
hsm
Cruise control FSM example The behavior of this system is state-dependent in that the executed actions correspond not only to the driver input, but also to the current state of the system and with the status of the engine and the brake. The figure above illustrates the modeling of this system with the "Automated Control" state acting as composite.
Before getting started, the required user-defined signals, variables, and entries of the transition table should be defined:
#define SIGNAL_ENGINE_ON ( (qSM_SigId_t)(1) )
#define SIGNAL_ACCEL ( (qSM_SigId_t)(2) )
#define SIGNAL_RESUME ( (qSM_SigId_t)(3) )
#define SIGNAL_OFF ( (qSM_SigId_t)(4) )
#define SIGNAL_BRAKE_PRESSED ( (qSM_SigId_t)(5) )
#define SIGNAL_CRUISE ( (qSM_SigId_t)(6) )
#define SIGNAL_REACHED_CRUISING ( (qSM_SigId_t)(7) )
#define SIGNAL_ENGINE_OFF ( (qSM_SigId_t)(8) )
qSM_State_t state_idle, state_initial, state_cruisingoff, state_automatedcontrol;
qSM_State_t state_accelerating, state_cruising, state_resuming;
{
{ SIGNAL_ENGINE_ON, SigAct_ClearDesiredSpeed, &state_initial , 0, NULL }
};
{
{ SIGNAL_ACCEL, SigAct_BrakeOff, &state_accelerating , 0, NULL }
};
{
{ SIGNAL_CRUISE, NULL, &state_cruising , 0, NULL }
};
{
{ SIGNAL_OFF, NULL, &state_cruisingoff , 0, NULL },
{ SIGNAL_ACCEL, NULL, &state_accelerating , 0, NULL }
};
{
{ SIGNAL_ACCEL, NULL, &state_accelerating , 0, NULL }
};
{
{ SIGNAL_ACCEL, SigAct_BrakeOff, &state_accelerating , 0, NULL },
{ SIGNAL_RESUME, SigAct_BrakeOff, &state_resuming , 0, NULL },
{ SIGNAL_ENGINE_OFF, NULL, &state_idle , 0, NULL }
};
{
{ SIGNAL_BRAKE_PRESSED, NULL, &state_cruisingoff , 0, NULL }
};
Then, signal-actions and state callbacks are later defined
(void)h;
Speed_ClearDesired();
}
(void)h;
}
break;
break;
}
return RetVal;
}
return qSM_EXIT_SUCCESS;
}
return qSM_EXIT_SUCCESS;
}
return qSM_EXIT_SUCCESS;
}
return qSM_EXIT_SUCCESS;
}
Speed_SelectDesired();
break;
default:
Speed_Increase();
break;
}
return qSM_EXIT_SUCCESS;
}
Cruising_Resume();
return qSM_EXIT_SUCCESS;
}
Speed_Maintain();
return qSM_EXIT_SUCCESS;
}
#define qFalse
A boolean value that represents false/failure/Off or Low.
Definition qtypes.h:157
Finally, the dedicated task for the FSM and related objects are configured.
Example with history pseudo-states
State transitions defined in high-level composite states often deal with events that require immediate attention; however, after handling them, the system should return to the most recent substate of the given composite state. UML statecharts address this situation with two kinds of history pseudostates: "shallow history" and "deep history"( denoted as the circled H
and H*
icon respectively in the figure).
fsmhist
Example with history pseudo-states
- Shallow history : A transition to the shallow history state in a composite state invokes the last state that was active, at the same depth as the history state itself, prior to the most recent exit of the composite state.
- Deep history : A transition to the deep history state within a composite state invokes the state that was active, immediately before the most recent exit of the composite state. The last active state can be nested at any depth.
Here, the way to specify this type of transition in QuarkTS is very straightforward, you only need to assign the history-mode in the last entry of the transition as shown below:
#define SIGNAL_A ( (qSM_SigId_t)(1) )
#define SIGNAL_B ( (qSM_SigId_t)(2) )
#define SIGNAL_C ( (qSM_SigId_t)(3) )
#define SIGNAL_D ( (qSM_SigId_t)(4) )
#define SIGNAL_E ( (qSM_SigId_t)(5) )
#define SIGNAL_F ( (qSM_SigId_t)(6) )
qSM_State_t state1 , state2 , state3 , state4 , state5 , state6;
};
{ SIGNAL_D , NULL , &state1 , 0, NULL }
};
{ SIGNAL_E , NULL , &state4 , 0, NULL }
};
{ SIGNAL_F , NULL , &state6 , 0, NULL }
};
@ qSM_TRANSITION_DEEP_HISTORY
Definition qfsm.h:191
@ qSM_TRANSITION_SHALLOW_HISTORY
Definition qfsm.h:190
@ qSM_TRANSITION_NO_HISTORY
Definition qfsm.h:189
Next, the configuration and topology of the state-machine is presented, including the default transitions (the small circles filled with black). Please do not forget to define the callbacks for each state.