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

Last change on this file since 15157 was 15147, checked in by tbretz, 12 years ago
Log a message when a STOP command was received.
File size: 16.1 KB
Line 
1#include "StateMachineDimControl.h"
2
3#include <boost/filesystem.hpp>
4
5#include "Dim.h"
6#include "Event.h"
7#include "RemoteControl.h"
8#include "Configuration.h"
9#include "Converter.h"
10
11using namespace std;
12
13// ------------------------------------------------------------------------
14
15bool StateMachineDimControl::fIsServer = false;
16
17string StateMachineDimControl::Line(const string &txt, char fill)
18{
19 const int n = (55-txt.length())/2;
20
21 ostringstream out;
22 out << setfill(fill);
23 out << setw(n) << fill << ' ';
24 out << txt;
25 out << ' ' << setw(n) << fill;
26
27 if (2*n+txt.length()+2 != 57)
28 out << fill;
29
30 return out.str();
31}
32
33int StateMachineDimControl::ChangeState(int qos, const Time &, int scriptdepth, string scriptfile, string user)
34{
35 string msg;
36 /*
37 switch (qos)
38 {
39 case -4: msg = "End"; break;
40 case -3: msg = "Loading"; break;
41 case -2: msg = "Compiling"; break;
42 case -1: msg = "Running"; break;
43 default:
44 {
45 ostringstream out;
46 out << "Label " << qos;
47 msg = out.str();
48 }
49 }
50 */
51
52 //if (qos<0)
53 msg += to_string(scriptdepth);
54
55 msg += ":"+scriptfile+"["+user+":"+to_string(getpid())+"]";
56
57 //if (fDebug)
58 //Write(time, Line(msg, qos<-1 ? '=' :'-'), MessageImp::kInternal);
59
60 if (qos==-4)
61 fScriptUser = fUser;
62
63 SetCurrentState(qos+4, msg.c_str());
64 //SetCurrentState(qos+4, Line(msg, qos<-1 ? '=' :'-').c_str());
65 return GetCurrentState();
66
67 //return qos+4;
68}
69
70int StateMachineDimControl::ChangeState(int state)
71{
72 return ChangeState(state, 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
88int StateMachineDimControl::StartScript(const EventImp &imp, const string &cmd)
89{
90 string opt(imp.GetString());
91
92 const map<string,string> data = Tools::Split(opt, true);
93 if (imp.GetSize()==0 || opt.size()==0 || opt[0]==0)
94 {
95 Error("File name missing in DIM_CONTROL/START");
96 return GetCurrentState();
97 }
98
99 if (fDebug)
100 Debug("Start '"+opt+"' received.");
101
102 if (fDebug)
103 Debug("Received data: "+imp.GetString());
104
105 const auto user = data.find("user");
106 fScriptUser = user==data.end() ? fUser : user->second;
107
108 if (fDebug)
109 {
110 for (auto it=data.begin(); it!=data.end(); it++)
111 Debug(" Arg: "+it->first+" = "+it->second);
112 }
113
114 string emit = cmd+imp.GetString();
115 if (cmd==".js ")
116 emit += fArgumentsJS;
117
118 Readline::SetExternalInput(emit);
119 return GetCurrentState();
120}
121
122int StateMachineDimControl::StopScript()
123{
124 Info("Stop received.");
125
126 Readline::StopScript();
127 InterpreterV8::JsStop();
128 return GetCurrentState();
129}
130
131bool 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("SendDimCommand - 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 (str.empty() && is->server==server)
152 return true;
153
154 if (is->server!=server || is->service!=name)
155 continue;
156
157 if (!is->iscmd)
158 throw runtime_error("'"+server+"/"+name+" not a command.");
159
160 // Avoid compiler warning of unused parameter
161 lout << flush;
162
163 // Convert the user entered data according to the format string
164 // into a data block which will be attached to the event
165#ifndef DEBUG
166 ostringstream sout;
167 const Converter conv(sout, is->format, false);
168#else
169 const Converter conv(lout, is->format, false);
170#endif
171 if (!conv)
172 throw runtime_error("Couldn't properly parse the format... ignored.");
173
174#ifdef DEBUG
175 lout << kBlue << server << '/' << name;
176#endif
177 const vector<char> v = conv.GetVector(str.substr(p0));
178#ifdef DEBUG
179 lout << kBlue << " [" << v.size() << "]" << endl;
180#endif
181 const string cmd = server + '/' + name;
182 const int rc = DimClient::sendCommand(cmd.c_str(), (void*)v.data(), v.size());
183 if (!rc)
184 throw runtime_error("ERROR - Sending command "+cmd+" failed.");
185
186 return true;
187 }
188
189 if (!str.empty())
190 throw runtime_error("SendDimCommand - Format information for "+server+"/"+name+" not yet available.");
191
192 return false;
193}
194
195int StateMachineDimControl::PrintStates(std::ostream &out, const std::string &serv)
196{
197 const lock_guard<mutex> guard(fMutex);
198
199 int rc = 0;
200 for (auto it=fServerList.begin(); it!=fServerList.end(); it++)
201 {
202 if (!serv.empty() && *it!=serv)
203 continue;
204
205 out << kRed << "----- " << *it << " -----" << endl;
206
207 int cnt = 0;
208 for (auto is=fStateDescriptionList.begin(); is!=fStateDescriptionList.end(); is++)
209 {
210 const string &server = is->first.first;
211
212 if (server!=*it)
213 continue;
214
215 const int32_t &state = is->first.second;
216 const string &name = is->second.first;
217 const string &comment = is->second.second;
218
219 out << kBold << setw(5) << state << kReset << ": ";
220 out << kYellow << name;
221 if (!comment.empty())
222 out << kBlue << " (" << comment << ")";
223 out << endl;
224
225 cnt++;
226 }
227
228 if (cnt==0)
229 out << " <no states>" << endl;
230 else
231 rc++;
232
233 out << endl;
234 }
235
236 return rc;
237}
238
239int StateMachineDimControl::PrintDescription(std::ostream &out, bool iscmd, const std::string &serv, const std::string &service)
240{
241 const lock_guard<mutex> guard(fMutex);
242
243 int rc = 0;
244 for (auto it=fServerList.begin(); it!=fServerList.end(); it++)
245 {
246 if (!serv.empty() && *it!=serv)
247 continue;
248
249 out << kRed << "----- " << *it << " -----" << endl << endl;
250
251 for (auto is=fServiceList.begin(); is!=fServiceList.end(); is++)
252 {
253 if (is->server!=*it)
254 continue;
255
256 if (!service.empty() && is->service!=service)
257 continue;
258
259 if (is->iscmd!=iscmd)
260 continue;
261
262 rc++;
263
264 out << " " << is->service;
265 if (!is->format.empty())
266 out << '[' << is->format << ']';
267
268 const auto id = fServiceDescriptionList.find(*it+"/"+is->service);
269 if (id!=fServiceDescriptionList.end())
270 {
271 const vector<Description> &v = id->second;
272
273 for (auto j=v.begin()+1; j!=v.end(); j++)
274 out << " <" << j->name << ">";
275 out << endl;
276
277 if (!v[0].comment.empty())
278 out << " " << v[0].comment << endl;
279
280 for (auto j=v.begin()+1; j!=v.end(); j++)
281 {
282 out << " " << kGreen << j->name;
283 if (!j->comment.empty())
284 out << kReset << ": " << kBlue << j->comment;
285 if (!j->unit.empty())
286 out << kYellow << " [" << j->unit << "]";
287 out << endl;
288 }
289 }
290 out << endl;
291 }
292 out << endl;
293 }
294
295 return rc;
296}
297
298int StateMachineDimControl::HandleStateChange(const string &server, DimDescriptions *dim)
299{
300 fMutex.lock();
301 const State descr = dim->description();
302 const State state = State(dim->state(), descr.index==DimState::kNotAvailable?"":descr.name, descr.comment, dim->cur.first);
303 fCurrentStateList[server] = state;
304 fMutex.unlock();
305
306 fStateCallback(server, state);
307
308 return GetCurrentState();
309}
310
311State StateMachineDimControl::GetServerState(const std::string &server)
312{
313 const lock_guard<mutex> guard(fMutex);
314
315 const auto it = fCurrentStateList.find(server);
316 return it==fCurrentStateList.end() ? State() : it->second;
317}
318
319int StateMachineDimControl::HandleStates(const string &server, DimDescriptions *dim)
320{
321 const lock_guard<mutex> guard(fMutex);
322
323 const auto is = fCurrentStateList.find(server);
324 for (auto it=dim->states.begin(); it!=dim->states.end(); it++)
325 {
326 fStateDescriptionList[make_pair(server, it->index)] = make_pair(it->name, it->comment);
327 if (is==fCurrentStateList.end())
328 continue;
329
330 State &s = is->second;
331 if (s.index==it->index)
332 {
333 s.name = it->name;
334 s.comment = it->comment;
335 }
336 }
337
338 return GetCurrentState();
339}
340
341int StateMachineDimControl::HandleDescriptions(DimDescriptions *dim)
342{
343 const lock_guard<mutex> guard(fMutex);
344
345 for (auto it=dim->descriptions.begin(); it!=dim->descriptions.end(); it++)
346 fServiceDescriptionList[it->front().name].assign(it->begin(), it->end());
347
348 return GetCurrentState();
349}
350
351std::vector<Description> StateMachineDimControl::GetDescription(const std::string &service)
352{
353 const lock_guard<mutex> guard(fMutex);
354
355 const auto it = fServiceDescriptionList.find(service);
356 return it==fServiceDescriptionList.end() ? vector<Description>() : it->second;
357}
358
359int StateMachineDimControl::HandleServerAdd(const string &server)
360{
361 if (server!="DIS_DNS")
362 {
363 struct Find : string
364 {
365 Find(const string &ref) : string(ref) { }
366 bool operator()(const DimDescriptions *dim) { return *this==dim->server; }
367 };
368
369 if (find_if(fDimDescriptionsList.begin(), fDimDescriptionsList.end(),
370 Find(server))==fDimDescriptionsList.end())
371 {
372 DimDescriptions *d = new DimDescriptions(server);
373
374 fDimDescriptionsList.push_back(d);
375 d->SetCallback(bind(&StateMachineDimControl::HandleStateChange, this, server, d));
376 d->SetCallbackStates(bind(&StateMachineDimControl::HandleStates, this, server, d));
377 d->SetCallbackDescriptions(bind(&StateMachineDimControl::HandleDescriptions, this, d));
378 d->Subscribe(*this);
379 }
380 }
381
382 // Make a copy of the list to be able to
383 // lock the access to the list
384
385 const lock_guard<mutex> guard(fMutex);
386 fServerList.insert(server);
387
388 return GetCurrentState();
389}
390
391int StateMachineDimControl::HandleServerRemove(const string &server)
392{
393 const lock_guard<mutex> guard(fMutex);
394 fServerList.erase(server);
395
396 return GetCurrentState();
397}
398
399vector<string> StateMachineDimControl::GetServerList()
400{
401 vector<string> rc;
402
403 const lock_guard<mutex> guard(fMutex);
404
405 rc.reserve(fServerList.size());
406 for (auto it=fServerList.begin(); it!=fServerList.end(); it++)
407 rc.push_back(*it);
408
409 return rc;
410}
411
412vector<string> StateMachineDimControl::GetCommandList(const string &server)
413{
414 const lock_guard<mutex> guard(fMutex);
415
416 const string s = server.substr(0, server.length()-1);
417
418 if (fServerList.find(s)==fServerList.end())
419 return vector<string>();
420
421 vector<string> rc;
422
423 for (auto it=fServiceList.begin(); it!=fServiceList.end(); it++)
424 if (it->iscmd && it->server==s)
425 rc.push_back(server+it->service);
426
427 return rc;
428}
429
430vector<string> StateMachineDimControl::GetCommandList()
431{
432 vector<string> rc;
433
434 const lock_guard<mutex> guard(fMutex);
435
436 for (auto it=fServiceList.begin(); it!=fServiceList.end(); it++)
437 if (it->iscmd)
438 rc.push_back(it->server+"/"+it->service);
439
440 return rc;
441}
442
443set<Service> StateMachineDimControl::GetServiceList()
444{
445 const lock_guard<mutex> guard(fMutex);
446 return fServiceList;
447}
448
449vector<State> StateMachineDimControl::GetStates(const string &server)
450{
451 const lock_guard<mutex> guard(fMutex);
452
453 vector<State> rc;
454
455 for (auto it=fStateDescriptionList.begin(); it!=fStateDescriptionList.end(); it++)
456 {
457 if (it->first.first!=server)
458 continue;
459
460 rc.push_back(State(it->first.second, it->second.first, it->second.second));
461 }
462
463 return rc;
464}
465
466
467int StateMachineDimControl::HandleAddService(const Service &svc)
468{
469 // Make a copy of the list to be able to
470 // lock the access to the list
471 const lock_guard<mutex> guard(fMutex);
472 fServiceList.insert(svc);
473
474 return GetCurrentState();
475}
476
477bool StateMachineDimControl::HasServer(const std::string &server)
478{
479 const lock_guard<mutex> guard(fMutex);
480 return fServerList.find(server)!=fServerList.end();
481}
482
483StateMachineDimControl::StateMachineDimControl(ostream &out) : StateMachineDim(out, fIsServer?"DIM_CONTROL":"")
484{
485 fDim.Subscribe(*this);
486 fDimList.Subscribe(*this);
487
488 fDimList.SetCallbackServerAdd (bind(&StateMachineDimControl::HandleServerAdd, this, placeholders::_1));
489 fDimList.SetCallbackServerRemove(bind(&StateMachineDimControl::HandleServerRemove, this, placeholders::_1));
490 fDimList.SetCallbackServiceAdd (bind(&StateMachineDimControl::HandleAddService, this, placeholders::_1));
491
492 // State names
493 AddStateName(0, "Idle", "No script currently in processing.");
494 AddStateName(1, "Loading", "Script is loading.");
495 AddStateName(2, "Compiling", "JavaScript is compiling.");
496 AddStateName(3, "Running", "Script is running.");
497
498 AddEvent("START", "C", 0)
499 (bind(&StateMachineDimControl::StartScript, this, placeholders::_1, ".js "))
500 ("Start a JavaScript");
501
502 AddEvent("EXECUTE", "C", 0)
503 (bind(&StateMachineDimControl::StartScript, this, placeholders::_1, ".x "))
504 ("Execute a batch script");
505
506 AddEvent("STOP", "C")
507 (bind(&StateMachineDimControl::StopScript, this))
508 ("Stop a runnning batch script or JavaScript");
509}
510
511StateMachineDimControl::~StateMachineDimControl()
512{
513 for (auto it=fDimDescriptionsList.begin(); it!=fDimDescriptionsList.end(); it++)
514 delete *it;
515}
516
517int StateMachineDimControl::EvalOptions(Configuration &conf)
518{
519 fDebug = conf.Get<bool>("debug");
520 fUser = conf.Get<string>("user");
521 fScriptUser = fUser;
522
523 // FIXME: Check fUser for quotes!
524
525 const map<string, string> &js = conf.GetOptions<string>("JavaScript.");
526 for (auto it=js.begin(); it!=js.end(); it++)
527 {
528 string key = it->first;
529 string val = it->second;
530
531 // Escape key
532 boost::replace_all(key, "\\", "\\\\");
533 boost::replace_all(key, "'", "\\'");
534 boost::replace_all(key, "\"", "\\\"");
535
536 // Escape value
537 boost::replace_all(val, "\\", "\\\\");
538 boost::replace_all(val, "'", "\\'");
539 boost::replace_all(val, "\"", "\\\"");
540
541 fArgumentsJS += " '"+key +"'='"+val+"'";
542 }
543
544 // fVerbosity = 40;
545
546 // if (conf.Has("verbosity"))
547 // fVerbosity = conf.Get<uint32_t>("verbosity");
548
549 // if (conf.Get<bool>("quiet"))
550 // fVerbosity = 90;
551
552 const boost::filesystem::path path(conf.GetName());
553 const string fname = path.filename().string();
554
555 if (fname=="dimserver")
556 return -1;
557
558 if (conf.Get<bool>("stop"))
559 return !Dim::SendCommand("DIM_CONTROL/STOP", fUser);
560
561 if (conf.Has("start"))
562 return !Dim::SendCommand("DIM_CONTROL/START", conf.Get<string>("start")+" user='"+fUser+"'"+fArgumentsJS);
563
564 if (conf.Has("batch"))
565 return !Dim::SendCommand("DIM_CONTROL/EXECUTE", conf.Get<string>("batch")+" user='"+fUser+"'");
566
567 if (conf.Has("msg"))
568 return !Dim::SendCommand("CHAT/MSG", fUser+": "+conf.Get<string>("msg"));
569
570 if (conf.Has("restart"))
571 return !Dim::SendCommand(conf.Get<string>("restart")+"/EXIT", uint32_t(126));
572
573 return -1;
574}
Note: See TracBrowser for help on using the repository browser.