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

Last change on this file since 11262 was 11110, checked in by tbretz, 13 years ago
Removed an obsolete debug output.
File size: 23.3 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 if (type=="RPC")
212 continue;
213
214 /*
215 const bool iscmd = type=="CMD";
216 if (type!=fType && fType!="*")
217 continue;
218 */
219
220 // Get format, name and command name
221 const string fmt = buffer.substr(buffer.find_first_of('|')+1, buffer.find_last_of('|')-buffer.find_first_of('|')-1);
222 const string name = buffer.substr(buffer.find_first_of('/')+1, buffer.find_first_of('|')-buffer.find_first_of('/')-1);
223 //const string cmd = buffer.substr(0, buffer.find_first_of('|'));
224
225 const bool iscmd = type=="CMD";
226
227 // FIXME: Do we need to check that the buffer starts with SERVER ?
228
229 if (buffer[0]=='-')
230 {
231 // Check if this server is not found in the list.
232 // This should never happen if Dim works reliable
233 const TypeList::iterator v = list.find(name);
234 if (v==list.end())
235 {
236 stringstream err;
237 err << "Service '" << server << "/" << name << "' not in list as it ought to be.";
238 throw runtime_error(err.str());
239 }
240
241 RemoveService(server, name, iscmd);
242 list.erase(v);
243
244 continue;
245 }
246
247 if (buffer[0]=='+')
248 {
249 // Check if this server is not found in the list.
250 // This should never happen if Dim works reliable
251 const TypeList::iterator v = list.find(name);
252 if (v!=list.end())
253 {
254 stringstream err;
255 err << "Service '" << server << "/" << name << "' already in list not as it ought to be.";
256 throw runtime_error(err.str());
257 }
258
259 list[name] = make_pair(fmt, iscmd);
260 AddService(server, name, fmt, iscmd);
261
262 continue;
263 }
264
265 // Add name the the list
266 list[name] = make_pair(fmt, iscmd);
267 AddService(server, name, fmt, iscmd);
268 }
269
270 return;
271 }
272
273 if (service=="SERVICE_DESC")
274 {
275 // For easy and fast access get the corresponding reference
276 DescriptionList &list = fServiceList[server].second;
277
278 list.clear();
279
280 string buffer;
281
282 stringstream stream(getInfo()->getString());
283 while (getline(stream, buffer, '\n'))
284 {
285 if (buffer.empty())
286 continue;
287
288 const vector<Description> v = Description::SplitDescription(buffer);
289
290 const string name = v[0].name.substr(v[0].name.find_first_of('/')+1);
291 const string comment = v[0].comment;
292
293 list[name] = make_pair(comment, vector<Description>(v.begin()+1, v.end()));
294
295 AddDescription(server, name, v);
296 }
297
298 return;
299 }
300
301 if (service=="STATE_LIST")
302 {
303 vector<State> &vec = fServiceList[server].third;
304 vec = State::SplitStates(getInfo()->getString());
305 AddStates(server, vec);
306
307 return;
308 }
309
310 DimServerList::infoHandler();
311}
312
313// --------------------------------------------------------------------------
314//
315//! Returns a list of all services available for the given server.
316//! Depending on iscmd either only services or only commands are returned.
317//!
318//! @param server
319//! server for which the list should be returned
320//!
321//! @param iscmd
322//! true if only commands should be returned, false for services
323//!
324//! @returns
325//! a vector<string> which contains all the service or command names for
326//! the given server. The names returned are always SERVER/SERVICE
327//! If the server was not fund an empty vector is returned.
328//
329vector<string> DimServiceInfoList::GetServiceList(const std::string &server, bool iscmd) const
330{
331 const ServiceList::const_iterator m = fServiceList.find(server);
332 if (m==fServiceList.end())
333 return vector<string>();
334
335 const TypeList &list = m->second.first;
336
337 vector<string> vec;
338 for (TypeList::const_iterator i=list.begin(); i!=list.end(); i++)
339 if (i->second.second==iscmd)
340 vec.push_back(server+'/'+i->first);
341
342 return vec;
343}
344
345// --------------------------------------------------------------------------
346//
347//! Returns a list of all services available in the network.
348//! Depending on iscmd either only services or only commands are returned.
349//!
350//! @param iscmd
351//! true if only commands should be returned, false for services
352//!
353//! @returns
354//! a vector<string> which contains all the service or command names in
355//! the network. The names returned are always SERVER/SERVICE
356//
357vector<string> DimServiceInfoList::GetServiceList(bool iscmd) const
358{
359 vector<string> vec;
360 for (ServiceList::const_iterator m=fServiceList.begin(); m!=fServiceList.end(); m++)
361 {
362 const TypeList &list = m->second.first;
363
364 for (TypeList::const_iterator i=list.begin(); i!=list.end(); i++)
365 if (i->second.second==iscmd)
366 vec.push_back(m->first+'/'+i->first);
367 }
368
369 return vec;
370}
371
372// --------------------------------------------------------------------------
373//
374//! Returns a list of all descriptions for the given service on the
375//! given server. Service in this context can also be a command.
376//!
377//! @param server
378//! Server name to look for
379//!
380//! @param service
381//! Service/command name to look for
382//!
383//! @returns
384//! a vector<Description> which contains all argument descriptions for
385//! the given service or command. The first entry contains the name
386//! and the general description for the given service. If the server
387//! or service was not found an empty vector is returned.
388//
389std::vector<Description> DimServiceInfoList::GetDescription(const std::string &server, const std::string &service) const
390{
391 const ServiceList::const_iterator s = fServiceList.find(server);
392 if (s==fServiceList.end())
393 return vector<Description>();
394
395 const DescriptionList &descs = s->second.second;
396
397 const DescriptionList::const_iterator d = descs.find(service);
398 if (d==descs.end())
399 return vector<Description>();
400
401 vector<Description> vec;
402 vec.push_back(Description(service, d->second.first));
403 vec.insert(vec.end(), d->second.second.begin(), d->second.second.end());
404
405 return vec;
406}
407
408// --------------------------------------------------------------------------
409//
410//! Returns a list of all states associated with the given server.
411//!
412//! @param server
413//! Server name to look for
414//!
415//! @returns
416//! a vector<State> which contains all state descriptions for
417//! the given server. If the server or service was not found an
418//! empty vector is returned.
419//
420vector<State> DimServiceInfoList::GetStates(const std::string &server) const
421{
422 const ServiceList::const_iterator s = fServiceList.find(server);
423 if (s==fServiceList.end())
424 return vector<State>();
425
426 return s->second.third;
427}
428
429// --------------------------------------------------------------------------
430//
431//! Returns the Description of the state as defined by the arguments.
432//! given server. Service in this context can also be a command.
433//!
434//! @param server
435//! Server name to look for
436//!
437//! @param state
438//! The state index to look for (e.g. 1)
439//!
440//! @returns
441//! The State object containing the description. If the server was
442//! not found the State object will contain the index -3, if the
443//! state was not found -2.
444//
445State DimServiceInfoList::GetState(const std::string &server, int state) const
446{
447 const ServiceList::const_iterator s = fServiceList.find(server);
448 if (s==fServiceList.end())
449 {
450 stringstream str;
451 str << "DimServiceInfoList::GetState: Searching for state #" << state << " server " << server << " not found.";
452 return State(-3, "Server not found", str.str());
453 }
454
455 const std::vector<State> &v = s->second.third;
456
457 for (vector<State>::const_iterator i=v.begin(); i!=v.end(); i++)
458 if (i->index==state)
459 return *i;
460
461 stringstream str;
462 str << "DimServiceInfoList::GetState: State #" << state << " not found on server " << server << ".";
463 return State(-2, "State not found", str.str());
464}
465
466// --------------------------------------------------------------------------
467//
468//! Returns whether the given service on the given server is a command
469//! or not.
470//!
471//! @param server
472//! Server name to look for
473//!
474//! @param service
475//! The service name to look for
476//!
477//! @returns
478//! 1 if it is a command, 0 if it is a service, -1 if the service
479//! was not found on the server, -2 if the server was not found.
480//
481int DimServiceInfoList::IsCommand(const std::string &server, const std::string &service) const
482{
483 const ServiceList::const_iterator s = fServiceList.find(server);
484 if (s==fServiceList.end())
485 return -2;
486
487 const TypeList &list = s->second.first;
488
489 const TypeList::const_iterator t = list.find(service);
490 if (t==list.end())
491 return -1;
492
493 return t->second.second;
494}
495
496
497// --------------------------------------------------------------------------
498//
499//! Print the full available documentation (description) of all available
500//! services or comments to the the given stream.
501//!
502//! @param out
503//! ostream to which the output is send.
504//!
505//! @param iscmd
506//! true if all commands should be printed, false for services.
507//!
508//! @param serv
509//! if a server is given, only the information for this server is printed
510//!
511//! @param service
512//! if a service is given, only information for this service is printed
513//!
514//! @returns
515//! the number of descriptions found
516//
517int DimServiceInfoList::PrintDescription(std::ostream &out, bool iscmd, const string &serv, const string &service) const
518{
519 int rc = 0;
520 for (ServiceList::const_iterator i=fServiceList.begin(); i!=fServiceList.end(); i++)
521 {
522 const string &server = i->first;
523
524 if (!serv.empty() && server!=serv)
525 continue;
526
527 out << kRed << "----- " << server << " -----" << endl;
528
529 const TypeList &types = i->second.first;
530 const DescriptionList &descs = i->second.second;
531
532 for (TypeList::const_iterator t=types.begin(); t!=types.end(); t++)
533 {
534 if (!service.empty() && t->first!=service)
535 continue;
536
537 if (t->second.second!=iscmd)
538 continue;
539
540 rc++;
541
542 out << " " << t->first;
543
544 // Check t->second->first for command or service
545 const string fmt = t->second.first;
546 if (!fmt.empty())
547 out << '[' << fmt << ']';
548
549 const DescriptionList::const_iterator d = descs.find(t->first);
550 if (d==descs.end())
551 {
552 out << endl;
553 continue;
554 }
555
556 const string comment = d->second.first;
557 const vector<Description> &v = d->second.second;
558
559 for (vector<Description>::const_iterator j=v.begin(); j!=v.end(); j++)
560 out << " <" << j->name << ">";
561 out << endl;
562
563 if (!comment.empty())
564 out << " " << comment << endl;
565
566 for (vector<Description>::const_iterator j=v.begin(); j!=v.end(); j++)
567 {
568 out << " " << kGreen << j->name;
569 if (!j->comment.empty())
570 out << kReset << ": " << kBlue << j->comment;
571 if (!j->unit.empty())
572 out << kYellow << " [" << j->unit << "]";
573 out << endl;
574 }
575 }
576 out << endl;
577 }
578
579 return rc;
580}
581
582// --------------------------------------------------------------------------
583//
584//! Print the full list of stated for the given server.
585//!
586//! @param out
587//! ostream to which the output is send.
588//!
589//! @param serv
590//! if a server is given, only the information for this server is printed
591//!
592//! @returns
593//! the number of states found
594//
595int DimServiceInfoList::PrintStates(std::ostream &out, const string &serv) const
596{
597 int rc = 0;
598 for (ServiceList::const_iterator i=fServiceList.begin(); i!=fServiceList.end(); i++)
599 {
600 const string &server = i->first;
601
602 if (!serv.empty() && server!=serv)
603 continue;
604
605 out << kRed << "----- " << server << " -----" << endl;
606
607 const vector<State> &v = i->second.third;
608
609 if (v.size()==0)
610 out << " <no states>" << endl;
611 else
612 rc++;
613
614 for (vector<State>::const_iterator s=v.begin(); s!=v.end(); s++)
615 {
616 out << kBold << setw(5) << s->index << kReset << ": ";
617 out << kYellow << s->name;
618 out << kBlue << " (" << s->comment << ")" << endl;
619 }
620 out << endl;
621 }
622
623 return rc;
624}
625
626
627// --------------------------------------------------------------------------
628//
629//! Tries to send a dim command according to the arguments.
630//! The command given is evaluated according to the available format string.
631//!
632//! @param server
633//! The name of the server to which the command should be send, e.g. DRIVE
634//!
635//! @param str
636//! Command and data, eg "TRACK 12.5 13.8"
637//!
638//! @param lout
639//! the ostream to which errors and debug output is redirected
640//!
641//! @throws
642//! runtime_error if the server or command was not found, or if the
643//! format associated with the command could not be properly parsed,
644//! or if the command could not successfully be emitted.
645//!
646void DimServiceInfoList::SendDimCommand(const string &server, string str, ostream &lout) const
647{
648 str = Tools::Trim(str);
649
650 // Find the delimiter between the command name and the data
651 size_t p0 = str.find_first_of(' ');
652 if (p0==string::npos)
653 p0 = str.length();
654
655 // Get just the command name separated from the data
656 const string name = str.substr(0, p0);
657
658 // Compile the command which will be sent to the state-machine
659 const string cmd = server + '/' + name;
660
661 const ServiceList::const_iterator m = fServiceList.find(server);
662 if (m==fServiceList.end())
663 throw runtime_error("Unkown server '"+server+"'");
664
665 const TypeList &services = m->second.first;
666
667 const TypeList::const_iterator t = services.find(name);
668 if (t==services.end())
669 throw runtime_error("Command '"+name+"' not known on server '"+server+"'");
670
671 if (!t->second.second)
672 throw runtime_error("'"+server+"/"+name+" not a command.");
673
674 // Get the format of the event data
675 const string fmt = t->second.first;
676
677 // Convert the user entered data according to the format string
678 // into a data block which will be attached to the event
679 const Converter conv(lout, fmt, false);
680 if (!conv)
681 throw runtime_error("Couldn't properly parse the format... ignored.");
682
683 lout << kBlue << cmd;
684 const vector<char> v = conv.GetVector(str.substr(p0));
685 lout << endl;
686
687 const int rc = DimClient::sendCommand(cmd.c_str(), (void*)v.data(), v.size());
688 if (!rc)
689 throw runtime_error("ERROR - Sending command "+cmd+" failed.");
690}
691
692// --------------------------------------------------------------------------
693//
694//! Catches the runtime_erros thrown by
695//! SendDimCommand(const string &, string, ostream &)
696//! and redirects the error message to the output stream.
697//!
698//! @param lout
699//! the ostream to which errors and debug output is redirected
700//!
701//! @param server
702//! The name of the server to which the command should be send, e.g. DRIVE
703//!
704//! @param str
705//! Command and data, eg "TRACK 12.5 13.8"
706//!
707//! @returns
708//! true if SendDimComment didn't throw an exception, false otherwise
709//!
710bool DimServiceInfoList::SendDimCommand(ostream &lout, const string &server, const string &str) const
711{
712 try
713 {
714 SendDimCommand(server, str, lout);
715 lout << kGreen << "Command emitted successfully to " << server << "." << endl;
716 return true;
717 }
718 catch (const runtime_error &e)
719 {
720 lout << kRed << e.what() << endl;
721 return false;
722 }
723}
724
725// --------------------------------------------------------------------------
726//
727//! Calls SendDimCommand(const string &, string, ostream &) and dumps
728//! the output.
729//!
730//! @param server
731//! The name of the server to which the command should be send, e.g. DRIVE
732//!
733//! @param str
734//! Command and data, eg "TRACK 12.5 13.8"
735//!
736//! @throws
737//! see SendDimCommand(const string &, string, ostream &)
738//
739void DimServiceInfoList::SendDimCommand(const std::string &server, const std::string &str) const
740{
741 ostringstream dummy;
742 SendDimCommand(server, str, dummy);
743}
Note: See TracBrowser for help on using the repository browser.