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

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