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

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