Skip to content

Commit e70712e

Browse files
committed
BREAKING CHANGE: State hooks onEnter, onExit now calls only if nextState differs from currentState
1 parent c745cde commit e70712e

File tree

5 files changed

+82
-69
lines changed

5 files changed

+82
-69
lines changed

src/active_object/active_object.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* ### Example:
1717
* @code
1818
* // Active Object inheritance
19-
* typedef enum { NO_STATE, STATE_1 = 1, STATES_MAX } TEST_STATE; // state names
19+
* typedef enum { INVALID_STATE = -1, NO_STATE, STATE_1 = 1, STATES_MAX } TEST_STATE; // state names
2020
* typedef enum { NO_SIG, EVENT_SIG_1 = 1, EVENTS_MAX } TEST_EVENT_SIG; // event signals names
2121
* TEvent events[QUEUE_MAX_SIZE]; // events list for the queue
2222
*
@@ -66,17 +66,17 @@ typedef bool (*TStateHook)(TActiveObject *const activeObject, void *const ctx);
6666

6767
/** @brief Struct representing a single state of an active object. */
6868
typedef struct {
69-
int32_t name;
70-
TStateHook onEnter;
71-
TStateHook onTraverse;
72-
TStateHook onExit;
69+
int name; /**< State name. */
70+
TStateHook onEnter; /**< State onEnter hook. If the next state is the same as the current state, this hook won't be called. */
71+
TStateHook onTraverse; /**< State onTraverse hook. If next state is the same as current state, this hook will be called. */
72+
TStateHook onExit; /**< State onExit hook. If the next state is the same as the current state, this hook won't be called. */
7373
} TState;
7474

7575
/** @brief Struct representing an active object. */
7676
struct TActiveObject {
77-
uint8_t id;
78-
const TState *state;
79-
TEventQueue queue;
77+
uint8_t id; /**< Object ID. */
78+
const TState *state; /**< Pointer to the current state. */
79+
TEventQueue queue; /**< Event queue. */
8080
};
8181

8282
/** @brief Initialize an active object.

src/event_queue/event_queue.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
#include <stdbool.h>
2424
#include <stddef.h>
2525

26-
2726
/**
2827
* @brief Event structure containing a signal and payload
2928
*/

src/fsm/fsm.c

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -13,31 +13,32 @@ inline bool FSM_IsValidState(const TState *const state) {
1313
};
1414

1515
const TState *FSM_ProcessEventToNextState(
16-
TActiveObject *const activeObject,
17-
TEvent event,
18-
uint32_t statesMax,
19-
uint32_t eventsMax,
20-
const TState statesList[statesMax],
21-
const TEventHandler transitionTable[statesMax][eventsMax]) {
16+
TActiveObject *const activeObject,
17+
TEvent event,
18+
uint32_t statesMax,
19+
uint32_t eventsMax,
20+
const TState statesList[statesMax],
21+
const TEventHandler transitionTable[statesMax][eventsMax]) {
2222

23-
/* Validate input args */
23+
/* Validate input args */
2424

2525
// Validate Active object
26-
if (NULL == activeObject
27-
|| NULL == statesList
28-
|| NULL == transitionTable) return &invalidState; // Handle null pointers as needed
26+
if (NULL == activeObject
27+
|| NULL == statesList
28+
|| NULL == transitionTable)
29+
return &invalidState; // Handle null pointers as needed
2930

3031
const TState *const currState = activeObject->state;
31-
32+
3233
// Validate current state
3334
if (currState->name < 0 || currState->name >= statesMax) return &invalidState;
34-
35+
3536
// Validate current event
3637
if (event.sig < 0 || event.sig >= eventsMax) return &invalidState;
3738

3839
// Lookup transition table to find the handler for the current state and event
3940
const TEventHandler eventHandler = transitionTable[currState->name][event.sig];
40-
41+
4142
// Call the handler to get the next state and make side effects
4243
if (eventHandler) {
4344
const TState *nextState = eventHandler(activeObject, event);
@@ -46,49 +47,48 @@ const TState *FSM_ProcessEventToNextState(
4647
}
4748

4849
// Return empty state if no transition exists
49-
return &emptyState;
50+
return &emptyState;
5051
};
5152

52-
bool FSM_TraverseNextState(
53-
TActiveObject *const activeObject,
54-
const TState *const nextState) {
53+
// Execute a state hook and return its status.
54+
bool _executeHook(TStateHook hook, TActiveObject *activeObject) {
55+
if (hook) {
56+
return hook(activeObject, NULL);
57+
}
58+
return true; // No hook to execute, so consider it successful
59+
}
5560

56-
// Null args checks
57-
if (NULL == activeObject
58-
|| NULL == nextState) {
61+
bool FSM_TraverseNextState(
62+
TActiveObject *const activeObject,
63+
const TState *const nextState) {
64+
// Null args checks
65+
if (!activeObject || !nextState) {
5966
return false;
60-
};
67+
}
6168

6269
// Validate next state
6370
if (!FSM_IsValidState(nextState)) {
6471
return false;
65-
};
66-
67-
// Call onExit of current state, if any
68-
if (NULL != activeObject->state->onExit) {
69-
bool isSuccessfulHook = activeObject->state->onExit(activeObject, NULL);
70-
if (!isSuccessfulHook) {
71-
return false; // Hook failed
72-
}
7372
}
7473

75-
// Update the state
76-
activeObject->state = (TState *)nextState;
77-
78-
// Call onEnter of next state, if any
79-
if (activeObject->state->onEnter) {
80-
bool isSuccessfulHook = activeObject->state->onEnter(activeObject, NULL);
81-
if (!isSuccessfulHook) {
82-
return false; // Hook failed
83-
}
74+
const TState *currState = &(*activeObject->state);
75+
76+
// If the next state is the same as the current state
77+
if (FSM_IsEqualStates(currState, nextState)) {
78+
return _executeHook(currState->onTraverse, activeObject);
79+
}
80+
81+
// If the next state is different, execute hooks for transitioning
82+
if (!_executeHook(currState->onExit, activeObject)) {
83+
return false;
8484
}
8585

86-
// Finally, call onTraverse of next state, if any
87-
if (activeObject->state->onTraverse) {
88-
bool isSuccessfulHook = activeObject->state->onTraverse(activeObject, NULL);
89-
if (!isSuccessfulHook) {
90-
return false; // Hook failed
91-
}
86+
// Update the state
87+
activeObject->state = (TState *) nextState;
88+
89+
if (!_executeHook(activeObject->state->onEnter, activeObject) ||
90+
!_executeHook(activeObject->state->onTraverse, activeObject)) {
91+
return false;
9292
}
9393

9494
return true;

src/fsm/fsm.h

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ typedef const TState* (*TEventHandler)(TActiveObject *const activeObject, TEvent
3333
* @returns -1 (INVALID_STATE) in case of invalid input args
3434
*/
3535
const TState *FSM_ProcessEventToNextState(
36-
TActiveObject *const activeObject,
37-
TEvent event,
38-
uint32_t statesMax,
39-
uint32_t eventsMax,
40-
const TState statesList[statesMax],
41-
const TEventHandler transitionTable[statesMax][eventsMax]);
36+
TActiveObject *const activeObject, /**< The active object. */
37+
TEvent event, /**< The incoming event. */
38+
uint32_t statesMax, /**< The maximum number of states. */
39+
uint32_t eventsMax, /**< The maximum number of events. */
40+
const TState statesList[statesMax], /**< The list of states. */
41+
const TEventHandler transitionTable[statesMax][eventsMax]); /**< The transition table for state-event. */
4242

4343
/**
4444
* @brief Transitions the active object to the next state
@@ -48,8 +48,6 @@ const TState *FSM_ProcessEventToNextState(
4848
*
4949
* @param[in,out] activeObject The active object.
5050
* @param[in] nextState The next state to transition to.
51-
* @param[in] statesMax The maximum number of states.
52-
* @param[in] statesList The list of states.
5351
*
5452
* @return True if the transition is successful, false otherwise.
5553
*/
@@ -63,12 +61,28 @@ bool FSM_IsEqualStates(const TState *const stateA, const TState *const stateB);
6361
/** @brief Checks if a state is valid (i.e., not an empty or invalid state). */
6462
bool FSM_IsValidState(const TState *const state);
6563

66-
#define DECLARE_GUARD(CONDITION_FUNCTION, ON_TRUE_FUNCTION) \
67-
const TState* GUARD_##CONDITION_FUNCTION##_##ON_TRUE_FUNCTION(TActiveObject *const activeObject, TEvent event) { \
68-
if (CONDITION_FUNCTION(activeObject, event)) return ON_TRUE_FUNCTION(activeObject, event); \
64+
/**
65+
* @brief Guard function declaration macro
66+
* @note should be invoked before using appropriate GUARD macro in transition table
67+
* @details Creates a guard function for a given condition function and two event handler functions.
68+
* For true condition result the first event handler function will be called, for false - the second one.
69+
*
70+
* ### Example
71+
* @code
72+
* TODO make an example
73+
* @endcode
74+
*/
75+
#define DECLARE_GUARD(CONDITION_FUNCTION, ON_TRUE_FUNCTION, ON_FALSE_FUNCTION) \
76+
const TState* GUARD_##CONDITION_FUNCTION##_##ON_TRUE_FUNCTION##_##ON_FALSE_FUNCTION(TActiveObject *const activeObject, TEvent event) { \
77+
if (CONDITION_FUNCTION(activeObject, event)) return ON_TRUE_FUNCTION(activeObject, event); \
78+
return ON_FALSE_FUNCTION(activeObject, event); \
6979
return (const TState*)NULL; \
7080
};
7181

72-
#define GUARD(CONDITION_FUNCTION, ON_TRUE_FUNCTION) (GUARD_##CONDITION_FUNCTION##_##ON_TRUE_FUNCTION)
82+
/**
83+
* @brief Guard function macro
84+
* @note DECLARE_GUARD should be invoked before using appropriate GUARD macro in transition table
85+
*/
86+
#define GUARD(CONDITION_FUNCTION, ON_TRUE_FUNCTION, ON_FALSE_FUNCTION) GUARD_##CONDITION_FUNCTION##_##ON_TRUE_FUNCTION##_##ON_FALSE_FUNCTION
7387

7488
#endif //FSM_H

test/fsm/fsm.test.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
typedef enum { NO_STATE, EMPTY_HOOKS_ST, SUCCESS_HOOKS_ST, FAILURE_HOOKS_ST, STATES_MAX } STATES_NAMES; // state names
99
typedef enum { NO_SIG, GO_EMPTY_HOOKS_ST, GO_SUCCESS_HOOKS_ST, GO_FAILURE_HOOKS_ST, EVENTS_MAX } EVENT_SIGS; // events signals names
1010

11-
bool _successHook(void *const activeObject, void *const ctx) { return true; };
12-
bool _failureHook(void *const activeObject, void *const ctx) { return false; };
11+
bool _successHook(TActiveObject *const AO, void *const ctx) { return true; };
12+
bool _failureHook(TActiveObject *const AO, void *const ctx) { return false; };
1313

1414
const TState statesList[STATES_MAX] = {
1515
[NO_STATE] = {.name = NO_STATE},
@@ -24,12 +24,12 @@ const TState* _goToFailureHooksState(TActiveObject *const activeObject, TEvent e
2424

2525
bool _onTrue(TActiveObject *const activeObject, TEvent event) { return true; };
2626

27-
DECLARE_GUARD(_onTrue, _goToFailureHooksState);
27+
DECLARE_GUARD(_onTrue, _goToFailureHooksState, _goToSuccessHooksState);
2828

2929
const TEventHandler transitionTable[STATES_MAX][EVENTS_MAX] = {
3030
[NO_STATE] = { [GO_EMPTY_HOOKS_ST] = _goToEmmptyHooksState },
3131
[EMPTY_HOOKS_ST] = { [GO_SUCCESS_HOOKS_ST] = _goToSuccessHooksState },
32-
[SUCCESS_HOOKS_ST] = { [GO_FAILURE_HOOKS_ST] = GUARD(_onTrue, _goToFailureHooksState) },
32+
[SUCCESS_HOOKS_ST] = { [GO_FAILURE_HOOKS_ST] = GUARD(_onTrue, _goToFailureHooksState, _goToSuccessHooksState) },
3333
[FAILURE_HOOKS_ST] = { [GO_SUCCESS_HOOKS_ST] = _goToSuccessHooksState },
3434
};
3535

0 commit comments

Comments
 (0)