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

Last change on this file since 14544 was 14540, checked in by tbretz, 13 years ago
Use State in JsState and added 'time' to the data members. Make sure there is no crash if an mpty event was received. Added 'size' to service objects.
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 State JsState(const std::string &server)
173 {
174 return fImp ? fImp->GetServerState(server) : State(-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 do
211 {
212 State st = fImp->GetServerState(server);
213 if (st.index==-256)
214 {
215 lout << kRed << "Server '" << server << "' disconnected." << endl;
216 T::StopScript();
217 return -1;
218 }
219 if (st.index==state)
220 {
221 rc = 1;
222 break;
223 }
224
225 usleep(1);
226 }
227 while (timeout>Time() && !T::IsScriptStopped());
228
229 T::Unlock();
230
231 return rc;
232 }
233
234 vector<Description> JsDescription(const string &service)
235 {
236 return fImp ? fImp->GetDescription(service) : vector<Description>();
237 }
238
239 // Keep a copy of the data for access by V8
240 map<string, EventImp *> fEvents; // For unsibscription
241 map<string, pair<uint64_t, Event>> fData;
242
243 std::mutex fMutex;
244
245 pair<uint64_t, EventImp *> JsGetEvent(const std::string &service)
246 {
247 const lock_guard<mutex> lock(fMutex);
248
249 const auto it = fData.find(service);
250
251 return it==fData.end() ? make_pair(uint64_t(0), (Event*)0) : make_pair(it->second.first, &it->second.second);
252 }
253
254 int Handle(const EventImp &evt, const string &service)
255 {
256 const lock_guard<mutex> lock(fMutex);
257
258 const auto it = fData.find(service);
259 if (it==fData.end())
260 fData[service] = make_pair(0, static_cast<Event>(evt));
261 else
262 {
263 it->second.first++;
264 it->second.second = static_cast<Event>(evt);
265 }
266
267 return StateMachineImp::kSM_KeepState;
268 }
269
270 void *JsSubscribe(const std::string &service)
271 {
272 if (!fImp)
273 return 0;
274
275 // Do not subscribe twice
276 if (fEvents.find(service)!=fEvents.end())
277 return 0;
278
279 return fEvents[service] = &fImp->Subscribe(service)(fImp->Wrap(bind(&RemoteControl<T>::Handle, this, placeholders::_1, service)));
280 }
281
282 bool JsUnsubscribe(const std::string &service)
283 {
284 if (!fImp)
285 return false;
286
287 const auto it = fEvents.find(service);
288 if (it==fEvents.end())
289 return false;
290
291 fImp->Unsubscribe(it->second);
292 fEvents.erase(it);
293
294 return true;
295 }
296
297 void UnsubscribeAll()
298 {
299 for (auto it=fEvents.begin(); it!=fEvents.end(); it++)
300 fImp->Unsubscribe(it->second);
301 fEvents.clear();
302 }
303
304 // ===========================================================================
305
306
307public:
308 // Redirect asynchronous output to the output window
309 RemoteControl(const char *name) : T(name),
310 RemoteControlImp(T::GetStreamOut(), T::GetStreamIn()), fImp(0)
311 {
312 }
313
314 bool PrintGeneralHelp()
315 {
316 T::PrintGeneralHelp();
317 lout << " " << kUnderline << "Specific commands:\n";
318 lout << kBold << " h,help <arg> " << kReset << "List help text for given server or command.\n";
319 lout << kBold << " svc,services " << kReset << "List all services in the network.\n";
320 lout << kBold << " st,states " << kReset << "List all states in the network.\n";
321 lout << kBold << " > <text> " << kReset << "Echo <text> to the output stream\n";
322 lout << kBold << " .s " << kReset << "Wait for the state-machine to change to the given state.\n";
323 lout << " " " .s <server> [<state> [<timeout> [<label>]]]\n";
324 lout << " " "<server> The server for which state to wait (e.g. FTM_CONTROL)\n";
325 lout << " " "<state> The state id (see 'states') for which to wait (e.g. 3)\n";
326 lout << " " "<imeout> A timeout in millisenconds how long to wait (e.g. 500)\n";
327 lout << " " "<label> A label (number) until which everything is skipped in case of timeout\n";
328 lout << endl;
329 return true;
330 }
331
332 bool PrintCommands()
333 {
334 lout << endl << kBold << "List of commands:" << endl;
335 PrintDescription(lout, true);
336 return true;
337 }
338
339 // returns whether a command should be put into the history
340 bool Process(const std::string &str)
341 {
342 if (str.substr(0, 2)=="h " || str.substr(0, 5)=="help ")
343 {
344 const size_t p1 = str.find_first_of(' ');
345 const string svc = str.substr(p1+1);
346
347 const size_t p3 = svc.find_first_of('/');
348 const string s = svc.substr(0, p3);
349 const string c = p3==string::npos?"":svc.substr(p3+1);
350
351 lout << endl;
352 if (!fCurrentServer.empty())
353 {
354 if (PrintDescription(lout, true, fCurrentServer, svc)==0)
355 lout << " " << svc << ": <not found>" << endl;
356 }
357 else
358 {
359 if (PrintDescription(lout, true, s, c)==0)
360 lout << " <no matches found>" <<endl;
361 }
362
363 return true;
364 }
365
366 if (str.substr(0, 4)==".js ")
367 {
368 string opt(str.substr(4));
369
370 map<string,string> data = Tools::Split(opt, true);
371 if (opt.size()==0)
372 {
373 if (data.size()==0)
374 lout << kRed << "JavaScript filename missing." << endl;
375 else
376 lout << kRed << "Equal sign missing in argument '" << data.begin()->first << "'" << endl;
377
378 return true;
379 }
380
381 T::fScript = opt;
382 JsRun(opt, data);
383 return true;
384 }
385
386 if (str.substr(0, 3)==".s ")
387 {
388 istringstream in(str.substr(3));
389
390 int state=-100, ms=0;
391 string server;
392
393 in >> server >> state >> ms;
394 if (state==-100)
395 {
396 lout << kRed << "Couldn't parse state id in '" << str.substr(3) << "'" << endl;
397 return true;
398 }
399
400 const int rc = JsWait(server, state, ms);
401
402 if (rc<0 || rc==1)
403 return true;
404
405 int label = -1;
406 in >> label;
407 if (in.fail() && !in.eof())
408 {
409 lout << kRed << "Invalid label in '" << str.substr(3) << "'" << endl;
410 T::StopScript();
411 return true;
412 }
413 T::SetLabel(label);
414
415 return true;
416 }
417
418 if (str[0]=='>')
419 {
420 fImp->Comment(Tools::Trim(str.substr(1)));
421 return true;
422 }
423
424 if (ReadlineColor::Process(lout, str))
425 return true;
426
427 if (T::Process(str))
428 return true;
429
430 if (str=="services" || str=="svc")
431 {
432 PrintDescription(lout, false);
433 return true;
434 }
435
436 if (str=="states" || str=="st")
437 {
438 PrintStates(lout);
439 return true;
440 }
441
442 return !ProcessCommand(str);
443 }
444
445 void SetReceiver(StateMachineDimControl &imp) { fImp = &imp; }
446};
447
448
449
450// **************************************************************************
451/** @class RemoteStream
452
453 */
454// **************************************************************************
455#include "Console.h"
456
457class RemoteStream : public RemoteControl<ConsoleStream>
458{
459public:
460 RemoteStream(const char *name, bool null = false)
461 : RemoteControl<ConsoleStream>(name) { SetNullOutput(null); }
462};
463
464// **************************************************************************
465/** @class RemoteConsole
466
467@brief Derives the RemoteControl from Control and adds a proper prompt
468
469This is basically a RemoteControl, which derives through the template
470argument from the Console class. It enhances the functionality of
471the remote control with a proper updated prompt.
472
473 */
474// **************************************************************************
475
476class RemoteConsole : public RemoteControl<Console>
477{
478public:
479 RemoteConsole(const char *name, bool continous=false) :
480 RemoteControl<Console>(name)
481 {
482 SetContinous(continous);
483 }
484 string GetUpdatePrompt() const;
485};
486
487// **************************************************************************
488/** @class RemoteShell
489
490@brief Derives the RemoteControl from Shell and adds colored prompt
491
492This is basically a RemoteControl, which derives through the template
493argument from the Shell class. It enhances the functionality of
494the local control with a proper updated prompt.
495
496 */
497// **************************************************************************
498#include "Shell.h"
499
500class RemoteShell : public RemoteControl<Shell>
501{
502public:
503 RemoteShell(const char *name, bool = false) :
504 RemoteControl<Shell>(name)
505 {
506 }
507 string GetUpdatePrompt() const;
508};
509
510#endif
Note: See TracBrowser for help on using the repository browser.