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

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