QP/C  6.9.3
Real-Time Embedded Framework
qep_hsm.c
Go to the documentation of this file.
1 
40 #define QP_IMPL /* this is QP implementation */
41 #include "qep_port.h" /* QEP port */
42 #include "qassert.h" /* QP embedded systems-friendly assertions */
43 #ifdef Q_SPY /* QS software tracing enabled? */
44  #include "qs_port.h" /* QS port */
45  #include "qs_pkg.h" /* QS facilities for pre-defined trace records */
46 #else
47  #include "qs_dummy.h" /* disable the QS software tracing */
48 #endif /* Q_SPY */
49 
50 Q_DEFINE_THIS_MODULE("qep_hsm")
51 
52 /****************************************************************************/
54 
55 /****************************************************************************/
57 enum {
64 };
65 
72 static QEvt const QEP_reservedEvt_[] = {
73  { (QSignal)QEP_EMPTY_SIG_, 0U, 0U },
74  { (QSignal)Q_ENTRY_SIG, 0U, 0U },
75  { (QSignal)Q_EXIT_SIG, 0U, 0U },
76  { (QSignal)Q_INIT_SIG, 0U, 0U }
77 };
78 
80 #define QEP_TRIG_(state_, sig_) \
81  ((*(state_))(me, &QEP_reservedEvt_[(sig_)]))
82 
84 #define QEP_EXIT_(state_, qs_id_) do { \
85  if (QEP_TRIG_((state_), Q_EXIT_SIG) == (QState)Q_RET_HANDLED) { \
86  QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, (qs_id_)) \
87  QS_OBJ_PRE_(me); \
88  QS_FUN_PRE_(state_); \
89  QS_END_PRE_() \
90  } \
91 } while (false)
92 
94 #define QEP_ENTER_(state_, qs_id_) do { \
95  if (QEP_TRIG_((state_), Q_ENTRY_SIG) == (QState)Q_RET_HANDLED) { \
96  QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, (qs_id_)) \
97  QS_OBJ_PRE_(me); \
98  QS_FUN_PRE_(state_); \
99  QS_END_PRE_() \
100  } \
101 } while (false)
102 
104 #ifdef Q_SPY
105 static int_fast8_t QHsm_tran_(QHsm * const me,
107  uint_fast8_t const qs_id);
108 #else
109 static int_fast8_t QHsm_tran_(QHsm * const me,
111 #endif
112 
113 /****************************************************************************/
133 void QHsm_ctor(QHsm * const me, QStateHandler initial) {
134  static struct QHsmVtable const vtable = { /* QHsm virtual table */
135  &QHsm_init_,
137 #ifdef Q_SPY
139 #endif
140  };
141  me->vptr = &vtable;
142  me->state.fun = Q_STATE_CAST(&QHsm_top);
143  me->temp.fun = initial;
144 }
145 
146 /****************************************************************************/
157 #ifdef Q_SPY
158 void QHsm_init_(QHsm * const me, void const * const e,
159  uint_fast8_t const qs_id)
160 #else
161 void QHsm_init_(QHsm * const me, void const * const e)
162 #endif
163 {
164  QStateHandler t = me->state.fun;
165  QState r;
167 
172  Q_REQUIRE_ID(200, (me->vptr != (struct QHsmVtable *)0)
173  && (me->temp.fun != Q_STATE_CAST(0))
174  && (t == Q_STATE_CAST(&QHsm_top)));
175 
176  /* execute the top-most initial tran. */
177  r = (*me->temp.fun)(me, Q_EVT_CAST(QEvt));
178 
179  /* the top-most initial transition must be taken */
180  Q_ASSERT_ID(210, r == (QState)Q_RET_TRAN);
181 
183  QS_OBJ_PRE_(me); /* this state machine object */
184  QS_FUN_PRE_(t); /* the source state */
185  QS_FUN_PRE_(me->temp.fun); /* the target of the initial transition */
186  QS_END_PRE_()
187 
188  /* drill down into the state hierarchy with initial transitions... */
189  do {
190  QStateHandler path[QHSM_MAX_NEST_DEPTH_]; /* tran entry path array */
191  int_fast8_t ip = 0; /* tran entry path index */
192 
193  path[0] = me->temp.fun;
194  (void)QEP_TRIG_(me->temp.fun, QEP_EMPTY_SIG_);
195  while (me->temp.fun != t) {
196  ++ip;
197  Q_ASSERT_ID(220, ip < (int_fast8_t)Q_DIM(path));
198  path[ip] = me->temp.fun;
199  (void)QEP_TRIG_(me->temp.fun, QEP_EMPTY_SIG_);
200  }
201  me->temp.fun = path[0];
202 
203  /* retrace the entry path in reverse (desired) order... */
204  do {
205  QEP_ENTER_(path[ip], qs_id); /* enter path[ip] */
206  --ip;
207  } while (ip >= 0);
208 
209  t = path[0]; /* current state becomes the new source */
210 
211  r = QEP_TRIG_(t, Q_INIT_SIG); /* execute initial transition */
212 
213 #ifdef Q_SPY
214  if (r == (QState)Q_RET_TRAN) {
216  QS_OBJ_PRE_(me); /* this state machine object */
217  QS_FUN_PRE_(t); /* the source state */
218  QS_FUN_PRE_(me->temp.fun); /* target of the initial tran. */
219  QS_END_PRE_()
220  }
221 #endif /* Q_SPY */
222 
223  } while (r == (QState)Q_RET_TRAN);
224 
226  QS_TIME_PRE_(); /* time stamp */
227  QS_OBJ_PRE_(me); /* this state machine object */
228  QS_FUN_PRE_(t); /* the new active state */
229  QS_END_PRE_()
230 
231  me->state.fun = t; /* change the current active state */
232  me->temp.fun = t; /* mark the configuration as stable */
233 }
234 
235 /****************************************************************************/
250 QState QHsm_top(void const * const me, QEvt const * const e) {
251  (void)me; /* suppress the "unused parameter" compiler warning */
252  (void)e; /* suppress the "unused parameter" compiler warning */
253  return (QState)Q_RET_IGNORED; /* the top state ignores all events */
254 }
255 
256 /****************************************************************************/
270 #ifdef Q_SPY
271 void QHsm_dispatch_(QHsm * const me, QEvt const * const e,
272  uint_fast8_t const qs_id)
273 #else
274 void QHsm_dispatch_(QHsm * const me, QEvt const * const e)
275 #endif
276 {
277  QStateHandler t = me->state.fun;
278  QStateHandler s;
279  QState r;
281 
285  Q_REQUIRE_ID(400, (t != Q_STATE_CAST(0))
286  && (t == me->temp.fun));
287 
289  QS_TIME_PRE_(); /* time stamp */
290  QS_SIG_PRE_(e->sig); /* the signal of the event */
291  QS_OBJ_PRE_(me); /* this state machine object */
292  QS_FUN_PRE_(t); /* the current state */
293  QS_END_PRE_()
294 
295  /* process the event hierarchically... */
296  do {
297  s = me->temp.fun;
298  r = (*s)(me, e); /* invoke state handler s */
299 
300  if (r == (QState)Q_RET_UNHANDLED) { /* unhandled due to a guard? */
301 
303  QS_SIG_PRE_(e->sig); /* the signal of the event */
304  QS_OBJ_PRE_(me); /* this state machine object */
305  QS_FUN_PRE_(s); /* the current state */
306  QS_END_PRE_()
307 
308  r = QEP_TRIG_(s, QEP_EMPTY_SIG_); /* find superstate of s */
309  }
310  } while (r == (QState)Q_RET_SUPER);
311 
312  /* transition taken? */
313  if (r >= (QState)Q_RET_TRAN) {
315  int_fast8_t ip;
316 
317  path[0] = me->temp.fun; /* save the target of the transition */
318  path[1] = t;
319  path[2] = s;
320 
321  /* exit current state to transition source s... */
322  for (; t != s; t = me->temp.fun) {
325  QS_OBJ_PRE_(me); /* this state machine object */
326  QS_FUN_PRE_(t); /* the exited state */
327  QS_END_PRE_()
328 
329  (void)QEP_TRIG_(t, QEP_EMPTY_SIG_); /* find superstate of t */
330  }
331  }
332 
333 #ifdef Q_SPY
334  ip = QHsm_tran_(me, path, qs_id);
335 #else
336  ip = QHsm_tran_(me, path);
337 #endif
338 
339 #ifdef Q_SPY
340  if (r == (QState)Q_RET_TRAN_HIST) {
341 
343  QS_OBJ_PRE_(me); /* this state machine object */
344  QS_FUN_PRE_(t); /* the source of the transition */
345  QS_FUN_PRE_(path[0]); /* the target of the tran. to history */
346  QS_END_PRE_()
347 
348  }
349 #endif /* Q_SPY */
350 
351  /* retrace the entry path in reverse (desired) order... */
352  for (; ip >= 0; --ip) {
353  QEP_ENTER_(path[ip], qs_id); /* enter path[ip] */
354  }
355 
356  t = path[0]; /* stick the target into register */
357  me->temp.fun = t; /* update the next state */
358 
359  /* drill into the target hierarchy... */
360  while (QEP_TRIG_(t, Q_INIT_SIG) == (QState)Q_RET_TRAN) {
361 
363  QS_OBJ_PRE_(me); /* this state machine object */
364  QS_FUN_PRE_(t); /* the source (pseudo)state */
365  QS_FUN_PRE_(me->temp.fun); /* the target of the transition */
366  QS_END_PRE_()
367 
368  ip = 0;
369  path[0] = me->temp.fun;
370 
371  (void)QEP_TRIG_(me->temp.fun, QEP_EMPTY_SIG_);/*find superstate */
372 
373  while (me->temp.fun != t) {
374  ++ip;
375  path[ip] = me->temp.fun;
376  (void)QEP_TRIG_(me->temp.fun, QEP_EMPTY_SIG_);/* find super */
377  }
378  me->temp.fun = path[0];
379 
380  /* entry path must not overflow */
382 
383  /* retrace the entry path in reverse (correct) order... */
384  do {
385  QEP_ENTER_(path[ip], qs_id); /* enter path[ip] */
386  --ip;
387  } while (ip >= 0);
388 
389  t = path[0]; /* current state becomes the new source */
390  }
391 
392  QS_BEGIN_PRE_(QS_QEP_TRAN, qs_id)
393  QS_TIME_PRE_(); /* time stamp */
394  QS_SIG_PRE_(e->sig); /* the signal of the event */
395  QS_OBJ_PRE_(me); /* this state machine object */
396  QS_FUN_PRE_(s); /* the source of the transition */
397  QS_FUN_PRE_(t); /* the new active state */
398  QS_END_PRE_()
399  }
400 
401 #ifdef Q_SPY
402  else if (r == (QState)Q_RET_HANDLED) {
403 
405  QS_TIME_PRE_(); /* time stamp */
406  QS_SIG_PRE_(e->sig); /* the signal of the event */
407  QS_OBJ_PRE_(me); /* this state machine object */
408  QS_FUN_PRE_(s); /* the source state */
409  QS_END_PRE_()
410 
411  }
412  else {
413 
415  QS_TIME_PRE_(); /* time stamp */
416  QS_SIG_PRE_(e->sig); /* the signal of the event */
417  QS_OBJ_PRE_(me); /* this state machine object */
418  QS_FUN_PRE_(me->state.fun); /* the current state */
419  QS_END_PRE_()
420 
421  }
422 #endif /* Q_SPY */
423 
424  me->state.fun = t; /* change the current active state */
425  me->temp.fun = t; /* mark the configuration as stable */
426 }
427 
428 /****************************************************************************/
442 #ifdef Q_SPY
443 static int_fast8_t QHsm_tran_(QHsm * const me,
445  uint_fast8_t const qs_id)
446 #else
447 static int_fast8_t QHsm_tran_(QHsm * const me,
449 #endif
450 {
451  int_fast8_t ip = -1; /* transition entry path index */
452  int_fast8_t iq; /* helper transition entry path index */
453  QStateHandler t = path[0];
454  QStateHandler const s = path[2];
455  QState r;
457 
458  /* (a) check source==target (transition to self)... */
459  if (s == t) {
460  QEP_EXIT_(s, qs_id); /* exit the source */
461  ip = 0; /* enter the target */
462  }
463  else {
464  (void)QEP_TRIG_(t, QEP_EMPTY_SIG_); /* find superstate of target */
465 
466  t = me->temp.fun;
467 
468  /* (b) check source==target->super... */
469  if (s == t) {
470  ip = 0; /* enter the target */
471  }
472  else {
473  (void)QEP_TRIG_(s, QEP_EMPTY_SIG_); /* find superstate of src */
474 
475  /* (c) check source->super==target->super... */
476  if (me->temp.fun == t) {
477  QEP_EXIT_(s, qs_id); /* exit the source */
478  ip = 0; /* enter the target */
479  }
480  else {
481  /* (d) check source->super==target... */
482  if (me->temp.fun == path[0]) {
483  QEP_EXIT_(s, qs_id); /* exit the source */
484  }
485  else {
486  /* (e) check rest of source==target->super->super..
487  * and store the entry path along the way
488  */
489  iq = 0; /* indicate that LCA not found */
490  ip = 1; /* enter target and its superstate */
491  path[1] = t; /* save the superstate of target */
492  t = me->temp.fun; /* save source->super */
493 
494  /* find target->super->super... */
495  r = QEP_TRIG_(path[1], QEP_EMPTY_SIG_);
496  while (r == (QState)Q_RET_SUPER) {
497  ++ip;
498  path[ip] = me->temp.fun; /* store the entry path */
499  if (me->temp.fun == s) { /* is it the source? */
500  iq = 1; /* indicate that LCA found */
501 
502  /* entry path must not overflow */
503  Q_ASSERT_ID(510,
504  ip < QHSM_MAX_NEST_DEPTH_);
505  --ip; /* do not enter the source */
506  r = (QState)Q_RET_HANDLED; /* terminate loop */
507  }
508  /* it is not the source, keep going up */
509  else {
510  r = QEP_TRIG_(me->temp.fun, QEP_EMPTY_SIG_);
511  }
512  }
513 
514  /* the LCA not found yet? */
515  if (iq == 0) {
516 
517  /* entry path must not overflow */
519 
520  QEP_EXIT_(s, qs_id); /* exit the source */
521 
522  /* (f) check the rest of source->super
523  * == target->super->super...
524  */
525  iq = ip;
526  r = (QState)Q_RET_IGNORED; /* LCA NOT found */
527  do {
528  if (t == path[iq]) { /* is this the LCA? */
529  r = (QState)Q_RET_HANDLED; /* LCA found */
530  ip = iq - 1; /* do not enter LCA */
531  iq = -1; /* cause termintion of the loop */
532  }
533  else {
534  --iq; /* try lower superstate of target */
535  }
536  } while (iq >= 0);
537 
538  /* LCA not found? */
539  if (r != (QState)Q_RET_HANDLED) {
540  /* (g) check each source->super->...
541  * for each target->super...
542  */
543  r = (QState)Q_RET_IGNORED; /* keep looping */
544  do {
545  /* exit t unhandled? */
546  if (QEP_TRIG_(t, Q_EXIT_SIG)
547  == (QState)Q_RET_HANDLED)
548  {
550  QS_OBJ_PRE_(me);
551  QS_FUN_PRE_(t);
552  QS_END_PRE_()
553 
554  (void)QEP_TRIG_(t, QEP_EMPTY_SIG_);
555  }
556  t = me->temp.fun; /* set to super of t */
557  iq = ip;
558  do {
559  /* is this LCA? */
560  if (t == path[iq]) {
561  /* do not enter LCA */
562  ip = (int_fast8_t)(iq - 1);
563  iq = -1; /* break out of inner loop */
564  /* break out of outer loop */
565  r = (QState)Q_RET_HANDLED;
566  }
567  else {
568  --iq;
569  }
570  } while (iq >= 0);
571  } while (r != (QState)Q_RET_HANDLED);
572  }
573  }
574  }
575  }
576  }
577  }
578  return ip;
579 }
580 
581 /****************************************************************************/
582 #ifdef Q_SPY
584  return me->state.fun;
585 }
586 #endif
587 
588 /****************************************************************************/
602 bool QHsm_isIn(QHsm * const me, QStateHandler const state) {
603  bool inState = false; /* assume that this HSM is not in 'state' */
604  QState r;
605 
607  Q_REQUIRE_ID(600, me->temp.fun == me->state.fun);
608 
609  /* scan the state hierarchy bottom-up */
610  do {
611  /* do the states match? */
612  if (me->temp.fun == state) {
613  inState = true; /* 'true' means that match found */
614  r = (QState)Q_RET_IGNORED; /* break out of the loop */
615  }
616  else {
617  r = QEP_TRIG_(me->temp.fun, QEP_EMPTY_SIG_);
618  }
619  } while (r != (QState)Q_RET_IGNORED); /* QHsm_top() state not reached */
620  me->temp.fun = me->state.fun; /* restore the stable state configuration */
621 
622  return inState; /* return the status */
623 }
624 
625 /****************************************************************************/
649  QStateHandler const parent)
650 {
651  QStateHandler child = me->state.fun; /* start with the current state */
652  bool isFound = false; /* start with the child not found */
653  QState r;
654 
655  /* establish stable state configuration */
656  me->temp.fun = me->state.fun;
657  do {
658  /* is this the parent of the current child? */
659  if (me->temp.fun == parent) {
660  isFound = true; /* child is found */
661  r = (QState)Q_RET_IGNORED; /* break out of the loop */
662  }
663  else {
664  child = me->temp.fun;
665  r = QEP_TRIG_(me->temp.fun, QEP_EMPTY_SIG_);
666  }
667  } while (r != (QState)Q_RET_IGNORED); /* QHsm_top() state not reached */
668  me->temp.fun = me->state.fun; /* establish stable state configuration */
669 
671  Q_ENSURE_ID(810, isFound != false);
672 #ifdef Q_NASSERT
673  (void)isFound; /* avoid compiler warning about unused variable */
674 #endif
675 
676  return child; /* return the child */
677 }
678 
signed int int_fast8_t
Definition: 16bit/stdint.h:35
unsigned int uint_fast8_t
Definition: 16bit/stdint.h:36
Customizable and memory-efficient assertions for embedded systems.
#define Q_DEFINE_THIS_MODULE(name_)
Definition: qassert.h:120
char char_t
Definition: qassert.h:77
#define Q_ASSERT_ID(id_, test_)
Definition: qassert.h:155
#define Q_ENSURE_ID(id_, test_)
Definition: qassert.h:298
#define Q_REQUIRE_ID(id_, test_)
Definition: qassert.h:279
#define Q_DIM(array_)
Definition: qassert.h:337
@ Q_RET_HANDLED
Definition: qep.h:604
@ Q_RET_IGNORED
Definition: qep.h:605
@ Q_RET_TRAN
Definition: qep.h:615
@ Q_RET_UNHANDLED
Definition: qep.h:601
@ Q_RET_SUPER
Definition: qep.h:599
@ Q_RET_TRAN_HIST
Definition: qep.h:620
#define QP_VERSION_STR
Definition: qep.h:54
#define Q_STATE_CAST(handler_)
Definition: qep.h:228
QState(* QStateHandler)(void *const me, QEvt const *const e)
Definition: qep.h:210
#define Q_EVT_CAST(class_)
Definition: qep.h:190
uint_fast8_t QState
Definition: qep.h:204
QStateHandler fun
Definition: qep.h:251
@ Q_INIT_SIG
Definition: qep.h:716
@ Q_EXIT_SIG
Definition: qep.h:715
@ Q_ENTRY_SIG
Definition: qep.h:714
uint16_t QSignal
Definition: qep.h:128
char_t const QP_versionStr[7]
Definition: qep_hsm.c:53
static QEvt const QEP_reservedEvt_[]
Definition: qep_hsm.c:72
#define QEP_EXIT_(state_, qs_id_)
Definition: qep_hsm.c:84
static int_fast8_t QHsm_tran_(QHsm *const me, QStateHandler path[QHSM_MAX_NEST_DEPTH_], uint_fast8_t const qs_id)
Definition: qep_hsm.c:443
#define QEP_ENTER_(state_, qs_id_)
Definition: qep_hsm.c:94
void QHsm_init_(QHsm *const me, void const *const e, uint_fast8_t const qs_id)
Definition: qep_hsm.c:158
@ QHSM_MAX_NEST_DEPTH_
Definition: qep_hsm.c:63
@ QEP_EMPTY_SIG_
Definition: qep_hsm.c:58
QStateHandler QHsm_getStateHandler_(QHsm *const me)
Definition: qep_hsm.c:583
#define QEP_TRIG_(state_, sig_)
Definition: qep_hsm.c:80
void QHsm_dispatch_(QHsm *const me, QEvt const *const e, uint_fast8_t const qs_id)
Definition: qep_hsm.c:271
QEP/C port, generic C11 compiler.
#define QS_CRIT_STAT_
Definition: qs.h:501
#define QS_TIME_PRE_()
Definition: qs.h:262
@ QS_QEP_STATE_INIT
Definition: qs.h:69
@ QS_QEP_TRAN_HIST
Definition: qs.h:145
@ QS_QEP_STATE_EXIT
Definition: qs.h:68
@ QS_QEP_INIT_TRAN
Definition: qs.h:70
@ QS_QEP_INTERN_TRAN
Definition: qs.h:71
@ QS_QEP_UNHANDLED
Definition: qs.h:75
@ QS_QEP_TRAN
Definition: qs.h:72
@ QS_QEP_DISPATCH
Definition: qs.h:74
@ QS_QEP_IGNORED
Definition: qs.h:73
Internal (package scope) QS/C interface.
#define QS_BEGIN_PRE_(rec_, qs_id_)
Definition: qs_pkg.h:51
#define QS_OBJ_PRE_(obj_)
Definition: qs_pkg.h:116
#define QS_FUN_PRE_(fun_)
Definition: qs_pkg.h:131
#define QS_END_PRE_()
Definition: qs_pkg.h:63
Definition: qep.h:151
QSignal sig
Definition: qep.h:152
Definition: qep.h:282
bool QHsm_isIn(QHsm *const me, QStateHandler const state)
Definition: qep_hsm.c:602
struct QHsmVtable const * vptr
Definition: qep.h:283
void QHsm_ctor(QHsm *const me, QStateHandler initial)
Definition: qep_hsm.c:133
union QHsmAttr state
Definition: qep.h:284
union QHsmAttr temp
Definition: qep.h:285
QStateHandler QHsm_childState_(QHsm *const me, QStateHandler const parent)
Definition: qep_hsm.c:648
QState QHsm_top(void const *const me, QEvt const *const e)
Definition: qep_hsm.c:250