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

Last change on this file since 18544 was 18425, checked in by tbretz, 10 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.