fseq.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. #ifndef FUNCTOR_SEQUENCER_H
  2. #define FUNCTOR_SEQUENCER_H
  3. #include <stdint.h>
  4. #include <stddef.h>
  5. //
  6. // Functor Sequencer v1.0
  7. //
  8. // Sychov A., jul/2019
  9. //
  10. // The functor sequencer.
  11. // This module implements the model of abstract programming (behaviour) that
  12. // implying a set of abstract states are switching sequently using so called
  13. // functors. The object that involves an user-defined context, user-defined
  14. // set of abstract states is called "sequencer". Each functor represents either
  15. // the only user-defined function with user context, or a set of such functions.
  16. // The set (@fFSeqEntry_t) consist of the user-defined function with user
  17. // context and two special user-defined functions - "enter" and "leave" -
  18. // that are called on the state changes. The state can be changed in two ways:
  19. // internally by the function itself and externally by calling module function
  20. // "Startup". The first way is the main and the only correct method of chaning
  21. // state of the running sequencer. The other method is should only used to start
  22. // up the sequencer with entering the specified state after constructing of a
  23. // sequencer object. To switch of running sequencer the "Shutdown" method is used.
  24. // The method "Dispatch" provides the sequencer operation and must be called
  25. // externally to make the sequencer to perform it's function.
  26. // The "entry" and "exit" functions are called automatically by the sequencer
  27. // if the functions are enabled by setting @FSEQ_ENTRY_EXIT_SUPPORT to 1.
  28. // The "entry" function has a prototype @fSeqEnter_t.
  29. // The "leave" function has a prototype @fSeqLeave_t.
  30. #define FSEQ_ENTRY_EXIT_SUPPORT 1
  31. // @FSEQ_ALT_INHERIT_METHOD_SUPPORT
  32. // Each state may have it's own private context. This macro chooses the method
  33. // of accessing to the private context.
  34. // If @FSEQ_ALT_INHERIT_METHOD_SUPPORT is zero (Method #0):
  35. // the state function body can get access to the state private context using casting
  36. // the pointer @fFSeqEntry_t* to the pointer to container:
  37. // const struct { fFSeqEntry_t state; void * ctx_ref_ven; } stateContainer;
  38. // This method uses the @ctx_ref_ven constant pointer a.k.a veneer, that refers
  39. // to the actual private context:
  40. // const fFSeqEntry_t * stateFunction( const fFSeqEntry_t * this,
  41. // tFSeqCtx_t ctx, const fFSeqEntry_t * * pnext )
  42. // {
  43. // // get access to the @ctx_ref_ven field from @stateContainer structure
  44. // const void * const_ctx_ref_ven = (const void*)(&this[1]);
  45. // ...
  46. // void * private_ctx = (void*)const_ctx_ref; // cast the pointer
  47. // ... use @private_ctx
  48. // }
  49. // Advantage: the @ctx_ref_ven is constant and refers to the private context directly,
  50. // easy to manage the private context for each state, easy code support,
  51. // you do not need to keep the state pointer into the RAM.
  52. // Disadvantage: the same, the @ctx_ref_ven is constant, and if you need multiple instances
  53. // of the the same state, you need to create multiply copies of the state entry,
  54. // due to you need to change @ctx_ref_ven field.
  55. //
  56. // If @FSEQ_ALT_INHERIT_METHOD_SUPPORT is non-zero (Method #1):
  57. // Alternative method. The state function body can get access to the state private context
  58. // the same way, using casting the pointer @fFSeqEntry_t* to the pointer to container, but
  59. // the container must be declared in the RAM:
  60. // typedef struct { ... } tPrivateContextData_t;
  61. // struct { fFSeqEntry_t state; tPrivateContextData_t data; } stateContainer;
  62. // // ...
  63. // const fFSeqEntry_t * stateFunction( const fFSeqEntry_t * this,
  64. // tFSeqCtx_t ctx, const fFSeqEntry_t * * pnext )
  65. // {
  66. // // get access to the actual private context @private_ctx (field from @stateContainer structure)
  67. // tPrivateContextData_t * private_ctx_data = (void*)(&this[1]);
  68. // ...
  69. // ... use @private_ctx_data
  70. // }
  71. //
  72. // Advantage: since the @stateContainer is located in the RAM, you don't need a veneer,
  73. // the private fields are located directly "after" the state entry in the memory,
  74. // you can easily to add new instances of the state by creating new container.
  75. // Disadvantage: each container for state instance must contain the state entry pointer, that
  76. // is located in the RAM, also the code support is more complicated due to
  77. // you need to link the dynamic container and the static state entry.
  78. //
  79. // Internally, the method #0 is quite differ than the method #1, because the state entry actually
  80. // does not contain any state functions in the method #1 but only the virtual table pointer,
  81. // and in the method #0 the state entry consist of the state functions itself. It is a important
  82. // point to understand how it works and how to inherit and embedd the state entry in your application.
  83. // But if you do not need a private context, it is does not matter which method you choose due to
  84. // the interface of the module does not depend on this issue.
  85. #define FSEQ_ALT_INHERIT_METHOD_SUPPORT 1
  86. // User is allowed to pass some user-dependent context via special parameter
  87. // of type "tFSeqCtx_t". This argument must be set on constructing the
  88. // sequencer object during the constructing function call (@fSeqConstruct).
  89. typedef void * tFSeqCtx_t; // user context
  90. // @fFSeqEntry_t (forward declaration)
  91. // The set of user-defined functions (see below)
  92. struct fFSeqEntry_t;
  93. typedef const struct fFSeqEntry_t * ( * fSeq_t)( const struct fFSeqEntry_t * this,
  94. tFSeqCtx_t ctx,
  95. const struct fFSeqEntry_t * * pnext );
  96. #if FSEQ_ENTRY_EXIT_SUPPORT
  97. typedef void ( * fSeqEnter_t )( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx );
  98. typedef void ( * fSeqLeave_t )( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx );
  99. #endif
  100. #if FSEQ_ALT_INHERIT_METHOD_SUPPORT
  101. // The user-defined state methods.
  102. // Is only used when FSEQ_ALT_INHERIT_METHOD_SUPPORT is enabled.
  103. // The structure represents the state virtual table methods.
  104. typedef struct fFSeqVTable_t
  105. {
  106. fSeq_t f; // function body
  107. #if FSEQ_ENTRY_EXIT_SUPPORT //
  108. fSeqEnter_t enter; // entry-function
  109. fSeqLeave_t leave; // exit-function
  110. #endif
  111. }
  112. fFSeqVTable_t;
  113. #endif
  114. // @fFSeqEntry_t
  115. // The user-defined state entry.
  116. // Can either contain the user-defined methods itself, or
  117. // contain only reference to the methods table (see @FSEQ_ALT_INHERIT_METHOD_SUPPORT)
  118. // User can use "inheritance" method to declare some
  119. // additional state-dependent context, see the example below.
  120. typedef struct fFSeqEntry_t
  121. {
  122. #if FSEQ_ALT_INHERIT_METHOD_SUPPORT
  123. const fFSeqVTable_t * vtbl; // state virtual methods table reference
  124. #else
  125. fSeq_t f; // function body
  126. #if FSEQ_ENTRY_EXIT_SUPPORT //
  127. fSeqEnter_t enter; // entry-function
  128. fSeqLeave_t leave; // exit-function
  129. #endif
  130. #endif
  131. }
  132. fFSeqEntry_t;
  133. #if FSEQ_ALT_INHERIT_METHOD_SUPPORT == 0
  134. // @DECLARE_FSEQ_STATE_ENTRY_WITH_CONTEXT
  135. // Declares user-defined state for sequencer
  136. // @name - the name of the state;
  137. // @ctx_var - user context variable (structure)
  138. // @... - va_arg arguments: state functions list (body, entry, exit)
  139. #define DECLARE_FSEQ_STATE_ENTRY_WITH_CONTEXT(statename, ctx_var, ...)\
  140. const struct {\
  141. fFSeqEntry_t stateEntry;\
  142. void * ctx_ref;\
  143. } statename = {\
  144. .stateEntry = { __VA_ARGS__ },\
  145. .ctx_ref = &ctx_var\
  146. };
  147. #define DECLARE_FSEQ_STATE_CONTEXT_BEGIN( statename, ctx_var )\
  148. struct {
  149. #define DECLARE_FSEQ_STATE_CONTEXT_END( statename, ctx_var )\
  150. } ctx_var;
  151. #define DECLARE_FSEQ_STATE_CONTEXT_END_INIT_BEGIN( statename, ctx_var )\
  152. } ctx_var = {
  153. #define DECLARE_FSEQ_STATE_CONTEXT_END_INIT_END( statename, ctx_var )\
  154. };
  155. #else
  156. #define DECLARE_FSEQ_STATE_ENTRY_WITH_CONTEXT(statename, ctx_var, ...)\
  157. const fFSeqVTable_t statename##_vtbl = { __VA_ARGS__ };
  158. #define DECLARE_FSEQ_STATE_CONTEXT_BEGIN( statename, ctx_var )\
  159. extern const fFSeqVTable_t statename##_vtbl;\
  160. const struct {\
  161. fFSeqEntry_t stateEntry;\
  162. #define DECLARE_FSEQ_STATE_CONTEXT_END( statename, ctx_var )\
  163. } ctx_var = { \
  164. .stateEntry = { .vtbl = &statename##_vtbl },\
  165. };
  166. #define DECLARE_FSEQ_STATE_CONTEXT_END_INIT_BEGIN( statename, ctx_var )\
  167. } ctx_var = { \
  168. .stateEntry = { .vtbl = &statename##_vtbl },
  169. #define DECLARE_FSEQ_STATE_CONTEXT_END_INIT_END( statename, ctx_var )\
  170. };
  171. #endif
  172. /*
  173. // ------------------------------------------------------------------------------------------------
  174. // Inheritance example (Method #0, see @FSEQ_ALT_INHERIT_METHOD_SUPPORT):
  175. xFSeqObj_t seq; // sequencer
  176. // ...
  177. // first state vtable method prototypes:
  178. const fFSeqEntry_t * firstStateFunction( const fFSeqEntry_t *, tFSeqCtx_t, const fFSeqEntry_t * * );
  179. void firstStateEnter( const fFSeqEntry_t *, tFSeqCtx_t );
  180. void firstStateLeave( const fFSeqEntry_t *, tFSeqCtx_t );
  181. // ...
  182. struct
  183. {
  184. int publicVar;
  185. }
  186. GlobalContext; // no comments
  187. // ...
  188. typedef struct
  189. {
  190. int privateVar;
  191. }
  192. tFirstStatePrivateContext;
  193. tFirstStatePrivateContext firstStatePrivateDynamicContext; // the dynamic private context of first state (RAM)
  194. // ...
  195. // The state user-structure, incuding the state entry itself (firstState) and the const pointer to the another object.
  196. const struct {
  197. fFSeqEntry_t firstState;
  198. void * ctx_ref;
  199. } firstStateExtended = {
  200. .firstState = { firstStateFunction, firstStateEnter, firstStateLeave },
  201. .ctx_ref = &firstStatePrivateDynamicContext // link the const pointer with the dynamic object
  202. };
  203. // ...
  204. void foo() // some user function
  205. {
  206. fSeqConstruct( &seq, &firstStateExtended.firstState, &GlobalContext ); // consturct sequencer and link the global context
  207. }
  208. // ...
  209. // @firstStateFunction
  210. // firstState function body
  211. const fFSeqEntry_t * firstStateFunction( const fFSeqEntry_t * this, tFSeqCtx_t ctx, const fFSeqEntry_t * * pnext )
  212. {
  213. const void * const_ctx_ref = (const void*)(&this[1]); // get access to the @ctx_ref field from @firstStateExtended structure
  214. // ...
  215. tFirstStatePrivateContext * private_ctx = (void*)const_ctx_ref; // cast the pointer
  216. // ...
  217. private_ctx->privateVar = 1; // use private context
  218. }
  219. // ------------------------------------------------------------------------------------------------
  220. */
  221. /*
  222. // ------------------------------------------------------------------------------------------------
  223. // Inheritance example (Method #1, see @FSEQ_ALT_INHERIT_METHOD_SUPPORT):
  224. xFSeqObj_t seq; // sequencer
  225. // ...
  226. // first state vtable method prototypes:
  227. const fFSeqEntry_t * firstStateFunction( const fFSeqEntry_t *, tFSeqCtx_t, const fFSeqEntry_t * * );
  228. void firstStateEnter( const fFSeqEntry_t *, tFSeqCtx_t );
  229. void firstStateLeave( const fFSeqEntry_t *, tFSeqCtx_t );
  230. // ...
  231. struct
  232. {
  233. int publicVar;
  234. }
  235. GlobalContext; // no comments
  236. // ...
  237. typedef struct
  238. {
  239. int privateVar;
  240. }
  241. tFirstStatePrivateContext;
  242. // ...
  243. typedef struct
  244. {
  245. fFSeqEntry_t firstState;
  246. tFirstStatePrivateContext privateContext;
  247. }
  248. tFirstStateAndPrivateContext;
  249. // the structure of state methods:
  250. const fFSeqVTable_t firstStateVTable = { firstStateFunction, firstStateEnter, firstStateLeave };
  251. // The state user-structure, incuding the state entry, and the private context itself:
  252. tFirstStateAndPrivateContext firstStateExtended = { // the dynamic private context of first state (RAM)
  253. .firstState.vtbl = &firstStateVTable,
  254. .privateContext = { .privateVar = 0, // some values...
  255. }
  256. };
  257. // ...
  258. // ...
  259. void foo() // some user function
  260. {
  261. fSeqConstruct( &seq, &firstStateExtended.firstState, &GlobalContext ); // consturct sequencer and link the global context
  262. }
  263. // ...
  264. // @firstStateFunction
  265. // firstState function body
  266. const fFSeqEntry_t * firstStateFunction( const fFSeqEntry_t * this, tFSeqCtx_t ctx, const fFSeqEntry_t * * pnext )
  267. {
  268. tFirstStatePrivateContext * private_ctx = (tFirstStatePrivateContext*)(&this[1]); // get access to the private context
  269. // ...
  270. private_ctx->privateVar = 1; // use private context
  271. }
  272. // ------------------------------------------------------------------------------------------------
  273. */
  274. // @xFSeqObj_t
  275. // The sequencer object, contains some private fields
  276. // that must not be accessed by user.
  277. typedef struct
  278. {
  279. const void * private[2];
  280. }
  281. xFSeqObj_t;
  282. // @fSeqConstruct
  283. // Construct the sequencer object, but not run it.
  284. // @fSeqObj - the object to construct
  285. // @first - the main state of the object, can not be NULL
  286. // @ctx - user-defined context, can be NULL
  287. void fSeqConstruct( xFSeqObj_t * fSeqObj, const fFSeqEntry_t * first, tFSeqCtx_t ctx );
  288. // @fSeqStartup
  289. // Starts the constructed sequencer.
  290. // @fSeqObj - the sequencer object to start
  291. // @first - the state to start from, can be NULL
  292. // If @first is NULL, the value @first from the @fSeqConstruct() call will be used.
  293. void fSeqStartup( xFSeqObj_t * fSeqObj, const fFSeqEntry_t * first );
  294. // @fSeqDispatch
  295. // Provides the sequencer work
  296. // Must be called to make the sequencer call the state functions
  297. // @fSeqObj - the sequencer object to operate with
  298. void fSeqDispatch( xFSeqObj_t * fSeqObj );
  299. // @fSeqShutdown
  300. // Shutdowns the running sequencer
  301. // @fSeqObj - the sequencer object to shutdown
  302. void fSeqShutdown( xFSeqObj_t * fSeqObj );
  303. /* Macro using example:
  304. const fFSeqEntry_t * state1Function( const fFSeqEntry_t *, tFSeqCtx_t, const fFSeqEntry_t * * );
  305. void state1Enter( const fFSeqEntry_t *, tFSeqCtx_t );
  306. void state1Leave( const fFSeqEntry_t *, tFSeqCtx_t );
  307. DECLARE_FSEQ_STATE_CONTEXT_BEGIN(seqState1, seqState1Context)
  308. struct {
  309. int state1_var;
  310. };
  311. DECLARE_FSEQ_STATE_CONTEXT_END(seqState1, seqState1Context)
  312. DECLARE_FSEQ_STATE_ENTRY_WITH_CONTEXT( seqState1, seqState1Context, state1Function, state1Enter, state1Leave );
  313. */
  314. #endif