420 lines
19 KiB
C
420 lines
19 KiB
C
/**********************************************************************************************************************
|
|
* FILE DESCRIPTION
|
|
* -----------------------------------------------------------------------------------------------------------------*/
|
|
/** \file
|
|
* \brief Event-driven hierarchical finite state machine implementation
|
|
*
|
|
* -------------------------------------------------------------------------------------------------------------------
|
|
* COPYRIGHT
|
|
* -------------------------------------------------------------------------------------------------------------------
|
|
* \par Copyright
|
|
* \verbatim
|
|
* Copyright (c) 2023 by Vector Informatik GmbH. All rights reserved.
|
|
*
|
|
* This software is copyright protected and proprietary to Vector Informatik GmbH.
|
|
* Vector Informatik GmbH grants to you only those rights as set out in the license conditions.
|
|
* All other rights remain with Vector Informatik GmbH.
|
|
* \endverbatim
|
|
*/
|
|
/*********************************************************************************************************************/
|
|
|
|
/**********************************************************************************************************************
|
|
* REVISION HISTORY
|
|
* -------------------------------------------------------------------------------------------------------------------
|
|
* Version Date Author Change Id Description
|
|
* -------------------------------------------------------------------------------------------------------------------
|
|
* 01.00.00 2012-10-30 visjhg - First implementation
|
|
* 01.00.01 2012-12-10 visjhg - No changes
|
|
* 01.01.00 2013-12-11 visjhg ESCAN00071901 Renamed package to FblLib_Fsm
|
|
* visase - Performed MISRA checks
|
|
* 01.01.01 2019-01-24 visrie ESCAN00101864 No changes
|
|
* 01.02.00 2023-12-15 vistbe FBL-7986 Perform MISRA analysis
|
|
* Added/adapted MemMap sections
|
|
*********************************************************************************************************************/
|
|
|
|
#define FBL_FSM_SOURCE
|
|
|
|
/**********************************************************************************************************************
|
|
* INCLUDES
|
|
*********************************************************************************************************************/
|
|
|
|
#include "fbl_fsm_inc.h"
|
|
|
|
/**********************************************************************************************************************
|
|
* VERSION
|
|
*********************************************************************************************************************/
|
|
|
|
/* --- Version check --- */
|
|
#if ( FBLLIB_FSM_VERSION != 0x0102u ) || \
|
|
( FBLLIB_FSM_RELEASE_VERSION != 0x00u )
|
|
# error "Error in FBL_FSM.C: Source and header file are inconsistent!"
|
|
#endif
|
|
|
|
#if ( FBLLIB_FSM_VERSION != _FBLLIB_FSM_VERSION ) || \
|
|
( FBLLIB_FSM_RELEASE_VERSION != _FBLLIB_FSM_RELEASE_VERSION )
|
|
# error "Error in FBL_FSM.C: Source and v_ver.h are inconsistent!"
|
|
#endif
|
|
|
|
/**********************************************************************************************************************
|
|
* LOCAL DATA TYPES AND STRUCTURES
|
|
*********************************************************************************************************************/
|
|
|
|
/** Type to select whether super-states should be iterated in case no matching event handler was found */
|
|
typedef enum
|
|
{
|
|
kFsmIterateHierarchy_Disabled, /**< Do not iterate super-states */
|
|
kFsmIterateHierarchy_Enabled /**< Iterate super-states until matching event handler was found */
|
|
} tFsmIterateHierarchy;
|
|
|
|
/**********************************************************************************************************************
|
|
* LOCAL FUNCTION PROTOTYPES
|
|
**********************************************************************************************************************/
|
|
#define FBLFSM_START_SEC_CODE
|
|
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */
|
|
|
|
static void FblFsmHandleEvent( tFblFsmContextPtr const pFsmContext, tFblFsmEvent event, tFsmIterateHierarchy iterate);
|
|
static vuint8 FblFsmGetStateHierachy( const V_MEMRAM1 tFblFsmContext V_MEMRAM2 V_MEMRAM3 * const pFsmContext, tFblFsmState state,
|
|
V_MEMRAM1 tFblFsmState V_MEMRAM2 V_MEMRAM3 * hierarchy );
|
|
#define FBLFSM_STOP_SEC_CODE
|
|
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */
|
|
|
|
/**********************************************************************************************************************
|
|
* LOCAL DATA
|
|
**********************************************************************************************************************/
|
|
|
|
#if defined( FBL_FSM_ENABLE_DEBUGGING )
|
|
#define FBLFSM_START_SEC_VAR
|
|
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */
|
|
/** Global state machine instance counter */
|
|
static tFblFsmInstance g_Instance;
|
|
#define FBLFSM_STOP_SEC_VAR
|
|
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */
|
|
#endif
|
|
|
|
/**********************************************************************************************************************
|
|
* LOCAL FUNCTIONS
|
|
**********************************************************************************************************************/
|
|
#define FBLFSM_START_SEC_CODE
|
|
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */
|
|
/**********************************************************************************************************************
|
|
* FblFsmGetStateHierachy
|
|
*********************************************************************************************************************/
|
|
/*! \brief Provides the state hierarchy starting with the given state
|
|
* \param[in] pFsmContext Pointer to context of state machine
|
|
* \param[in] state The current state
|
|
* \param[out] hierarchy The returned state hierarchy
|
|
*********************************************************************************************************************/
|
|
static vuint8 FblFsmGetStateHierachy( const V_MEMRAM1 tFblFsmContext V_MEMRAM2 V_MEMRAM3 * const pFsmContext,
|
|
tFblFsmState state,
|
|
V_MEMRAM1 tFblFsmState V_MEMRAM2 V_MEMRAM3 * hierarchy )
|
|
{
|
|
vuint8 index = 0u;
|
|
tFblFsmState localState = state;
|
|
|
|
while (FBL_FSM_DEFAULT_STATE != localState)
|
|
{
|
|
/* Check for hierarchy depth and stateCount to prevent buffer overrun(s) */
|
|
if ( (index >= FBL_FSM_MAX_HIERARCHY_DEPTH)
|
|
|| (localState >= pFsmContext->stateCount))
|
|
{
|
|
FBL_FSM_DEBUG_ERROR_PRINT(pFsmContext->instance);
|
|
|
|
index = 0u;
|
|
localState = FBL_FSM_DEFAULT_STATE;
|
|
}
|
|
else
|
|
{
|
|
hierarchy[index] = localState;
|
|
index++;
|
|
|
|
localState = pFsmContext->stateDefinitions[localState].superState;
|
|
}
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
/**********************************************************************************************************************
|
|
* FblFsmHandleEvent
|
|
*********************************************************************************************************************/
|
|
/*! \brief Handles the given event
|
|
* \param[in] pFsmContext Pointer to context of state machine
|
|
* \param[in] event The event to be handled
|
|
* \param[out] iterate Indicates whether state hierarchy iteration is allowed
|
|
*********************************************************************************************************************/
|
|
static void FblFsmHandleEvent( tFblFsmContextPtr const pFsmContext, tFblFsmEvent event, tFsmIterateHierarchy iterate )
|
|
{
|
|
vuint16 index;
|
|
vuint16 hierarchy = 0u;
|
|
tFblFsmState state = pFsmContext->state;
|
|
tFblFsmGuard guard;
|
|
V_MEMROM1 tFblFsmStateDefinition V_MEMROM2 V_MEMROM3 * stateDef;
|
|
V_MEMROM1 tFblFsmEventEntry V_MEMROM2 V_MEMROM3 * triggers;
|
|
|
|
FBL_FSM_DEBUG_PRINT2(pFsmContext->instance, event, state);
|
|
|
|
while ( (FBL_FSM_DEFAULT_STATE != state)
|
|
&& (state < pFsmContext->stateCount))
|
|
{
|
|
stateDef = &pFsmContext->stateDefinitions[state];
|
|
triggers = stateDef->triggers;
|
|
|
|
/* Search for matching event handler */
|
|
for (index = 0u; index < stateDef->triggerCount; index++)
|
|
{
|
|
if ( (triggers[index].trigger == event)
|
|
|| (triggers[index].trigger == (tFblFsmEvent)kFblFsmDefaultEvent_Any) )
|
|
{
|
|
/* Handler matching event found, now execute handler */
|
|
guard = triggers[index].handler(pFsmContext, event);
|
|
|
|
FBL_FSM_DEBUG_PRINT3(pFsmContext->instance, guard);
|
|
|
|
if (kFblFsmGuard_False != guard)
|
|
{
|
|
if (kFblFsmGuard_True == guard)
|
|
{
|
|
/* Pending state may be "none" */
|
|
pFsmContext->pendingState = triggers[index].nextState;
|
|
}
|
|
else /* kFblFsmGuard_Overwrite */
|
|
{
|
|
/* Do not set pendingState, this is (has to be) done by the event handler */
|
|
}
|
|
|
|
/* Stop searching for event handlers as event was handled */
|
|
state = FBL_FSM_DEFAULT_STATE;
|
|
break;
|
|
}
|
|
else /* kFblFsmGuard_False */
|
|
{
|
|
/* State shall not be left, continue to search for other matching event handlers */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check if event has been already handled */
|
|
if (FBL_FSM_DEFAULT_STATE != state)
|
|
{
|
|
/* No matching event handler returning kFblFsmGuard_True could be found for current state,
|
|
* continue search in (encapsulating) super state, if possible */
|
|
if (kFsmIterateHierarchy_Enabled == iterate)
|
|
{
|
|
hierarchy++;
|
|
|
|
if (hierarchy < FBL_FSM_MAX_HIERARCHY_DEPTH)
|
|
{
|
|
state = stateDef->superState;
|
|
}
|
|
else
|
|
{
|
|
/* Abort search to avoid (potential) infinite loop */
|
|
FBL_FSM_DEBUG_ERROR_PRINT(pFsmContext->instance);
|
|
|
|
state = FBL_FSM_DEFAULT_STATE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* State traversing not allowed, abort search at this point */
|
|
state = FBL_FSM_DEFAULT_STATE;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined( FBL_FSM_ENABLE_DEBUGGING )
|
|
if ( (state >= pFsmContext->stateCount)
|
|
&& (FBL_FSM_DEFAULT_STATE != state) )
|
|
{
|
|
FBL_FSM_DEBUG_ERROR_PRINT(pFsmContext->instance);
|
|
}
|
|
#endif /* FBL_FSM_ENABLE_DEBUGGING */
|
|
}
|
|
|
|
/**********************************************************************************************************************
|
|
* GLOBAL FUNCTIONS
|
|
**********************************************************************************************************************/
|
|
|
|
/**********************************************************************************************************************
|
|
* FblFsmInitPowerOn
|
|
*********************************************************************************************************************/
|
|
/*! \brief Module initialization
|
|
*********************************************************************************************************************/
|
|
void FblFsmInitPowerOn( void )
|
|
{
|
|
#if defined( FBL_FSM_ENABLE_DEBUGGING )
|
|
g_Instance = 0u;
|
|
#endif
|
|
}
|
|
|
|
/**********************************************************************************************************************
|
|
* FblFsmInit
|
|
*********************************************************************************************************************/
|
|
/*! \brief State machine initialization
|
|
* \param[in] pFsmContext Pointer to context of state machine
|
|
* \param[in] initialState The initial state of the state machine
|
|
*********************************************************************************************************************/
|
|
void FblFsmInit( tFblFsmContextPtr const pFsmContext, tFblFsmState initialState )
|
|
{
|
|
#if defined( FBL_FSM_ENABLE_DEBUGGING )
|
|
pFsmContext->instance = g_Instance;
|
|
g_Instance++;
|
|
#endif
|
|
|
|
/* Start with internal default state */
|
|
pFsmContext->state = FBL_FSM_DEFAULT_STATE;
|
|
pFsmContext->pendingState = initialState;
|
|
pFsmContext->pendingEvent = (tFblFsmEvent)kFblFsmDefaultEvent_None;
|
|
|
|
/* Enter provided initial state */
|
|
FblFsmStateTask(pFsmContext);
|
|
}
|
|
|
|
/**********************************************************************************************************************
|
|
* FblFsmDeinit
|
|
*********************************************************************************************************************/
|
|
/*! \brief State machine deinitialization
|
|
* \param[in] pFsmContext Pointer to context of state machine
|
|
*********************************************************************************************************************/
|
|
void FblFsmDeinit( tFblFsmContextPtr const pFsmContext )
|
|
{
|
|
/* Switch to internal default state to avoid further processing */
|
|
pFsmContext->state = FBL_FSM_DEFAULT_STATE;
|
|
pFsmContext->pendingState = FBL_FSM_DEFAULT_STATE;
|
|
pFsmContext->pendingEvent = (tFblFsmEvent)kFblFsmDefaultEvent_None;
|
|
}
|
|
|
|
/**********************************************************************************************************************
|
|
* FblFsmStateTask
|
|
*********************************************************************************************************************/
|
|
/*! \brief State machine handling
|
|
* \details This function checks for pending events / state transitions and handles them appropriately.
|
|
* \param[in] pFsmContext Pointer to context of state machine
|
|
*********************************************************************************************************************/
|
|
void FblFsmStateTask( tFblFsmContextPtr const pFsmContext )
|
|
{
|
|
tFblFsmState stateHierarchyExit[FBL_FSM_MAX_HIERARCHY_DEPTH];
|
|
tFblFsmState stateHierarchyEntry[FBL_FSM_MAX_HIERARCHY_DEPTH];
|
|
|
|
tFblFsmState currentState;
|
|
tFblFsmState pendingState;
|
|
|
|
vsint8 index;
|
|
vuint8 exitCount;
|
|
vuint8 entryCount;
|
|
vsint8 exitIndex;
|
|
vsint8 entryIndex;
|
|
|
|
/* Check for pending event, which could cause state change */
|
|
tFblFsmEvent event = pFsmContext->pendingEvent;
|
|
|
|
if ((tFblFsmEvent)kFblFsmDefaultEvent_None != event)
|
|
{
|
|
/* Reset event */
|
|
pFsmContext->pendingEvent = (tFblFsmEvent)kFblFsmDefaultEvent_None;
|
|
|
|
/* Process event and trigger state change if necessary */
|
|
FblFsmHandleEvent(pFsmContext, event, kFsmIterateHierarchy_Enabled);
|
|
}
|
|
|
|
/* Check whether state has to be changed */
|
|
while (FBL_FSM_DEFAULT_STATE != pFsmContext->pendingState)
|
|
{
|
|
currentState = pFsmContext->state;
|
|
pendingState = pFsmContext->pendingState;
|
|
|
|
FBL_FSM_DEBUG_PRINT1(pFsmContext->instance, currentState, pendingState);
|
|
|
|
/* Reset event which triggered state change */
|
|
pFsmContext->pendingState = FBL_FSM_DEFAULT_STATE;
|
|
|
|
/* Get state hierarchy of current state and pending state */
|
|
exitCount = FblFsmGetStateHierachy(pFsmContext, currentState, stateHierarchyExit);
|
|
entryCount = FblFsmGetStateHierachy(pFsmContext, pendingState, stateHierarchyEntry);
|
|
|
|
/* Check for re-entry of current state */
|
|
if (currentState == pendingState)
|
|
{
|
|
exitIndex = 0;
|
|
entryIndex = 0;
|
|
}
|
|
else
|
|
{
|
|
exitIndex = ((vsint8)exitCount - 1);
|
|
entryIndex = ((vsint8)entryCount - 1);
|
|
|
|
/* Search for common super state of current state and pending state */
|
|
while ( (exitIndex >= 0)
|
|
&& (stateHierarchyExit[exitIndex] == stateHierarchyEntry[entryIndex]) )
|
|
{
|
|
exitIndex--;
|
|
entryIndex--;
|
|
}
|
|
|
|
/* An exitIndex / entryIndex larger than 0 indicates that both state have a common super state */
|
|
}
|
|
|
|
/* Leave all super-states
|
|
* A state is always left, even if the pending state is the same (re-entry),
|
|
* but not if the target state is a sub-state of the current state.
|
|
* An exit handler is executed after the transition action has been executed */
|
|
for (index = 0; index <= exitIndex; index++)
|
|
{
|
|
pFsmContext->state = stateHierarchyExit[index];
|
|
|
|
/* Leave previous state by calling exit handler */
|
|
FblFsmHandleEvent(pFsmContext, (tFblFsmEvent)kFblFsmDefaultEvent_Exit, kFsmIterateHierarchy_Disabled);
|
|
}
|
|
|
|
/* Enter all super-states
|
|
* A pending state is only entered if it is not a previous super-state */
|
|
for (index = entryIndex; index >= 0; index--)
|
|
{
|
|
pFsmContext->state = stateHierarchyEntry[index];
|
|
|
|
/* Now enter new state by calling entry handler */
|
|
FblFsmHandleEvent(pFsmContext, (tFblFsmEvent)kFblFsmDefaultEvent_Entry, kFsmIterateHierarchy_Disabled);
|
|
}
|
|
|
|
/* Set final state, necessary if new state is super-state of previous state (no re-entry) */
|
|
pFsmContext->state = pendingState;
|
|
}
|
|
}
|
|
|
|
/**********************************************************************************************************************
|
|
* FblFsmTriggerEvent
|
|
*********************************************************************************************************************/
|
|
/*! \brief Triggers event for given state machine
|
|
* \param[in] pFsmContext Pointer to context of state machine
|
|
* \param[in] event The event to be triggered
|
|
*********************************************************************************************************************/
|
|
void FblFsmTriggerEvent( tFblFsmContextPtr const pFsmContext, tFblFsmEvent event )
|
|
{
|
|
pFsmContext->pendingEvent = event;
|
|
}
|
|
|
|
/**********************************************************************************************************************
|
|
* FblFsmEventHandlerDefault
|
|
*********************************************************************************************************************/
|
|
/*! \brief Default event handler
|
|
* \param[in] pFsmContext Pointer to context of state machine
|
|
* \param[in] event The event that triggered the handler execution
|
|
*********************************************************************************************************************/
|
|
tFblFsmGuard FblFsmEventHandlerDefault( tFblFsmContextPtr const pFsmContext, tFblFsmEvent event ) /* PRQA S 3673 */ /* MD_MSR_Rule8.13 */
|
|
{
|
|
#if defined( V_ENABLE_USE_DUMMY_STATEMENT )
|
|
(void)pFsmContext;
|
|
(void)event;
|
|
#endif /* V_ENABLE_USE_DUMMY_STATEMENT */
|
|
|
|
/* Do nothing, but allow state transition */
|
|
|
|
return kFblFsmGuard_True;
|
|
}
|
|
#define FBLFSM_STOP_SEC_CODE
|
|
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */
|
|
|
|
/**********************************************************************************************************************
|
|
* END OF FILE: FBL_FSM.C
|
|
*********************************************************************************************************************/
|