Motorcortex Core  version: 2.7.6
sm_state.h
1 /*
2  * Developer : Alexey Zakharov (alexey.zakharov@vectioneer.com)
3  * All rights reserved. Copyright (c) 2014-2016 VECTIONEER.
4  */
5 
6 #ifndef STATE_MACHINE_SM_STATE_H
7 #define STATE_MACHINE_SM_STATE_H
8 
9 #include "sg_monitorbase.h"
10 #include "sg_signalidgroup.h"
11 #include "sm_errorhandlebase.h"
12 #include "sm_event.h"
13 #include <algorithm>
14 #include <cstdint>
15 #include <limits>
16 #include <map>
17 #include <set>
18 #include <string>
19 #include <vector>
20 
21 #define LOGIC_STATE_ID(x) x::id(mcx::state_machine::id_caller<x>())
22 
23 namespace mcx::state_machine {
24 
25 constexpr auto EVENT_DEFAULT_TIMEOUT_SEC = std::numeric_limits<double>::max();
26 
27 template <class...>
28 struct id_caller {};
29 
30 template <class SUPER_STATE>
32 
33 template <class SUPER_STATE>
34 class State {
35 
36  template <class STATE>
37  struct Container {
38  static int id;
39  };
40 
41  int* idPtr;
42 
44  std::string defaultName;
45 
46  struct event_name {
47  Event<SUPER_STATE>* event;
48  std::string name;
49  };
50  std::vector<event_name> eventList;
51 
52  inline std::string stateName(const std::string& prettyFunction) {
53  const std::string stateBeginToken = "STATE = ";
54  const std::string stateEndTokenGCC = ";";
55  const std::string stateEndTokenClang = ",";
56  size_t begin = prettyFunction.find(stateBeginToken) + stateBeginToken.length();
57  size_t end = prettyFunction.find(stateEndTokenGCC, begin);
58  if (end == std::string::npos) {
59  end = prettyFunction.find(stateEndTokenClang, begin);
60  }
61  if (begin == std::string::npos || end == std::string::npos)
62  return "Undefined";
63 
64  return prettyFunction.substr(begin, end - begin);
65  }
66 
67  struct ErrorMonitorData {
68  ErrorHandleBase* errorHandle{};
69  ErrorGroup errors[signal_monitor::MonitorAction::NUMBER_OF_ACTIONS];
70  };
71 
72  ErrorMonitorData errorMonitor;
73 
74 protected:
75  void addToEventList(const event_name& event) {
76  for (auto& e : eventList) {
77  if (event.event->compare(e.event)) {
78  delete event.event; // event already exits, need to remove
79  return;
80  }
81  }
82  eventList.push_back(event);
83  }
84 
85  void addEventName(const Event0<SUPER_STATE>& event, const std::string& eventName) {
86  addToEventList({.event = event.getEvent(), .name = eventName});
87  }
88 
89  void addEventName(const Event1<SUPER_STATE>& event, const std::string& eventName) {
90  eventList.push_back({.event = event.getEvent(), .name = eventName});
91  }
92 
93  void addEventName(const Event2<SUPER_STATE>& event, const std::string& eventName) {
94  eventList.push_back({.event = event.getEvent(), .name = eventName});
95  }
96 
97  void addEventName(const Event3<SUPER_STATE>& event, const std::string& eventName) {
98  eventList.push_back({.event = event.getEvent(), .name = eventName});
99  }
100 
101 public:
102  template <class STATE, class MACHINE>
103  void create(MACHINE* newMachine, const STATE& state, int id, ErrorHandleBase* errorHandle) {
104  setMachine(newMachine);
105  Container<STATE>::id = id;
106  idPtr = &Container<STATE>::id;
107  errorMonitor.errorHandle = errorHandle;
108  defaultName = stateName(__PRETTY_FUNCTION__);
109  this->addEventName(&SUPER_STATE::delayEvent, "delayEvent");
110  this->addEventName(&SUPER_STATE::terminateEvent, "terminateEvent");
111  this->addEventName(&SUPER_STATE::acknowledgeErrors, "acknowledgeErrors");
112  this->addEventName(&SUPER_STATE::warning_, "warning");
113  this->addEventName(&SUPER_STATE::forcedDisengaged_, "forcedDisengaged");
114  this->addEventName(&SUPER_STATE::shutdown_, "shutdown");
115  this->addEventName(&SUPER_STATE::emergencyStop_, "emergencyStop");
116  registerUserEvents();
117  }
118 
119  void addErrorsHelper(ErrorGroup* stateErrors, const ErrorGroup& errorsToAdd) {
120  auto groupBegin = stateErrors->begin();
121  for (auto& error : errorsToAdd) {
122  groupBegin = std::find(groupBegin, stateErrors->end(), error);
123  if (groupBegin == stateErrors->end()) {
124  stateErrors->add(error);
125  }
126  ++groupBegin;
127  }
128  }
129 
130  void enableErrorsHelper(ErrorGroup* stateErrors, const ErrorGroup& refErrors, const ErrorGroup& errorsToAdd) {
131  for (auto& error : errorsToAdd) {
132  auto refBegin = refErrors.begin();
133  while (true) {
134  // check every new error, if it exists in the reference group
135  refBegin = std::find(refBegin, refErrors.end(), error);
136  if (refBegin != refErrors.end()) {
137  auto groupBegin = std::find(stateErrors->begin(), stateErrors->end(), *refBegin);
138  if (groupBegin == stateErrors->end()) {
139  stateErrors->add(*refBegin);
140  }
141  } else {
142  break;
143  }
144  ++refBegin;
145  }
146  }
147  }
148 
149  void disableErrorsHelper(ErrorGroup* stateErrors, const ErrorGroup& errorsToAdd) {
150  auto groupBegin = stateErrors->begin();
151  for (auto& error : errorsToAdd) {
152  while (true) {
153  groupBegin = std::find(groupBegin, stateErrors->end(), error);
154  if (groupBegin != stateErrors->end()) {
155  stateErrors->erase(groupBegin);
156  } else {
157  break;
158  }
159  }
160  }
161  }
162 
163  void addErrors(const ErrorGroup& codes, signal_monitor::MonitorAction newAction) {
164  auto& action = errorMonitor.errors;
165  using namespace signal_monitor;
166  auto& stateErrorsGroup = action[MonitorAction::ADD];
167  switch (newAction) {
168  case MonitorAction::ADD: {
169  addErrorsHelper(&stateErrorsGroup, codes);
170  break;
171  }
172  case MonitorAction::RESET: {
173  auto& resetGroup = action[MonitorAction::RESET];
174  enableErrorsHelper(&resetGroup, stateErrorsGroup, codes);
175  break;
176  }
177  case MonitorAction::ENABLE: {
178  auto& enableGroup = action[MonitorAction::ENABLE];
179  enableErrorsHelper(&enableGroup, stateErrorsGroup, codes);
180  auto& disableGroup = action[MonitorAction::DISABLE];
181  disableErrorsHelper(&disableGroup, codes);
182  break;
183  }
184  case MonitorAction::DISABLE: {
185  auto& disableGroup = action[MonitorAction::DISABLE];
186  enableErrorsHelper(&disableGroup, stateErrorsGroup, codes);
187  auto& enableGroup = action[MonitorAction::ENABLE];
188  disableErrorsHelper(&enableGroup, codes);
189  break;
190  }
191  default:
192  log_error("Failed to add unknown motion action: {}", newAction);
193  break;
194  }
195  }
196 
197  virtual void registerUserEvents() {}
198 
199  template <class MACHINE>
200  void setMachine(MACHINE* newMachine) {
201  mach = newMachine;
202  }
203 
204  template <class MACHINE>
205  MACHINE* getMachine() {
206  return mach;
207  }
208 
209  State() : mach() { idPtr = nullptr; }
210 
211  virtual ~State() {
212  for (auto& e : eventList) {
213  delete e.event;
214  }
215  eventList.clear();
216  }
217 
218  double getDtSec() const { return mach->getDtSec(); }
219 
220  template <class STATE>
221  static int id(id_caller<STATE>) {
222  return Container<STATE>::id;
223  }
224 
225  int id() { return *idPtr; }
226 
227  void clearEventQueue(bool highPriority = true, bool lowPriority = true) { mach->clear(highPriority, lowPriority); }
228 
229  SUPER_STATE* setActiveState(int state_id) { return mach->setActiveState(state_id, true); }
230 
231  template <class NEWSTATE>
232  NEWSTATE* setActiveState() {
233  return mach->template setActiveState<NEWSTATE>(true);
234  }
235 
236  template <class NEWSTATE>
237  NEWSTATE* setActiveState(const NEWSTATE& state) {
238  return mach->template setActiveState<NEWSTATE>(true);
239  }
240 
241  template <typename... STATES>
242  bool isStateActive() {
243  return mach->template isStateActive<STATES...>();
244  }
245 
246  bool isStateActive(int id) { return mach->isStateActive(id); }
247 
248  void addEvent(const Event0<SUPER_STATE>& event, double timeoutSec = EVENT_DEFAULT_TIMEOUT_SEC,
249  EventPriority priority = HIGH_PRIORITY_EVENT) {
250  mach->addEvent(event, timeoutSec, priority);
251  }
252 
253  void addEvent(const Event1<SUPER_STATE>& event, double timeoutSec = EVENT_DEFAULT_TIMEOUT_SEC,
254  EventPriority priority = HIGH_PRIORITY_EVENT) {
255  mach->addEvent(event, timeoutSec, priority);
256  }
257 
258  void addEvent(const Event2<SUPER_STATE>& event, double timeoutSec = EVENT_DEFAULT_TIMEOUT_SEC,
259  EventPriority priority = HIGH_PRIORITY_EVENT) {
260  mach->addEvent(event, timeoutSec, priority);
261  }
262 
263  void addEvent(const Event3<SUPER_STATE>& event, double timeoutSec = EVENT_DEFAULT_TIMEOUT_SEC,
264  EventPriority priority = HIGH_PRIORITY_EVENT) {
265  mach->addEvent(event, timeoutSec, priority);
266  }
267 
268  double getActiveTimeoutSec() { return mach->getActiveTimeoutSec(); }
269 
270  virtual const char* stateName() const { return defaultName.c_str(); }
271 
272  virtual const char* eventName(Event<SUPER_STATE>* event) {
273  for (unsigned int i = 0; i < eventList.size(); i++) {
274  if (event->compare(eventList[i].event)) {
275  return eventList[i].name.c_str();
276  }
277  }
278 
279  return "Undefined name";
280  }
281 
282  virtual EventStatus acknowledgeErrors() { return EVENT_NONE; }
283 
284  bool clearErrorMonitor(bool warnings_only = false) { return getErrorMonitor()->acknowledge(warnings_only); }
285 
286  ErrorHandleBase* getErrorMonitor() { return errorMonitor.errorHandle; }
287 
288  EventStatus warning(const Error& error) {
289  auto result = mach->executeEvent({&SUPER_STATE::warning_, error}, true);
290  if (result == EventStatus::EVENT_DONE) {
291  if (getErrorMonitor()->warning(error)) {
292  return EVENT_DONE;
293  }
294  }
295  return EVENT_NONE;
296  }
297 
298  EventStatus forcedDisengaged(const Error& error) {
299  auto result = mach->executeEvent({&SUPER_STATE::forcedDisengaged_, error});
300  if (result == EventStatus::EVENT_DONE) {
301  if (getErrorMonitor()->forcedDisengaged(error)) {
302  return EVENT_DONE;
303  }
304  }
305  return EVENT_NONE;
306  }
307 
308  EventStatus shutdown(const Error& error) {
309  auto result = mach->executeEvent({&SUPER_STATE::shutdown_, error});
310  if (result == EventStatus::EVENT_DONE) {
311  if (getErrorMonitor()->shutdown(error)) {
312  return EVENT_DONE;
313  }
314  }
315  return EVENT_NONE;
316  }
317 
318  EventStatus emergencyStop(const Error& error) {
319  auto result = mach->executeEvent({&SUPER_STATE::emergencyStop_, error});
320  if (result == EventStatus::EVENT_DONE) {
321  if (getErrorMonitor()->emergencyStop(error)) {
322  return EVENT_DONE;
323  }
324  }
325  return EVENT_NONE;
326  }
327 
328  EventStatus delayEvent() { return EVENT_REPEAT_NO_TERMINATE; }
329 
330  virtual EventStatus terminateEvent() { return EVENT_DONE; }
331 
332  void errorMonitorAction() {
333  auto monitor = errorMonitor.errorHandle->getSignalMonitor();
334  using namespace signal_monitor;
335  for (int action = 0; action < MonitorAction::NUMBER_OF_ACTIONS; action++) {
336  auto& errors = errorMonitor.errors[action];
337  switch (action) {
338  case MonitorAction::ENABLE:
339  monitor->enable(errors);
340  break;
341  case MonitorAction::DISABLE:
342  monitor->disable(errors);
343  break;
344  case MonitorAction::RESET:
345  monitor->reset(errors);
346  break;
347  default:
348  break;
349  }
350  }
351  }
352 
353  virtual void enter() {}
354 
355  virtual void enter(int prev_state_id) {}
356 
357  virtual void leave() {}
358 
359  virtual void leave(int next_state_id) {}
360 
361  virtual void iterate(double dt_sec) {}
362 
363 protected:
364  virtual EventStatus warning_(const Error& error) = 0;
365 
366  virtual EventStatus forcedDisengaged_(const Error& error) = 0;
367 
368  virtual EventStatus shutdown_(const Error& error) = 0;
369 
370  virtual EventStatus emergencyStop_(const Error& error) = 0;
371 };
372 
373 } // namespace mcx::state_machine
374 
375 #endif // STATE_MACHINE_SM_STATE_H
mcx::state_machine::Event1
Definition: sm_event.h:169
mcx::state_machine::StateMachine
States machine manager and event interpreter.
Definition: sm_state.h:31
mcx::state_machine::Event< SUPER_STATE >
mcx::signal_monitor::SignalId
Definition: sg_signalid.h:25
mcx::state_machine::Event0
Definition: sm_event.h:163
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::id_caller
Definition: sm_state.h:28
mcx::signal_monitor::SignalIdGroup
Definition: sg_signalidgroup.h:20
mcx::state_machine::ErrorHandleBase
Definition: sm_errorhandlebase.h:19
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::Event3
Definition: sm_event.h:187
mcx::state_machine::State
Definition: sm_state.h:34
mcx::state_machine::Event2
Definition: sm_event.h:178