Motorcortex Core  version: 2.7.6
sm_statemachine.h
1 /*
2  * Developer : Alexey Zakharov (alexey.zakharov@vectioneer.com)
3  * All rights reserved. Copyright (c) 2014-2018 VECTIONEER.
4  */
5 
6 #ifndef STATE_MACHINE_SM_STATEMACHINE_H_
7 #define STATE_MACHINE_SM_STATEMACHINE_H_
8 
9 #include "sg_monitor.h"
10 #include "sm_errorhandle.h"
11 #include "sm_event.h"
12 #include "sm_state.h"
13 #include "utl_log.h"
14 #include <deque>
15 #include <initializer_list>
16 #include <string>
17 
18 namespace mcx::state_machine {
19 
20 constexpr auto MAX_NUMBER_OF_STATES = 100;
21 constexpr auto DEFAULT_TERMINATE_EVENT = nullptr;
22 
33 template <class SUPER_STATE>
34 class StateMachine {
35  friend class ErrorHandle<SUPER_STATE>;
36 
37  SUPER_STATE* activeState;
38  int activeStateId;
39  SUPER_STATE* stateArray[MAX_NUMBER_OF_STATES];
40  std::string machineName;
41  unsigned int eventStackOffset{0};
42  double dtSec{0};
43 
44  bool setActiveStateRoutine(int id) {
45  log_assert(id > -1, "State is not initialized!");
46  if (id > -1) {
47  SUPER_STATE* newState = stateArray[id];
48  if (newState) {
49  activeState = newState;
50  activeStateId = id;
51  return true;
52  }
53  }
54  return false;
55  }
56 
57  std::deque<Event<SUPER_STATE>*> highPriorityEvents;
58  std::deque<Event<SUPER_STATE>*> lowPriorityEvents;
59 
60  int numberOfStates;
61 
62  EventStatus executeEvent(Event<SUPER_STATE>* event, bool silently) {
63  EventStatus status = EVENT_NONE;
64  if (activeState) {
65  auto prevState = activeState;
66  status = event->dispatchEvent(activeState);
67  if ((!event->getExecutedOnceFlag() || (status != EVENT_REPEAT && status != EVENT_REPEAT_NO_TERMINATE)) &&
68  !silently && status != EVENT_NONE) {
69  log_sm("{}: State: {} Event: {} {}", getName(), prevState->stateName(), prevState->eventName(event),
70  status == EVENT_REPEAT || status == EVENT_REPEAT_NO_TERMINATE ? "[repeat]"
71  : status == EVENT_DONE ? "[done]"
72  : status == EVENT_TERMINATE ? "[terminate]"
73  : "[unknown]");
74  }
75  } else {
76  log_sm("{}: Active state is not set, can not execute event!", getName());
77  }
78 
79  return status;
80  }
81 
82  SUPER_STATE* getStatePtr(uint32_t index) {
83  if (static_cast<int>(index) < numberOfStates) {
84  return stateArray[index];
85  }
86  return nullptr;
87  }
88 
89  template <typename... STATES>
90  void action_variadic(signal_monitor::SignalMonitorBase* monitor, const ErrorGroup& error_group,
91  signal_monitor::MonitorAction action, id_caller<STATES>... states) {
92  auto id_list = {(STATES::id(id_caller<STATES>()))...};
93  for (auto& id : id_list) {
94  stateArray[id]->addErrors(monitor, error_group, action);
95  }
96  }
97 
98  // dummy for empty parameter pack
99 
100  void action_variadic(signal_monitor::SignalMonitorBase* monitor, const ErrorGroup& error_group,
101  signal_monitor::MonitorAction action) {}
102 
103  state_machine::ErrorHandle<SUPER_STATE> errorHandle;
104 
105  void addEvent(Event<SUPER_STATE>* event, double timeoutSec, EventPriority priority,
106  EventStatus(SUPER_STATE::*terminateEvent)) {
107  event->setTimeoutSec(timeoutSec);
108  if (!terminateEvent) {
109  event->setTerminateFunc(&State<SUPER_STATE>::terminateEvent);
110  }
111  if (priority == HIGH_PRIORITY_EVENT) {
112  highPriorityEvents.insert(highPriorityEvents.begin() + eventStackOffset, event);
113  ++eventStackOffset;
114  // highPriorityEvents.push_back(event);
115  } else {
116  lowPriorityEvents.push_back(event);
117  }
118  }
119 
120 public:
121  StateMachine() : stateArray(), errorHandle(this) {
122  activeState = nullptr;
123  numberOfStates = 0;
124  activeStateId = -1;
125  eventStackOffset = 0;
126  }
127 
128  virtual ~StateMachine() { deinitialize(); }
129 
134  void deinitialize() {
135  for (auto& i : stateArray) {
136  if (i) {
137  delete i;
138  i = nullptr;
139  }
140  }
141 
142  for (auto& e : highPriorityEvents) {
143  delete e;
144  }
145 
146  for (auto& e : lowPriorityEvents) {
147  delete e;
148  }
149 
150  highPriorityEvents.clear();
151  lowPriorityEvents.clear();
152  }
153 
158  void setName(const std::string& new_name) {
159  machineName = new_name;
160  errorHandle.setName(new_name);
161  }
162 
167  const std::string& getName() const { return machineName; }
168 
181  template <class STATE>
182  void createState() {
183  int id = STATE::id(id_caller<STATE>());
184  if (id == -1) {
185  id = numberOfStates++;
186  } else {
187  numberOfStates = numberOfStates < (id + 1) ? id + 1 : numberOfStates;
188  }
189 
190  if (!stateArray[id]) {
191  stateArray[id] = new STATE();
192  }
193  stateArray[id]->create(this, STATE(), id, &errorHandle);
194  log_sm("{}: Initialized state: {}", getName(), stateArray[id]->stateName());
195  }
196 
217  template <class STATE>
218  void createState(const STATE& new_state) {
219  int id = STATE::id(id_caller<STATE>());
220  if (id == -1) {
221  id = numberOfStates++;
222  } else {
223  numberOfStates = numberOfStates < (id + 1) ? id + 1 : numberOfStates;
224  }
225 
226  log_assert(id <= MAX_NUMBER_OF_STATES, "Number of states exceeded maximum value: {}!", MAX_NUMBER_OF_STATES);
227 
228  if (!stateArray[id]) {
229  stateArray[id] = new STATE(new_state);
230  }
231 
232  stateArray[id]->create(this, new_state, id, &errorHandle);
233  log_sm("{}: Initialized state: {}", getName(), stateArray[id]->stateName());
234  }
235 
245  template <class STATE>
246  STATE* setActiveState(bool output = true) {
247  int id = STATE::id(id_caller<STATE>());
248  return dynamic_cast<STATE*>(setActiveState(id, output));
249  }
250 
270  SUPER_STATE* setActiveState(int state_id, bool output = true) {
271  auto prevStateId = activeStateId;
272  if (activeState) {
273  activeState->leave();
274  activeState->leave(state_id);
275  }
276  if (setActiveStateRoutine(state_id)) {
277  if (output) {
278  log_sm("{}: Enter state: {}", getName(), activeState->stateName());
279  }
280  activeState->errorMonitorAction();
281  activeState->enter();
282  activeState->enter(prevStateId);
283  return activeState;
284  } else {
285  log_sm("{}: Can not enter state, it is not initialized!", getName());
286  }
287 
288  return NULL;
289  }
290 
296  template <typename STATE>
297  int getStateId() {
298  return STATE::id(id_caller<STATE>());
299  }
300 
306  template <typename... STATES>
307  bool isStateActive() {
308  if (activeStateId > -1) {
309  auto id_list = {(STATES::id(id_caller<STATES>()))...};
310  for (auto& id : id_list) {
311  if (id == activeStateId) {
312  return true;
313  }
314  }
315  // return activeStateId == STATE::id(id_caller<STATE>()) ? true : false;
316  }
317  return false;
318  }
319 
325  bool isStateActive(int id) {
326  if (activeStateId > -1) {
327  return activeStateId == id;
328  }
329  return false;
330  }
331 
337  bool isStateActive(std::initializer_list<int> id_list) {
338  if (activeStateId > -1) {
339  for (auto& id : id_list) {
340  if (id == activeStateId) {
341  return true;
342  }
343  }
344  }
345  return false;
346  }
347 
370  EventStatus executeEvent(const Event0<SUPER_STATE>& event, bool silently = false) {
371  EventStatus status = executeEvent(event.getEvent(), silently);
372  delete event.getEvent();
373  return status;
374  }
375 
398  EventStatus executeEvent(const Event1<SUPER_STATE>& event, bool silently = false) {
399  EventStatus status = executeEvent(event.getEvent(), silently);
400  delete event.getEvent();
401  return status;
402  }
403 
427  EventStatus executeEvent(const Event2<SUPER_STATE>& event, bool silently = false) {
428  EventStatus status = executeEvent(event.getEvent(), silently);
429  delete event.getEvent();
430  return status;
431  }
432 
454  EventStatus executeEvent(const Event3<SUPER_STATE>& event, bool silently = false) {
455  EventStatus status = executeEvent(event.getEvent(), silently);
456  delete event.getEvent();
457  return status;
458  }
459 
488  void addEvent(const Event0<SUPER_STATE>& event, double timeoutSec = EVENT_DEFAULT_TIMEOUT_SEC,
489  EventPriority priority = LOW_PRIORITY_EVENT,
490  EventStatus(SUPER_STATE::*terminateEvent) = DEFAULT_TERMINATE_EVENT) {
491  addEvent(event.getEvent(), timeoutSec, priority, terminateEvent);
492  }
493 
522  void addEvent(const Event1<SUPER_STATE>& event, double timeoutSec = EVENT_DEFAULT_TIMEOUT_SEC,
523  EventPriority priority = LOW_PRIORITY_EVENT,
524  EventStatus(SUPER_STATE::*terminateEvent) = DEFAULT_TERMINATE_EVENT) {
525  addEvent(event.getEvent(), timeoutSec, priority, terminateEvent);
526  }
527 
561  void addEvent(const Event2<SUPER_STATE>& event, double timeoutSec = EVENT_DEFAULT_TIMEOUT_SEC,
562  EventPriority priority = LOW_PRIORITY_EVENT,
563  EventStatus(SUPER_STATE::*terminateEvent) = DEFAULT_TERMINATE_EVENT) {
564  addEvent(event.getEvent(), timeoutSec, priority, terminateEvent);
565  }
566 
598  void addEvent(const Event3<SUPER_STATE>& event, double timeoutSec = EVENT_DEFAULT_TIMEOUT_SEC,
599  EventPriority priority = LOW_PRIORITY_EVENT,
600  EventStatus(SUPER_STATE::*terminateEvent) = DEFAULT_TERMINATE_EVENT) {
601  addEvent(event.getEvent(), timeoutSec, priority, terminateEvent);
602  }
603 
604  double getDtSec() const { return dtSec; }
605 
612  EventStatus iterate(double cycleTimeSec) {
613  dtSec = cycleTimeSec;
614 
615  EventStatus status = EVENT_NONE;
616 
617  errorHandle.iterate(cycleTimeSec);
618 
619  if (activeState) {
620  activeState->iterate(cycleTimeSec);
621  }
622 
623  if (highPriorityEvents.empty() && !lowPriorityEvents.empty()) {
624  highPriorityEvents.push_back(lowPriorityEvents.front());
625  lowPriorityEvents.pop_front();
626  }
627 
628  if (!highPriorityEvents.empty()) {
629  Event<SUPER_STATE>* event = highPriorityEvents.front();
630  eventStackOffset = 0;
631  status = executeEvent(event, false);
632  event->setExecutedOnceFlag(true);
633  if (status == EVENT_REPEAT || status == EVENT_REPEAT_NO_TERMINATE) {
634  double timeout = event->getTimeoutSec() - cycleTimeSec;
635  event->setTimeoutSec(timeout);
636  if (timeout <= 0.0f) {
637  highPriorityEvents.erase(highPriorityEvents.begin() + eventStackOffset);
638  // highPriorityEvents.pop_front();
639  switch (status) {
640  case EVENT_REPEAT: {
641  status = EVENT_TERMINATE;
642  Event<SUPER_STATE>* termFuncPtr = Event0<SUPER_STATE>(event->getTerminateFunc()).getEvent();
643  if (termFuncPtr) {
644  highPriorityEvents.push_front(termFuncPtr);
645  }
646  break;
647  }
648  case EVENT_REPEAT_NO_TERMINATE: {
649  status = EVENT_DONE;
650  break;
651  }
652  default:
653  break;
654  }
655  delete event;
656  }
657  } else {
658  highPriorityEvents.erase(highPriorityEvents.begin() + eventStackOffset);
659  // highPriorityEvents.pop_front();
660  delete event;
661  }
662  }
663 
664  return status;
665  }
666 
672  void clear(bool highPriority = true, bool lowPriority = true) {
673  if (highPriority) {
674  eventStackOffset = 0;
675  highPriorityEvents.clear();
676  }
677  if (lowPriority) {
678  lowPriorityEvents.clear();
679  }
680  }
681 
688  size_t length(EventPriority priority = LOW_PRIORITY_EVENT) {
689  switch (priority) {
690  case LOW_PRIORITY_EVENT: {
691  return lowPriorityEvents.size();
692  }
693  case HIGH_PRIORITY_EVENT: {
694  return highPriorityEvents.size();
695  }
696  }
697 
698  return 0;
699  }
700 
705  ErrorHandle<SUPER_STATE>& getErrorHandle() { return errorHandle; }
706 
711  const ErrorHandle<SUPER_STATE>& getErrorHandle() const { return errorHandle; }
712 
722  bool warning(const Error& error) {
723  if (activeState) {
724  return activeState->warning(error) == EventStatus::EVENT_DONE;
725  }
726  return false;
727  }
728 
738  bool forcedDisengaged(const Error& error) {
739  if (activeState) {
740  return activeState->forcedDisengaged(error) == EventStatus::EVENT_DONE;
741  }
742  return false;
743  }
744 
754  bool shutdown(const Error& error) {
755  if (activeState) {
756  return activeState->shutdown(error) == EventStatus::EVENT_DONE;
757  }
758  return false;
759  }
760 
770  bool emergencyStop(const Error& error) {
771  if (activeState) {
772  return activeState->emergencyStop(error) == EventStatus::EVENT_DONE;
773  }
774  return false;
775  }
776 };
777 
778 template <class BaseState>
779 template <class STATE>
780 int State<BaseState>::Container<STATE>::id = -1;
781 
784 } // namespace mcx::state_machine
785 
786 #endif // STATE_MACHINE_SM_STATEMACHINE_H_
mcx::state_machine::StateMachine::createState
void createState(const STATE &new_state)
Creates new state from the copy constructor.
Definition: sm_statemachine.h:218
mcx::state_machine::StateMachine::deinitialize
void deinitialize()
Deinitialization of the queues and states.
Definition: sm_statemachine.h:134
mcx::state_machine::StateMachine::getStateId
int getStateId()
Given a state class name returns its id.
Definition: sm_statemachine.h:297
mcx::state_machine::StateMachine::getErrorHandle
ErrorHandle< SUPER_STATE > & getErrorHandle()
Returns reference to the error handler.
Definition: sm_statemachine.h:705
mcx::state_machine::StateMachine::warning
bool warning(const Error &error)
Executes warning routine, add error code to the error list.
Definition: sm_statemachine.h:722
mcx::state_machine::Event1
Definition: sm_event.h:169
mcx::state_machine::StateMachine::length
size_t length(EventPriority priority=LOW_PRIORITY_EVENT)
Returns length of the event queue.
Definition: sm_statemachine.h:688
mcx::state_machine::StateMachine::shutdown
bool shutdown(const Error &error)
Executes shutdown routine, add error code to the error list.
Definition: sm_statemachine.h:754
mcx::state_machine::StateMachine::emergencyStop
bool emergencyStop(const Error &error)
Executes e-stop routine, add error code to the error list.
Definition: sm_statemachine.h:770
mcx::state_machine::Event< SUPER_STATE >
mcx::state_machine::StateMachine::setName
void setName(const std::string &new_name)
Sets name of the state machine.
Definition: sm_statemachine.h:158
mcx::state_machine::StateMachine::getName
const std::string & getName() const
Gets name of the state machine.
Definition: sm_statemachine.h:167
mcx::state_machine::StateMachine::addEvent
void addEvent(const Event3< SUPER_STATE > &event, double timeoutSec=EVENT_DEFAULT_TIMEOUT_SEC, EventPriority priority=LOW_PRIORITY_EVENT, EventStatus(SUPER_STATE::*terminateEvent)=DEFAULT_TERMINATE_EVENT)
Adds event with three parameters to the event queue.
Definition: sm_statemachine.h:598
mcx::state_machine::StateMachine::isStateActive
bool isStateActive(std::initializer_list< int > id_list)
Checks if given states are active by the list of state id.
Definition: sm_statemachine.h:337
mcx::signal_monitor::SignalId
Definition: sg_signalid.h:25
mcx::state_machine::StateMachine::executeEvent
EventStatus executeEvent(const Event1< SUPER_STATE > &event, bool silently=false)
Executes event in the active state.
Definition: sm_statemachine.h:398
mcx::state_machine::StateMachine::addEvent
void addEvent(const Event2< SUPER_STATE > &event, double timeoutSec=EVENT_DEFAULT_TIMEOUT_SEC, EventPriority priority=LOW_PRIORITY_EVENT, EventStatus(SUPER_STATE::*terminateEvent)=DEFAULT_TERMINATE_EVENT)
Adds event with two parameters to the event execution queue.
Definition: sm_statemachine.h:561
mcx::state_machine::StateMachine::isStateActive
bool isStateActive(int id)
Checks if given state is active by state id.
Definition: sm_statemachine.h:325
mcx::state_machine::StateMachine::addEvent
void addEvent(const Event0< SUPER_STATE > &event, double timeoutSec=EVENT_DEFAULT_TIMEOUT_SEC, EventPriority priority=LOW_PRIORITY_EVENT, EventStatus(SUPER_STATE::*terminateEvent)=DEFAULT_TERMINATE_EVENT)
Adds event without parameters to the event execution queue.
Definition: sm_statemachine.h:488
mcx::state_machine::Event0
Definition: sm_event.h:163
mcx::state_machine::StateMachine::setActiveState
SUPER_STATE * setActiveState(int state_id, bool output=true)
Sets active state by class id.
Definition: sm_statemachine.h:270
mcx::state_machine::StateMachine::clear
void clear(bool highPriority=true, bool lowPriority=true)
Clears event queue.
Definition: sm_statemachine.h:672
mcx::state_machine::StateMachine::setActiveState
STATE * setActiveState(bool output=true)
Sets active state by class name.
Definition: sm_statemachine.h:246
mcx::state_machine::StateMachine::executeEvent
EventStatus executeEvent(const Event2< SUPER_STATE > &event, bool silently=false)
Executes event in the active state.
Definition: sm_statemachine.h:427
mcx::state_machine::id_caller
Definition: sm_state.h:28
mcx::state_machine::StateMachine::executeEvent
EventStatus executeEvent(const Event0< SUPER_STATE > &event, bool silently=false)
Executes event in the active state.
Definition: sm_statemachine.h:370
mcx::state_machine::StateMachine::getErrorHandle
const ErrorHandle< SUPER_STATE > & getErrorHandle() const
Returns const reference to the error handler.
Definition: sm_statemachine.h:711
mcx::state_machine::StateMachine::executeEvent
EventStatus executeEvent(const Event3< SUPER_STATE > &event, bool silently=false)
Executes event in the active state.
Definition: sm_statemachine.h:454
mcx::state_machine::StateMachine::forcedDisengaged
bool forcedDisengaged(const Error &error)
Executes forced disengaged routine, add error code to the error list.
Definition: sm_statemachine.h:738
mcx::state_machine::StateMachine::iterate
EventStatus iterate(double cycleTimeSec)
Iterates an execution cycle of the state machine.
Definition: sm_statemachine.h:612
mcx::state_machine::StateMachine::isStateActive
bool isStateActive()
Checks if given state is active by state class name.
Definition: sm_statemachine.h:307
mcx::state_machine::StateMachine::createState
void createState()
Creates new state.
Definition: sm_statemachine.h:182
mcx::state_machine::Event3
Definition: sm_event.h:187
mcx::state_machine::State
Definition: sm_state.h:34
mcx::state_machine::ErrorHandle
Definition: sm_errorhandle.h:18
mcx::state_machine::StateMachine::addEvent
void addEvent(const Event1< SUPER_STATE > &event, double timeoutSec=EVENT_DEFAULT_TIMEOUT_SEC, EventPriority priority=LOW_PRIORITY_EVENT, EventStatus(SUPER_STATE::*terminateEvent)=DEFAULT_TERMINATE_EVENT)
Adds event with one parameter to the event execution queue.
Definition: sm_statemachine.h:522
mcx::state_machine::Event2
Definition: sm_event.h:178