source: trunk/FACT++/src/StateMachineDimControl.cc@ 18367

Last change on this file since 18367 was 18167, checked in by tbretz, 10 years ago
Print not only the identification of the script but also the user who sent the STOP command.
File size: 17.2 KB
Line 
1#include "StateMachineDimControl.h"
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
14using namespace std;
15
16// ------------------------------------------------------------------------
17
18bool StateMachineDimControl::fIsServer = false;
19
20string 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
36int 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
73int StateMachineDimControl::ChangeState(int state)
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
91int 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
121 Readline::SetExternalInput(emit);
122 return GetCurrentState();
123}
124
125int StateMachineDimControl::StopScript(const EventImp &imp)
126{
127 const string str(imp.GetString());
128
129 string msg("Stop received");
130 msg += str.empty() ? "." : " ["+str+"]";
131
132 Info(msg);
133
134 Readline::StopScript();
135 InterpreterV8::JsStop();
136 return GetCurrentState();
137}
138
139int StateMachineDimControl::InterruptScript(const EventImp &evt)
140{
141 if (!fInterruptHandler)
142 return GetCurrentState();
143
144 string str = evt.GetString();
145
146 const size_t p = str.find_last_of('\n');
147 if (p!=string::npos)
148 str[p] = ':';
149
150 if (GetCurrentState()<3)
151 {
152 Warn("Interrupt request received ["+str+"]... but no running script.");
153 return GetCurrentState();
154 }
155
156 Info("Interrupt request received ["+str+"]");
157 return fInterruptHandler(evt);
158}
159
160bool StateMachineDimControl::SendDimCommand(const string &server, string str, ostream &lout)
161{
162 const lock_guard<mutex> guard(fMutex);
163
164 if (fServerList.find(server)==fServerList.end())
165 throw runtime_error("SendDimCommand - Server '"+server+"' not online.");
166
167 str = Tools::Trim(str);
168
169 // Find the delimiter between the command name and the data
170 size_t p0 = str.find_first_of(' ');
171 if (p0==string::npos)
172 p0 = str.length();
173
174 // Get just the command name separated from the data
175 const string name = str.substr(0, p0);
176
177 // Compile the command which will be sent to the state-machine
178 for (auto is=fServiceList.begin(); is!=fServiceList.end(); is++)
179 {
180 if (str.empty() && is->server==server)
181 return true;
182
183 if (is->server!=server || is->service!=name)
184 continue;
185
186 if (!is->iscmd)
187 throw runtime_error("'"+server+"/"+name+" not a command.");
188
189 // Avoid compiler warning of unused parameter
190 lout << flush;
191
192 // Convert the user entered data according to the format string
193 // into a data block which will be attached to the event
194#ifndef DEBUG
195 ostringstream sout;
196 const Converter conv(sout, is->format, false);
197#else
198 const Converter conv(lout, is->format, false);
199#endif
200 if (!conv)
201 throw runtime_error("Couldn't properly parse the format... ignored.");
202
203#ifdef DEBUG
204 lout << kBlue << server << '/' << name;
205#endif
206 const vector<char> v = conv.GetVector(str.substr(p0));
207#ifdef DEBUG
208 lout << kBlue << " [" << v.size() << "]" << endl;
209#endif
210 const string cmd = server + '/' + name;
211 const int rc = DimClient::sendCommand(cmd.c_str(), (void*)v.data(), v.size());
212 if (!rc)
213 throw runtime_error("ERROR - Sending command "+cmd+" failed.");
214
215 return true;
216 }
217
218 if (!str.empty())
219 throw runtime_error("SendDimCommand - Format information for "+server+"/"+name+" not yet available.");
220
221 return false;
222}
223
224int StateMachineDimControl::PrintStates(std::ostream &out, const std::string &serv)
225{
226 const lock_guard<mutex> guard(fMutex);
227
228 int rc = 0;
229 for (auto it=fServerList.begin(); it!=fServerList.end(); it++)
230 {
231 if (!serv.empty() && *it!=serv)
232 continue;
233
234 out << kRed << "----- " << *it << " -----" << endl;
235
236 int cnt = 0;
237 for (auto is=fStateDescriptionList.begin(); is!=fStateDescriptionList.end(); is++)
238 {
239 const string &server = is->first.first;
240
241 if (server!=*it)
242 continue;
243
244 const int32_t &state = is->first.second;
245 const string &name = is->second.first;
246 const string &comment = is->second.second;
247
248 out << kBold << setw(5) << state << kReset << ": ";
249 out << kYellow << name;
250 if (!comment.empty())
251 out << kBlue << " (" << comment << ")";
252 out << endl;
253
254 cnt++;
255 }
256
257 if (cnt==0)
258 out << " <no states>" << endl;
259 else
260 rc++;
261
262 out << endl;
263 }
264
265 return rc;
266}
267
268int StateMachineDimControl::PrintDescription(std::ostream &out, bool iscmd, const std::string &serv, const std::string &service)
269{
270 const lock_guard<mutex> guard(fMutex);
271
272 int rc = 0;
273 for (auto it=fServerList.begin(); it!=fServerList.end(); it++)
274 {
275 if (!serv.empty() && *it!=serv)
276 continue;
277
278 out << kRed << "----- " << *it << " -----" << endl << endl;
279
280 for (auto is=fServiceList.begin(); is!=fServiceList.end(); is++)
281 {
282 if (is->server!=*it)
283 continue;
284
285 if (!service.empty() && is->service!=service)
286 continue;
287
288 if (is->iscmd!=iscmd)
289 continue;
290
291 rc++;
292
293 out << " " << is->service;
294 if (!is->format.empty())
295 out << '[' << is->format << ']';
296
297 const auto id = fServiceDescriptionList.find(*it+"/"+is->service);
298 if (id!=fServiceDescriptionList.end())
299 {
300 const vector<Description> &v = id->second;
301
302 for (auto j=v.begin()+1; j!=v.end(); j++)
303 out << " <" << j->name << ">";
304 out << endl;
305
306 if (!v[0].comment.empty())
307 out << " " << v[0].comment << endl;
308
309 for (auto j=v.begin()+1; j!=v.end(); j++)
310 {
311 out << " " << kGreen << j->name;
312 if (!j->comment.empty())
313 out << kReset << ": " << kBlue << j->comment;
314 if (!j->unit.empty())
315 out << kYellow << " [" << j->unit << "]";
316 out << endl;
317 }
318 }
319 out << endl;
320 }
321 out << endl;
322 }
323
324 return rc;
325}
326
327int StateMachineDimControl::HandleStateChange(const string &server, DimDescriptions *dim)
328{
329 fMutex.lock();
330 const State descr = dim->description();
331 const State state = State(dim->state(), descr.index==DimState::kNotAvailable?"":descr.name, descr.comment, dim->cur.first);
332 fCurrentStateList[server] = state;
333 fMutex.unlock();
334
335 fStateCallback(server, state);
336
337 return GetCurrentState();
338}
339
340State StateMachineDimControl::GetServerState(const std::string &server)
341{
342 const lock_guard<mutex> guard(fMutex);
343
344 const auto it = fCurrentStateList.find(server);
345 return it==fCurrentStateList.end() ? State() : it->second;
346}
347
348int StateMachineDimControl::HandleStates(const string &server, DimDescriptions *dim)
349{
350 const lock_guard<mutex> guard(fMutex);
351
352 const auto is = fCurrentStateList.find(server);
353 for (auto it=dim->states.begin(); it!=dim->states.end(); it++)
354 {
355 fStateDescriptionList[make_pair(server, it->index)] = make_pair(it->name, it->comment);
356 if (is==fCurrentStateList.end())
357 continue;
358
359 State &s = is->second;
360 if (s.index==it->index)
361 {
362 s.name = it->name;
363 s.comment = it->comment;
364 }
365 }
366
367 return GetCurrentState();
368}
369
370int StateMachineDimControl::HandleDescriptions(DimDescriptions *dim)
371{
372 const lock_guard<mutex> guard(fMutex);
373
374 for (auto it=dim->descriptions.begin(); it!=dim->descriptions.end(); it++)
375 fServiceDescriptionList[it->front().name].assign(it->begin(), it->end());
376
377 return GetCurrentState();
378}
379
380std::vector<Description> StateMachineDimControl::GetDescription(const std::string &service)
381{
382 const lock_guard<mutex> guard(fMutex);
383
384 const auto it = fServiceDescriptionList.find(service);
385 return it==fServiceDescriptionList.end() ? vector<Description>() : it->second;
386}
387
388int StateMachineDimControl::HandleServerAdd(const string &server)
389{
390 if (server!="DIS_DNS")
391 {
392 struct Find : string
393 {
394 Find(const string &ref) : string(ref) { }
395 bool operator()(const DimDescriptions *dim) { return *this==dim->server; }
396 };
397
398 if (find_if(fDimDescriptionsList.begin(), fDimDescriptionsList.end(),
399 Find(server))==fDimDescriptionsList.end())
400 {
401 DimDescriptions *d = new DimDescriptions(server);
402
403 fDimDescriptionsList.push_back(d);
404 d->SetCallback(bind(&StateMachineDimControl::HandleStateChange, this, server, d));
405 d->SetCallbackStates(bind(&StateMachineDimControl::HandleStates, this, server, d));
406 d->SetCallbackDescriptions(bind(&StateMachineDimControl::HandleDescriptions, this, d));
407 d->Subscribe(*this);
408 }
409 }
410
411 // Make a copy of the list to be able to
412 // lock the access to the list
413
414 const lock_guard<mutex> guard(fMutex);
415 fServerList.insert(server);
416
417 return GetCurrentState();
418}
419
420int StateMachineDimControl::HandleServerRemove(const string &server)
421{
422 const lock_guard<mutex> guard(fMutex);
423 fServerList.erase(server);
424
425 return GetCurrentState();
426}
427
428vector<string> StateMachineDimControl::GetServerList()
429{
430 vector<string> rc;
431
432 const lock_guard<mutex> guard(fMutex);
433
434 rc.reserve(fServerList.size());
435 for (auto it=fServerList.begin(); it!=fServerList.end(); it++)
436 rc.push_back(*it);
437
438 return rc;
439}
440
441vector<string> StateMachineDimControl::GetCommandList(const string &server)
442{
443 const lock_guard<mutex> guard(fMutex);
444
445 const string s = server.substr(0, server.length()-1);
446
447 if (fServerList.find(s)==fServerList.end())
448 return vector<string>();
449
450 vector<string> rc;
451
452 for (auto it=fServiceList.begin(); it!=fServiceList.end(); it++)
453 if (it->iscmd && it->server==s)
454 rc.push_back(server+it->service);
455
456 return rc;
457}
458
459vector<string> StateMachineDimControl::GetCommandList()
460{
461 vector<string> rc;
462
463 const lock_guard<mutex> guard(fMutex);
464
465 for (auto it=fServiceList.begin(); it!=fServiceList.end(); it++)
466 if (it->iscmd)
467 rc.push_back(it->server+"/"+it->service);
468
469 return rc;
470}
471
472set<Service> StateMachineDimControl::GetServiceList()
473{
474 const lock_guard<mutex> guard(fMutex);
475 return fServiceList;
476}
477
478vector<State> StateMachineDimControl::GetStates(const string &server)
479{
480 const lock_guard<mutex> guard(fMutex);
481
482 vector<State> rc;
483
484 for (auto it=fStateDescriptionList.begin(); it!=fStateDescriptionList.end(); it++)
485 {
486 if (it->first.first!=server)
487 continue;
488
489 rc.emplace_back(it->first.second, it->second.first, it->second.second);
490 }
491
492 return rc;
493}
494
495
496int StateMachineDimControl::HandleAddService(const Service &svc)
497{
498 // Make a copy of the list to be able to
499 // lock the access to the list
500 const lock_guard<mutex> guard(fMutex);
501 fServiceList.insert(svc);
502
503 return GetCurrentState();
504}
505
506bool StateMachineDimControl::HasServer(const std::string &server)
507{
508 const lock_guard<mutex> guard(fMutex);
509 return fServerList.find(server)!=fServerList.end();
510}
511
512StateMachineDimControl::StateMachineDimControl(ostream &out) : StateMachineDim(out, fIsServer?"DIM_CONTROL":"")
513{
514 fDim.Subscribe(*this);
515 fDimList.Subscribe(*this);
516
517 fDimList.SetCallbackServerAdd (bind(&StateMachineDimControl::HandleServerAdd, this, placeholders::_1));
518 fDimList.SetCallbackServerRemove(bind(&StateMachineDimControl::HandleServerRemove, this, placeholders::_1));
519 fDimList.SetCallbackServiceAdd (bind(&StateMachineDimControl::HandleAddService, this, placeholders::_1));
520
521 // State names
522 AddStateName(0, "Idle", "No script currently in processing.");
523 AddStateName(1, "Loading", "Script is loading.");
524 AddStateName(2, "Compiling", "JavaScript is compiling.");
525 AddStateName(3, "Running", "Script is running.");
526
527 AddEvent("START", "C", 0)
528 (bind(&StateMachineDimControl::StartScript, this, placeholders::_1, ".js "))
529 ("Start a JavaScript");
530
531 AddEvent("EXECUTE", "C", 0)
532 (bind(&StateMachineDimControl::StartScript, this, placeholders::_1, ".x "))
533 ("Execute a batch script");
534
535 AddEvent("STOP", "C")
536 (bind(&StateMachineDimControl::StopScript, this, placeholders::_1))
537 ("Stop a runnning batch script or JavaScript");
538
539 AddEvent("INTERRUPT", "C")
540 (bind(&StateMachineDimControl::InterruptScript, this, placeholders::_1))
541 ("Send an interrupt request (IRQ) to a running JavaScript");
542}
543
544StateMachineDimControl::~StateMachineDimControl()
545{
546 for (auto it=fDimDescriptionsList.begin(); it!=fDimDescriptionsList.end(); it++)
547 delete *it;
548}
549
550int StateMachineDimControl::EvalOptions(Configuration &conf)
551{
552 fDebug = conf.Get<bool>("debug");
553 fUser = conf.Get<string>("user");
554 fScriptUser = fUser;
555
556 // FIXME: Check fUser for quotes!
557
558 const map<string, string> &js = conf.GetOptions<string>("JavaScript.");
559 for (auto it=js.begin(); it!=js.end(); it++)
560 {
561 string key = it->first;
562 string val = it->second;
563
564 // Escape key
565 boost::replace_all(key, "\\", "\\\\");
566 boost::replace_all(key, "'", "\\'");
567 boost::replace_all(key, "\"", "\\\"");
568
569 // Escape value
570 boost::replace_all(val, "\\", "\\\\");
571 boost::replace_all(val, "'", "\\'");
572 boost::replace_all(val, "\"", "\\\"");
573
574 fArgumentsJS += " '"+key +"'='"+val+"'";
575 }
576
577 // fVerbosity = 40;
578
579 // if (conf.Has("verbosity"))
580 // fVerbosity = conf.Get<uint32_t>("verbosity");
581
582 // if (conf.Get<bool>("quiet"))
583 // fVerbosity = 90;
584
585#if BOOST_VERSION < 104600
586 const string fname = boost::filesystem::path(conf.GetName()).filename();
587#else
588 const string fname = boost::filesystem::path(conf.GetName()).filename().string();
589#endif
590
591 if (fname=="dimserver")
592 return -1;
593
594 if (conf.Get<bool>("stop"))
595 return !Dim::SendCommand("DIM_CONTROL/STOP", fUser);
596
597 if (conf.Has("interrupt"))
598 return !Dim::SendCommand("DIM_CONTROL/INTERRUPT", conf.Get<string>("interrupt")+"\n"+fUser);
599
600 if (conf.Has("start"))
601 return !Dim::SendCommand("DIM_CONTROL/START", conf.Get<string>("start")+" user='"+fUser+"'"+fArgumentsJS);
602
603 if (conf.Has("batch"))
604 return !Dim::SendCommand("DIM_CONTROL/EXECUTE", conf.Get<string>("batch")+" user='"+fUser+"'");
605
606 if (conf.Has("msg"))
607 return !Dim::SendCommand("CHAT/MSG", fUser+": "+conf.Get<string>("msg"));
608
609 if (conf.Has("restart"))
610 return !Dim::SendCommand(conf.Get<string>("restart")+"/EXIT", uint32_t(126));
611
612 return -1;
613}
Note: See TracBrowser for help on using the repository browser.