Skip to content

Commit

Permalink
fsm, active-object: refactor by adding assers, improve description an…
Browse files Browse the repository at this point in the history
…d reformat, fix blinky example
  • Loading branch information
polesskiy-dev committed Aug 9, 2023
1 parent 5942a49 commit 896724e
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 86 deletions.
Binary file added .DS_Store
Binary file not shown.
14 changes: 8 additions & 6 deletions examples/request-fsm/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ REQUEST_STATE processEventToNextState(REQUEST_AO *const activeObject, REQUEST_EV

DECLARE_GUARD(REQUEST_AO, REQUEST_EVENT, canRetry, performRequest, requestError);

// state transitions table, [state][event] => state handler f pointer
REQUEST_STATE_HANDLE_F requestTransitionTable[REQUEST_ST_MAX][REQUEST_SIG_MAX] = {
// state transitions table, [state][event] => event handler f pointer
REQUEST_EVENT_HANDLE_F requestTransitionTable[REQUEST_ST_MAX][REQUEST_SIG_MAX] = {
[REQUEST_NO_ST]= {[MAKE_REQUEST_SIG]=performRequest},
[PENDING_ST]= {[REQUEST_SUCCESS_SIG]=requestSuccess, [REQUEST_ERROR_SIG]=GUARD(canRetry, performRequest, requestError), [TIMEOUT_SIG]=GUARD(canRetry, performRequest, requestError)}
};
Expand Down Expand Up @@ -111,6 +111,12 @@ void runTasks() {
REQUEST_AO_ProcessQueue(&requestActiveObject, processEventToNextState, NULL);
};

REQUEST_STATE processEventToNextState(REQUEST_AO *const activeObject, REQUEST_EVENT event) {
REQUEST_STATE nextState = REQUEST_AO_FSM_ProcessEventToNextState(activeObject, event, requestTransitionTable);

return nextState;
};

REQUEST_STATE performRequest(REQUEST_AO *const activeObject, REQUEST_EVENT event) {
if (REQUEST_ERROR_SIG == event.sig || TIMEOUT_SIG == event.sig) activeObject->fields.maxRetries--;

Expand All @@ -123,10 +129,6 @@ bool canRetry(REQUEST_AO *const activeObject, REQUEST_EVENT event) {
return false;
};

REQUEST_STATE processEventToNextState(REQUEST_AO *const activeObject, REQUEST_EVENT event) {
return REQUEST_AO_FSM_ProcessEventToNextState(activeObject, event, requestTransitionTable);
};

REQUEST_STATE requestError(REQUEST_AO *const activeObject, REQUEST_EVENT event) {
printf("Request error occurs\n");
activeObject->fields.maxRetries--;
Expand Down
2 changes: 1 addition & 1 deletion examples/request-retry-fsm/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ REQUEST_STATE_HANDLE_F requestTransitionTable[REQUEST_ST_MAX][REQUEST_SIG_MAX] =
};

int main(void) {
printf("Starting Request with retey FSM\n\n");
printf("Starting Request with retry FSM\n\n");

return 0;
};
Expand Down
18 changes: 7 additions & 11 deletions examples/simple-blinky-fsm/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const char *const STATES_STRINGS[] = {
/**
* Active Object Declarations
*/
DECLARE_ACTIVE_OBJECT(BLINKY_AO, BLINKY_EVENT, BLINKY_STATE, AO_BLINKY_ID, BLINKY_QUEUE_MAX_CAPACITY);
DECLARE_ACTIVE_OBJECT(BLINKY_AO, BLINKY_EVENT, BLINKY_STATE, void*, AO_BLINKY_ID, BLINKY_QUEUE_MAX_CAPACITY);

/**
* Application and local declarations
Expand All @@ -50,13 +50,12 @@ BLINKY_AO blinkyActiveObject;
void runTasks();
/** local FSM functions, */
BLINKY_STATE LOCAL_FSM_GetNextState(BLINKY_AO * const activeObject, BLINKY_EVENT e);
void LOCAL_FSM_ProcessEvent(BLINKY_AO * const activeObject, BLINKY_EVENT e);
void LOCAL_FSM_NextStateTransition(BLINKY_AO * const activeObject, BLINKY_STATE nextState);
BLINKY_STATE LOCAL_FSM_ProcessEvent(BLINKY_AO * const activeObject, BLINKY_EVENT e);

// TODO get rid of INIT state

int main(void) {
BLINKY_AO_Ctor(&blinkyActiveObject, BLINKY_INIT_ST);
BLINKY_AO_Ctor(&blinkyActiveObject, BLINKY_NO_ST, NULL);

printf("Starting Blinky FSM\n");
printf("Dispatching INIT Event\n");
Expand Down Expand Up @@ -87,6 +86,7 @@ BLINKY_STATE LOCAL_FSM_GetNextState(BLINKY_AO * const activeObject, BLINKY_EVENT
const BLINKY_STATE currState = activeObject->state;

switch(currState) {
case BLINKY_NO_ST:
case BLINKY_INIT_ST:
switch (e.sig) {
case BLINKY_INIT_SIG:
Expand Down Expand Up @@ -116,12 +116,8 @@ void runTasks() {
BLINKY_AO_ProcessQueue(&blinkyActiveObject, &LOCAL_FSM_ProcessEvent, NULL);
}

void LOCAL_FSM_ProcessEvent(BLINKY_AO *const activeObject, BLINKY_EVENT e) {
BLINKY_STATE LOCAL_FSM_ProcessEvent(BLINKY_AO *const activeObject, BLINKY_EVENT e) {
BLINKY_STATE nextState = LOCAL_FSM_GetNextState(activeObject, e);
// set state
LOCAL_FSM_NextStateTransition(activeObject, nextState);
};

void LOCAL_FSM_NextStateTransition(BLINKY_AO *const activeObject, BLINKY_STATE nextState) {
activeObject->state = nextState;
}
return nextState;
};
Binary file modified main
Binary file not shown.
123 changes: 81 additions & 42 deletions src/active-object/active-object.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* @file queue.h
* @file active-object.h
*
* @version 0.0.1
* @author apolisskyi
Expand All @@ -10,6 +10,7 @@
#define ACTIVE_OBJECT_H

#include <stdbool.h>
#include <stdint.h>

#include "../queue/queue.h"

Expand Down Expand Up @@ -48,50 +49,88 @@ void ACTIVE_OBJECT_BASE_Ctor(ACTIVE_OBJECT_BASE *const self, uint8_t id) {
* @param id ID value
* @param maxQueueCapacity Queue capacity value
*/
#define DECLARE_ACTIVE_OBJECT(ACTIVE_OBJECT_T, EVENT_T, STATE_T, FIELDS_T, id, maxQueueCapacity) \
\
DECLARE_QUEUE(EVENT_T, maxQueueCapacity); \
\
typedef struct { \
ACTIVE_OBJECT_BASE super; \
QUEUE_##EVENT_T queue; \
STATE_T state; \
FIELDS_T fields; \
} ACTIVE_OBJECT_T; \
\
#define DECLARE_ACTIVE_OBJECT(ACTIVE_OBJECT_T, EVENT_T, STATE_T, FIELDS_T, id, maxQueueCapacity) \
\
DECLARE_QUEUE(EVENT_T, maxQueueCapacity); \
\
/**
* @brief Event handler callback function type
*/\
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); \
\
void ACTIVE_OBJECT_T##_Ctor(ACTIVE_OBJECT_T *const self, STATE_T initialState, FIELDS_T fields) { \
ACTIVE_OBJECT_BASE_Ctor(&self->super, id); \
QUEUE_##EVENT_T##_Ctor(&self->queue); \
self->state = initialState; \
self->fields = fields; \
}; \
\
void ACTIVE_OBJECT_T##_Dispatch(ACTIVE_OBJECT_T *const self, EVENT_T event) { \
QUEUE_##EVENT_T##_Enqueue(&self->queue, event); \
* @brief Struct for defining the Active Object
*
* @param super Base of the Active Object containing ID
* @param queue Queue to store events for the Active Object
* @param state Current state of the Active Object
* @param fields User-defined fields to store additional information
*/\
typedef struct { \
ACTIVE_OBJECT_BASE super; \
QUEUE_##EVENT_T queue; \
STATE_T state; \
FIELDS_T fields; \
} ACTIVE_OBJECT_T; \
\
/**
* @brief Event handler callback function type
*/ \
typedef STATE_T (*EVENT_T##_HANDLER_F)(ACTIVE_OBJECT_T *const self, EVENT_T event); \
\
/**
* @brief Callback for handling an empty queue
*/ \
typedef void (*ACTIVE_OBJECT_T##HAS_EMPTY_QUEUE_F)(ACTIVE_OBJECT_T *const self); \
\
/**
* @brief Constructor for Active Object
*
* @param self Pointer to the Active Object
* @param initialState Initial state of the Active Object
* @param fields User-defined fields
*/ \
void ACTIVE_OBJECT_T##_Ctor(ACTIVE_OBJECT_T *const self, STATE_T initialState, FIELDS_T fields) { \
ACTIVE_OBJECT_BASE_Ctor(&self->super, id); \
QUEUE_##EVENT_T##_Ctor(&self->queue); \
self->state = initialState; \
self->fields = fields; \
}; \
\
void transitionToNextState(ACTIVE_OBJECT_T *const self, STATE_T nextState) { \
self->state = nextState; \
};\
\
/**
* Run it in Tasks()
* // TODO maybe add arg hasEmptyQueueCb() - to notify that MCU can go to sleep?
*/\
void ACTIVE_OBJECT_T##_ProcessQueue(ACTIVE_OBJECT_T *const self, EVENT_T##_HANDLER_F eventHandlerCb, ACTIVE_OBJECT_T##HAS_EMPTY_QUEUE_F hasEmptyQueueCb) { \
bool isEmptyQueue = EMPTY_QUEUE == QUEUE_##EVENT_T##_GetSize(&self->queue); \
\
if (isEmptyQueue) return hasEmptyQueueCb(self); \
\
EVENT_T e = QUEUE_##EVENT_T##_Dequeue(&self->queue); \
* @brief Dispatch an event to the Active Object
*
* @param self Pointer to the Active Object
* @param event Event to be dispatched
*/ \
void ACTIVE_OBJECT_T##_Dispatch(ACTIVE_OBJECT_T *const self, EVENT_T event) { \
/*if (QUEUE_##EVENT_T##_IsFull(&self->queue)) return;*/ /* Handle error, e.g., logging */ \
QUEUE_##EVENT_T##_Enqueue(&self->queue, event); \
}; \
\
/**
* @brief Transition to the next state of the Active Object
*
* @param self Pointer to the Active Object
* @param nextState Next state to transition to
*
* TODO should be improved, temporary inline
*/ \
static inline void ACTIVE_OBJECT_T##_transitionToNextState(ACTIVE_OBJECT_T *const self, STATE_T nextState) { \
self->state = nextState; \
}; \
\
/**
* @brief Process the event queue of the Active Object
*
* @param self Pointer to the Active Object
* @param eventHandlerCb Callback to handle events
* @param hasEmptyQueueCb Callback to handle empty queue
*/ \
void ACTIVE_OBJECT_T##_ProcessQueue(ACTIVE_OBJECT_T *const self, EVENT_T##_HANDLER_F eventHandlerCb, \
ACTIVE_OBJECT_T##HAS_EMPTY_QUEUE_F hasEmptyQueueCb) { \
bool isEmptyQueue = EMPTY_QUEUE == QUEUE_##EVENT_T##_GetSize(&self->queue); \
\
if (isEmptyQueue) return hasEmptyQueueCb(self); \
\
EVENT_T e = QUEUE_##EVENT_T##_Dequeue(&self->queue); \
STATE_T nextState = eventHandlerCb(self, e); \
transitionToNextState(self, nextState); \
};
ACTIVE_OBJECT_T##_transitionToNextState(self, nextState); \
};\

#endif //ACTIVE_OBJECT_H
56 changes: 38 additions & 18 deletions src/fsm/fsm.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,39 +11,59 @@

#include <assert.h>

// TODO for HSM add to STATE struct pointers: to Parent and to Child Nodes, Level from top level state

/*
* typedef struct { \
STATE_T##_HANDLE_F enter; \
STATE_T##_HANDLE_F traverse; \
STATE_T##_HANDLE_F exit; \
} STATE_T##_HANDLE_FUNCTIONS; \
\
STATE_T##_HANDLE_FUNCTIONS stateHandleFunctionsList[statesMax]\\
*/

/**
* @brief Active Object FSM - A Hybrid of Mealy and Moore State Machines.
*
* This Finite State Machine (FSM) implementation combines the elements of both Mealy and Moore models:
*
* - **Moore Machine**: The FSM provides an `enter` function for states. This function is executed as soon as a state is entered,
* which is characteristic of Moore machines where outputs (or actions in our case) depend solely on the current state.
*
* - **Mealy Machine**: The FSM processes events or inputs using functions like `run`. This behavior is reminiscent of Mealy machines
* where outputs (or actions) are dependent on both the current state and the input event.
*
* By combining these elements, this FSM offers the flexibility of both models, allowing for actions on both state entry and based on events.
*/
#define DECLARE_FSM(ACTIVE_OBJECT_T, EVENT_T, STATE_T, eventsMax, statesMax) \
/**
* @brief State handler function type
*/\
typedef STATE_T (*STATE_T##_HANDLE_F)(ACTIVE_OBJECT_T *activeObject, EVENT_T event); \
typedef bool (*STATE_T##_GUARD_F)(ACTIVE_OBJECT_T *activeObject, EVENT_T event); \
\
typedef struct { \
STATE_T##_HANDLE_F run; \
STATE_T##_HANDLE_F enter; \
STATE_T##_HANDLE_F exit; \
} STATE_T##_STATE; \
\
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); \
/**
* @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 event[in] - new event
* @param transitionTable[in] - state handlers functions 2D array
* @param transitionTable[in] - event handlers functions 2D array
* @param stateHandleFunctionsList[in] - state handle functions array
* @return next state
*/\
STATE_T ACTIVE_OBJECT_T##_FSM_ProcessEventToNextState(\
ACTIVE_OBJECT_T *const activeObject, \
EVENT_T event, \
STATE_T##_HANDLE_F transitionTable[statesMax][eventsMax] \
) { \
EVENT_T event, \
EVENT_T##_HANDLE_F transitionTable[statesMax][eventsMax] \
) { \
STATE_T currState = activeObject->state; \
STATE_T##_HANDLE_F stateHandler = transitionTable[currState][event.sig]; \
assert(NULL != stateHandler); \
STATE_T nextState = stateHandler(activeObject, event); \
return nextState; \
EVENT_T##_HANDLE_F stateHandler = transitionTable[currState][event.sig]; \
\
assert(NULL != stateHandler); \
\
STATE_T nextState = stateHandler(activeObject, event); \
\
return nextState; \
}; \


Expand Down
8 changes: 0 additions & 8 deletions src/queue/queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@
#include <stdbool.h>
#include <assert.h>

/**
* @name QUEUE TEMPLATE
*
* @{
*/

/** suggested max queue capacity */
#define QUEUE_MAX_CAPACITY (16)
#define EMPTY_QUEUE (0)
Expand Down Expand Up @@ -132,6 +126,4 @@
return q->elements[q->front]; \
}

/** @} */

#endif
12 changes: 12 additions & 0 deletions test/active-object/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#TODO fix it

CC = gcc
CFLAGS = -I../../libraries/Unity/src/ -I../../src/active-object/
SRC_FILES = active-object.c unity.c active-object.test.c
OUT_FILE = test_active_object

all:
$(CC) $(CFLAGS) $(SRC_FILES) -o $(OUT_FILE)

test: all
./$(OUT_FILE)
Loading

0 comments on commit 896724e

Please sign in to comment.