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

Last change on this file since 14551 was 14551, checked in by tbretz, 12 years ago
Fixed a severe bug which caused some events tobe added twice and implemented a callback for state changes
File size: 14.5 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 -3: msg = "End"; break;
38 case -2: msg = "Load"; break;
39 case -1: msg = "Start"; break;
40 default:
41 {
42 ostringstream out;
43 out << "Label " << qos;
44 msg = out.str();
45 }
46 }
47 */
48
49 //if (qos<0)
50 msg += to_string(scriptdepth);
51
52 msg += ":"+scriptfile+"["+user+":"+to_string(getpid())+"]";
53
54 //if (fDebug)
55 //Write(time, Line(msg, qos<-1 ? '=' :'-'), MessageImp::kInternal);
56
57 if (qos==-3)
58 fScriptUser = fUser;
59
60 SetCurrentState(qos+3, msg.c_str());
61 //SetCurrentState(qos+3, Line(msg, qos<-1 ? '=' :'-').c_str());
62 return GetCurrentState();
63
64 //return qos+3;
65}
66
67int StateMachineDimControl::Write(const Time &time, const std::string &txt, int qos)
68{
69 if (txt=="")
70 {
71 return ChangeState(qos, time, Readline::GetScriptDepth(), Readline::GetScript(), fScriptUser);
72 /*
73 === This might be necessary for thread safety,
74 === but it break that the signal for the start of a new
75 === script arrives synchronously before the first output
76 === from the script
77
78 // Post an anonymous event to the event loop
79 Event evt("");
80 evt.AssignFunction(bind(&StateMachineDimControl::ChangeState, this,
81 qos, time, Readline::GetScriptDepth(),
82 Readline::GetScript(), fScriptUser));
83 return PostEvent(evt);
84 */
85 }
86
87 // if (qos<fVerbosity)
88 // return 0;
89
90 return StateMachineDim::Write(time, txt, qos);
91}
92
93int StateMachineDimControl::StartScript(const EventImp &imp, const string &cmd)
94{
95 string opt(imp.GetString());
96
97 const map<string,string> data = Tools::Split(opt, true);
98
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 Readline::SetExternalInput(cmd+imp.GetString());
121 return 1;
122}
123
124int StateMachineDimControl::StopScript()
125{
126 Readline::StopScript();
127 InterpreterV8::JsStop();
128 return GetCurrentState();
129}
130
131void 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("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 (is->server!=server || is->service!=name)
152 continue;
153
154 if (!is->iscmd)
155 throw runtime_error("'"+server+"/"+name+" not a command.");
156
157 // Avoid compiler warning of unused parameter
158 lout << flush;
159
160 // Convert the user entered data according to the format string
161 // into a data block which will be attached to the event
162#ifndef DEBUG
163 ostringstream sout;
164 const Converter conv(sout, is->format, false);
165#else
166 const Converter conv(lout, is->format, false);
167#endif
168 if (!conv)
169 throw runtime_error("Couldn't properly parse the format... ignored.");
170
171#ifdef DEBUG
172 lout << kBlue << server << '/' << name;
173#endif
174 const vector<char> v = conv.GetVector(str.substr(p0));
175#ifdef DEBUG
176 lout << kBlue << " [" << v.size() << "]" << endl;
177#endif
178 const string cmd = server + '/' + name;
179 const int rc = DimClient::sendCommand(cmd.c_str(), (void*)v.data(), v.size());
180 if (!rc)
181 throw runtime_error("ERROR - Sending command "+cmd+" failed.");
182
183 return;
184 }
185
186 throw runtime_error("Unkown server '"+server+"'");
187}
188
189int StateMachineDimControl::PrintStates(std::ostream &out, const std::string &serv)
190{
191 const lock_guard<mutex> guard(fMutex);
192
193 int rc = 0;
194 for (auto it=fServerList.begin(); it!=fServerList.end(); it++)
195 {
196 if (!serv.empty() && *it!=serv)
197 continue;
198
199 out << kRed << "----- " << *it << " -----" << endl;
200
201 int cnt = 0;
202 for (auto is=fStateDescriptionList.begin(); is!=fStateDescriptionList.end(); is++)
203 {
204 const string &server = is->first.first;
205
206 if (server!=*it)
207 continue;
208
209 const int32_t &state = is->first.second;
210 const string &name = is->second.first;
211 const string &comment = is->second.second;
212
213 out << kBold << setw(5) << state << kReset << ": ";
214 out << kYellow << name;
215 if (!comment.empty())
216 out << kBlue << " (" << comment << ")";
217 out << endl;
218
219 cnt++;
220 }
221
222 if (cnt==0)
223 out << " <no states>" << endl;
224 else
225 rc++;
226
227 out << endl;
228 }
229
230 return rc;
231}
232
233int StateMachineDimControl::PrintDescription(std::ostream &out, bool iscmd, const std::string &serv, const std::string &service)
234{
235 const lock_guard<mutex> guard(fMutex);
236
237 int rc = 0;
238 for (auto it=fServerList.begin(); it!=fServerList.end(); it++)
239 {
240 if (!serv.empty() && *it!=serv)
241 continue;
242
243 out << kRed << "----- " << *it << " -----" << endl << endl;
244
245 for (auto is=fServiceList.begin(); is!=fServiceList.end(); is++)
246 {
247 if (is->server!=*it)
248 continue;
249
250 if (!service.empty() && is->service!=service)
251 continue;
252
253 if (is->iscmd!=iscmd)
254 continue;
255
256 rc++;
257
258 out << " " << is->service;
259 if (!is->format.empty())
260 out << '[' << is->format << ']';
261
262 const auto id = fServiceDescriptionList.find(*it+"/"+is->service);
263 if (id!=fServiceDescriptionList.end())
264 {
265 const vector<Description> &v = id->second;
266
267 for (auto j=v.begin()+1; j!=v.end(); j++)
268 out << " <" << j->name << ">";
269 out << endl;
270
271 if (!v[0].comment.empty())
272 out << " " << v[0].comment << endl;
273
274 for (auto j=v.begin()+1; j!=v.end(); j++)
275 {
276 out << " " << kGreen << j->name;
277 if (!j->comment.empty())
278 out << kReset << ": " << kBlue << j->comment;
279 if (!j->unit.empty())
280 out << kYellow << " [" << j->unit << "]";
281 out << endl;
282 }
283 }
284 out << endl;
285 }
286 out << endl;
287 }
288
289 return rc;
290}
291
292int StateMachineDimControl::HandleStateChange(const string &server, DimDescriptions *dim)
293{
294 fMutex.lock();
295 const State state = dim->description();
296 fCurrentStateList[server] = State(state.index, state.index==DimState::kNotAvailable?"":state.name, state.comment, state.time);
297 fMutex.unlock();
298
299 fStateCallback(server, state);
300
301 return GetCurrentState();
302}
303
304State StateMachineDimControl::GetServerState(const std::string &server)
305{
306 const lock_guard<mutex> guard(fMutex);
307
308 const auto it = fCurrentStateList.find(server);
309 return it==fCurrentStateList.end() ? State() : it->second;
310}
311
312int StateMachineDimControl::HandleStates(const string &server, DimDescriptions *dim)
313{
314 fMutex.lock();
315 for (auto it=dim->states.begin(); it!=dim->states.end(); it++)
316 fStateDescriptionList[make_pair(server, it->index)] = make_pair(it->name, it->comment);
317 fMutex.unlock();
318
319 return GetCurrentState();
320}
321
322int StateMachineDimControl::HandleDescriptions(DimDescriptions *dim)
323{
324 fMutex.lock();
325
326 for (auto it=dim->descriptions.begin(); it!=dim->descriptions.end(); it++)
327 fServiceDescriptionList[it->front().name].assign(it->begin(), it->end());
328
329 fMutex.unlock();
330
331 return GetCurrentState();
332}
333
334std::vector<Description> StateMachineDimControl::GetDescription(const std::string &service)
335{
336 const lock_guard<mutex> guard(fMutex);
337
338 const auto it = fServiceDescriptionList.find(service);
339 return it==fServiceDescriptionList.end() ? vector<Description>() : it->second;
340}
341
342int StateMachineDimControl::HandleServerAdd(const string &server)
343{
344 if (server!="DIS_DNS")
345 {
346 struct Find : string
347 {
348 Find(const string &ref) : string(ref) { }
349 bool operator()(const DimDescriptions *dim) { return *this==dim->server; }
350 };
351
352 if (find_if(fDimDescriptionsList.begin(), fDimDescriptionsList.end(),
353 Find(server))==fDimDescriptionsList.end())
354 {
355 DimDescriptions *d = new DimDescriptions(server);
356
357 fDimDescriptionsList.push_back(d);
358 d->SetCallback(bind(&StateMachineDimControl::HandleStateChange, this, server, d));
359 d->SetCallbackStates(bind(&StateMachineDimControl::HandleStates, this, server, d));
360 d->SetCallbackDescriptions(bind(&StateMachineDimControl::HandleDescriptions, this, d));
361 d->Subscribe(*this);
362 }
363 }
364
365 // Make a copy of the list to be able to
366 // lock the access to the list
367 fMutex.lock();
368 fServerList.insert(server);
369 fMutex.unlock();
370
371 return GetCurrentState();
372}
373
374int StateMachineDimControl::HandleServerRemove(const string &server)
375{
376 fMutex.lock();
377 fServerList.erase(server);
378 fMutex.unlock();
379
380 return GetCurrentState();
381}
382
383vector<string> StateMachineDimControl::GetServerList()
384{
385 vector<string> rc;
386
387 fMutex.lock();
388
389 rc.reserve(fServerList.size());
390 for (auto it=fServerList.begin(); it!=fServerList.end(); it++)
391 rc.push_back(*it);
392
393 fMutex.unlock();
394
395 return rc;
396}
397
398vector<string> StateMachineDimControl::GetCommandList(const string &server)
399{
400 const lock_guard<mutex> guard(fMutex);
401
402 const string s = server.substr(0, server.length()-1);
403
404 if (fServerList.find(s)==fServerList.end())
405 return vector<string>();
406
407 vector<string> rc;
408
409 for (auto it=fServiceList.begin(); it!=fServiceList.end(); it++)
410 if (it->iscmd && it->server==s)
411 rc.push_back(server+it->service);
412
413 return rc;
414}
415
416vector<string> StateMachineDimControl::GetCommandList()
417{
418 vector<string> rc;
419
420 fMutex.lock();
421
422 for (auto it=fServiceList.begin(); it!=fServiceList.end(); it++)
423 if (it->iscmd)
424 rc.push_back(it->server+"/"+it->service);
425
426 fMutex.unlock();
427 return rc;
428}
429
430
431int StateMachineDimControl::HandleAddService(const Service &svc)
432{
433 // Make a copy of the list to be able to
434 // lock the access to the list
435 fMutex.lock();
436 fServiceList.insert(svc);
437 fMutex.unlock();
438
439 return GetCurrentState();
440}
441
442bool StateMachineDimControl::HasServer(const std::string &server)
443{
444 fMutex.lock();
445 const bool rc = fServerList.find(server)!=fServerList.end();
446 fMutex.unlock();
447
448 return rc;
449}
450
451StateMachineDimControl::StateMachineDimControl(ostream &out) : StateMachineDim(out, fIsServer?"DIM_CONTROL":"")
452{
453 fDim.Subscribe(*this);
454 fDimList.Subscribe(*this);
455
456 fDimList.SetCallbackServerAdd (bind(&StateMachineDimControl::HandleServerAdd, this, placeholders::_1));
457 fDimList.SetCallbackServerRemove(bind(&StateMachineDimControl::HandleServerRemove, this, placeholders::_1));
458 fDimList.SetCallbackServiceAdd (bind(&StateMachineDimControl::HandleAddService, this, placeholders::_1));
459
460 // State names
461 AddStateName(0, "Idle", "No script currently in processing.");
462 AddStateName(1, "Load", "Script being loaded.");
463 AddStateName(2, "Started", "Script execution started.");
464
465 AddEvent("START", "C", 0)
466 (bind(&StateMachineDimControl::StartScript, this, placeholders::_1, ".js "))
467 ("Start a JavaScript");
468
469 AddEvent("EXECUTE", "C", 0)
470 (bind(&StateMachineDimControl::StartScript, this, placeholders::_1, ".x "))
471 ("Execute a batch script");
472
473 AddEvent("STOP", "C")
474 (bind(&StateMachineDimControl::StopScript, this))
475 ("Stop a runnning batch script or JavaScript");
476}
477
478StateMachineDimControl::~StateMachineDimControl()
479{
480 for (auto it=fDimDescriptionsList.begin(); it!=fDimDescriptionsList.end(); it++)
481 delete *it;
482}
483
484int StateMachineDimControl::EvalOptions(Configuration &conf)
485{
486 fDebug = conf.Get<bool>("debug");
487 fUser = conf.Get<string>("user");
488 fScriptUser = fUser;
489
490 // fVerbosity = 40;
491
492 // if (conf.Has("verbosity"))
493 // fVerbosity = conf.Get<uint32_t>("verbosity");
494
495 // if (conf.Get<bool>("quiet"))
496 // fVerbosity = 90;
497
498 const int cnt = conf.Get<bool>("stop")+conf.Has("start")+conf.Has("batch");
499
500 if (fIsServer)
501 {
502 if (cnt>0)
503 {
504 Error("--start, --batch, --stop are mutually exclusive to --server.");
505 return 3;
506 }
507 return -1;
508 }
509
510 if (cnt>1)
511 {
512 Error("--start, --batch and --stop are all mutually exclusive.");
513 return 4;
514 }
515
516 if (conf.Get<bool>("stop"))
517 return Dim::SendCommand("DIM_CONTROL/STOP", fUser) + 1;
518
519 if (conf.Has("start"))
520 return Dim::SendCommand("DIM_CONTROL/START", conf.Get<string>("start")+" user="+fUser) + 1;
521
522 if (conf.Has("batch"))
523 return Dim::SendCommand("DIM_CONTROL/EXECUTE", conf.Get<string>("batch")+" user="+fUser) + 1;
524
525 if (conf.Has("msg"))
526 return Dim::SendCommand("CHAT/MSG", fUser+": "+conf.Get<string>("msg")) + 1;
527
528 return -1;
529}
Note: See TracBrowser for help on using the repository browser.