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

Last change on this file since 15075 was 15041, checked in by tbretz, 12 years ago
Use the lock guard where appropriate; added GetServiceList member function
File size: 16.2 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 const lock_guard<mutex> guard(fMutex);
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 return GetCurrentState();
335}
336
337int StateMachineDimControl::HandleDescriptions(DimDescriptions *dim)
338{
339 const lock_guard<mutex> guard(fMutex);
340
341 for (auto it=dim->descriptions.begin(); it!=dim->descriptions.end(); it++)
342 fServiceDescriptionList[it->front().name].assign(it->begin(), it->end());
343
344 return GetCurrentState();
345}
346
347std::vector<Description> StateMachineDimControl::GetDescription(const std::string &service)
348{
349 const lock_guard<mutex> guard(fMutex);
350
351 const auto it = fServiceDescriptionList.find(service);
352 return it==fServiceDescriptionList.end() ? vector<Description>() : it->second;
353}
354
355int StateMachineDimControl::HandleServerAdd(const string &server)
356{
357 if (server!="DIS_DNS")
358 {
359 struct Find : string
360 {
361 Find(const string &ref) : string(ref) { }
362 bool operator()(const DimDescriptions *dim) { return *this==dim->server; }
363 };
364
365 if (find_if(fDimDescriptionsList.begin(), fDimDescriptionsList.end(),
366 Find(server))==fDimDescriptionsList.end())
367 {
368 DimDescriptions *d = new DimDescriptions(server);
369
370 fDimDescriptionsList.push_back(d);
371 d->SetCallback(bind(&StateMachineDimControl::HandleStateChange, this, server, d));
372 d->SetCallbackStates(bind(&StateMachineDimControl::HandleStates, this, server, d));
373 d->SetCallbackDescriptions(bind(&StateMachineDimControl::HandleDescriptions, this, d));
374 d->Subscribe(*this);
375 }
376 }
377
378 // Make a copy of the list to be able to
379 // lock the access to the list
380
381 const lock_guard<mutex> guard(fMutex);
382 fServerList.insert(server);
383
384 return GetCurrentState();
385}
386
387int StateMachineDimControl::HandleServerRemove(const string &server)
388{
389 const lock_guard<mutex> guard(fMutex);
390 fServerList.erase(server);
391
392 return GetCurrentState();
393}
394
395vector<string> StateMachineDimControl::GetServerList()
396{
397 vector<string> rc;
398
399 const lock_guard<mutex> guard(fMutex);
400
401 rc.reserve(fServerList.size());
402 for (auto it=fServerList.begin(); it!=fServerList.end(); it++)
403 rc.push_back(*it);
404
405 return rc;
406}
407
408vector<string> StateMachineDimControl::GetCommandList(const string &server)
409{
410 const lock_guard<mutex> guard(fMutex);
411
412 const string s = server.substr(0, server.length()-1);
413
414 if (fServerList.find(s)==fServerList.end())
415 return vector<string>();
416
417 vector<string> rc;
418
419 for (auto it=fServiceList.begin(); it!=fServiceList.end(); it++)
420 if (it->iscmd && it->server==s)
421 rc.push_back(server+it->service);
422
423 return rc;
424}
425
426vector<string> StateMachineDimControl::GetCommandList()
427{
428 vector<string> rc;
429
430 const lock_guard<mutex> guard(fMutex);
431
432 for (auto it=fServiceList.begin(); it!=fServiceList.end(); it++)
433 if (it->iscmd)
434 rc.push_back(it->server+"/"+it->service);
435
436 return rc;
437}
438
439set<Service> StateMachineDimControl::GetServiceList()
440{
441 const lock_guard<mutex> guard(fMutex);
442 return fServiceList;
443}
444
445vector<State> StateMachineDimControl::GetStates(const string &server)
446{
447 const lock_guard<mutex> guard(fMutex);
448
449 vector<State> rc;
450
451 for (auto it=fStateDescriptionList.begin(); it!=fStateDescriptionList.end(); it++)
452 {
453 if (it->first.first!=server)
454 continue;
455
456 rc.push_back(State(it->first.second, it->second.first, it->second.second));
457 }
458
459 return rc;
460}
461
462
463int StateMachineDimControl::HandleAddService(const Service &svc)
464{
465 // Make a copy of the list to be able to
466 // lock the access to the list
467 const lock_guard<mutex> guard(fMutex);
468 fServiceList.insert(svc);
469
470 return GetCurrentState();
471}
472
473bool StateMachineDimControl::HasServer(const std::string &server)
474{
475 const lock_guard<mutex> guard(fMutex);
476 return fServerList.find(server)!=fServerList.end();
477}
478
479StateMachineDimControl::StateMachineDimControl(ostream &out) : StateMachineDim(out, fIsServer?"DIM_CONTROL":"")
480{
481 fDim.Subscribe(*this);
482 fDimList.Subscribe(*this);
483
484 fDimList.SetCallbackServerAdd (bind(&StateMachineDimControl::HandleServerAdd, this, placeholders::_1));
485 fDimList.SetCallbackServerRemove(bind(&StateMachineDimControl::HandleServerRemove, this, placeholders::_1));
486 fDimList.SetCallbackServiceAdd (bind(&StateMachineDimControl::HandleAddService, this, placeholders::_1));
487
488 // State names
489 AddStateName(0, "Idle", "No script currently in processing.");
490 AddStateName(1, "Loading", "Script is loading.");
491 AddStateName(2, "Compiling", "JavaScript is compiling.");
492 AddStateName(3, "Running", "Script is running.");
493
494 AddEvent("START", "C", 0)
495 (bind(&StateMachineDimControl::StartScript, this, placeholders::_1, ".js "))
496 ("Start a JavaScript");
497
498 AddEvent("EXECUTE", "C", 0)
499 (bind(&StateMachineDimControl::StartScript, this, placeholders::_1, ".x "))
500 ("Execute a batch script");
501
502 AddEvent("STOP", "C")
503 (bind(&StateMachineDimControl::StopScript, this))
504 ("Stop a runnning batch script or JavaScript");
505}
506
507StateMachineDimControl::~StateMachineDimControl()
508{
509 for (auto it=fDimDescriptionsList.begin(); it!=fDimDescriptionsList.end(); it++)
510 delete *it;
511}
512
513int StateMachineDimControl::EvalOptions(Configuration &conf)
514{
515 fDebug = conf.Get<bool>("debug");
516 fUser = conf.Get<string>("user");
517 fScriptUser = fUser;
518
519 // FIXME: Check fUser for quotes!
520
521 const map<string, string> &js = conf.GetOptions<string>("JavaScript.");
522 for (auto it=js.begin(); it!=js.end(); it++)
523 {
524 string key = it->first;
525 string val = it->second;
526
527 // Escape key
528 boost::replace_all(key, "\\", "\\\\");
529 boost::replace_all(key, "'", "\\'");
530 boost::replace_all(key, "\"", "\\\"");
531
532 // Escape value
533 boost::replace_all(val, "\\", "\\\\");
534 boost::replace_all(val, "'", "\\'");
535 boost::replace_all(val, "\"", "\\\"");
536
537 fArgumentsJS += " '"+key +"'='"+val+"'";
538 }
539
540 // fVerbosity = 40;
541
542 // if (conf.Has("verbosity"))
543 // fVerbosity = conf.Get<uint32_t>("verbosity");
544
545 // if (conf.Get<bool>("quiet"))
546 // fVerbosity = 90;
547
548 const int cnt = conf.Get<bool>("stop")+conf.Has("start")+conf.Has("batch");
549
550 if (fIsServer)
551 {
552 if (cnt>0)
553 {
554 Error("--start, --batch, --stop are mutually exclusive to --server.");
555 return 3;
556 }
557 return -1;
558 }
559
560 if (cnt>1)
561 {
562 Error("--start, --batch and --stop are all mutually exclusive.");
563 return 4;
564 }
565
566 if (conf.Get<bool>("stop"))
567 return !Dim::SendCommand("DIM_CONTROL/STOP", fUser);
568
569 if (conf.Has("start"))
570 return !Dim::SendCommand("DIM_CONTROL/START", conf.Get<string>("start")+" user='"+fUser+"'");
571
572 if (conf.Has("batch"))
573 return !Dim::SendCommand("DIM_CONTROL/EXECUTE", conf.Get<string>("batch")+" user='"+fUser+"'");
574
575 if (conf.Has("msg"))
576 return !Dim::SendCommand("CHAT/MSG", fUser+": "+conf.Get<string>("msg"));
577
578 if (conf.Has("restart"))
579 return !Dim::SendCommand(conf.Get<string>("restart")+"/EXIT", uint32_t(126));
580
581 return -1;
582}
Note: See TracBrowser for help on using the repository browser.