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

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