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

Last change on this file since 14735 was 14718, checked in by tbretz, 13 years ago
The workaround with the empty text to change the state is outdated and has been removed.
File size: 15.6 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 1;
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
447
448int StateMachineDimControl::HandleAddService(const Service &svc)
449{
450 // Make a copy of the list to be able to
451 // lock the access to the list
452 fMutex.lock();
453 fServiceList.insert(svc);
454 fMutex.unlock();
455
456 return GetCurrentState();
457}
458
459bool StateMachineDimControl::HasServer(const std::string &server)
460{
461 fMutex.lock();
462 const bool rc = fServerList.find(server)!=fServerList.end();
463 fMutex.unlock();
464
465 return rc;
466}
467
468StateMachineDimControl::StateMachineDimControl(ostream &out) : StateMachineDim(out, fIsServer?"DIM_CONTROL":"")
469{
470 fDim.Subscribe(*this);
471 fDimList.Subscribe(*this);
472
473 fDimList.SetCallbackServerAdd (bind(&StateMachineDimControl::HandleServerAdd, this, placeholders::_1));
474 fDimList.SetCallbackServerRemove(bind(&StateMachineDimControl::HandleServerRemove, this, placeholders::_1));
475 fDimList.SetCallbackServiceAdd (bind(&StateMachineDimControl::HandleAddService, this, placeholders::_1));
476
477 // State names
478 AddStateName(0, "Idle", "No script currently in processing.");
479 AddStateName(1, "Loading", "Script is loading.");
480 AddStateName(2, "Compiling", "JavaScript is compiling.");
481 AddStateName(3, "Running", "Script is running.");
482
483 AddEvent("START", "C", 0)
484 (bind(&StateMachineDimControl::StartScript, this, placeholders::_1, ".js "))
485 ("Start a JavaScript");
486
487 AddEvent("EXECUTE", "C", 0)
488 (bind(&StateMachineDimControl::StartScript, this, placeholders::_1, ".x "))
489 ("Execute a batch script");
490
491 AddEvent("STOP", "C")
492 (bind(&StateMachineDimControl::StopScript, this))
493 ("Stop a runnning batch script or JavaScript");
494}
495
496StateMachineDimControl::~StateMachineDimControl()
497{
498 for (auto it=fDimDescriptionsList.begin(); it!=fDimDescriptionsList.end(); it++)
499 delete *it;
500}
501
502int StateMachineDimControl::EvalOptions(Configuration &conf)
503{
504 fDebug = conf.Get<bool>("debug");
505 fUser = conf.Get<string>("user");
506 fScriptUser = fUser;
507
508 const map<string, string> &js = conf.GetOptions<string>("JavaScript.");
509 for (auto it=js.begin(); it!=js.end(); it++)
510 {
511 string key = it->first;
512 string val = it->second;
513
514 // Escape key
515 boost::replace_all(key, "\\", "\\\\");
516 boost::replace_all(key, "'", "\\'");
517 boost::replace_all(key, "\"", "\\\"");
518
519 // Escape value
520 boost::replace_all(val, "\\", "\\\\");
521 boost::replace_all(val, "'", "\\'");
522 boost::replace_all(val, "\"", "\\\"");
523
524 fArgumentsJS += " '"+key +"'='"+val+"'";
525 }
526
527 // fVerbosity = 40;
528
529 // if (conf.Has("verbosity"))
530 // fVerbosity = conf.Get<uint32_t>("verbosity");
531
532 // if (conf.Get<bool>("quiet"))
533 // fVerbosity = 90;
534
535 const int cnt = conf.Get<bool>("stop")+conf.Has("start")+conf.Has("batch");
536
537 if (fIsServer)
538 {
539 if (cnt>0)
540 {
541 Error("--start, --batch, --stop are mutually exclusive to --server.");
542 return 3;
543 }
544 return -1;
545 }
546
547 if (cnt>1)
548 {
549 Error("--start, --batch and --stop are all mutually exclusive.");
550 return 4;
551 }
552
553 if (conf.Get<bool>("stop"))
554 return Dim::SendCommand("DIM_CONTROL/STOP", fUser) + 1;
555
556 if (conf.Has("start"))
557 return Dim::SendCommand("DIM_CONTROL/START", conf.Get<string>("start")+" user="+fUser) + 1;
558
559 if (conf.Has("batch"))
560 return Dim::SendCommand("DIM_CONTROL/EXECUTE", conf.Get<string>("batch")+" user="+fUser) + 1;
561
562 if (conf.Has("msg"))
563 return Dim::SendCommand("CHAT/MSG", fUser+": "+conf.Get<string>("msg")) + 1;
564
565 return -1;
566}
Note: See TracBrowser for help on using the repository browser.