Skip to content

Commit

Permalink
fsm: add state hooks invokation on state transition
Browse files Browse the repository at this point in the history
  • Loading branch information
polesskiy-dev committed Aug 23, 2023
1 parent fcde0be commit 0a862a1
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 36 deletions.
10 changes: 4 additions & 6 deletions src/active-object/active_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/**
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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

Expand Down
11 changes: 5 additions & 6 deletions src/active-object/active_object_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
76 changes: 59 additions & 17 deletions src/fsm/fsm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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, \
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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

Expand Down
45 changes: 38 additions & 7 deletions src/fsm/fsm_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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]; \
Expand All @@ -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

0 comments on commit 0a862a1

Please sign in to comment.