FACT++  1.0
RemoteControl.h
Go to the documentation of this file.
1 #ifndef FACT_RemoteControl
2 #define FACT_RemoteControl
3 
4 // **************************************************************************
17 // **************************************************************************
18 #include <string>
19 
20 using namespace std;
21 
23 {
24 protected:
25  std::ostream &lin;
26  std::ostream &lout;
27 
28  std::string fCurrentServer;
29 
30 protected:
31  // Redirect asynchronous output to the output window
32  RemoteControlImp(std::ostream &out, std::ostream &in) : lin(out), lout(in)
33  {
34  }
35  virtual ~RemoteControlImp() { }
36  bool ProcessCommand(const std::string &str, bool change=true);
37 
38  virtual bool HasServer(const std::string &) { return false; }
39  virtual bool SendDimCommand(ostream &, const std::string &, const std::string &, bool = false) { return false; }
40 };
41 
42 // **************************************************************************
58 // **************************************************************************
59 #include "StateMachineDimControl.h"
60 
61 #include "InterpreterV8.h"
62 #include "ReadlineColor.h"
63 #include "Event.h"
64 #include "tools.h"
65 
66 template <class T>
67 class RemoteControl : public T, public RemoteControlImp, public InterpreterV8
68 {
69 protected:
71 
72  void SetSection(int s) { if (fImp) fImp->ChangeState(s); }
73 
74  int Write(const Time &time, const std::string &txt, int qos=MessageImp::kMessage)
75  {
76  if (!fImp)
77  return 0;
78  return fImp ? fImp->Write(time, txt, qos) : MessageImp::Write(time, txt, qos);
79  }
80 
81  void exitHandler(int code) { if (dynamic_cast<MainImp*>(fImp)) dynamic_cast<MainImp*>(fImp)->Stop(code); else exit(code); }
82 
83  // ==================== Readline tab-completion =====================
84 
85  static void append(std::string &str)
86  {
87  str.append("/");
88  }
89  static void chop(std::string &str)
90  {
91  const size_t p = str.find_first_of('/');
92  if (p!=string::npos)
93  str = str.substr(p+1);
94  }
95 
96  // This funtion defines which generator should be called.
97  // If it returns 0 the standard readline generator are called.
98  // Otherwise set the right generator with rl_completion_matches.
99  char **Completion(const char *text, int start, int)
100  {
101  if (T::fScript=="java")
102  {
103  return T::Complete(JsGetCommandList(text, start), text);
104  }
105 
106  // Get the whole buffer before the tab-position
107  const string b = string(T::GetBuffer());
108  const string s = b.substr(0, start);
109  const string l = Tools::Trim(s.c_str());
110  if (l.empty())
111  {
112  if (fCurrentServer.empty())
113  {
114  const size_t p1 = b.find_first_of(' ');
115  const size_t p2 = b.find_first_of('/');
116 
117  if (p1==string::npos && p2!=string::npos)
118  return T::Complete(GetCommandList(), text);
119 
120  std::vector<std::string> v = GetServerList();
121  for_each(v.begin(), v.end(), RemoteControl::append);
122  return T::Complete(v, text);
123  }
124  else
125  {
126  std::vector<std::string> v = GetCommandList(fCurrentServer);
127  for_each(v.begin(), v.end(), RemoteControl::chop);
128  return T::Complete(v, text);
129  }
130  }
131  return T::Complete(GetCommandList(l), text);
132  }
133 
134  void EventHook(bool newline)
135  {
136  if (fImp && !fImp->HasServer(fCurrentServer))
137  fCurrentServer = "";
138 
139  T::EventHook(newline);
140  }
141 
142  // ===== Interface to access the DIM network through the StateMachine ====
143 
144  bool HasServer(const std::string &server) { return fImp ? fImp->HasServer(server) : false; }
145  vector<string> GetServerList() const { return fImp ? fImp->GetServerList() : vector<string>(); }
146  vector<string> GetCommandList(const string &server) const { return fImp ? fImp->GetCommandList(server) : vector<string>(); }
147  vector<string> GetCommandList() const { return fImp ? fImp->GetCommandList() : vector<string>(); }
148  int PrintStates(std::ostream &out, const std::string &serv="") const { return fImp ? fImp->PrintStates(out, serv) : 0; }
149  int PrintDescription(std::ostream &out, bool iscmd, const std::string &serv="", const std::string &service="") const
150  { return fImp ? fImp->PrintDescription(out, iscmd, serv, service) : 0; }
151  bool SendDimCommand(ostream &out, const std::string &server, const std::string &str, bool do_throw=false)
152  {
153  if (do_throw)
154  return fImp ? fImp->SendDimCommand(server, str, out) : false;
155 
156  try
157  {
158  return fImp ? fImp->SendDimCommand(server, str, out) : false;
159  }
160  catch (const runtime_error &e)
161  {
162  lout << kRed << e.what() << endl;
163  return false;
164  }
165  }
166 
167  // ============ Pseudo-callback interface for the JavaScrip engine =======
168 
169  void JsLoad(const std::string &) { SetSection(-3); InterpreterV8::JsLoad(); }
170  void JsStart(const std::string &) { SetSection(-2); }
171  void JsEnd(const std::string &) { UnsubscribeAll(); InterpreterV8::JsEnd(); SetSection(-4); }
172  bool JsSend(const std::string &str) { return ProcessCommand(str, false); }
173  void JsOut(const std::string &msg) { lin << kDefault << msg << endl; }
174  void JsWarn(const std::string &msg) { lin << kYellow << msg << endl; }
175  void JsResult(const std::string &msg) { lin << kBlue << " = " << msg << '\n' << endl; }
176  void JsPrint(const std::string &msg) { if (fImp) fImp->Comment(msg); }
177  void JsAlarm(const std::string &msg) { if (fImp) fImp->Alarm(msg); }
178  void JsException(const std::string &str) { if (fImp) fImp->Error(str.empty()?"|":("| "+str)); }
179  bool JsHasState(int s) const { return fImp && fImp->HasState(s); }
180  bool JsHasState(const string &n) const { return fImp && (fImp->GetStateIndex(n)!=StateMachineImp::kSM_NotAvailable); }
181  bool JsSetState(int s) { if (!fImp || fImp->GetCurrentState()<2) return false; SetSection(s-4); return true; }
182  int JsGetState(const string &n) const { return fImp ? fImp->GetStateIndex(n) : StateMachineImp::kSM_NotAvailable; }
183  vector<State> JsGetStates(const string &server) { return fImp ? fImp->GetStates(server) : vector<State>(); }
184  set<Service> JsGetServices() { return fImp ? fImp->GetServiceList() : set<Service>(); }
185  vector<Description> JsGetDescription(const string &server) { return fImp ? fImp->GetDescription(server) : vector<Description>(); }
187  {
188  if (!fImp)
189  return State();
190  const int idx = fImp->GetCurrentState();
191  return State(idx, fImp->GetStateName(idx), fImp->GetStateDescription(idx));
192  }
193  State JsState(const std::string &server) { return fImp ? fImp->GetServerState(server) : State(-256, string()); }
194  bool JsNewState(int s, const string &n, const string &c)
195  {
196  return fImp && fImp->AddStateName(s, n, c);
197  }
198 
199  /*
200  void JsSleep(uint32_t ms)
201  {
202  const Time timeout = Time()+boost::posix_time::millisec(ms==0?1:ms);
203 
204  T::Lock();
205 
206  while (timeout>Time() && !T::IsScriptStopped())
207  usleep(1);
208 
209  T::Unlock();
210  }*/
211 
212  int JsWait(const string &server, int32_t state, uint32_t ms)
213  {
214  if (!fImp)
215  {
216  lout << kRed << "RemoteControl class not fully initialized." << endl;
217  T::StopScript();
218  return -1;
219  }
220 
221  if (!HasServer(server))
222  {
223  lout << kRed << "Server '" << server << "' not found." << endl;
224  T::StopScript();
225  return -1;
226  }
227 
228  T::Lock();
229 
230  const Time timeout = ms<=0 ? Time(Time::none) : Time()+boost::posix_time::millisec(ms);
231 
232  int rc = 0;
233  do
234  {
235  State st = fImp->GetServerState(server);
236  if (st.index==-256)
237  {
238  lout << kRed << "Server '" << server << "' disconnected." << endl;
239  T::StopScript();
240  return -1;
241  }
242  if (st.index==state)
243  {
244  rc = 1;
245  break;
246  }
247 
248  usleep(1);
249  }
250  while (timeout>Time() && !T::IsScriptStopped());
251 
252  T::Unlock();
253 
254  return rc;
255  }
256 
257  vector<Description> JsDescription(const string &service)
258  {
259  return fImp ? fImp->GetDescription(service) : vector<Description>();
260  }
261 
262  struct EventInfo
263  {
265  uint64_t counter;
267  EventInfo(EventImp *p) : ptr(p), counter(0) { }
268  };
269 
270  // Keep a copy of the data for access by V8
271  map<string, EventInfo> fInfo;
272  std::mutex fMutex;
273 
274  pair<uint64_t, EventImp *> JsGetEvent(const std::string &service)
275  {
276  // This function is called from JavaScript
277  const lock_guard<mutex> lock(fMutex);
278 
279  const auto it = fInfo.find(service);
280 
281  // No subscription for this event available
282  if (it==fInfo.end())
283  return make_pair(0, static_cast<EventImp*>(NULL));
284 
285  EventInfo &info = it->second;
286 
287  // No event was received yet
288  if (info.counter==0)
289  return make_pair(0, static_cast<EventImp*>(NULL));
290 
291  return make_pair(info.counter-1, (EventImp*)&info.data);
292  }
293 
294  int Handle(const EventImp &evt, const string &service)
295  {
296  // This function is called from the StateMachine
297  fMutex.lock();
298 
299  const auto it = fInfo.find(service);
300 
301  // This should never happen... but just in case.
302  if (it==fInfo.end())
303  {
304  fMutex.unlock();
306  }
307 
308  EventInfo &info = it->second;
309 
310  const uint64_t cnt = ++info.counter;
311  info.data = static_cast<Event>(evt);
312 
313  fMutex.unlock();
314 
315  JsHandleEvent(evt, cnt, service);
316 
318  }
319 
320  void *JsSubscribe(const std::string &service)
321  {
322  if (!fImp)
323  return 0;
324 
325  // This function is called from JavaScript
326  const lock_guard<mutex> lock(fMutex);
327 
328  // Do not subscribe twice
329  if (fInfo.find(service)!=fInfo.end())
330  return 0;
331 
332  EventImp *ptr = &fImp->Subscribe(service)(fImp->Wrap(bind(&RemoteControl<T>::Handle, this, placeholders::_1, service)));
333  fInfo.insert(make_pair(service, EventInfo(ptr)));
334  return ptr;
335  }
336 
337  bool JsUnsubscribe(const std::string &service)
338  {
339  if (!fImp)
340  return false;
341 
342  // This function is called from JavaScript
343  const lock_guard<mutex> lock(fMutex);
344 
345  const auto it = fInfo.find(service);
346  if (it==fInfo.end())
347  return false;
348 
349  fImp->Unsubscribe(it->second.ptr);
350  fInfo.erase(it);
351 
352  return true;
353  }
354 
356  {
357  // This function is called from JavaScript
358  const lock_guard<mutex> lock(fMutex);
359 
360  for (auto it=fInfo.begin(); it!=fInfo.end(); it++)
361  fImp->Unsubscribe(it->second.ptr);
362 
363  fInfo.clear();
364  }
365 
366  // ===========================================================================
367 
368 
369 public:
370  // Redirect asynchronous output to the output window
371  RemoteControl(const char *name) : T(name),
372  RemoteControlImp(T::GetStreamOut(), T::GetStreamIn()), fImp(0)
373  {
374  }
375 
377  {
379  lout << " " << kUnderline << "Specific commands:\n";
380  lout << kBold << " h,help <arg> " << kReset << "List help text for given server or command.\n";
381  lout << kBold << " svc,services " << kReset << "List all services in the network.\n";
382  lout << kBold << " st,states " << kReset << "List all states in the network.\n";
383  lout << kBold << " > <text> " << kReset << "Echo <text> to the output stream\n";
384  lout << kBold << " .s " << kReset << "Wait for the state-machine to change to the given state.\n";
385  lout << " " " .s <server> [<state> [<timeout> [<label>]]]\n";
386  lout << " " "<server> The server for which state to wait (e.g. FTM_CONTROL)\n";
387  lout << " " "<state> The state id (see 'states') for which to wait (e.g. 3)\n";
388  lout << " " "<imeout> A timeout in millisenconds how long to wait (e.g. 500)\n";
389  lout << " " "<label> A label (number) until which everything is skipped in case of timeout\n";
390  lout << kBold << " .js file " << kReset << "Execute a JavaScript\n";
392  lout << kBold << " .java " << kReset << "Start JavaScript interpreter\n";
393  lout << endl;
394  return true;
395  }
396 
398  {
399  lout << endl << kBold << "List of commands:" << endl;
400  PrintDescription(lout, true);
401  return true;
402  }
403 
404  // returns whether a command should be put into the history
405  bool Process(const std::string &str)
406  {
407  if (str.substr(0, 2)=="h " || str.substr(0, 5)=="help ")
408  {
409  const size_t p1 = str.find_first_of(' ');
410  const string svc = str.substr(p1+1);
411 
412  const size_t p3 = svc.find_first_of('/');
413  const string s = svc.substr(0, p3);
414  const string c = p3==string::npos?"":svc.substr(p3+1);
415 
416  lout << endl;
417  if (!fCurrentServer.empty())
418  {
419  if (PrintDescription(lout, true, fCurrentServer, svc)==0)
420  lout << " " << svc << ": <not found>" << endl;
421  }
422  else
423  {
424  if (PrintDescription(lout, true, s, c)==0)
425  lout << " <no matches found>" <<endl;
426  }
427 
428  return true;
429  }
430 
431  if (str.substr(0, 4)==".js ")
432  {
433  string opt(str.substr(4));
434 
435  map<string,string> data = Tools::Split(opt, true);
436  if (opt.size()==0)
437  {
438  if (data.size()==0)
439  lout << kRed << "JavaScript filename missing." << endl;
440  else
441  lout << kRed << "Equal sign missing in argument '" << data.begin()->first << "'" << endl;
442 
443  return true;
444  }
445 
446  T::fScript = opt;
447 
448  T::Lock();
449  JsRun(opt, data);
450  T::Unlock();
451 
452  return true;
453  }
454 
455  if (str==".java" && !StateMachineDimControl::fIsServer)
456  {
457  T::fScript = "java";
458 
459  T::Lock();
460  JsRun("");
461  T::Unlock();
462 
463  T::fScript = "";
464 
465  return true;
466  }
467 
468  if (str.substr(0, 3)==".s ")
469  {
470  istringstream in(str.substr(3));
471 
472  int state=-100, ms=0;
473  string server;
474 
475  in >> server >> state >> ms;
476  if (state==-100)
477  {
478  lout << kRed << "Couldn't parse state id in '" << str.substr(3) << "'" << endl;
479  return true;
480  }
481 
482  T::Lock();
483  const int rc = JsWait(server, state, ms);
484  T::Unlock();
485 
486  if (rc<0 || rc==1)
487  return true;
488 
489  int label = -1;
490  in >> label;
491  if (in.fail() && !in.eof())
492  {
493  lout << kRed << "Invalid label in '" << str.substr(3) << "'" << endl;
494  T::StopScript();
495  return true;
496  }
497  T::SetLabel(label);
498 
499  return true;
500  }
501 
502  if (str[0]=='>')
503  {
504  fImp->Comment(Tools::Trim(str.substr(1)));
505  return true;
506  }
507 
508  if (ReadlineColor::Process(lout, str))
509  return true;
510 
511  if (T::Process(str))
512  return true;
513 
514  if (str=="services" || str=="svc")
515  {
516  PrintDescription(lout, false);
517  return true;
518  }
519 
520  if (str=="states" || str=="st")
521  {
522  PrintStates(lout);
523  return true;
524  }
525 
526  return !ProcessCommand(str);
527  }
528 
530  {
531  fImp = &imp;
532  fImp->SetStateCallback(bind(&InterpreterV8::JsHandleState, this, placeholders::_1, placeholders::_2));
533  fImp->SetInterruptHandler(bind(&InterpreterV8::JsHandleInterrupt, this, placeholders::_1));
534  }
535 };
536 
537 
538 
539 // **************************************************************************
543 // **************************************************************************
544 #include "Console.h"
545 
546 class RemoteStream : public RemoteControl<ConsoleStream>
547 {
548 public:
549  RemoteStream(const char *name, bool null = false)
550  : RemoteControl<ConsoleStream>(name) { SetNullOutput(null); }
551 };
552 
553 // **************************************************************************
563 // **************************************************************************
564 
565 class RemoteConsole : public RemoteControl<Console>
566 {
567 public:
568  RemoteConsole(const char *name, bool continous=false) :
569  RemoteControl<Console>(name)
570  {
571  SetContinous(continous);
572  }
573  string GetUpdatePrompt() const;
574 };
575 
576 // **************************************************************************
586 // **************************************************************************
587 #include "Shell.h"
588 
589 class RemoteShell : public RemoteControl<Shell>
590 {
591 public:
592  RemoteShell(const char *name, bool = false) :
593  RemoteControl<Shell>(name)
594  {
595  }
596  string GetUpdatePrompt() const;
597 };
598 
599 #endif
int start(int initState)
Definition: feeserver.c:1740
int GetStateIndex(const std::string &name) const
Derives the RemoteControl from Shell and adds colored prompt.
int Comment(const std::string &str)
Definition: MessageImp.h:52
This is an extension to the Readline class provding a colored output.
Definition: Console.h:7
void JsHandleState(const std::string &, const State &)
bool PrintGeneralHelp(std::ostream &out, const std::string &name)
void * JsSubscribe(const std::string &service)
std::mutex fMutex
static void chop(std::string &str)
Definition: RemoteControl.h:89
bool HasServer(const std::string &server)
void JsOut(const std::string &msg)
int GetCurrentState() const
return the current state of the machine
void SetInterruptHandler(const std::function< int(const EventImp &)> &func=std::function< int(const EventImp &)>())
A general base-class describing events issues in a state machine.
Definition: EventImp.h:11
bool JsSend(const std::string &str)
void EventHook(bool newline)
void JsException(const std::string &str)
void JsPrint(const std::string &msg)
std::vector< std::string > GetCommandList(const std::string &server)
bool SendDimCommand(const std::string &server, std::string str, std::ostream &lout)
This is an extension to the Readline class provding buffered output.
Definition: Console.h:34
Adds some functionality to boost::posix_time::ptime for our needs.
Definition: Time.h:30
State JsState(const std::string &server)
bool JsNewState(int s, const string &n, const string &c)
char str[80]
Definition: test_client.c:7
const std::string GetStateDescription(int state) const
Definition: MainImp.h:4
Set color Yellow.
Definition: WindowLog.h:19
This implements the basic functions of a remote control via dim.
Definition: RemoteControl.h:22
Set color Red.
Definition: WindowLog.h:17
int Alarm(const std::string &str)
Definition: MessageImp.h:50
virtual void JsEnd(const std::string &="")
bool JsHasState(const string &n) const
Set default colors.
Definition: WindowLog.h:16
STL namespace.
void JsLoad(const std::string &)
bool Process(const std::string &str)
void SetStateCallback(const std::function< void(const std::string &, const State &)> &func)
StateMachineDimControl * fImp
Definition: RemoteControl.h:70
virtual bool HasServer(const std::string &)
Definition: RemoteControl.h:38
bool SendDimCommand(ostream &out, const std::string &server, const std::string &str, bool do_throw=false)
void UnsubscribeAll()
void exitHandler(int code)
Definition: RemoteControl.h:81
int PrintDescription(std::ostream &out, bool iscmd, const std::string &serv="", const std::string &service="")
const std::string GetStateName(int state) const
void SetReceiver(StateMachineDimControl &imp)
State JsGetCurrentState() const
vector< Description > JsDescription(const string &service)
RemoteControl(const char *name)
void JsResult(const std::string &msg)
int index
Definition: State.h:11
int PrintStates(std::ostream &out, const std::string &serv="")
int Write(const Time &time, const std::string &txt, int qos=MessageImp::kMessage)
Definition: RemoteControl.h:74
std::ostream & lin
Definition: RemoteControl.h:25
int JsWait(const string &server, int32_t state, uint32_t ms)
virtual ~RemoteControlImp()
Definition: RemoteControl.h:35
Set attribute Underline.
Definition: WindowLog.h:32
RemoteConsole(const char *name, bool continous=false)
Implements a remote control based on a Readline class for the dim network.
Definition: RemoteControl.h:67
vector< Description > JsGetDescription(const string &server)
Just a message, usually obsolete.
Definition: MessageImp.h:16
virtual int Write(const Time &time, const std::string &txt, int qos=kMessage)
Definition: MessageImp.cc:133
int JsHandleInterrupt(const EventImp &)
Derives the RemoteControl from Control and adds a proper prompt.
bool JsSetState(int s)
std::ostream & lout
Output stream for local synchrounous output.
Definition: RemoteControl.h:26
bool JsUnsubscribe(const std::string &service)
Definition: dis.c:69
uint16_t qos
Definition: HeadersGPS.h:29
std::set< Service > GetServiceList()
vector< string > GetCommandList(const string &server) const
bool JsHasState(int s) const
int Write(const Time &time, const std::string &txt, int qos=kMessage)
Redirect our own logging to fLog.
vector< string > GetCommandList() const
void JsStart(const std::string &)
void JsWarn(const std::string &msg)
int Error(const std::string &str)
Definition: MessageImp.h:49
Set color Blue.
Definition: WindowLog.h:20
char ** Completion(const char *text, int start, int)
Definition: RemoteControl.h:99
std::vector< std::string > GetServerList()
map< string, EventInfo > fInfo
std::string fCurrentServer
Output stream for local synchrounous output.
Definition: RemoteControl.h:28
Warning because the service this data corrsponds to might have been last updated longer ago than Local time
Definition: smartfact.txt:92
std::map< std::string, std::string > Split(std::string &, bool=false)
Definition: tools.cc:230
bool HasState(int index) const
vector< string > GetServerList() const
vector< State > JsGetStates(const string &server)
State GetServerState(const std::string &server)
int PrintDescription(std::ostream &out, bool iscmd, const std::string &serv="", const std::string &service="") const
void JsEnd(const std::string &)
bool Process(std::ostream &out, const std::string &str)
static void append(std::string &str)
Definition: RemoteControl.h:85
virtual void JsLoad(const std::string &="")
float data[4 *1440]
int ChangeState(int qos, const Time &time, int scriptdepth, std::string scriptfile, std::string user)
EventImp & Subscribe(const std::string &name)
std::vector< Description > GetDescription(const std::string &service)
Concerete implementation of an EventImp stroring name, format, data and time.
Definition: Event.h:6
RemoteStream(const char *name, bool null=false)
std::vector< State > GetStates(const std::string &server)
int JsGetState(const string &n) const
Possible return value for GetStateIndex.
virtual bool SendDimCommand(ostream &, const std::string &, const std::string &, bool=false)
Definition: RemoteControl.h:39
bool PrintGeneralHelp()
set< Service > JsGetServices()
std::string Trim(const std::string &str)
Definition: tools.cc:68
void Unsubscribe(EventImp *evt)
void SetSection(int s)
Definition: RemoteControl.h:72
RemoteControlImp(std::ostream &out, std::ostream &in)
The server to which we currently cd&#39;ed.
Definition: RemoteControl.h:32
bool HasServer(const std::string &server)
int PrintStates(std::ostream &out, const std::string &serv="") const
bool AddStateName(const int state, const std::string &name, const std::string &doc="")
Reset all attributes.
Definition: WindowLog.h:29
bool PrintCommands()
Do not initialize the time.
Definition: Time.h:51
pair< uint64_t, EventImp * > JsGetEvent(const std::string &service)
int Handle(const EventImp &evt, const string &service)
Set attribute Bold.
Definition: WindowLog.h:36
RemoteShell(const char *name, bool=false)
Implementation of a console based user shell with an input and output window.
Definition: Shell.h:12
std::function< int(const EventImp &)> Wrap(const std::function< int(const EventImp &)> &func)
void JsAlarm(const std::string &msg)