Skip to content

Commit

Permalink
queue, active obect: split template to declaration and implementation…
Browse files Browse the repository at this point in the history
…. Lift documentation. Add examples to docs
  • Loading branch information
polesskiy-dev committed Aug 12, 2023
1 parent d7b4557 commit aff9292
Show file tree
Hide file tree
Showing 12 changed files with 346 additions and 235 deletions.
5 changes: 3 additions & 2 deletions examples/request-fsm/main.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#include "stdio.h"
#include "stdint.h"

#include "../../src/queue/queue.h"
#include "../../src/active-object/active-object.h"
#include "../../src/active-object/active_object.h"
#include "../../src/active-object/active_object_impl.h"
#include "../../src/fsm/fsm.h"

#define REQUEST_AO REQUEST_AO
Expand Down Expand Up @@ -43,6 +43,7 @@ typedef struct {
} REQUEST_AO_FIELDS;

DECLARE_ACTIVE_OBJECT(REQUEST_AO, REQUEST_EVENT, REQUEST_STATE, REQUEST_AO_FIELDS, REQUEST_QUEUE_MAX_CAPACITY);
ACTIVE_OBJECT_IMPLEMENTATION(REQUEST_AO, REQUEST_EVENT, REQUEST_STATE, REQUEST_AO_FIELDS, REQUEST_QUEUE_MAX_CAPACITY);
DECLARE_FSM(REQUEST_AO, REQUEST_EVENT, REQUEST_STATE, REQUEST_SIG_MAX, REQUEST_ST_MAX);

/**
Expand Down
5 changes: 3 additions & 2 deletions examples/request-retry-fsm/main.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#include "stdio.h"
#include "stdint.h"

#include "../../src/queue/queue.h"
#include "../../src/active-object/active-object.h"
#include "../../src/active-object/active_object.h"
#include "../../src/active-object/active_object_impl.h"
#include "../../src/fsm/fsm.h"

#define REQUEST_AO REQUEST_AO
Expand Down Expand Up @@ -44,6 +44,7 @@ typedef struct {
} REQUEST_AO_FIELDS;

DECLARE_ACTIVE_OBJECT(REQUEST_AO, REQUEST_EVENT, REQUEST_STATE, REQUEST_AO_FIELDS, REQUEST_QUEUE_MAX_CAPACITY);
ACTIVE_OBJECT_IMPLEMENTATION(REQUEST_AO, REQUEST_EVENT, REQUEST_STATE, REQUEST_AO_FIELDS, REQUEST_QUEUE_MAX_CAPACITY);
DECLARE_FSM(REQUEST_AO, REQUEST_EVENT, REQUEST_STATE, REQUEST_SIG_MAX, REQUEST_ST_MAX);

/**
Expand Down
9 changes: 4 additions & 5 deletions examples/simple-blinky-fsm/main.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#include "stdio.h"
#include "stdint.h"

#include "../../src/queue/queue.h"
#include "../../src/active-object/active-object.h"
#include "../../src/active-object/active_object.h"
#include "../../src/active-object/active_object_impl.h"

#define BLINKY_AO BLINKY_AO
#define BLINKY_QUEUE_MAX_CAPACITY (8)
Expand Down Expand Up @@ -38,9 +38,10 @@ const char *const STATES_STRINGS[] = {
};

/**
* Active Object Declarations
* Active Object
*/
DECLARE_ACTIVE_OBJECT(BLINKY_AO, BLINKY_EVENT, BLINKY_STATE, void*, BLINKY_QUEUE_MAX_CAPACITY);
ACTIVE_OBJECT_IMPLEMENTATION(BLINKY_AO, BLINKY_EVENT, BLINKY_STATE, void*, BLINKY_QUEUE_MAX_CAPACITY);

/**
* Application and local declarations
Expand All @@ -52,8 +53,6 @@ void runTasks();
BLINKY_STATE LOCAL_FSM_GetNextState(BLINKY_AO * const activeObject, BLINKY_EVENT e);
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, AO_BLINKY_ID, BLINKY_NO_ST, NULL);

Expand Down
5 changes: 0 additions & 5 deletions src/active-object/active-object.c

This file was deleted.

122 changes: 0 additions & 122 deletions src/active-object/active-object.h

This file was deleted.

1 change: 1 addition & 0 deletions src/active-object/active_object.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include "./active_object.h"
133 changes: 133 additions & 0 deletions src/active-object/active_object.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/**
* @file active_object.h
*
* @brief Active Object Design Pattern Implementation
*
* @details
* The Active Object design pattern decouples method execution from method invocation.
* This pattern provides a higher level of concurrency while keeping synchronization
* simple.
*
* This implementation is built upon:
* - An event-driven model where events are stored in a queue.
* - A mechanism to dispatch and handle these events.
* - State management to control the behavior and state transitions of the Active Object.
*
* Features:
* - Uses a queue for event storage, ensuring that events are processed in a First-In-First-Out (FIFO) manner.
* - Supports user-defined event types, state types, and additional fields - to store particular data.
* - Provides mechanisms to dispatch events, transition between states, and process the event queue.
*
* This file provides the macros, data structures, and function prototypes required
* to create and operate an Active Object.
*
* @see queue.h for the underlying queue implementation.
*
* @author apolisskyi
*/

#ifndef ACTIVE_OBJECT_H
#define ACTIVE_OBJECT_H

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

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

// TODO should we implement publish/subscribe? So we can have a list of subscriptions inside the object?
// The main idea here is to resolve a situation when multiple objects consumes same event e.g. from ISR

// TODO it seems that global superloop is kinda "scheduler" and we need there "isIdle" function to sleep on lack of events

/**
* @struct Struct for defining the Active Object
* @param queue Queue to store events for the Active Object
* @param id Base of the Active Object containing ID
* @param state Current state of the Active Object
* @param fields User-defined fields
*/

/**
* @fn void ACTIVE_OBJECT_T##_Ctor(ACTIVE_OBJECT_T *const self, uint8_t id, STATE_T initialState, FIELDS_T fields)
* @brief Active Object Constructor
*
* @param self Pointer to the Active Object
* @param initialState Initial state of the Active Object
* @param fields User-defined fields
*
* @example Create a new active object:
* MY_ACTIVE_OBJECT myObject;
* MY_ACTIVE_OBJECT_Ctor(&myObject, 1, initialStateEnum, myFields);
*/

/**
* @fn void ACTIVE_OBJECT_T##_Dispatch(ACTIVE_OBJECT_T *const self, EVENT_T event);
* @brief Dispatch an event to the Active Object
*
* @param self Pointer to the Active Object
* @param event Event to be dispatched
*
* @example Dispatching an event to the active object:
* MY_EVENT myEvent = { .sig=SOME_SIG };
* MY_ACTIVE_OBJECT_Dispatch(&myObject, myEvent);
*/

/**
* @fn bool ACTIVE_OBJECT_T##_basicTransitionToNextState(ACTIVE_OBJECT_T *const self, STATE_T nextState)
* @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, handle here exit -> enter -> traverse for states, implementing Mealy&Moore Hybrid FSM
*
* @example Transition to a new state:
* MY_STATE newState = ...;
* MY_ACTIVE_OBJECT_basicTransitionToNextState(&myObject, newState);
*/

/**
* @fn void ACTIVE_OBJECT_T##_ProcessQueue(ACTIVE_OBJECT_T *const self, EVENT_T##_HANDLER_F eventHandlerCb, STATE_T##_TRANSITION_F transitionToNextStateCb, ACTIVE_OBJECT_T##HAS_EMPTY_QUEUE_F hasEmptyQueueCb)
* @brief Process the event queue of the Active Object
*
* @param self Pointer to the Active Object
* @param eventHandlerCb Callback to handle queue event, returns next state
* @param transitionToNextStateCb Callback to transition to the next state
* @param hasEmptyQueueCb Callback to handle empty queue
*
* @example Processing the event queue:
* MY_ACTIVE_OBJECT_ProcessQueue(&myObject, myEventHandler, myTransitionCb, myEmptyQueueHandler); \
*/

/**
* @def Active object Template
*
* @param ACTIVE_OBJECT_T Active Object Type
* @param EVENT_T Event Type
* @param STATE_T State Type
* @param FIELDS_T Active Object field Type
* @param id ID value
* @param maxQueueCapacity Queue capacity value
*
* @example DECLARE_ACTIVE_OBJECT(MY_ACTIVE_OBJECT, MY_EVENT, MY_STATE, MY_FIELDS, 16)
*/
#define DECLARE_ACTIVE_OBJECT(ACTIVE_OBJECT_T, EVENT_T, STATE_T, FIELDS_T, maxQueueCapacity) \
DECLARE_QUEUE(EVENT_T, maxQueueCapacity); \
\
typedef struct { \
QUEUE_##EVENT_T queue; \
uint8_t id; \
STATE_T state; \
FIELDS_T fields; \
} 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); \
\
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##_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, ACTIVE_OBJECT_T##HAS_EMPTY_QUEUE_F hasEmptyQueueCb);

#endif //ACTIVE_OBJECT_H
35 changes: 35 additions & 0 deletions src/active-object/active_object_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#ifndef ACTIVE_OBJECT_IMPL_H
#define ACTIVE_OBJECT_IMPL_H

#include "./active_object.h"
#include "../queue/queue_impl.h"

#define ACTIVE_OBJECT_IMPLEMENTATION(ACTIVE_OBJECT_T, EVENT_T, STATE_T, FIELDS_T, maxQueueCapacity) \
QUEUE_IMPLEMENTATION(EVENT_T, maxQueueCapacity); \
\
void ACTIVE_OBJECT_T##_Ctor(ACTIVE_OBJECT_T *const self, uint8_t id, STATE_T initialState, FIELDS_T fields) { \
QUEUE_##EVENT_T##_Ctor(&self->queue); \
self->id = id; \
self->state = initialState; \
self->fields = fields; \
} \
\
void ACTIVE_OBJECT_T##_Dispatch(ACTIVE_OBJECT_T *const self, EVENT_T event) { \
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##_basicTransitionToNextState(ACTIVE_OBJECT_T *const self, STATE_T nextState) { \
self->state = nextState; \
return true; \
} \
\
void ACTIVE_OBJECT_T##_ProcessQueue(ACTIVE_OBJECT_T *const self, EVENT_T##_HANDLER_F eventHandlerCb, STATE_T##_TRANSITION_F transitionToNextStateCb, 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); \
transitionToNextStateCb(self, nextState); \
}

#endif //ACTIVE_OBJECT_IMPL_H
Loading

0 comments on commit aff9292

Please sign in to comment.