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

Last change on this file since 18716 was 18425, checked in by tbretz, 9 years ago
Make sure that in case of an external 'exit' the V8 threads are properly disposed.
File size: 17.3 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
139void StateMachineDimControl::Stop(int code)
140{
141 InterpreterV8::JsStop();
142 StateMachineDim::Stop(code);
143}
144
145int StateMachineDimControl::InterruptScript(const EventImp &evt)
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
166bool 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
230int 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
274int 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
333int StateMachineDimControl::HandleStateChange(const string &server, DimDescriptions *dim)
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
346State StateMachineDimControl::GetServerState(const std::string &server)
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
354int StateMachineDimControl::HandleStates(const string &server, DimDescriptions *dim)
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
376int StateMachineDimControl::HandleDescriptions(DimDescriptions *dim)
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
386std::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
394int StateMachineDimControl::HandleServerAdd(const string &server)
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));
412 d->SetCallbackDescriptions(bind(&StateMachineDimControl::HandleDescriptions, this, 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
426int StateMachineDimControl::HandleServerRemove(const string &server)
427{
428 const lock_guard<mutex> guard(fMutex);
429 fServerList.erase(server);
430
431 return GetCurrentState();
432}
433
434vector<string> StateMachineDimControl::GetServerList()
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
447vector<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
465vector<string> StateMachineDimControl::GetCommandList()
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
478set<Service> StateMachineDimControl::GetServiceList()
479{
480 const lock_guard<mutex> guard(fMutex);
481 return fServiceList;
482}
483
484vector<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
502int StateMachineDimControl::HandleAddService(const Service &svc)
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
512bool StateMachineDimControl::HasServer(const std::string &server)
513{
514 const lock_guard<mutex> guard(fMutex);
515 return fServerList.find(server)!=fServerList.end();
516}
517
518StateMachineDimControl::StateMachineDimControl(ostream &out) : StateMachineDim(out, fIsServer?"DIM_CONTROL":"")
519{
520 fDim.Subscribe(*this);
521 fDimList.Subscribe(*this);
522
523 fDimList.SetCallbackServerAdd (bind(&StateMachineDimControl::HandleServerAdd, this, placeholders::_1));
524 fDimList.SetCallbackServerRemove(bind(&StateMachineDimControl::HandleServerRemove, this, placeholders::_1));
525 fDimList.SetCallbackServiceAdd (bind(&StateMachineDimControl::HandleAddService, this, placeholders::_1));
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
550StateMachineDimControl::~StateMachineDimControl()
551{
552 for (auto it=fDimDescriptionsList.begin(); it!=fDimDescriptionsList.end(); it++)
553 delete *it;
554}
555
556int StateMachineDimControl::EvalOptions(Configuration &conf)
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}
Note: See TracBrowser for help on using the repository browser.