FLEX-FORD-OBC-BM/Source/bsw/Fbl/fbl_fsm.c

420 lines
19 KiB
C
Raw Normal View History

2026-03-19 11:49:16 +01:00
/**********************************************************************************************************************
* 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
*********************************************************************************************************************/