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

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