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

Last change on this file since 14186 was 14132, checked in by tbretz, 12 years ago
Added new version of the core of dimctrl with callbacks to access the data from JavaScript.
File size: 13.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 &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 2;
483 }
484 return -1;
485 }
486
487 if (cnt>1)
488 {
489 Error("--start, --batch and --stop are all mutually exclusive.");
490 return 1;
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 return -1;
503}
Note: See TracBrowser for help on using the repository browser.