FACT++  1.0
StateMachineDimControl.cc
Go to the documentation of this file.
2 
3 #include <boost/filesystem.hpp>
4 
5 #include "Dim.h"
6 #include "Event.h"
7 #include "Readline.h"
8 #include "InterpreterV8.h"
9 #include "Configuration.h"
10 #include "Converter.h"
11 
12 #include "tools.h"
13 
14 using namespace std;
15 
16 // ------------------------------------------------------------------------
17 
19 
20 string StateMachineDimControl::Line(const string &txt, char fill)
21 {
22  const int n = (55-txt.length())/2;
23 
24  ostringstream out;
25  out << setfill(fill);
26  out << setw(n) << fill << ' ';
27  out << txt;
28  out << ' ' << setw(n) << fill;
29 
30  if (2*n+txt.length()+2 != 57)
31  out << fill;
32 
33  return out.str();
34 }
35 
36 int StateMachineDimControl::ChangeState(int qos, const Time &, int scriptdepth, string scriptfile, string user)
37 {
38  string msg;
39  /*
40  switch (qos)
41  {
42  case -4: msg = "End"; break;
43  case -3: msg = "Loading"; break;
44  case -2: msg = "Compiling"; break;
45  case -1: msg = "Running"; break;
46  default:
47  {
48  ostringstream out;
49  out << "Label " << qos;
50  msg = out.str();
51  }
52  }
53  */
54 
55  //if (qos<0)
56  msg += to_string(scriptdepth);
57 
58  msg += ":"+scriptfile+"["+user+":"+to_string(getpid())+"]";
59 
60  //if (fDebug)
61  //Write(time, Line(msg, qos<-1 ? '=' :'-'), MessageImp::kInternal);
62 
63  if (qos==-4)
64  fScriptUser = fUser;
65 
66  SetCurrentState(qos+4, msg.c_str());
67  //SetCurrentState(qos+4, Line(msg, qos<-1 ? '=' :'-').c_str());
68  return GetCurrentState();
69 
70  //return qos+4;
71 }
72 
74 {
75  return ChangeState(state, Time(), Readline::GetScriptDepth(), Readline::GetScript(), fScriptUser);
76  /*
77  === This might be necessary for thread safety,
78  === but it break that the signal for the start of a new
79  === script arrives synchronously before the first output
80  === from the script
81 
82  // Post an anonymous event to the event loop
83  Event evt("");
84  evt.AssignFunction(bind(&StateMachineDimControl::ChangeState, this,
85  qos, time, Readline::GetScriptDepth(),
86  Readline::GetScript(), fScriptUser));
87  return PostEvent(evt);
88  */
89 }
90 
91 int StateMachineDimControl::StartScript(const EventImp &imp, const string &cmd)
92 {
93  string opt(imp.GetString());
94 
95  const map<string,string> data = Tools::Split(opt, true);
96  if (imp.GetSize()==0 || opt.size()==0 || opt[0]==0)
97  {
98  Error("File name missing in DIM_CONTROL/START");
99  return GetCurrentState();
100  }
101 
102  if (fDebug)
103  Debug("Start '"+opt+"' received.");
104 
105  if (fDebug)
106  Debug("Received data: "+imp.GetString());
107 
108  const auto user = data.find("user");
109  fScriptUser = user==data.end() ? fUser : user->second;
110 
111  if (fDebug)
112  {
113  for (auto it=data.begin(); it!=data.end(); it++)
114  Debug(" Arg: "+it->first+" = "+it->second);
115  }
116 
117  string emit = cmd+imp.GetString();
118  if (cmd==".js ")
119  emit += fArgumentsJS;
120 
122  return GetCurrentState();
123 }
124 
126 {
127  const string str(imp.GetString());
128 
129  string msg("Stop received");
130  msg += str.empty() ? "." : " ["+str+"]";
131 
132  Info(msg);
133 
136  return GetCurrentState();
137 }
138 
140 {
142  StateMachineDim::Stop(code);
143 }
144 
146 {
147  if (!fInterruptHandler)
148  return GetCurrentState();
149 
150  string str = evt.GetString();
151 
152  const size_t p = str.find_last_of('\n');
153  if (p!=string::npos)
154  str[p] = ':';
155 
156  if (GetCurrentState()<3)
157  {
158  Warn("Interrupt request received ["+str+"]... but no running script.");
159  return GetCurrentState();
160  }
161 
162  Info("Interrupt request received ["+str+"]");
163  return fInterruptHandler(evt);
164 }
165 
166 bool StateMachineDimControl::SendDimCommand(const string &server, string str, ostream &lout)
167 {
168  const lock_guard<mutex> guard(fMutex);
169 
170  if (fServerList.find(server)==fServerList.end())
171  throw runtime_error("SendDimCommand - Server '"+server+"' not online.");
172 
173  str = Tools::Trim(str);
174 
175  // Find the delimiter between the command name and the data
176  size_t p0 = str.find_first_of(' ');
177  if (p0==string::npos)
178  p0 = str.length();
179 
180  // Get just the command name separated from the data
181  const string name = str.substr(0, p0);
182 
183  // Compile the command which will be sent to the state-machine
184  for (auto is=fServiceList.begin(); is!=fServiceList.end(); is++)
185  {
186  if (str.empty() && is->server==server)
187  return true;
188 
189  if (is->server!=server || is->service!=name)
190  continue;
191 
192  if (!is->iscmd)
193  throw runtime_error("'"+server+"/"+name+" not a command.");
194 
195  // Avoid compiler warning of unused parameter
196  lout << flush;
197 
198  // Convert the user entered data according to the format string
199  // into a data block which will be attached to the event
200 #ifndef DEBUG
201  ostringstream sout;
202  const Converter conv(sout, is->format, false);
203 #else
204  const Converter conv(lout, is->format, false);
205 #endif
206  if (!conv)
207  throw runtime_error("Couldn't properly parse the format... ignored.");
208 
209 #ifdef DEBUG
210  lout << kBlue << server << '/' << name;
211 #endif
212  const vector<char> v = conv.GetVector(str.substr(p0));
213 #ifdef DEBUG
214  lout << kBlue << " [" << v.size() << "]" << endl;
215 #endif
216  const string cmd = server + '/' + name;
217  const int rc = DimClient::sendCommand(cmd.c_str(), (void*)v.data(), v.size());
218  if (!rc)
219  throw runtime_error("ERROR - Sending command "+cmd+" failed.");
220 
221  return true;
222  }
223 
224  if (!str.empty())
225  throw runtime_error("SendDimCommand - Format information for "+server+"/"+name+" not yet available.");
226 
227  return false;
228 }
229 
230 int StateMachineDimControl::PrintStates(std::ostream &out, const std::string &serv)
231 {
232  const lock_guard<mutex> guard(fMutex);
233 
234  int rc = 0;
235  for (auto it=fServerList.begin(); it!=fServerList.end(); it++)
236  {
237  if (!serv.empty() && *it!=serv)
238  continue;
239 
240  out << kRed << "----- " << *it << " -----" << endl;
241 
242  int cnt = 0;
243  for (auto is=fStateDescriptionList.begin(); is!=fStateDescriptionList.end(); is++)
244  {
245  const string &server = is->first.first;
246 
247  if (server!=*it)
248  continue;
249 
250  const int32_t &state = is->first.second;
251  const string &name = is->second.first;
252  const string &comment = is->second.second;
253 
254  out << kBold << setw(5) << state << kReset << ": ";
255  out << kYellow << name;
256  if (!comment.empty())
257  out << kBlue << " (" << comment << ")";
258  out << endl;
259 
260  cnt++;
261  }
262 
263  if (cnt==0)
264  out << " <no states>" << endl;
265  else
266  rc++;
267 
268  out << endl;
269  }
270 
271  return rc;
272 }
273 
274 int StateMachineDimControl::PrintDescription(std::ostream &out, bool iscmd, const std::string &serv, const std::string &service)
275 {
276  const lock_guard<mutex> guard(fMutex);
277 
278  int rc = 0;
279  for (auto it=fServerList.begin(); it!=fServerList.end(); it++)
280  {
281  if (!serv.empty() && *it!=serv)
282  continue;
283 
284  out << kRed << "----- " << *it << " -----" << endl << endl;
285 
286  for (auto is=fServiceList.begin(); is!=fServiceList.end(); is++)
287  {
288  if (is->server!=*it)
289  continue;
290 
291  if (!service.empty() && is->service!=service)
292  continue;
293 
294  if (is->iscmd!=iscmd)
295  continue;
296 
297  rc++;
298 
299  out << " " << is->service;
300  if (!is->format.empty())
301  out << '[' << is->format << ']';
302 
303  const auto id = fServiceDescriptionList.find(*it+"/"+is->service);
304  if (id!=fServiceDescriptionList.end())
305  {
306  const vector<Description> &v = id->second;
307 
308  for (auto j=v.begin()+1; j!=v.end(); j++)
309  out << " <" << j->name << ">";
310  out << endl;
311 
312  if (!v[0].comment.empty())
313  out << " " << v[0].comment << endl;
314 
315  for (auto j=v.begin()+1; j!=v.end(); j++)
316  {
317  out << " " << kGreen << j->name;
318  if (!j->comment.empty())
319  out << kReset << ": " << kBlue << j->comment;
320  if (!j->unit.empty())
321  out << kYellow << " [" << j->unit << "]";
322  out << endl;
323  }
324  }
325  out << endl;
326  }
327  out << endl;
328  }
329 
330  return rc;
331 }
332 
334 {
335  fMutex.lock();
336  const State descr = dim->description();
337  const State state = State(dim->state(), descr.index==DimState::kNotAvailable?"":descr.name, descr.comment, dim->cur.first);
338  fCurrentStateList[server] = state;
339  fMutex.unlock();
340 
341  fStateCallback(server, state);
342 
343  return GetCurrentState();
344 }
345 
347 {
348  const lock_guard<mutex> guard(fMutex);
349 
350  const auto it = fCurrentStateList.find(server);
351  return it==fCurrentStateList.end() ? State() : it->second;
352 }
353 
355 {
356  const lock_guard<mutex> guard(fMutex);
357 
358  const auto is = fCurrentStateList.find(server);
359  for (auto it=dim->states.begin(); it!=dim->states.end(); it++)
360  {
361  fStateDescriptionList[make_pair(server, it->index)] = make_pair(it->name, it->comment);
362  if (is==fCurrentStateList.end())
363  continue;
364 
365  State &s = is->second;
366  if (s.index==it->index)
367  {
368  s.name = it->name;
369  s.comment = it->comment;
370  }
371  }
372 
373  return GetCurrentState();
374 }
375 
377 {
378  const lock_guard<mutex> guard(fMutex);
379 
380  for (auto it=dim->descriptions.begin(); it!=dim->descriptions.end(); it++)
381  fServiceDescriptionList[it->front().name].assign(it->begin(), it->end());
382 
383  return GetCurrentState();
384 }
385 
386 std::vector<Description> StateMachineDimControl::GetDescription(const std::string &service)
387 {
388  const lock_guard<mutex> guard(fMutex);
389 
390  const auto it = fServiceDescriptionList.find(service);
391  return it==fServiceDescriptionList.end() ? vector<Description>() : it->second;
392 }
393 
395 {
396  if (server!="DIS_DNS")
397  {
398  struct Find : string
399  {
400  Find(const string &ref) : string(ref) { }
401  bool operator()(const DimDescriptions *dim) { return *this==dim->server; }
402  };
403 
404  if (find_if(fDimDescriptionsList.begin(), fDimDescriptionsList.end(),
405  Find(server))==fDimDescriptionsList.end())
406  {
407  DimDescriptions *d = new DimDescriptions(server);
408 
409  fDimDescriptionsList.push_back(d);
410  d->SetCallback(bind(&StateMachineDimControl::HandleStateChange, this, server, d));
411  d->SetCallbackStates(bind(&StateMachineDimControl::HandleStates, this, server, d));
413  d->Subscribe(*this);
414  }
415  }
416 
417  // Make a copy of the list to be able to
418  // lock the access to the list
419 
420  const lock_guard<mutex> guard(fMutex);
421  fServerList.insert(server);
422 
423  return GetCurrentState();
424 }
425 
427 {
428  const lock_guard<mutex> guard(fMutex);
429  fServerList.erase(server);
430 
431  return GetCurrentState();
432 }
433 
435 {
436  vector<string> rc;
437 
438  const lock_guard<mutex> guard(fMutex);
439 
440  rc.reserve(fServerList.size());
441  for (auto it=fServerList.begin(); it!=fServerList.end(); it++)
442  rc.push_back(*it);
443 
444  return rc;
445 }
446 
447 vector<string> StateMachineDimControl::GetCommandList(const string &server)
448 {
449  const lock_guard<mutex> guard(fMutex);
450 
451  const string s = server.substr(0, server.length()-1);
452 
453  if (fServerList.find(s)==fServerList.end())
454  return vector<string>();
455 
456  vector<string> rc;
457 
458  for (auto it=fServiceList.begin(); it!=fServiceList.end(); it++)
459  if (it->iscmd && it->server==s)
460  rc.push_back(server+it->service);
461 
462  return rc;
463 }
464 
466 {
467  vector<string> rc;
468 
469  const lock_guard<mutex> guard(fMutex);
470 
471  for (auto it=fServiceList.begin(); it!=fServiceList.end(); it++)
472  if (it->iscmd)
473  rc.push_back(it->server+"/"+it->service);
474 
475  return rc;
476 }
477 
479 {
480  const lock_guard<mutex> guard(fMutex);
481  return fServiceList;
482 }
483 
484 vector<State> StateMachineDimControl::GetStates(const string &server)
485 {
486  const lock_guard<mutex> guard(fMutex);
487 
488  vector<State> rc;
489 
490  for (auto it=fStateDescriptionList.begin(); it!=fStateDescriptionList.end(); it++)
491  {
492  if (it->first.first!=server)
493  continue;
494 
495  rc.emplace_back(it->first.second, it->second.first, it->second.second);
496  }
497 
498  return rc;
499 }
500 
501 
503 {
504  // Make a copy of the list to be able to
505  // lock the access to the list
506  const lock_guard<mutex> guard(fMutex);
507  fServiceList.insert(svc);
508 
509  return GetCurrentState();
510 }
511 
512 bool StateMachineDimControl::HasServer(const std::string &server)
513 {
514  const lock_guard<mutex> guard(fMutex);
515  return fServerList.find(server)!=fServerList.end();
516 }
517 
518 StateMachineDimControl::StateMachineDimControl(ostream &out) : StateMachineDim(out, fIsServer?"DIM_CONTROL":"")
519 {
520  fDim.Subscribe(*this);
521  fDimList.Subscribe(*this);
522 
526 
527  // State names
528  AddStateName(0, "Idle", "No script currently in processing.");
529  AddStateName(1, "Loading", "Script is loading.");
530  AddStateName(2, "Compiling", "JavaScript is compiling.");
531  AddStateName(3, "Running", "Script is running.");
532 
533  AddEvent("START", "C", 0)
534  (bind(&StateMachineDimControl::StartScript, this, placeholders::_1, ".js "))
535  ("Start a JavaScript");
536 
537  AddEvent("EXECUTE", "C", 0)
538  (bind(&StateMachineDimControl::StartScript, this, placeholders::_1, ".x "))
539  ("Execute a batch script");
540 
541  AddEvent("STOP", "C")
542  (bind(&StateMachineDimControl::StopScript, this, placeholders::_1))
543  ("Stop a runnning batch script or JavaScript");
544 
545  AddEvent("INTERRUPT", "C")
546  (bind(&StateMachineDimControl::InterruptScript, this, placeholders::_1))
547  ("Send an interrupt request (IRQ) to a running JavaScript");
548 }
549 
551 {
552  for (auto it=fDimDescriptionsList.begin(); it!=fDimDescriptionsList.end(); it++)
553  delete *it;
554 }
555 
557 {
558  fDebug = conf.Get<bool>("debug");
559  fUser = conf.Get<string>("user");
560  fScriptUser = fUser;
561 
562  // FIXME: Check fUser for quotes!
563 
564  const map<string, string> &js = conf.GetOptions<string>("JavaScript.");
565  for (auto it=js.begin(); it!=js.end(); it++)
566  {
567  string key = it->first;
568  string val = it->second;
569 
570  // Escape key
571  boost::replace_all(key, "\\", "\\\\");
572  boost::replace_all(key, "'", "\\'");
573  boost::replace_all(key, "\"", "\\\"");
574 
575  // Escape value
576  boost::replace_all(val, "\\", "\\\\");
577  boost::replace_all(val, "'", "\\'");
578  boost::replace_all(val, "\"", "\\\"");
579 
580  fArgumentsJS += " '"+key +"'='"+val+"'";
581  }
582 
583  // fVerbosity = 40;
584 
585  // if (conf.Has("verbosity"))
586  // fVerbosity = conf.Get<uint32_t>("verbosity");
587 
588  // if (conf.Get<bool>("quiet"))
589  // fVerbosity = 90;
590 
591 #if BOOST_VERSION < 104600
592  const string fname = boost::filesystem::path(conf.GetName()).filename();
593 #else
594  const string fname = boost::filesystem::path(conf.GetName()).filename().string();
595 #endif
596 
597  if (fname=="dimserver")
598  return -1;
599 
600  if (conf.Get<bool>("stop"))
601  return !Dim::SendCommand("DIM_CONTROL/STOP", fUser);
602 
603  if (conf.Has("interrupt"))
604  return !Dim::SendCommand("DIM_CONTROL/INTERRUPT", conf.Get<string>("interrupt")+"\n"+fUser);
605 
606  if (conf.Has("start"))
607  return !Dim::SendCommand("DIM_CONTROL/START", conf.Get<string>("start")+" user='"+fUser+"'"+fArgumentsJS);
608 
609  if (conf.Has("batch"))
610  return !Dim::SendCommand("DIM_CONTROL/EXECUTE", conf.Get<string>("batch")+" user='"+fUser+"'");
611 
612  if (conf.Has("msg"))
613  return !Dim::SendCommand("CHAT/MSG", fUser+": "+conf.Get<string>("msg"));
614 
615  if (conf.Has("restart"))
616  return !Dim::SendCommand(conf.Get<string>("restart")+"/EXIT", uint32_t(126));
617 
618  return -1;
619 }
EventImp & AddEvent(const std::string &name, const std::string &states, const std::string &fmt)
void Stop(int code=0)
Request to stop the mainloop.
static int sendCommand(const char *name, int data)
Definition: diccpp.cxx:1098
virtual void Stop(int code=0)
Request to stop the mainloop.
A general base-class describing events issues in a state machine.
Definition: EventImp.h:11
int HandleAddService(const Service &svc)
std::string comment
Name (e.g. &#39;Connected&#39;)
Definition: State.h:13
bool SendDimCommand(const std::string &server, std::string str, std::ostream &lout)
Set color Green.
Definition: WindowLog.h:18
Adds some functionality to boost::posix_time::ptime for our needs.
Definition: Time.h:30
char str[80]
Definition: test_client.c:7
int HandleStates(const std::string &server, DimDescriptions *state)
T Get(const std::string &var)
Set color Yellow.
Definition: WindowLog.h:19
static int GetScriptDepth()
Definition: Readline.h:114
Set color Red.
Definition: WindowLog.h:17
STL namespace.
std::vector< State > states
Definition: DimState.h:132
int EvalOptions(Configuration &conf)
const std::map< std::string, T > GetOptions(const std::string &opt)
const int32_t & state() const
Definition: DimState.h:80
StateMachineDimControl(std::ostream &out=std::cout)
int PrintDescription(std::ostream &out, bool iscmd, const std::string &serv="", const std::string &service="")
std::string GetString() const
Definition: EventImp.cc:194
int HandleDescriptions(DimDescriptions *state)
State description() const
Definition: DimState.h:160
std::string server
Definition: DimState.h:61
int index
Definition: State.h:11
int PrintStates(std::ostream &out, const std::string &serv="")
int HandleStateChange(const std::string &server, DimDescriptions *state)
bool SendCommand(const std::string &command)
Definition: Dim.h:26
void SetCallbackStates(const callback_desc &cb)
Definition: DimState.h:142
bool Has(const std::string &var)
Definition: dis.c:69
uint16_t qos
Definition: HeadersGPS.h:29
std::set< Service > GetServiceList()
std::string fArgumentsJS
Default arguments provided to very java script.
static void JsStop()
Set color Blue.
Definition: WindowLog.h:20
void SetCallbackServiceAdd(const callback_svc &cb)
Definition: DimState.h:448
std::vector< std::string > GetServerList()
static void StopScript()
Definition: Readline.h:112
std::map< std::string, std::string > Split(std::string &, bool=false)
Definition: tools.cc:230
Commandline parsing, resource file parsing and database access.
Definition: Configuration.h:9
static void SetExternalInput(const std::string &inp)
Definition: Readline.h:116
State GetServerState(const std::string &server)
void SetCallbackServerRemove(const callback_srv &cb)
Definition: DimState.h:373
std::vector< std::vector< Description > > descriptions
Definition: DimState.h:187
int HandleServerRemove(const std::string &server)
int StartScript(const EventImp &imp, const std::string &cmd)
std::string Line(const std::string &txt, char fill)
float data[4 *1440]
virtual void Subscribe(StateMachineImp &imp)
Definition: DimState.h:68
int ChangeState(int qos, const Time &time, int scriptdepth, std::string scriptfile, std::string user)
std::vector< Description > GetDescription(const std::string &service)
int HandleServerAdd(const std::string &server)
Class for a state machine implementation within a DIM network.
std::vector< State > GetStates(const std::string &server)
void SetCallbackDescriptions(const callback_desc &cb)
Definition: DimState.h:197
static int Debug
Definition: dns.c:78
std::vector< DimDescriptions * > fDimDescriptionsList
std::vector< std::string > GetCommandList()
Error()
Definition: HeadersFTM.h:197
if(extraDns) new Dns
std::string name
Index (e.g. 1)
Definition: State.h:12
std::string Trim(const std::string &str)
Definition: tools.cc:68
std::pair< Time, int32_t > cur
Definition: DimState.h:65
int StopScript(const EventImp &imp)
int InterruptScript(const EventImp &imp)
bool HasServer(const std::string &server)
virtual void Subscribe(StateMachineImp &imp)
Definition: DimState.h:189
void SetCallbackServerAdd(const callback_srv &cb)
Definition: DimState.h:368
A compiler for the DIM data format string.
Definition: Converter.h:16
bool AddStateName(const int state, const std::string &name, const std::string &doc="")
Reset all attributes.
Definition: WindowLog.h:29
void SetCallback(const callback &cb)
Definition: DimState.h:74
static std::string GetScript()
Definition: Readline.h:118
const std::string & GetName() const
Set attribute Bold.
Definition: WindowLog.h:36
void Subscribe(StateMachineImp &imp)
Definition: DimState.h:442
std::vector< char > GetVector(const void *d, size_t size) const
Definition: Converter.cc:724
virtual size_t GetSize() const
Definition: EventImp.h:55