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

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