fsmbase.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. void fsmbDebugPrint (string s)
  2. {
  3. #ifdef FSM_DEBUG
  4. PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
  5. #else
  6. //Print("" + s); // comment/uncomment to hide/see debug logs
  7. #endif
  8. }
  9. void fsmbDebugSpam (string s)
  10. {
  11. #ifdef FSM_DEBUG_SPAM
  12. PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
  13. #else
  14. //Print("" + s); // comment/uncomment to hide/see debug logs
  15. #endif
  16. }
  17. /**@class FSMTransition
  18. * @brief represents transition src ---- event[guard]/action ----|> dst
  19. **/
  20. class FSMTransition<Class FSMStateBase, Class FSMEventBase, Class FSMActionBase, Class FSMGuardBase>
  21. {
  22. ref FSMStateBase m_srcState;
  23. ref FSMEventBase m_event; // @NOTE: NULL event means "completion transition" in UML speak
  24. ref FSMStateBase m_dstState; // @NOTE: NULL dst state == UML terminate pseudonode
  25. ref FSMActionBase m_action;
  26. ref FSMGuardBase m_guard;
  27. void FSMTransition (FSMStateBase src, FSMEventBase e, FSMStateBase dst, FSMActionBase a = NULL, FSMGuardBase g = NULL)
  28. {
  29. m_srcState = src;
  30. m_event = e;
  31. m_dstState = dst;
  32. m_action = a;
  33. m_guard = g;
  34. }
  35. };
  36. enum ProcessEventResult
  37. {
  38. FSM_OK,
  39. FSM_TERMINATED,
  40. FSM_ABORTED,
  41. FSM_NO_TRANSITION,
  42. };
  43. /**@class FSMBase
  44. * @brief base class for finite state machine
  45. *
  46. * stores current state (m_state) and transition table with possible transitions from each state
  47. * to another state via event
  48. **/
  49. class FSMBase<Class FSMStateBase, Class FSMEventBase, Class FSMActionBase, Class FSMGuardBase>
  50. {
  51. protected ref FSMStateBase m_state; /// current fsm state
  52. protected ref FSMStateBase m_initialState; /// configurable initial state of the machine
  53. protected ref FSMEventBase m_initialEvent; /// configurable initial event to start the machine (null by default)
  54. protected ref array<ref FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase>> m_transitions; /// fsm transition table
  55. void FSMBase ()
  56. {
  57. m_transitions = new array<ref FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase>>;
  58. }
  59. /**@fn GetCurrentState
  60. * @brief returns currently active state
  61. * @return current state the FSM is in (or NULL)
  62. **/
  63. FSMStateBase GetCurrentState ()
  64. {
  65. return m_state;
  66. }
  67. /**@fn SetInitialState
  68. * @brief sets the initial_state for starting the machine
  69. **/
  70. void SetInitialState (FSMStateBase initial_state)
  71. {
  72. m_initialState = initial_state;
  73. }
  74. /**@fn Start
  75. * @brief starts the state machine by entering the initial_state (using intial_event as argument to initial state's onEntry)
  76. * @param[in] e \p optional event for starting the machind
  77. **/
  78. void Start (FSMEventBase initial_event = NULL)
  79. {
  80. if (LogManager.IsInventoryHFSMLogEnable()) fsmbDebugPrint("[fsm] " + this.ToString() + "::Start(" + initial_event.ToString() + "), init_state=" + m_initialState.ToString());
  81. m_state = m_initialState;
  82. m_state.OnEntry(initial_event);
  83. }
  84. /**@fn IsRunning
  85. * @brief returns true if machine is in running state
  86. **/
  87. bool IsRunning () { return m_state != NULL; }
  88. /**@fn Terminate
  89. * @brief terminates the state machine
  90. **/
  91. void Terminate (FSMEventBase terminal_event = NULL)
  92. {
  93. if (IsRunning())
  94. {
  95. m_state.OnExit(terminal_event);
  96. m_state = NULL;
  97. }
  98. }
  99. /**@fn Update
  100. * @brief if machine running, call OnUpdate() on current state
  101. **/
  102. void Update (float dt)
  103. {
  104. if (IsRunning())
  105. m_state.OnUpdate(dt);
  106. }
  107. /**@fn AddTransition
  108. * @brief adds transition into transition table
  109. **/
  110. void AddTransition (FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase> t)
  111. {
  112. m_transitions.Insert(t);
  113. }
  114. /**@fn ProcessEvent
  115. * @brief instructs the state machine to process the event e
  116. * @param[in] e \p event that will be used to find suitable transition from current state
  117. * @return FSM_OK if transition found and allowed by guard (if any)
  118. **/
  119. ProcessEventResult ProcessEvent (FSMEventBase e)
  120. {
  121. FSMStateBase curr_state = m_state;
  122. int count = m_transitions.Count();
  123. for (int i = 0; i < count; ++i)
  124. {
  125. FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase> row = m_transitions.Get(i);
  126. if (row.m_srcState.Type() == curr_state.Type() && row.m_event.Type() == e.Type())
  127. {
  128. FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase> t = m_transitions.Get(i);
  129. bool hasGuard = t.m_guard != NULL;
  130. if (!hasGuard || (hasGuard && t.m_guard.GuardCondition(e))) // 1) exec guard (if any)
  131. {
  132. ProcessLocalTransition(t, e); // 2) process transition allowed by guard
  133. }
  134. }
  135. }
  136. return ProcessEventResult.FSM_NO_TRANSITION;
  137. }
  138. /**@fn ProcessLocalTransition
  139. * @brief instructs the state machine to process the event locally - no hierarchy is crossed
  140. * @param[in] t \p the transition in m_transitions
  141. * @param[in] e \p event that will be used to process transition from current state
  142. * @return FSM_OK or FSM_TERMINATED
  143. **/
  144. protected ProcessEventResult ProcessLocalTransition (FSMTransition<FSMStateBase, FSMEventBase, FSMActionBase, FSMGuardBase> t, FSMEventBase e)
  145. {
  146. if (LogManager.IsInventoryHFSMLogEnable()) fsmbDebugPrint("[fsm] (local) state=" + t.m_srcState.ToString() + "-------- event=" + e.ToString() + "[G=" + t.m_guard.ToString() +"]/A=" + t.m_action.ToString() + " --------|> dst=" + t.m_dstState.ToString());
  147. m_state.OnExit(e); // 1) call onExit on old state
  148. if (t.m_action)
  149. t.m_action.Action(e); // 2) execute transition action (if any)
  150. m_state = t.m_dstState; // 3) change state to new
  151. if (t.m_dstState != NULL)
  152. {
  153. m_state.OnEntry(e); // 4a) call onEntry on new state
  154. return ProcessEventResult.FSM_OK;
  155. }
  156. else
  157. {
  158. if (LogManager.IsInventoryHFSMLogEnable()) fsmbDebugPrint("[fsm] terminating fsm: state=" + t.m_srcState.ToString() + " event=" + e.ToString());
  159. return ProcessEventResult.FSM_TERMINATED; // 4b) or terminate
  160. }
  161. }
  162. };