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

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