source: trunk/FACT++/src/DimServiceInfoList.cc@ 10487

Last change on this file since 10487 was 10429, checked in by tbretz, 14 years ago
Moved the tools function into their own namespace to get rid of problems whenlinking with root.
File size: 22.9 KB
Line 
1// **************************************************************************
2/** @class DimServiceInfoList
3
4@brief Maintains a list of all services available in the Dim network
5
6The idea of this class is to maintain a list of services available
7in the Dim network as well as state descriptions if available.
8
9Therefore, it subscribes to the SERVICE_LIST, SERVICE_DESC and STATE_LIST
10services of all servers.
11
12To maintain the list it derives from DimServerList which maintains
13a list of all servers.
14
15To maintain the subscriptions it overwrites:
16
17- void DimServerList::AddServer(const std::string &s)
18- void DimServerList::RemoveServer(const std::string &s)
19- void DimServerList::RemoveAllServers()
20
21If a derived class also overwrites these functions it must be ensured that
22the member functions of DimServiceInfoList are still called properly.
23
24Whenever a service is added or removed, or all services of one server
25is removed the following virtual functions are called:
26
27- virtual void AddService(const std::string &server, const std::string &service, const std::string &fmt, bool iscmd)
28- virtual void RemoveService(const std::string &server, const std::string &service, bool iscmd)
29- virtual void RemoveAllServices(const std::string &server)
30
31Note, that these functions are not called from the RemoveServer() and
32RemoveAllServer() functions. It might be a difference whether all services
33were removed but the server is still online or the server went offline.
34
35If a description or a state was added, this is signaled though:
36
37- virtual void AddDescription(const std::string &server, const std::string &service, const std::vector<Description> &vec)
38- virtual void AddStates(const std::string &server, const std::vector<State> &vec)
39
40Note, that Descriptions and States are never removed except a service or
41server goes offline. It is expected that if a service comes online also
42the list of descritions is sent again.
43
44*/
45// **************************************************************************
46#include "DimServiceInfoList.h"
47
48#include <sstream>
49
50#include "WindowLog.h"
51#include "Converter.h"
52
53#include "tools.h"
54#include "Time.h"
55
56using namespace std;
57
58// --------------------------------------------------------------------------
59//
60//! A helper to shorten the call to create a DimInfo.
61//!
62//! @param str
63//! name of the server to which we want to subscribe
64//!
65//! @param svc
66//! name of the servic on the server to which we want to subscribe
67//!
68//! @returns
69//! a pointer to the newly created DimInfo
70//!
71DimInfo *DimServiceInfoList::CreateDimInfo(const string &str, const string &svc) const
72{
73 return new DimInfo((str+'/'+svc).c_str(),
74 const_cast<char*>(""),
75 const_cast<DimServiceInfoList*>(this));
76}
77
78// --------------------------------------------------------------------------
79//
80//! Adds the service subscription for SERVICE_LIST, SERVICE_DESC and
81//! STATE_LIST for the given server. Don't forget to call this function
82//! if it is overwritten in a derived class.
83//!
84//! @param s
85//! server which should be added
86//!
87//! @throws
88//! a runtime_error is the server is already in the list
89//
90void DimServiceInfoList::AddServer(const string &s)
91{
92 // Check if this server is already in the list.
93 // This should never happen if Dim works reliable
94 const ServiceInfoList::iterator v = fServiceInfoList.find(s);
95 if (v!=fServiceInfoList.end())
96 {
97 stringstream err;
98 err << "Server '" << s << "' in list not as it ought to be.";
99 throw runtime_error(err.str());
100 }
101
102 fServiceInfoList[s].push_back(CreateSL(s));
103 fServiceInfoList[s].push_back(CreateFMT(s));
104 fServiceInfoList[s].push_back(CreateDS(s));
105}
106
107// --------------------------------------------------------------------------
108//
109//! Removes the service subscription for SERVICE_LIST, SERVICE_DESC and
110//! STATE_LIST for the given server, as well as the stored informations.
111//! Don't forget to call this function if it is overwritten in a derived
112//! class.
113//!
114//! @param s
115//! server which should be removed
116//!
117//! @throws
118//! a runtime_error is the server to be removed is not in the list
119//
120void DimServiceInfoList::RemoveServer(const string &s)
121{
122 const ServiceInfoList::iterator v = fServiceInfoList.find(s);
123 if (v==fServiceInfoList.end())
124 {
125 stringstream err;
126 err << "Server '" << s << "' not in list as it ought to be.";
127 throw runtime_error(err.str());
128 }
129
130 // Remove the server from the server list
131 delete v->second[0];
132 delete v->second[1];
133 delete v->second[2];
134
135 fServiceInfoList.erase(v);
136 fServiceList.erase(fServiceList.find(s));
137}
138
139// --------------------------------------------------------------------------
140//
141//! Removes the service subscription for SERVICE_LIST, SERVICE_DESC and
142//! STATE_LIST for all servers, as well as all stored informations.
143//! Don't forget to call this function if it is overwritten in a derived
144//! class.
145//!
146void DimServiceInfoList::RemoveAllServers()
147{
148 for (ServiceInfoList::iterator i=fServiceInfoList.begin();
149 i!=fServiceInfoList.end(); i++)
150 {
151 delete i->second[0];
152 delete i->second[1];
153 delete i->second[2];
154 }
155
156 fServiceInfoList.clear();
157 fServiceList.clear();
158}
159
160
161// --------------------------------------------------------------------------
162//
163//! This function processes the update of the SERVICE_LIST, SERVICE_DESC,
164//! and STATE_LIST updates.
165//!
166//! Whenever a service is added or removed or all services of a server are
167//! removed (the list is newly sent completely) the virtual functions
168//! AddService(), RemoveService() and RemoveAllServices() aee called.
169//!
170//! If a new description or a new state is added, the virtual functions
171//! AddDescription() and AddStates() respectively are called.
172//
173void DimServiceInfoList::infoHandler()
174{
175 // Get the name of the service
176 const string svc = getInfo()->getName();
177
178 // Get the server name from the service name
179 const string server = svc.substr(0, svc.find_first_of('/'));
180 const string service = svc.substr(svc.find_first_of('/')+1);
181
182 if (service=="SERVICE_LIST")
183 {
184 // For easy and fast access get the corresponding reference
185 TypeList &list = fServiceList[server].first;
186
187 const string str = getInfo()->getString();
188
189 // WHAT's THIS???
190 if (str.length()==0)
191 return;
192
193 // Initialize the entry with an empty list
194 if (str[0]!='+' && str[0]!='-')
195 {
196 RemoveAllServices(server);
197 list.clear();
198 }
199
200 string buffer;
201
202 // Tokenize the stream into lines
203 stringstream stream(str);
204 while (getline(stream, buffer, '\n'))
205 {
206 if (buffer.empty())
207 continue;
208
209 // Get the type and compare it with fType
210 const string type = buffer.substr(buffer.find_last_of('|')+1);
211
212 /*
213 const bool iscmd = type=="CMD";
214 if (type!=fType && fType!="*")
215 continue;
216 */
217
218 // Get format, name and command name
219 const string fmt = buffer.substr(buffer.find_first_of('|')+1, buffer.find_last_of('|')-buffer.find_first_of('|')-1);
220 const string name = buffer.substr(buffer.find_first_of('/')+1, buffer.find_first_of('|')-buffer.find_first_of('/')-1);
221 //const string cmd = buffer.substr(0, buffer.find_first_of('|'));
222
223 const bool iscmd = type=="CMD";
224
225 // FIXME: Do we need to check that the buffer starts with SERVER ?
226
227 if (buffer[0]=='-')
228 {
229 // Check if this server is not found in the list.
230 // This should never happen if Dim works reliable
231 const TypeList::iterator v = list.find(name);
232 if (v==list.end())
233 {
234 stringstream err;
235 err << "Service '" << server << "/" << name << "' not in list as it ought to be.";
236 throw runtime_error(err.str());
237 }
238
239 RemoveService(server, name, iscmd);
240 list.erase(v);
241
242 continue;
243 }
244
245 if (buffer[0]=='+')
246 {
247 // Check if this server is not found in the list.
248 // This should never happen if Dim works reliable
249 const TypeList::iterator v = list.find(name);
250 if (v!=list.end())
251 {
252 stringstream err;
253 err << "Service '" << server << "/" << name << "' already in list not as it ought to be.";
254 throw runtime_error(err.str());
255 }
256
257 list[name] = make_pair(fmt, iscmd);
258 AddService(server, name, fmt, iscmd);
259
260 continue;
261 }
262
263 // Add name the the list
264 list[name] = make_pair(fmt, iscmd);
265 AddService(server, name, fmt, iscmd);
266 }
267
268 return;
269 }
270
271 if (service=="SERVICE_DESC")
272 {
273 // For easy and fast access get the corresponding reference
274 DescriptionList &list = fServiceList[server].second;
275
276 list.clear();
277
278 string buffer;
279
280 stringstream stream(getInfo()->getString());
281 while (getline(stream, buffer, '\n'))
282 {
283 if (buffer.empty())
284 continue;
285
286 const vector<Description> v = Description::SplitDescription(buffer);
287
288 const string name = v[0].name.substr(v[0].name.find_first_of('/')+1);
289 const string comment = v[0].comment;
290
291 list[name] = make_pair(comment, vector<Description>(v.begin()+1, v.end()));
292
293 AddDescription(server, name, v);
294 }
295
296 return;
297 }
298
299 if (service=="STATE_LIST")
300 {
301 vector<State> &vec = fServiceList[server].third;
302 vec = State::SplitStates(getInfo()->getString());
303 AddStates(server, vec);
304
305 return;
306 }
307
308 DimServerList::infoHandler();
309}
310
311// --------------------------------------------------------------------------
312//
313//! Returns a list of all services available for the given server.
314//! Depending on iscmd either only services or only commands are returned.
315//!
316//! @param server
317//! server for which the list should be returned
318//!
319//! @param iscmd
320//! true if only commands should be returned, false for services
321//!
322//! @returns
323//! a vector<string> which contains all the service or command names for
324//! the given server. The names returned are always SERVER/SERVICE
325//! If the server was not fund an empty vector is returned.
326//
327vector<string> DimServiceInfoList::GetServiceList(const std::string &server, bool iscmd) const
328{
329 const ServiceList::const_iterator m = fServiceList.find(server);
330 if (m==fServiceList.end())
331 return vector<string>();
332
333 const TypeList &list = m->second.first;
334
335 vector<string> vec;
336 for (TypeList::const_iterator i=list.begin(); i!=list.end(); i++)
337 if (i->second.second==iscmd)
338 vec.push_back(server+'/'+i->first);
339
340 return vec;
341}
342
343// --------------------------------------------------------------------------
344//
345//! Returns a list of all services available in the network.
346//! Depending on iscmd either only services or only commands are returned.
347//!
348//! @param iscmd
349//! true if only commands should be returned, false for services
350//!
351//! @returns
352//! a vector<string> which contains all the service or command names in
353//! the network. The names returned are always SERVER/SERVICE
354//
355vector<string> DimServiceInfoList::GetServiceList(bool iscmd) const
356{
357 vector<string> vec;
358 for (ServiceList::const_iterator m=fServiceList.begin(); m!=fServiceList.end(); m++)
359 {
360 const TypeList &list = m->second.first;
361
362 for (TypeList::const_iterator i=list.begin(); i!=list.end(); i++)
363 if (i->second.second==iscmd)
364 vec.push_back(m->first+'/'+i->first);
365 }
366
367 return vec;
368}
369
370// --------------------------------------------------------------------------
371//
372//! Returns a list of all descriptions for the given service on the
373//! given server. Service in this context can also be a command.
374//!
375//! @param server
376//! Server name to look for
377//!
378//! @param service
379//! Service/command name to look for
380//!
381//! @returns
382//! a vector<Description> which contains all argument descriptions for
383//! the given service or command. The first entry contains the name
384//! and the general description for the given service. If the server
385//! or service was not found an empty vector is returned.
386//
387std::vector<Description> DimServiceInfoList::GetDescription(const std::string &server, const std::string &service) const
388{
389 const ServiceList::const_iterator s = fServiceList.find(server);
390 if (s==fServiceList.end())
391 return vector<Description>();
392
393 const DescriptionList &descs = s->second.second;
394
395 const DescriptionList::const_iterator d = descs.find(service);
396 if (d==descs.end())
397 return vector<Description>();
398
399 vector<Description> vec;
400 vec.push_back(Description(service, d->second.first));
401 vec.insert(vec.end(), d->second.second.begin(), d->second.second.end());
402
403 return vec;
404}
405
406// --------------------------------------------------------------------------
407//
408//! Returns a list of all states associated with the given server.
409//!
410//! @param server
411//! Server name to look for
412//!
413//! @returns
414//! a vector<State> which contains all state descriptions for
415//! the given server. If the server or service was not found an
416//! empty vector is returned.
417//
418vector<State> DimServiceInfoList::GetStates(const std::string &server) const
419{
420 const ServiceList::const_iterator s = fServiceList.find(server);
421 if (s==fServiceList.end())
422 return vector<State>();
423
424 return s->second.third;
425}
426
427// --------------------------------------------------------------------------
428//
429//! Returns the Description of the state as defined by the arguments.
430//! given server. Service in this context can also be a command.
431//!
432//! @param server
433//! Server name to look for
434//!
435//! @param state
436//! The state index to look for (e.g. 1)
437//!
438//! @returns
439//! The State object containing the description. If teh server was
440//! not found the State object will contain the index -3, if the
441//! state was not found -2.
442//
443State DimServiceInfoList::GetState(const std::string &server, int state) const
444{
445 const ServiceList::const_iterator s = fServiceList.find(server);
446 if (s==fServiceList.end())
447 return State(-3, "", "");
448
449 const std::vector<State> &v = s->second.third;
450
451 for (vector<State>::const_iterator i=v.begin(); i!=v.end(); i++)
452 if (i->index==state)
453 return *i;
454
455 return State(-2, "", "");
456}
457
458// --------------------------------------------------------------------------
459//
460//! Returns whether the given service on the given server is a command
461//! or not.
462//!
463//! @param server
464//! Server name to look for
465//!
466//! @param service
467//! The service name to look for
468//!
469//! @returns
470//! 1 if it is a command, 0 if it is a service, -1 if the service
471//! was not found on the server, -2 if the server was not found.
472//
473int DimServiceInfoList::IsCommand(const std::string &server, const std::string &service) const
474{
475 const ServiceList::const_iterator s = fServiceList.find(server);
476 if (s==fServiceList.end())
477 return -2;
478
479 const TypeList &list = s->second.first;
480
481 const TypeList::const_iterator t = list.find(service);
482 if (t==list.end())
483 return -1;
484
485 return t->second.second;
486}
487
488
489// --------------------------------------------------------------------------
490//
491//! Print the full available documentation (description) of all available
492//! services or comments to the the given stream.
493//!
494//! @param out
495//! ostream to which the output is send.
496//!
497//! @param iscmd
498//! true if all commands should be printed, false for services.
499//!
500//! @param serv
501//! if a server is given, only the information for this server is printed
502//!
503//! @param service
504//! if a service is given, only information for this service is printed
505//!
506//! @returns
507//! the number of descriptions found
508//
509int DimServiceInfoList::PrintDescription(std::ostream &out, bool iscmd, const string &serv, const string &service) const
510{
511 int rc = 0;
512 for (ServiceList::const_iterator i=fServiceList.begin(); i!=fServiceList.end(); i++)
513 {
514 const string &server = i->first;
515
516 if (!serv.empty() && server!=serv)
517 continue;
518
519 out << kRed << "----- " << server << " -----" << endl;
520
521 const TypeList &types = i->second.first;
522 const DescriptionList &descs = i->second.second;
523
524 for (TypeList::const_iterator t=types.begin(); t!=types.end(); t++)
525 {
526 if (!service.empty() && t->first!=service)
527 continue;
528
529 if (t->second.second!=iscmd)
530 continue;
531
532 rc++;
533
534 out << " " << t->first;
535
536 // Check t->second->first for command or service
537 const string fmt = t->second.first;
538 if (!fmt.empty())
539 out << '[' << fmt << ']';
540
541 const DescriptionList::const_iterator d = descs.find(t->first);
542 if (d==descs.end())
543 {
544 out << endl;
545 continue;
546 }
547
548 const string comment = d->second.first;
549 const vector<Description> &v = d->second.second;
550
551 for (vector<Description>::const_iterator j=v.begin(); j!=v.end(); j++)
552 out << " <" << j->name << ">";
553 out << endl;
554
555 if (!comment.empty())
556 out << " " << comment << endl;
557
558 for (vector<Description>::const_iterator j=v.begin(); j!=v.end(); j++)
559 {
560 out << " " << kGreen << j->name;
561 if (!j->comment.empty())
562 out << kReset << ": " << kBlue << j->comment;
563 if (!j->unit.empty())
564 out << kYellow << " [" << j->unit << "]";
565 out << endl;
566 }
567 }
568 out << endl;
569 }
570
571 return rc;
572}
573
574// --------------------------------------------------------------------------
575//
576//! Print the full list of stated for the given server.
577//!
578//! @param out
579//! ostream to which the output is send.
580//!
581//! @param serv
582//! if a server is given, only the information for this server is printed
583//!
584//! @returns
585//! the number of states found
586//
587int DimServiceInfoList::PrintStates(std::ostream &out, const string &serv) const
588{
589 int rc = 0;
590 for (ServiceList::const_iterator i=fServiceList.begin(); i!=fServiceList.end(); i++)
591 {
592 const string &server = i->first;
593
594 if (!serv.empty() && server!=serv)
595 continue;
596
597 out << kRed << "----- " << server << " -----" << endl;
598
599 const vector<State> &v = i->second.third;
600
601 if (v.size()==0)
602 out << " <no states>" << endl;
603 else
604 rc++;
605
606 for (vector<State>::const_iterator s=v.begin(); s!=v.end(); s++)
607 {
608 out << kBold << setw(5) << s->index << kReset << ": ";
609 out << kYellow << s->name;
610 out << kBlue << " (" << s->comment << ")" << endl;
611 }
612 out << endl;
613 }
614
615 return rc;
616}
617
618
619// --------------------------------------------------------------------------
620//
621//! Tries to send a dim command according to the arguments.
622//! The command given is evaluated according to the available format string.
623//!
624//! @param server
625//! The name of the server to which the command should be send, e.g. DRIVE
626//!
627//! @param str
628//! Command and data, eg "TRACK 12.5 13.8"
629//!
630//! @param lout
631//! the ostream to which errors and debug output is redirected
632//!
633//! @throws
634//! runtime_error if the server or command was not found, or if the
635//! format associated with the command could not be properly parsed,
636//! or if the command could not successfully be emitted.
637//!
638void DimServiceInfoList::SendDimCommand(const string &server, string str, ostream &lout) const
639{
640 str = Tools::Trim(str);
641
642 // Find the delimiter between the command name and the data
643 size_t p0 = str.find_first_of(' ');
644 if (p0==string::npos)
645 p0 = str.length();
646
647 // Get just the command name separated from the data
648 const string name = str.substr(0, p0);
649
650 // Compile the command which will be sent to the state-machine
651 const string cmd = server + '/' + name;
652
653 const ServiceList::const_iterator m = fServiceList.find(server);
654 if (m==fServiceList.end())
655 throw runtime_error("Unkown server '"+server+"'");
656
657 const TypeList &services = m->second.first;
658
659 const TypeList::const_iterator t = services.find(name);
660 if (t==services.end())
661 throw runtime_error("Command '"+name+"' not known on server '"+server+"'");
662
663 if (!t->second.second)
664 throw runtime_error("'"+server+"/"+name+" not a command.");
665
666 // Get the format of the event data
667 const string fmt = t->second.first;
668
669 // Convert the user entered data according to the format string
670 // into a data block which will be attached to the event
671 const Converter conv(lout, fmt, false);
672 if (!conv)
673 throw runtime_error("Couldn't properly parse the format... ignored.");
674
675 lout << kBlue << cmd;
676 const vector<char> v = conv.GetVector(str.substr(p0));
677 lout << endl;
678
679 const int rc = DimClient::sendCommand(cmd.c_str(), (void*)v.data(), v.size());
680 if (!rc)
681 throw runtime_error("ERROR - Sending command "+cmd+" failed.");
682}
683
684// --------------------------------------------------------------------------
685//
686//! Catches the runtime_erros thrown by
687//! SendDimCommand(const string &, string, ostream &)
688//! and redirects the error message to the output stream.
689//!
690//! @param lout
691//! the ostream to which errors and debug output is redirected
692//!
693//! @param server
694//! The name of the server to which the command should be send, e.g. DRIVE
695//!
696//! @param str
697//! Command and data, eg "TRACK 12.5 13.8"
698//!
699//! @returns
700//! true if SendDimComment didn't throw an exception, false otherwise
701//!
702bool DimServiceInfoList::SendDimCommand(ostream &lout, const string &server, const string &str) const
703{
704 try
705 {
706 SendDimCommand(server, str, lout);
707 lout << kGreen << "Command emitted successfully to " << server << "." << endl;
708 return true;
709 }
710 catch (const runtime_error &e)
711 {
712 lout << kRed << e.what() << endl;
713 return false;
714 }
715}
716
717// --------------------------------------------------------------------------
718//
719//! Calls SendDimCommand(const string &, string, ostream &) and dumps
720//! the output.
721//!
722//! @param server
723//! The name of the server to which the command should be send, e.g. DRIVE
724//!
725//! @param str
726//! Command and data, eg "TRACK 12.5 13.8"
727//!
728//! @throws
729//! see SendDimCommand(const string &, string, ostream &)
730//
731void DimServiceInfoList::SendDimCommand(const std::string &server, const std::string &str) const
732{
733 ostringstream dummy;
734 SendDimCommand(server, str, dummy);
735}
Note: See TracBrowser for help on using the repository browser.