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

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