From 0a862a1a93501c24dcd19f88442dba7bed8b7bf1 Mon Sep 17 00:00:00 2001 From: apolisskyi Date: Wed, 23 Aug 2023 23:46:56 +0300 Subject: [PATCH] fsm: add state hooks invokation on state transition --- src/active-object/active_object.h | 10 ++-- src/active-object/active_object_impl.h | 11 ++-- src/fsm/fsm.h | 76 ++++++++++++++++++++------ src/fsm/fsm_impl.h | 45 ++++++++++++--- 4 files changed, 106 insertions(+), 36 deletions(-) diff --git a/src/active-object/active_object.h b/src/active-object/active_object.h index 623222b..49be4b3 100755 --- a/src/active-object/active_object.h +++ b/src/active-object/active_object.h @@ -65,13 +65,11 @@ } ACTIVE_OBJECT_T; \ \ typedef STATE_T (*EVENT_T##_HANDLER_F)(ACTIVE_OBJECT_T *const self, EVENT_T event); \ - typedef void (*ACTIVE_OBJECT_T##HAS_EMPTY_QUEUE_F)(ACTIVE_OBJECT_T *const self); \ - typedef bool (*STATE_T##_TRANSITION_F)(ACTIVE_OBJECT_T *const self, STATE_T state); \ + typedef bool (*STATE_T##_TRANSITION_F)(ACTIVE_OBJECT_T *const self, STATE_T state, void* ctx); \ \ void ACTIVE_OBJECT_T##_Ctor(ACTIVE_OBJECT_T *const self, uint8_t id, STATE_T initialState, FIELDS_T fields); \ void ACTIVE_OBJECT_T##_Dispatch(ACTIVE_OBJECT_T *const self, EVENT_T event); \ bool ACTIVE_OBJECT_T##_HasEmptyQueue(ACTIVE_OBJECT_T *const self); \ - bool ACTIVE_OBJECT_T##_basicTransitionToNextState(ACTIVE_OBJECT_T *const self, STATE_T nextState); \ void ACTIVE_OBJECT_T##_ProcessQueue(ACTIVE_OBJECT_T *const self, EVENT_T##_HANDLER_F eventHandlerCb, STATE_T##_TRANSITION_F transitionToNextStateCb); /** @@ -116,7 +114,7 @@ void ACTIVE_OBJECT_T_Ctor(ACTIVE_OBJECT_T *const self, uint8_t id, STATE_T initi * MY_ACTIVE_OBJECT_Dispatch(&myObject, myEvent); * @endcode */ -void ACTIVE_OBJECT_T##_Dispatch(ACTIVE_OBJECT_T *const self, EVENT_T event); +void ACTIVE_OBJECT_T_Dispatch(ACTIVE_OBJECT_T *const self, EVENT_T event); /** * @brief Check Active Object queue empty @@ -125,7 +123,7 @@ void ACTIVE_OBJECT_T##_Dispatch(ACTIVE_OBJECT_T *const self, EVENT_T event); * * @return is queue empty */ -bool ACTIVE_OBJECT_T##_HasEmptyQueue(ACTIVE_OBJECT_T *const self); +bool ACTIVE_OBJECT_T_HasEmptyQueue(ACTIVE_OBJECT_T *const self); /** * @brief Process the event queue of the Active Object @@ -140,7 +138,7 @@ bool ACTIVE_OBJECT_T##_HasEmptyQueue(ACTIVE_OBJECT_T *const self); * if (!MY_ACTIVE_OBJECT_HasEmptyQueue(&myObject)) MY_ACTIVE_OBJECT_ProcessQueue(&myObject, myEventHandler, myTransitionCb); \ * @endcode */ -void ACTIVE_OBJECT_T_ProcessQueue(ACTIVE_OBJECT_T *const self, EVENT_T##_HANDLER_F eventHandlerCb, STATE_T_TRANSITION_F transitionToNextStateCb) +void ACTIVE_OBJECT_T_ProcessQueue(ACTIVE_OBJECT_T *const self, EVENT_T##_HANDLER_F eventHandlerCb, STATE_T##_TRANSITION_F transitionToNextStateCb) #endif diff --git a/src/active-object/active_object_impl.h b/src/active-object/active_object_impl.h index 63504c9..1bca103 100755 --- a/src/active-object/active_object_impl.h +++ b/src/active-object/active_object_impl.h @@ -18,23 +18,22 @@ if (QUEUE_##EVENT_T##_IsFull(&self->queue)) return; /* TODO Handle error, e.g., logging */ \ QUEUE_##EVENT_T##_Enqueue(&self->queue, event); \ } \ - \ - \ bool ACTIVE_OBJECT_T##_HasEmptyQueue(ACTIVE_OBJECT_T *const self) {\ return EMPTY_QUEUE == QUEUE_##EVENT_T##_GetSize(&self->queue); \ }\ \ - void ACTIVE_OBJECT_T##_ProcessQueue( \ - ACTIVE_OBJECT_T *const self, \ - EVENT_T##_HANDLER_F eventHandlerCb, \ + void ACTIVE_OBJECT_T##_ProcessQueue(\ + ACTIVE_OBJECT_T *const self, \ + EVENT_T##_HANDLER_F eventHandlerCb,\ STATE_T##_TRANSITION_F transitionToNextStateCb) { \ /* TODO debug log it, just double check to no dequeue empty queue */ \ if (ACTIVE_OBJECT_T##_HasEmptyQueue(self)) return; \ \ EVENT_T e = QUEUE_##EVENT_T##_Dequeue(&self->queue); \ STATE_T nextState = eventHandlerCb(self, e); \ - transitionToNextStateCb(self, nextState); \ + void* ctx = e.payload ? e.payload : NULL; \ + transitionToNextStateCb(self, nextState, ctx); \ } #endif //ACTIVE_OBJECT_IMPL_H diff --git a/src/fsm/fsm.h b/src/fsm/fsm.h index 9c08adf..5ab7430 100755 --- a/src/fsm/fsm.h +++ b/src/fsm/fsm.h @@ -41,14 +41,14 @@ STATE_T##_HANDLE_FUNCTIONS stateHandleFunctionsList[statesMax]\\ #define DECLARE_FSM(ACTIVE_OBJECT_T, EVENT_T, STATE_T, eventsMax, statesMax) \ /* State handler function type */ \ typedef STATE_T (*EVENT_T##_HANDLE_F)(ACTIVE_OBJECT_T *const activeObject, EVENT_T event); \ -typedef bool (*EVENT_T##_GUARD_F)(ACTIVE_OBJECT_T *const activeObject, EVENT_T event); \ -typedef void (*STATE_T##_HANDLE_F)(ACTIVE_OBJECT_T *const activeObject, EVENT_T event); \ +typedef bool (*EVENT_T##_GUARD_F)(ACTIVE_OBJECT_T *const activeObject, EVENT_T event); \ +typedef bool (*STATE_T##_HOOK_F)(ACTIVE_OBJECT_T *const activeObject, void *const ctx); \ \ typedef struct { \ - STATE_T##_HANDLE_F enter; \ - STATE_T##_HANDLE_F traverse; \ - STATE_T##_HANDLE_F exit; \ -} STATE_T##_HANDLE_FUNCTIONS; \ + STATE_T##_HOOK_F enter; \ + STATE_T##_HOOK_F traverse; \ + STATE_T##_HOOK_F exit; \ +} STATE_T##_HOOKS; \ \ STATE_T ACTIVE_OBJECT_T##_FSM_ProcessEventToNextState(\ ACTIVE_OBJECT_T *const activeObject, \ @@ -57,6 +57,7 @@ STATE_T ACTIVE_OBJECT_T##_FSM_ProcessEventToNextState(\ STATE_T##_HANDLE_FUNCTIONS stateHandlersList[statesMax] \ );\ bool ACTIVE_OBJECT_T##_FSM_BasicTransitionToNextState(ACTIVE_OBJECT_T *const activeObject, STATE_T nextState);\ +bool ACTIVE_OBJECT_T##_HooksTransitionToNextState(ACTIVE_OBJECT_T *const activeObject, STATE_T nextState, void *const ctx, STATE_T##_HOOKS statesHooksList[statesMax]); /** * @def DECLARE_GUARD(ACTIVE_OBJECT_T, EVENT_T, CONDITION_FUNCTION, ON_TRUE_FUNCTION, ON_FALSE_FUNCTION) @@ -69,7 +70,7 @@ bool ACTIVE_OBJECT_T##_FSM_BasicTransitionToNextState(ACTIVE_OBJECT_T *const act * @tparam ON_TRUE_FUNCTION The function to be called if the guard condition evaluates to true. * @tparam ON_FALSE_FUNCTION The function to be called if the guard condition evaluates to false. * - * #### Example: + * ### Example: * @code * // TODO * @endcode @@ -86,7 +87,7 @@ bool ACTIVE_OBJECT_T##_FSM_BasicTransitionToNextState(ACTIVE_OBJECT_T *const act * @brief Convenience macro to represent a specific guard function. * @details This macro simplifies the invocation of the declared guard function based on the provided CONDITION_FUNCTION, ON_TRUE_FUNCTION, and ON_FALSE_FUNCTION. * - * #### Example: + * ### Example: * @code * // TODO * @endcode @@ -99,7 +100,7 @@ bool ACTIVE_OBJECT_T##_FSM_BasicTransitionToNextState(ACTIVE_OBJECT_T *const act /** * @brief Invoke state handler f from transitions table by current state and event: [state][event] => f(event) * - * @param self[in,out] - active object object + * @param activeObject[in,out] - active object object * @param event[in] - new event * @param transitionTable[in] - event handlers functions 2D array * @param stateHandleFunctionsList[in] - state handle functions array @@ -108,21 +109,62 @@ bool ACTIVE_OBJECT_T##_FSM_BasicTransitionToNextState(ACTIVE_OBJECT_T *const act STATE_T ACTIVE_OBJECT_T_FSM_ProcessEventToNextState(ACTIVE_OBJECT_T *const activeObject, EVENT_T event, EVENT_T_HANDLE_F transitionTable[statesMax][eventsMax]) /** - * @brief Transition Active Object to the next state + * @brief Active Object state transition * - * @param self Pointer to the Active Object + * @param activeObject Pointer to the Active Object * @param nextState Next state to transition to * * @return whether transition succeed * - * #### Example: - * @code - * // Transition to a new state: - * MY_STATE newState = ...; - * MY_ACTIVE_OBJECT_FSM_BasicTransitionToNextState(&myObject, newState); + * ### Example: + * @code{.c} + * MyActiveObjectType myActiveObject; + * MyStateType nextState = SOME_STATE; + * + * bool result = MyActiveObjectType##_BasicTransitionToNextState(&myActiveObject, nextState); + * if (result) { + * printf("Transition successful!\n"); + * } else { + * printf("Transition failed!\n"); + * } + * @endcode + */ +bool ACTIVE_OBJECT_T_FSM_BasicTransitionToNextState(ACTIVE_OBJECT_T *const activeObject, STATE_T nextState); + +/** + * @brief Performs a state transition with hooks. + * + * This function performs a transition to the given next state, and calls the appropriate hooks + * based on the transition type (entry, exit, or self-transition). + * + * @param activeObject Pointer to the active object. + * @param nextState The state to transition to. + * @param ctx Context pointer that could be passed to hooks. + * @param statesHooksList List of hooks for each state. + * + * @return True if all hooks executed successfully, false otherwise. + * + * ### Example: + * @code{.c} + * MyActiveObjectType myActiveObject; + * MyStateType nextState = SOME_STATE; + * void *context = NULL; // Replace with actual context if needed + * MyStateType_HOOKS statesHooksList[MAX_STATES]; + * + * // Initialize statesHooksList + * statesHooksList[SOME_STATE].enter = MyEnterHookFunction; + * statesHooksList[SOME_STATE].exit = MyExitHookFunction; + * statesHooksList[SOME_STATE].traverse = MyTraverseHookFunction; + * + * bool result = MyActiveObjectType_HooksTransitionToNextState(&myActiveObject, nextState, context, statesHooksList); + * if (result) { + * printf("Transition with hooks successful!\n"); + * } else { + * printf("Transition with hooks failed!\n"); + * } * @endcode */ -bool ACTIVE_OBJECT_T##_FSM_BasicTransitionToNextState(ACTIVE_OBJECT_T *const activeObject, STATE_T nextState); +bool ACTIVE_OBJECT_T_HooksTransitionToNextState(ACTIVE_OBJECT_T *const activeObject, STATE_T nextState, void *const ctx, STATE_T##_HOOKS statesHooksList[statesMax]); #endif diff --git a/src/fsm/fsm_impl.h b/src/fsm/fsm_impl.h index 9b34b4c..b33458c 100755 --- a/src/fsm/fsm_impl.h +++ b/src/fsm/fsm_impl.h @@ -8,8 +8,7 @@ STATE_T ACTIVE_OBJECT_T##_FSM_ProcessEventToNextState(\ ACTIVE_OBJECT_T *const activeObject, \ EVENT_T event, \ - EVENT_T##_HANDLE_F transitionTable[statesMax][eventsMax], \ - STATE_T##_HANDLE_FUNCTIONS stateHandlersList[statesMax] \ + EVENT_T##_HANDLE_F transitionTable[statesMax][eventsMax] \ ) { \ STATE_T currState = activeObject->state; \ EVENT_T##_HANDLE_F stateHandler = transitionTable[currState][event.sig]; \ @@ -21,12 +20,44 @@ STATE_T ACTIVE_OBJECT_T##_FSM_ProcessEventToNextState(\ \ return nextState; \ };\ - - /* TODO add non basic transition, handle start, traverse, exit state functions */ \ \ - bool ACTIVE_OBJECT_T##_basicTransitionToNextState(ACTIVE_OBJECT_T *const self, STATE_T nextState) { \ - self->state = nextState; \ + bool ACTIVE_OBJECT_T##_BasicTransitionToNextState(ACTIVE_OBJECT_T *const activeObject, STATE_T nextState, void *const ctx) { \ + activeObject->state = nextState; \ return true; \ - } \ + } \ + \ + bool ACTIVE_OBJECT_T##_HooksTransitionToNextState(ACTIVE_OBJECT_T *const activeObject, STATE_T nextState, void *const ctx, STATE_T##_HOOKS statesHooksList[statesMax]) {\ + bool hooksExecutionResult = false;\ + /* Next state can't be NULL */\ + if((STATE_T){0} == nextState) return false;\ + \ + STATE_T currState = activeObject->state;\ + \ + /* 0 -> nextState: Transition from initial state (NULL state), enter + traverse hooks */\ + if ((STATE_T){0} == currState) {\ + if (NULL != statesHooksList[nextState] && NULL != statesHooksList[nextState].enter)\ + hooksExecutionResult &= statesHooksList[nextState].enter(activeObject, ctx); \ + if (NULL != statesHooksList[nextState].traverse)\ + hooksExecutionResult &= statesHooksList[nextState].traverse(activeObject, ctx); \ + \ + return hooksExecutionResult;\ + };\ + \ + /* currState -> currState: activeObject-transition, run only traverse hook */\ + if (currState == nextState) {\ + if (NULL != statesHooksList[currState] && NULL != statesHooksList[currState].traverse) \ + return statesHooksList[currState].traverse(activeObject, ctx);\ + };\ + /* currState -> nextState: Full scenario, exit, traverse, enter hooks */\ + if (currState != nextState) {\ + if (NULL != statesHooksList[currState] && NULL != statesHooksList[currState].exit)\ + hooksExecutionResult &= statesHooksList[currState].exit(activeObject, ctx); \ + if (NULL != statesHooksList[nextState] && NULL != statesHooksList[nextState].enter)\ + hooksExecutionResult &= statesHooksList[nextState].enter(activeObject, ctx); \ + if (NULL != statesHooksList[nextState] && NULL != statesHooksList[nextState].traverse)\ + hooksExecutionResult &= statesHooksList[nextState].traverse(activeObject, ctx); \ + };\ + return hooksExecutionResult;\ + } #endif //FSM_IMPL_H