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

Last change on this file since 14541 was 14541, checked in by tbretz, 12 years ago
Added time to State so that the time of state reception is propagated.
File size: 14.1 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 return GetCurrentState();
300}
301
302State StateMachineDimControl::GetServerState(const std::string &server)
303{
304 const lock_guard<mutex> guard(fMutex);
305
306 const auto it = fCurrentStateList.find(server);
307 return it==fCurrentStateList.end() ? State() : it->second;
308}
309
310int StateMachineDimControl::HandleStates(const string &server, DimDescriptions *dim)
311{
312 fMutex.lock();
313 for (auto it=dim->states.begin(); it!=dim->states.end(); it++)
314 fStateDescriptionList[make_pair(server, it->index)] = make_pair(it->name, it->comment);
315 fMutex.unlock();
316
317 return GetCurrentState();
318}
319
320int StateMachineDimControl::HandleDescriptions(DimDescriptions *dim)
321{
322 fMutex.lock();
323
324 for (auto it=dim->descriptions.begin(); it!=dim->descriptions.end(); it++)
325 fServiceDescriptionList[it->front().name].assign(it->begin(), it->end());
326
327 fMutex.unlock();
328
329 return GetCurrentState();
330}
331
332std::vector<Description> StateMachineDimControl::GetDescription(const std::string &service)
333{
334 const lock_guard<mutex> guard(fMutex);
335
336 const auto it = fServiceDescriptionList.find(service);
337 return it==fServiceDescriptionList.end() ? vector<Description>() : it->second;
338}
339
340int StateMachineDimControl::HandleServerAdd(const string &server)
341{
342 if (server!="DIS_DNS")
343 {
344 DimDescriptions *d = new DimDescriptions(server);
345
346 fDimDescriptionsList.push_back(d);
347 d->SetCallback(bind(&StateMachineDimControl::HandleStateChange, this, server, d));
348 d->SetCallbackStates(bind(&StateMachineDimControl::HandleStates, this, server, d));
349 d->SetCallbackDescriptions(bind(&StateMachineDimControl::HandleDescriptions, this, d));
350 d->Subscribe(*this);
351 }
352
353 // Make a copy of the list to be able to
354 // lock the access to the list
355 fMutex.lock();
356 fServerList.insert(server);
357 fMutex.unlock();
358
359 return GetCurrentState();
360}
361
362int StateMachineDimControl::HandleServerRemove(const string &server)
363{
364 fMutex.lock();
365 fServerList.erase(server);
366 fMutex.unlock();
367
368 return GetCurrentState();
369}
370
371vector<string> StateMachineDimControl::GetServerList()
372{
373 vector<string> rc;
374
375 fMutex.lock();
376
377 rc.reserve(fServerList.size());
378 for (auto it=fServerList.begin(); it!=fServerList.end(); it++)
379 rc.push_back(*it);
380
381 fMutex.unlock();
382
383 return rc;
384}
385
386vector<string> StateMachineDimControl::GetCommandList(const string &server)
387{
388 const lock_guard<mutex> guard(fMutex);
389
390 const string s = server.substr(0, server.length()-1);
391
392 if (fServerList.find(s)==fServerList.end())
393 return vector<string>();
394
395 vector<string> rc;
396
397 for (auto it=fServiceList.begin(); it!=fServiceList.end(); it++)
398 if (it->iscmd && it->server==s)
399 rc.push_back(server+it->service);
400
401 return rc;
402}
403
404vector<string> StateMachineDimControl::GetCommandList()
405{
406 vector<string> rc;
407
408 fMutex.lock();
409
410 for (auto it=fServiceList.begin(); it!=fServiceList.end(); it++)
411 if (it->iscmd)
412 rc.push_back(it->server+"/"+it->service);
413
414 fMutex.unlock();
415 return rc;
416}
417
418
419int StateMachineDimControl::HandleAddService(const Service &svc)
420{
421 // Make a copy of the list to be able to
422 // lock the access to the list
423 fMutex.lock();
424 fServiceList.insert(svc);
425 fMutex.unlock();
426
427 return GetCurrentState();
428}
429
430bool StateMachineDimControl::HasServer(const std::string &server)
431{
432 fMutex.lock();
433 const bool rc = fServerList.find(server)!=fServerList.end();
434 fMutex.unlock();
435
436 return rc;
437}
438
439StateMachineDimControl::StateMachineDimControl(ostream &out) : StateMachineDim(out, fIsServer?"DIM_CONTROL":"")
440{
441 fDim.Subscribe(*this);
442 fDimList.Subscribe(*this);
443
444 fDimList.SetCallbackServerAdd (bind(&StateMachineDimControl::HandleServerAdd, this, placeholders::_1));
445 fDimList.SetCallbackServerRemove(bind(&StateMachineDimControl::HandleServerRemove, this, placeholders::_1));
446 fDimList.SetCallbackServiceAdd (bind(&StateMachineDimControl::HandleAddService, this, placeholders::_1));
447
448 // State names
449 AddStateName(0, "Idle", "No script currently in processing.");
450 AddStateName(1, "Load", "Script being loaded.");
451 AddStateName(2, "Started", "Script execution started.");
452
453 AddEvent("START", "C", 0)
454 (bind(&StateMachineDimControl::StartScript, this, placeholders::_1, ".js "))
455 ("Start a JavaScript");
456
457 AddEvent("EXECUTE", "C", 0)
458 (bind(&StateMachineDimControl::StartScript, this, placeholders::_1, ".x "))
459 ("Execute a batch script");
460
461 AddEvent("STOP", "C")
462 (bind(&StateMachineDimControl::StopScript, this))
463 ("Stop a runnning batch script or JavaScript");
464}
465
466StateMachineDimControl::~StateMachineDimControl()
467{
468 for (auto it=fDimDescriptionsList.begin(); it!=fDimDescriptionsList.end(); it++)
469 delete *it;
470}
471
472int StateMachineDimControl::EvalOptions(Configuration &conf)
473{
474 fDebug = conf.Get<bool>("debug");
475 fUser = conf.Get<string>("user");
476 fScriptUser = fUser;
477
478 // fVerbosity = 40;
479
480 // if (conf.Has("verbosity"))
481 // fVerbosity = conf.Get<uint32_t>("verbosity");
482
483 // if (conf.Get<bool>("quiet"))
484 // fVerbosity = 90;
485
486 const int cnt = conf.Get<bool>("stop")+conf.Has("start")+conf.Has("batch");
487
488 if (fIsServer)
489 {
490 if (cnt>0)
491 {
492 Error("--start, --batch, --stop are mutually exclusive to --server.");
493 return 3;
494 }
495 return -1;
496 }
497
498 if (cnt>1)
499 {
500 Error("--start, --batch and --stop are all mutually exclusive.");
501 return 4;
502 }
503
504 if (conf.Get<bool>("stop"))
505 return Dim::SendCommand("DIM_CONTROL/STOP", fUser) + 1;
506
507 if (conf.Has("start"))
508 return Dim::SendCommand("DIM_CONTROL/START", conf.Get<string>("start")+" user="+fUser) + 1;
509
510 if (conf.Has("batch"))
511 return Dim::SendCommand("DIM_CONTROL/EXECUTE", conf.Get<string>("batch")+" user="+fUser) + 1;
512
513 if (conf.Has("msg"))
514 return Dim::SendCommand("CHAT/MSG", fUser+": "+conf.Get<string>("msg")) + 1;
515
516 return -1;
517}
Note: See TracBrowser for help on using the repository browser.