FACT++  1.0
StateMachineImp.cc
Go to the documentation of this file.
1 // **************************************************************************
80 // **************************************************************************
81 #include "StateMachineImp.h"
82 
83 #include "Time.h"
84 #include "Event.h"
85 
86 #include "WindowLog.h"
87 #include "Converter.h"
88 
89 #include "tools.h"
90 
91 using namespace std;
92 
93 // --------------------------------------------------------------------------
94 //
121 //
122 StateMachineImp::StateMachineImp(ostream &out, const std::string &name)
123  : MessageImp(out), fName(name), fCurrentState(kSM_NotReady),
124  fBufferEvents(true), fRunning(false), fExitRequested(0)
125 {
127 }
128 
129 // --------------------------------------------------------------------------
130 //
132 //
134 {
135  // For this to work EventImp must be the first class from which
136  // the object inherits
137  for (vector<EventImp*>::iterator cmd=fListOfEvents.begin(); cmd!=fListOfEvents.end(); cmd++)
138  delete *cmd;
139 }
140 
141 // --------------------------------------------------------------------------
142 //
145 //
147 {
148  AddStateName(kSM_NotReady, "NotReady", "State machine not ready, events are ignored.");
149  AddStateName(kSM_Ready, "Ready", "State machine ready to receive events.");
150  AddStateName(kSM_Error, "ERROR", "Common error state.");
151  AddStateName(kSM_FatalError, "FATAL", "A fatal error occured, the eventloop is stopped.");
152 }
153 
154 // --------------------------------------------------------------------------
155 //
164 //
166 {
167  const lock_guard<mutex> guard(fMutex);
168  fEventQueue.emplace_back(cmd);
169  fCond.notify_one();
170 }
171 
172 // --------------------------------------------------------------------------
173 //
180 //
181 shared_ptr<Event> StateMachineImp::PopEvent()
182 {
183  const lock_guard<mutex> guard(fMutex);
184 
185  // Get the next event from the stack
186  // and remove event from the stack
187  const shared_ptr<Event> cmd = fEventQueue.front();
188  fEventQueue.pop_front();
189  return cmd;
190 }
191 
192 // --------------------------------------------------------------------------
193 //
210 //
211 bool StateMachineImp::PostEvent(ostream &lout, const string &str)
212 {
213  // Find the delimiter between the command name and the data
214  size_t p0 = str.find_first_of(' ');
215  if (p0==string::npos)
216  p0 = str.length();
217 
218  // Compile the command which will be sent to the state-machine
219  const string name = fName + "/" + str.substr(0, p0);
220 
221  // Check if this command is existing at all
222  EventImp *evt = FindEvent(name);
223  if (!evt)
224  {
225  lout << kRed << "Unknown command '" << name << "'" << endl;
226  return false;
227  }
228 
229  // Get the format of the event data
230  const string fmt = evt->GetFormat();
231 
232  // Convert the user entered data according to the format string
233  // into a data block which will be attached to the event
234 #ifndef DEBUG
235  ostringstream sout;
236  const Converter conv(sout, fmt, false);
237 #else
238  const Converter conv(lout, fmt, false);
239 #endif
240  if (!conv)
241  {
242  lout << kRed << "Couldn't properly parse the format... ignored." << endl;
243  return false;
244  }
245 
246  try
247  {
248 #ifdef DEBUG
249  lout << kBlue << name;
250 #endif
251  const vector<char> v = conv.GetVector(str.substr(p0));
252 #ifdef DEBUG
253  lout << endl;
254 #endif
255 
256  return PostEvent(*evt, v.data(), v.size());
257  }
258  catch (const std::runtime_error &e)
259  {
260  lout << endl << kRed << e.what() << endl;
261  }
262 
263  return false;
264 }
265 
266 // --------------------------------------------------------------------------
267 //
294 //
295 bool StateMachineImp::PostEvent(const EventImp &evt, const char *ptr, size_t siz)
296 {
297  if (/*GetCurrentState()<0 ||*/ GetCurrentState()==kSM_FatalError)
298  {
299  Out() << kYellow << "State<0 or FatalError: Event ignored." << endl;
300  return false;
301  }
302 
303  if (IsRunning() || fBufferEvents)
304  {
305  Event *event = new Event(evt, ptr, siz);
306  //Debug("Posted: "+event->GetName());
307  PushEvent(event);
308  }
309  else
310  {
311  // FIXME: Is this thread safe? (Yes, because the data is copied)
312  // But two handlers could be called at the same time. Do we
313  // need to lock the handlers? (Dim + console)
314  // FIXME: Is copying of the data necessary?
315  const Event event(evt, ptr, siz);
316  Lock();
317  HandleEvent(event);
318  UnLock();
319  }
320  return true;
321 }
322 
323 // --------------------------------------------------------------------------
324 //
342 //
344 {
345  if (/*GetCurrentState()<0 ||*/ GetCurrentState()==kSM_FatalError)
346  {
347  Out() << kYellow << "State<0 or FatalError: Event ignored." << endl;
348  return false;
349  }
350 
351  if (IsRunning() || fBufferEvents)
352  PushEvent(new Event(evt));
353  else
354  {
355  // FIXME: Is this thread safe? (Yes, because it is only used
356  // by Dim and this is thread safe) But two handlers could
357  // be called at the same time. Do we need to lock the handlers?
358  HandleEvent(evt);
359  }
360  return true;
361 }
362 
363 // --------------------------------------------------------------------------
364 //
370 //
371 const vector<string> StateMachineImp::GetEventNames()
372 {
373  vector<string> v;
374 
375  const string &name = fName + "/";
376  const int len = name.length();
377 
378  const lock_guard<mutex> guard(fMutexEvt);
379 
380  for (vector<EventImp*>::const_iterator i=fListOfEvents.begin();
381  i!=fListOfEvents.end(); i++)
382  {
383  const string evt = (*i)->GetName();
384 
385  v.push_back(evt.substr(0, len)==name ? evt.substr(len) : evt);
386  }
387 
388  return v;
389 }
390 
391 // --------------------------------------------------------------------------
392 //
401 //
402 void StateMachineImp::PrintListOfEvents(ostream &out, const string &evt)
403 {
404  const lock_guard<mutex> guard(fMutexEvt);
405 
406  for (vector<EventImp*>::const_iterator c=fListOfEvents.begin(); c!=fListOfEvents.end(); c++)
407  if (evt.empty() || GetName()+'/'+evt==(*c)->GetName())
408  (*c)->Print(out, true);
409 }
410 
411 // --------------------------------------------------------------------------
412 //
419 //
421 {
422  const lock_guard<mutex> guard(fMutexEvt);
423 
424  for (vector<EventImp*>::const_iterator c=fListOfEvents.begin(); c!=fListOfEvents.end(); c++)
425  if ((*c)->IsStateAllowed(fCurrentState))
426  (*c)->Print(out, true);
427 }
428 
429 // --------------------------------------------------------------------------
430 //
435 //
436 //
438 {
439  PrintListOfEvents(Out(), str);
440 }
441 
442 // --------------------------------------------------------------------------
443 //
448 //
449 void StateMachineImp::PrintListOfStates(std::ostream &out) const
450 {
451  out << endl;
452  out << kBold << "List of available states:" << endl;
453  for (StateNames::const_iterator i=fStateNames.begin(); i!=fStateNames.end(); i++)
454  {
455  ostringstream state;
456  state << i->first;
457  out << " -[" << kBold << state.str() << kReset << "]:" << setfill(' ') << setw(6-state.str().length()) << ' ' << kYellow << i->second.first << kBlue << " (" << i->second.second << ")" << endl;
458  }
459  out << endl;
460 }
461 
462 // --------------------------------------------------------------------------
463 //
465 //
467 {
469 }
470 
471 // --------------------------------------------------------------------------
472 //
477 //
479 {
480  // Find the event from the list of commands and queue it
481  const lock_guard<mutex> guard(fMutexEvt);
482  return find(fListOfEvents.begin(), fListOfEvents.end(), cmd)!=fListOfEvents.end();
483 }
484 
485 // --------------------------------------------------------------------------
486 //
493 //
495 {
496  // Find the command from the list of commands and queue it
497  const lock_guard<mutex> guard(fMutexEvt);
498  for (vector<EventImp*>::const_iterator c=fListOfEvents.begin(); c!=fListOfEvents.end(); c++)
499  if (evt == (*c)->GetName())
500  return *c;
501 
502  return 0;
503 }
504 
505 // --------------------------------------------------------------------------
506 //
528 //
529 EventImp &StateMachineImp::AddEvent(const string &name, const string &states, const string &fmt)
530 {
531  EventImp *evt = CreateEvent(name, fmt);
532 
533  evt->AddAllowedStates(states);
534 
535 #ifdef DEBUG
536  Out() << ": " << Time().GetAsStr("%H:%M:%S.%f");
537  Out() << " - Adding command " << evt->GetName();
538  Out() << endl;
539 #endif
540 
541  const lock_guard<mutex> guard(fMutexEvt);
542  fListOfEvents.push_back(evt);
543  return *evt;
544 }
545 
546 // --------------------------------------------------------------------------
547 //
560 //
561 EventImp &StateMachineImp::AddEvent(const string &name, int s1, int s2, int s3, int s4, int s5)
562 {
563  ostringstream str;
564  str << s1 << ' ' << s2 << ' ' << s3 << ' ' << s4 << ' ' << s5;
565  return AddEvent(name, str.str(), "");
566 }
567 
568 // --------------------------------------------------------------------------
569 //
588 //
589 EventImp &StateMachineImp::AddEvent(const string &name, const string &fmt, int s1, int s2, int s3, int s4, int s5)
590 {
591  ostringstream str;
592  str << s1 << ' ' << s2 << ' ' << s3 << ' ' << s4 << ' ' << s5;
593  return AddEvent(name, str.str(), fmt);
594 }
595 
597 {
598  return new EventImp();
599 }
600 
601 // --------------------------------------------------------------------------
602 //
604 {
605  EventImp *evt = CreateService(name);
606 
607  const lock_guard<mutex> guard(fMutexEvt);
608  fListOfEvents.push_back(evt);
609  return *evt;
610 }
611 
613 {
614  {
615  const lock_guard<mutex> guard(fMutexEvt);
616 
617  auto it = find(fListOfEvents.begin(), fListOfEvents.end(), evt);
618  if (it==fListOfEvents.end())
619  return;
620 
621  fListOfEvents.erase(it);
622  }
623  delete evt;
624 }
625 
626 // --------------------------------------------------------------------------
627 //
644 bool StateMachineImp::AddStateName(const int state, const std::string &name, const std::string &doc)
645 {
646  //auto it = fStateNames.find(state);
647 
648  //if (/*it!=fStateNames.end() &&*/ !it->second.first.empty())
649  // return false;
650 
651  fStateNames[state] = make_pair(name, doc);
652  return true;
653 }
654 
655 // --------------------------------------------------------------------------
656 //
665 int StateMachineImp::GetStateIndex(const string &name) const
666 {
667  for (auto it=fStateNames.begin(); it!=fStateNames.end(); it++)
668  if (it->second.first==name)
669  return it->first;
670 
671  return kSM_NotAvailable;
672 }
673 
674 // --------------------------------------------------------------------------
675 //
684 const string StateMachineImp::GetStateName(int state) const
685 {
686  const StateNames::const_iterator i = fStateNames.find(state);
687  return i==fStateNames.end() || i->second.first.empty() ? to_string(state) : i->second.first;
688 }
689 
690 // --------------------------------------------------------------------------
691 //
698 bool StateMachineImp::HasState(int state) const
699 {
700  return fStateNames.find(state) != fStateNames.end();
701 }
702 
703 // --------------------------------------------------------------------------
704 //
713 const string StateMachineImp::GetStateDesc(int state) const
714 {
715  const StateNames::const_iterator i = fStateNames.find(state);
716  return i==fStateNames.end() ? "" : i->second.second;
717 }
718 
719 // --------------------------------------------------------------------------
720 //
732 //
733 const string StateMachineImp::GetStateDescription(int state) const
734 {
735  const string &str = GetStateName(state);
736 
737  ostringstream s;
738  s << state;
739  if (str==s.str())
740  return str;
741 
742  return str.empty() ? s.str() : (str+'['+s.str()+']');
743 }
744 
745 // --------------------------------------------------------------------------
746 //
771 //
772 string StateMachineImp::SetCurrentState(int state, const char *txt, const std::string &cmd)
773 {
774  if (state==fCurrentState)
775  {
776  Out() << " -- " << Time().GetAsStr("%H:%M:%S.%f") << " - State " << GetStateDescription(state) << " already set... ";
777  if (!cmd.empty())
778  Out() << "'" << cmd << "' ignored.";
779  Out() << endl;
780  return "";
781  }
782 
783  const int old = fCurrentState;
784 
785  const string nold = GetStateDescription(old);
786  const string nnew = GetStateDescription(state);
787 
788  string msg = nnew + " " + txt;
789  if (!cmd.empty())
790  msg += " (" + cmd + ")";
791 
792  fCurrentState = state;
793 
794  // State might have changed already again...
795  // Not very likely, but possible. That's why state is used
796  // instead of fCurrentState.
797 
798  ostringstream str;
799  str << "State Transition from " << nold << " to " << nnew << " (" << txt;
800  if (!cmd.empty())
801  str << ": " << cmd;
802  str << ")";
803  Message(str);
804 
805  return msg;
806 }
807 
808 // --------------------------------------------------------------------------
809 //
831 //
832 bool StateMachineImp::HandleNewState(int newstate, const EventImp *evt,
833  const char *txt)
834 {
835  if (newstate==kSM_FatalError)
836  return false;
837 
838  if (newstate==fCurrentState || newstate==kSM_KeepState)
839  return true;
840 
841  SetCurrentState(newstate, txt, evt ? evt->GetName() : "");
842 
843  return true;
844 }
845 
846 // --------------------------------------------------------------------------
847 //
881 //
883 {
884  if (!evt.HasFunc())
885  {
886  Warn(evt.GetName()+": No function assigned... ignored.");
887  return true;
888 
889  }
890 
891 #ifdef DEBUG
892  ostringstream out;
893  out << "Handle: " << evt.GetName() << "[" << evt.GetSize() << "]";
894  Debug(out);
895 #endif
896 
897  // Check if the received command is allow in the current state
898  if (!evt.IsStateAllowed(fCurrentState))
899  {
900  Warn(evt.GetName()+": Not allowed in state "+GetStateDescription()+"... ignored.");
901  return true;
902  }
903 
904  return HandleNewState(evt.ExecFunc(), &evt,
905  "by ExecFunc function-call");
906 }
907 
908 // --------------------------------------------------------------------------
909 //
993 //
994 int StateMachineImp::Run(bool dummy)
995 {
997  {
998  Error("Run() can only be called in the NotReady state.");
999  return -1;
1000  }
1001 
1002  if (!fExitRequested)
1003  {
1004  fRunning = !dummy;
1005 
1006  SetCurrentState(kSM_Ready, "by Run()");
1007 
1008  std::unique_lock<std::mutex> lock(fMutex);
1009  fMutex.unlock();
1010 
1011  while (1)
1012  {
1013  fMutex.lock();
1014  if (IsQueueEmpty())
1015  fCond.wait_for(lock, chrono::microseconds(10000));
1016  fMutex.unlock();
1017 
1018  if (fExitRequested)
1019  break;
1020 
1021  if (dummy)
1022  continue;
1023 
1024  // If the command stack is empty go on with processing in the
1025  // current state
1026  if (!IsQueueEmpty())
1027  {
1028  // Pop the next command which arrived from the stack
1029  const shared_ptr<Event> cmd(PopEvent());
1030  if (!HandleEvent(*cmd))
1031  break;
1032  }
1033 
1034  // Execute a step in the current state of the state machine
1035  if (!HandleNewState(Execute(), 0, "by Execute-command"))
1036  break;
1037  }
1038 
1039  fRunning = false;
1040 
1041  if (!fExitRequested)
1042  {
1043  Fatal("Fatal Error occured... shutting down.");
1044  return -1;
1045  }
1046 
1047  SetCurrentState(kSM_NotReady, "due to return from Run().");
1048  }
1049 
1050  const int exitcode = fExitRequested-1;
1051 
1052  // Prepare for next call
1053  fExitRequested = 0;
1054 
1055  return exitcode;
1056 }
1057 
1058 // --------------------------------------------------------------------------
1059 //
1072 //
1074 {
1075  fExitRequested = code+1;
1076 }
int GetStateIndex(const std::string &name) const
void AddAllowedStates(const std::string &states)
Definition: EventImp.cc:151
int fCurrentState
Name of the state-machine / server (e.g. DRIVE)
void PrintListOfAllowedEvents()
EventImp & AddEvent(const std::string &name, const std::string &states, const std::string &fmt)
int fExitRequested
Machine is in main-loop.
virtual void Stop(int code=0)
Request to stop the mainloop.
Mainloop running, state machine in operation.
int GetCurrentState() const
return the current state of the machine
A general base-class describing events issues in a state machine.
Definition: EventImp.h:11
void PrintListOfStates() const
Print a list of all states with descriptions.
std::list< std::shared_ptr< Event > > fEventQueue
List of available commands as setup by user.
bool PostEvent(std::ostream &lout, const std::string &str)
Post an event to the event queue.
int Debug(const std::string &str)
Definition: MessageImp.h:45
int i
Definition: db_dim_client.c:21
The base implementation of a distributed messaging system.
Definition: MessageImp.h:10
const std::string & GetName() const
Fatal error: stop program.
Adds some functionality to boost::posix_time::ptime for our needs.
Definition: Time.h:30
char str[80]
Definition: test_client.c:7
Set color Yellow.
Definition: WindowLog.h:19
Set color Red.
Definition: WindowLog.h:17
bool HasFunc() const
Definition: EventImp.h:31
STL namespace.
bool HandleEvent(const EventImp &evt)
EventImp * FindEvent(const std::string &evt)
virtual void UnLock()
std::ostream & Out() const
Definition: MessageImp.h:73
bool HandleNewState(int newstate, const EventImp *evt, const char *txt)
bool HasEvent(const EventImp *cmd)
bool fRunning
Flag if events should be buffered outside the event loop.
const std::string GetStateDesc() const
int ExecFunc() const
Definition: EventImp.h:32
virtual std::string GetFormat() const
Definition: EventImp.h:52
int Error(const std::string &str)
Definition: MessageImp.h:49
Set color Blue.
Definition: WindowLog.h:20
virtual EventImp * CreateEvent(const std::string &name, const std::string &fmt)=0
Is called when a configuration event is to be processed (no transition of state)
bool IsStateAllowed(int state) const
Definition: EventImp.cc:174
Mainloop not running, state machine stopped.
int Warn(const std::string &str)
Definition: MessageImp.h:48
bool HasState(int index) const
Warning states
Definition: smartfact.txt:92
std::vector< EventImp * > fListOfEvents
const std::vector< std::string > GetEventNames()
EventImp & Subscribe(const std::string &name)
virtual void PushEvent(Event *cmd)
This is a flag which is set true if the main loop should stop.
const std::string GetStateDescription() const
StateNames fStateNames
Human readable names associated with the states.
Concerete implementation of an EventImp stroring name, format, data and time.
Definition: Event.h:6
virtual std::string GetName() const
Definition: EventImp.h:51
virtual std::string SetCurrentState(int state, const char *txt="", const std::string &cmd="")
void PrintListOfEvents(std::ostream &out, const std::string &evt="")
Error states should be between 0x100 and 0xffff.
Possible return value for GetStateIndex.
virtual void Lock()
int Message(const std::string &str)
Definition: MessageImp.h:46
int Fatal(const std::string &str)
Definition: MessageImp.h:51
void Unsubscribe(EventImp *evt)
bool IsQueueEmpty() const
std::string fName
virtual bool AddStateName(const int state, const std::string &name, const std::string &doc="")
std::condition_variable fCond
Mutex to ensure thread-safe access to the command fifo.
std::string GetAsStr(const char *fmt="%Y-%m-%d %H:%M:%S") const
Definition: Time.cc:240
const std::string GetStateName() const
std::mutex fMutexEvt
Mutex to ensure thread-safe access to the command fifo.
std::shared_ptr< Event > PopEvent()
Pop a command from the fifo.
bool IsRunning() const
Used to check if the main loop is already running or still running.
bool fBufferEvents
Conditional to signal run the an event is waiting.
A compiler for the DIM data format string.
Definition: Converter.h:16
Reset all attributes.
Definition: WindowLog.h:29
virtual int Execute()
Is called continously to execute actions in the current state.
~StateMachineImp()
delete all object stored in fListOfEvent and in fEventQueue
StateMachineImp(std::ostream &out=std::cout, const std::string &name="")
virtual EventImp * CreateService(const std::string &)
Set attribute Bold.
Definition: WindowLog.h:36
std::mutex fMutex
Event queue (fifo) for the received commands.
std::vector< char > GetVector(const void *d, size_t size) const
Definition: Converter.cc:724
virtual size_t GetSize() const
Definition: EventImp.h:55