source: trunk/FACT++/src/RemoteControl.h@ 14524

Last change on this file since 14524 was 14497, checked in by tbretz, 12 years ago
Added some basic infrastructure to send alarms e.g. to smartfact.
File size: 16.0 KB
Line 
1#ifndef FACT_RemoteControl
2#define FACT_RemoteControl
3
4// **************************************************************************
5/** @class RemoteControlImp
6
7@brief This implements the basic functions of a remote control via dim
8
9Through a ServiceList object this object subscribes to all available
10SERVICE_LISTs in the dim network. This allows to keep an up-to-date
11list of all servers and services. Its ProcessCommand member function
12allows to emit commands according to the services found in the network.
13Its infoHandler() is called as an update notifier from the ClientList
14object.
15
16**/
17// **************************************************************************
18#include <string>
19
20using namespace std;
21
22class RemoteControlImp
23{
24protected:
25 std::ostream &lout; /// Output stream for local synchrounous output
26
27 std::string fCurrentServer; /// The server to which we currently cd'ed
28
29protected:
30 // Redirect asynchronous output to the output window
31 RemoteControlImp(std::ostream &, std::ostream &in) : lout(in)
32 {
33 }
34 virtual ~RemoteControlImp() { }
35 bool ProcessCommand(const std::string &str);
36
37 virtual bool HasServer(const std::string &) { return false; }
38 virtual bool SendDimCommand(ostream &, std::string &, const std::string &) { return false; }
39};
40
41// **************************************************************************
42/** @class RemoteControl
43
44@brief Implements a remote control based on a Readline class for the dim network
45
46This template implements all functions which overwrite any function from the
47Readline class. Since several derivatives of the Readline class implement
48different kind of Readline access, this class can be derived by any of them
49due to its template argument. However, the normal case will be
50deriving it from either Console or Shell.
51
52@tparam T
53 The base class for RemoteControl. Either Readlien or a class
54 deriving from it. This is usually either Console or Shell.
55
56**/
57// **************************************************************************
58#include "StateMachineDimControl.h"
59
60#include "InterpreterV8.h"
61#include "ReadlineColor.h"
62#include "Event.h"
63#include "tools.h"
64
65template <class T>
66class RemoteControl : public T, public RemoteControlImp, public InterpreterV8
67{
68protected:
69 StateMachineDimControl *fImp;
70
71 void SetSection(int s) { if (fImp) fImp->Write(Time(), "", s); }
72
73 int Write(const Time &time, const std::string &txt, int qos=MessageImp::kMessage)
74 {
75 if (!fImp)
76 return 0;
77 return fImp ? fImp->Write(time, txt, qos) : MessageImp::Write(time, txt, qos);
78 }
79
80 void exitHandler(int code) { if (dynamic_cast<MainImp*>(fImp)) dynamic_cast<MainImp*>(fImp)->Stop(code); else exit(code); }
81
82 // ==================== Readline tab-completion =====================
83
84 static void append(std::string &str)
85 {
86 str.append("/");
87 }
88 static void chop(std::string &str)
89 {
90 const size_t p = str.find_first_of('/');
91 if (p!=string::npos)
92 str = str.substr(p+1);
93 }
94
95 // This funtion defines which generator should be called.
96 // If it returns 0 the standard reaqdline generator are called.
97 // Otherwise set the right generator with rl_completion_matches.
98 char **Completion(const char *text, int start, int)
99 {
100 // Get the whole buffer before the tab-position
101 const string b = string(T::GetBuffer());
102 const string s = b.substr(0, start);
103 const string l = Tools::Trim(s.c_str());
104 if (l.empty())
105 {
106 if (fCurrentServer.empty())
107 {
108 const size_t p1 = b.find_first_of(' ');
109 const size_t p2 = b.find_first_of('/');
110
111 if (p1==string::npos && p2!=string::npos)
112 return T::Complete(GetCommandList(), text);
113
114 std::vector<std::string> v = GetServerList();
115 for_each(v.begin(), v.end(), RemoteControl::append);
116 return T::Complete(v, text);
117 }
118 else
119 {
120 std::vector<std::string> v = GetCommandList(fCurrentServer);
121 for_each(v.begin(), v.end(), RemoteControl::chop);
122 return T::Complete(v, text);
123 }
124 }
125 return T::Complete(GetCommandList(l), text);
126 }
127
128 void EventHook()
129 {
130 if (fImp && !fImp->HasServer(fCurrentServer))
131 fCurrentServer = "";
132
133 T::EventHook();
134 }
135
136 // ===== Interface to access the DIM network through the StateMachine ====
137
138 bool HasServer(const std::string &server) { return fImp ? fImp->HasServer(server) : false; }
139 vector<string> GetServerList() const { return fImp ? fImp->GetServerList() : vector<string>(); }
140 vector<string> GetCommandList(const string &server) const { return fImp ? fImp->GetCommandList(server) : vector<string>(); }
141 vector<string> GetCommandList() const { return fImp ? fImp->GetCommandList() : vector<string>(); }
142 int PrintStates(std::ostream &out, const std::string &serv="") const { return fImp ? fImp->PrintStates(out, serv) : 0; }
143 int PrintDescription(std::ostream &out, bool iscmd, const std::string &serv="", const std::string &service="") const
144 { return fImp ? fImp->PrintDescription(out, iscmd, serv, service) : 0; }
145 bool SendDimCommand(ostream &out, std::string &server, const std::string &str)
146 {
147 try
148 {
149 if (fImp)
150 fImp->SendDimCommand(server, str, out);
151 //lout << kGreen << "Command emitted successfully to " << server << "." << endl;
152 return true;
153 }
154 catch (const runtime_error &e)
155 {
156 lout << kRed << e.what() << endl;
157 return false;
158 }
159 }
160
161 // ============ Pseudo-callback interface for the JavaScrip engine =======
162
163 virtual void JsLoad(const std::string &) { SetSection(-2); }
164 virtual void JsStart(const std::string &) { SetSection(-1); }
165 virtual void JsEnd(const std::string &) { UnsubscribeAll(); SetSection(-3); }
166 virtual bool JsSend(const std::string &str) { return ProcessCommand(str); }
167 virtual void JsOut(const std::string &msg) { lout << msg << endl; }
168 virtual void JsPrint(const std::string &msg) { if (fImp) fImp->Comment(msg.empty()?" ":msg); }
169 virtual void JsAlarm(const std::string &msg) { if (fImp) fImp->Alarm(msg.empty()?" ":msg); }
170 virtual void JsException(const std::string &str) { if (fImp) fImp->Error(str.empty()?" ":str); }
171
172 pair<int32_t, string> JsState(const std::string &server)
173 {
174 return fImp ? fImp->GetServerState(server) : make_pair(-256, string());
175 }
176
177 void JsSleep(uint32_t ms)
178 {
179 const Time timeout = Time()+boost::posix_time::millisec(ms==0?1:ms);
180
181 T::Lock();
182
183 while (timeout>Time() && !T::IsScriptStopped())
184 usleep(1);
185
186 T::Unlock();
187 }
188
189 int JsWait(const string &server, int32_t state, uint32_t ms)
190 {
191 if (!fImp)
192 {
193 lout << kRed << "RemoteControl class not fully initialized." << endl;
194 T::StopScript();
195 return -1;
196 }
197
198 if (!HasServer(server))
199 {
200 lout << kRed << "Server '" << server << "' not found." << endl;
201 T::StopScript();
202 return -1;
203 }
204
205 T::Lock();
206
207 const Time timeout = ms<=0 ? Time(Time::none) : Time()+boost::posix_time::millisec(ms);
208
209 int rc = 0;
210 while (timeout>Time() && !T::IsScriptStopped())
211 {
212 const pair<int32_t, string> st = fImp->GetServerState(server);
213 if (st.first==-256)
214 {
215 lout << kRed << "Server '" << server << "' disconnected." << endl;
216 T::StopScript();
217 return -1;
218 }
219 if (st.first==state)
220 {
221 rc = 1;
222 break;
223 }
224
225 usleep(1);
226 }
227 T::Unlock();
228
229 return rc;
230 }
231
232 vector<Description> JsDescription(const string &service)
233 {
234 return fImp ? fImp->GetDescription(service) : vector<Description>();
235 }
236
237 // Keep a copy of the data for access by V8
238 map<string, EventImp *> fEvents; // For unsibscription
239 map<string, pair<uint64_t, Event>> fData;
240
241 std::mutex fMutex;
242
243 pair<uint64_t, EventImp *> JsGetEvent(const std::string &service)
244 {
245 const lock_guard<mutex> lock(fMutex);
246
247 const auto it = fData.find(service);
248
249 return it==fData.end() ? make_pair(uint64_t(0), (Event*)0) : make_pair(it->second.first, &it->second.second);
250 }
251
252 int Handle(const EventImp &evt, const string &service)
253 {
254 const lock_guard<mutex> lock(fMutex);
255
256 const auto it = fData.find(service);
257 if (it==fData.end())
258 fData[service] = make_pair(0, static_cast<Event>(evt));
259 else
260 {
261 it->second.first++;
262 it->second.second = static_cast<Event>(evt);
263 }
264
265 return StateMachineImp::kSM_KeepState;
266 }
267
268 void *JsSubscribe(const std::string &service)
269 {
270 if (!fImp)
271 return 0;
272
273 // Do not subscribe twice
274 if (fEvents.find(service)!=fEvents.end())
275 return 0;
276
277 return fEvents[service] = &fImp->Subscribe(service)(fImp->Wrap(bind(&RemoteControl<T>::Handle, this, placeholders::_1, service)));
278 }
279
280 bool JsUnsubscribe(const std::string &service)
281 {
282 if (!fImp)
283 return false;
284
285 const auto it = fEvents.find(service);
286 if (it==fEvents.end())
287 return false;
288
289 fImp->Unsubscribe(it->second);
290 fEvents.erase(it);
291
292 return true;
293 }
294
295 void UnsubscribeAll()
296 {
297 for (auto it=fEvents.begin(); it!=fEvents.end(); it++)
298 fImp->Unsubscribe(it->second);
299 fEvents.clear();
300 }
301
302 // ===========================================================================
303
304
305public:
306 // Redirect asynchronous output to the output window
307 RemoteControl(const char *name) : T(name),
308 RemoteControlImp(T::GetStreamOut(), T::GetStreamIn()), fImp(0)
309 {
310 }
311
312 bool PrintGeneralHelp()
313 {
314 T::PrintGeneralHelp();
315 lout << " " << kUnderline << "Specific commands:\n";
316 lout << kBold << " h,help <arg> " << kReset << "List help text for given server or command.\n";
317 lout << kBold << " svc,services " << kReset << "List all services in the network.\n";
318 lout << kBold << " st,states " << kReset << "List all states in the network.\n";
319 lout << kBold << " > <text> " << kReset << "Echo <text> to the output stream\n";
320 lout << kBold << " .s " << kReset << "Wait for the state-machine to change to the given state.\n";
321 lout << " " " .s <server> [<state> [<timeout> [<label>]]]\n";
322 lout << " " "<server> The server for which state to wait (e.g. FTM_CONTROL)\n";
323 lout << " " "<state> The state id (see 'states') for which to wait (e.g. 3)\n";
324 lout << " " "<imeout> A timeout in millisenconds how long to wait (e.g. 500)\n";
325 lout << " " "<label> A label (number) until which everything is skipped in case of timeout\n";
326 lout << endl;
327 return true;
328 }
329
330 bool PrintCommands()
331 {
332 lout << endl << kBold << "List of commands:" << endl;
333 PrintDescription(lout, true);
334 return true;
335 }
336
337 // returns whether a command should be put into the history
338 bool Process(const std::string &str)
339 {
340 if (str.substr(0, 2)=="h " || str.substr(0, 5)=="help ")
341 {
342 const size_t p1 = str.find_first_of(' ');
343 const string svc = str.substr(p1+1);
344
345 const size_t p3 = svc.find_first_of('/');
346 const string s = svc.substr(0, p3);
347 const string c = p3==string::npos?"":svc.substr(p3+1);
348
349 lout << endl;
350 if (!fCurrentServer.empty())
351 {
352 if (PrintDescription(lout, true, fCurrentServer, svc)==0)
353 lout << " " << svc << ": <not found>" << endl;
354 }
355 else
356 {
357 if (PrintDescription(lout, true, s, c)==0)
358 lout << " <no matches found>" <<endl;
359 }
360
361 return true;
362 }
363
364 if (str.substr(0, 4)==".js ")
365 {
366 string opt(str.substr(4));
367
368 map<string,string> data = Tools::Split(opt, true);
369 if (opt.size()==0)
370 {
371 if (data.size()==0)
372 lout << kRed << "JavaScript filename missing." << endl;
373 else
374 lout << kRed << "Equal sign missing in argument '" << data.begin()->first << "'" << endl;
375
376 return true;
377 }
378
379 T::fScript = opt;
380 JsRun(opt, data);
381 return true;
382 }
383
384 if (str.substr(0, 3)==".s ")
385 {
386 istringstream in(str.substr(3));
387
388 int state=-100, ms=0;
389 string server;
390
391 in >> server >> state >> ms;
392 if (state==-100)
393 {
394 lout << kRed << "Couldn't parse state id in '" << str.substr(3) << "'" << endl;
395 return true;
396 }
397
398 const int rc = JsWait(server, state, ms);
399
400 if (rc<0 || rc==1)
401 return true;
402
403 int label = -1;
404 in >> label;
405 if (in.fail() && !in.eof())
406 {
407 lout << kRed << "Invalid label in '" << str.substr(3) << "'" << endl;
408 T::StopScript();
409 return true;
410 }
411 T::SetLabel(label);
412
413 return true;
414 }
415
416 if (str[0]=='>')
417 {
418 fImp->Comment(Tools::Trim(str.substr(1)));
419 return true;
420 }
421
422 if (ReadlineColor::Process(lout, str))
423 return true;
424
425 if (T::Process(str))
426 return true;
427
428 if (str=="services" || str=="svc")
429 {
430 PrintDescription(lout, false);
431 return true;
432 }
433
434 if (str=="states" || str=="st")
435 {
436 PrintStates(lout);
437 return true;
438 }
439
440 return !ProcessCommand(str);
441 }
442
443 void SetReceiver(StateMachineDimControl &imp) { fImp = &imp; }
444};
445
446
447
448// **************************************************************************
449/** @class RemoteStream
450
451 */
452// **************************************************************************
453#include "Console.h"
454
455class RemoteStream : public RemoteControl<ConsoleStream>
456{
457public:
458 RemoteStream(const char *name, bool null = false)
459 : RemoteControl<ConsoleStream>(name) { SetNullOutput(null); }
460};
461
462// **************************************************************************
463/** @class RemoteConsole
464
465@brief Derives the RemoteControl from Control and adds a proper prompt
466
467This is basically a RemoteControl, which derives through the template
468argument from the Console class. It enhances the functionality of
469the remote control with a proper updated prompt.
470
471 */
472// **************************************************************************
473
474class RemoteConsole : public RemoteControl<Console>
475{
476public:
477 RemoteConsole(const char *name, bool continous=false) :
478 RemoteControl<Console>(name)
479 {
480 SetContinous(continous);
481 }
482 string GetUpdatePrompt() const;
483};
484
485// **************************************************************************
486/** @class RemoteShell
487
488@brief Derives the RemoteControl from Shell and adds colored prompt
489
490This is basically a RemoteControl, which derives through the template
491argument from the Shell class. It enhances the functionality of
492the local control with a proper updated prompt.
493
494 */
495// **************************************************************************
496#include "Shell.h"
497
498class RemoteShell : public RemoteControl<Shell>
499{
500public:
501 RemoteShell(const char *name, bool = false) :
502 RemoteControl<Shell>(name)
503 {
504 }
505 string GetUpdatePrompt() const;
506};
507
508#endif
Note: See TracBrowser for help on using the repository browser.