source: trunk/FACT++/src/ServiceList.cc@ 10340

Last change on this file since 10340 was 10340, checked in by tbretz, 11 years ago
Fixed description for PrintDescription
File size: 23.8 KB
Line 
1// **************************************************************************
2/** @class ServiceList
3
4@brief Maintains a list of all servers and services available in the Dim nbetwork
5
6The idea of this class is to maintain a list of servers and services
7available in the Dim network. The servers are retrieved by subscribing to
8DIS_DNS/SERVER_LIST. The services are retrieved by subscribing to all
9servers SERVICE_LIST service.
10
11The server names and the corresponidng DimInfo ovjects for their service
12lists are stored in fServerList.
13
14The services of all servers are stored in the fServiceList.
15
16From the services a lookup table fFormatList is created storing the
17received formats of all services/commands. The format list is only
18updated. So it might happen that formats for commands not available
19anymore are still in the list.
20
21Whether commands or services are stored can be selected in the constructor
22by the type argument. Use "CMD" for commands and "" for services.
23
24
25@todo
26- Resolve the dependancy on WindowLog, maybe we can issue the log-messages
27 via MessageImp or we provide more general modfiers (loike in MLogManip)
28- Maybe we also get updates (+/-) on the SERVCIE_LIST?
29- check if we really need our own logging stream
30- Implement fType=="*"
31
32*/
33// **************************************************************************
34#include "ServiceList.h"
35
36#include <sstream>
37
38#include "WindowLog.h"
39#include "Converter.h"
40
41#include "tools.h"
42#include "Time.h"
43
44using namespace std;
45
46// --------------------------------------------------------------------------
47//
48//! Instantiates the default output-stream, subscribes to the Dim service
49//! DIS_DNS/SERVER_LIST, which is supposed to contain all servers available
50//! in the network, sets fType to type and inistialises fHandler with 0
51//!
52//! @param type
53//! The type of rows which is filtered out of the retrieved server list.
54//! Use "CMD" for commands and "" for services.
55//!
56//! @param out
57//! A log-stream to which errors are sent. This however is something
58//! which should not happen anyway.
59//
60ServiceList::ServiceList(const char *type, ostream &out) :
61 wout(out), fDimServers("DIS_DNS/SERVER_LIST", const_cast<char*>(""), this),
62 fType(type), fHandler(0)
63{
64}
65
66// --------------------------------------------------------------------------
67//
68//! Instantiates the default output-stream, subscribes to the Dim service
69//! DIS_DNS/SERVER_LIST, which is supposed to contain all servers available
70//! in the network, sets fType to "CMD" and inistialises fHandler with 0
71//!
72//! @param out
73//! A log-stream to which errors are sent. This however is something
74//! which should not happen anyway.
75//
76ServiceList::ServiceList(ostream &out) :
77 wout(out), fDimServers("DIS_DNS/SERVER_LIST", const_cast<char*>(""), this),
78 fType(""), fHandler(0)
79{
80}
81
82// --------------------------------------------------------------------------
83//
84//! Delete the allocated memory from fServerList
85//
86ServiceList::~ServiceList()
87{
88 for (ServerMap::iterator i=fServerList.begin(); i!=fServerList.end(); i++)
89 {
90 delete i->second.first;
91 delete i->second.second;
92 }
93}
94
95// --------------------------------------------------------------------------
96//
97//! The infoHandler which is called when an update to one of our subscribed
98//! services is available. If it is the service list, it calls
99//! ProcessServerList() and ProcessServiceList() otherwise.
100//!
101//! After the update has been processed the infoHandler() of fHandler
102//! is called if fHandler is available to signal an update to a parent
103//! class.
104//
105void ServiceList::infoHandler()
106{
107 if (getInfo()==&fDimServers)
108 ProcessServerList();
109 else
110 ProcessServiceList(*getInfo());
111
112 if (fHandler)
113 {
114 fHandler->itsService = 0;
115 fHandler->infoHandler();
116 }
117}
118
119// --------------------------------------------------------------------------
120//
121//! A helper to shorten the call to create a DimInfo.
122//!
123//! @param str
124//! name of the server to which we want to subscribe
125//!
126//! @param svc
127//! name of the servic on the server to which we want to subscribe
128//!
129//! @returns
130//! a pointer to the newly created DimInfo
131//!
132DimInfo *ServiceList::CreateDimInfo(const string &str, const string &svc) const
133{
134 return new DimInfo((str+"/"+svc).c_str(),
135 const_cast<char*>(""),
136 const_cast<ServiceList*>(this));
137}
138
139// --------------------------------------------------------------------------
140//
141//! This function processes the update of the DIS_DNS/SERVER_LIST update.
142//! After this function the server list should be up-to-date again.
143//!
144//! For each new server a SERVER/SERVICE_LIST service subscription is
145//! created and stored in the fServerList. For each removed server
146//! the corresponding object are deleted, as well as the corresponding
147//! entries from the fServiceList.
148//!
149void ServiceList::ProcessServerList()
150{
151 // Get the received string from the handler
152 const string str = fDimServers.getString();
153
154 // Check if it starts with + or -
155 if (str[0]!='-' && str[0]!='+')
156 {
157 // If it doesn't start with + or - remove all existing servers
158 // we have received a full server list
159 for (ServerMap::iterator i=fServerList.begin(); i!=fServerList.end(); i++)
160 {
161 delete i->second.first;
162 delete i->second.second;
163
164 ServiceMap::iterator x = fServiceList.find(i->first);
165 fServiceList.erase(x);
166 //wout << "Delete: " << i->first << endl;
167 }
168
169 fServerList.clear();
170 }
171
172 // Create a stringstream to tokenize the received string
173 stringstream stream(str);
174
175 // Loop over the seperating tokens
176 string buffer;
177 while (getline(stream, buffer, '|'))
178 {
179 // The first part before the first @ is the server name
180 const string server = buffer.substr(0, buffer.find_first_of('@'));
181 if (server.empty())
182 continue;
183
184 // If it starts with a - we have to remove an entry
185 if (server[0]=='-')
186 {
187 const string trunc = server.substr(1);
188
189 // Check if this server is not found in the list.
190 // This should never happen if Dim works reliable
191 const ServerMap::iterator v = fServerList.find(trunc);
192 if (v==fServerList.end())
193 {
194 wout << kRed << "Server '" << trunc << "' not in list as it ought to be." << endl;
195 continue;
196 }
197
198 // Remove the server from the server list
199 delete v->second.first;
200 delete v->second.second;
201 fServerList.erase(v);
202
203 // Remove the server from the command list
204 ServiceMap::iterator w = fServiceList.find(trunc);
205 fServiceList.erase(w);
206
207 wout << " -> " << Time().GetAsStr() << " - " << trunc << "/SERVICE_LIST: Disconnected." << endl;
208 //wout << "Remove: " << server << endl;
209 continue;
210 }
211
212 // If it starts with a + we have to add an entry
213 if (server[0]=='+')
214 {
215 const string trunc = server.substr(1);
216
217 // Check if this server is already in the list.
218 // This should never happen if Dim works reliable
219 const ServerMap::iterator v = fServerList.find(trunc);
220 if (v!=fServerList.end())
221 {
222 wout << kRed << "Server '" << trunc << "' in list not as it ought to be." << endl;
223 continue;
224 }
225
226 // Add the new server to the server list
227 fServerList[trunc] = make_pair(CreateSL(trunc), CreateFMT(trunc));
228
229 wout << " -> " << Time().GetAsStr() << " - " << trunc << "/SERVICE_LIST: Connected." << endl;
230 //wout << "Add : " << server << endl;
231 continue;
232 }
233
234 // In any other case we just add the entry to the list
235 fServerList[server] = make_pair(CreateSL(server), CreateFMT(server));
236 //wout << "Add 0: " << server << endl;
237 }
238}
239
240// --------------------------------------------------------------------------
241//
242//! Process an update of the SERVICE_LIST service of the given DimInfo
243//!
244//! All services found are stored in the fServiceList map to be accessible
245//! through the server name. Their format is format is stored in the
246//! fFormatList. Note, that the list if only updated. So it will also
247//! contain services which are not available anymore. For an up-to-date
248//! list of service use fServiceList
249//!
250//! Only entries matching the fType data member are stored.
251//!
252//! @todo
253//! Make sure that we do not receive +/- updates on the SERVICE_LIST
254//! like on the SERVER_LIST
255//!
256void ServiceList::ProcessServiceList(DimInfo &info)
257{
258 const string str = info.getString();
259 if (str.empty())
260 return;
261
262 // Get the name of the service
263 string buffer = info.getName();
264
265 if (buffer.find("SERVICE_DESC")!=buffer.length()-12)
266 {
267 // Get the server name from the service name
268 const string server = buffer.substr(0, buffer.find_first_of('/'));
269
270 // Initialize the entry with an empty list
271 if (str[0]!='+')
272 fServiceList[server] = vector<string>();
273
274 // For easy and fast access get the corresponding reference
275 vector<string> &list = fServiceList[server];
276
277 // Tokenize the stream into lines
278 stringstream stream(str);
279 while (getline(stream, buffer, '\n'))
280 {
281 if (buffer.empty())
282 continue;
283
284 // Get the type and compare it with fType
285 const string type = buffer.substr(buffer.find_last_of('|')+1);
286 if (type!=fType)
287 continue;
288
289 // Get format, name and command name
290 const string fmt = buffer.substr(buffer.find_first_of('|')+1, buffer.find_last_of('|')-buffer.find_first_of('|')-1);
291 const string name = buffer.substr(buffer.find_first_of('/')+1, buffer.find_first_of('|')-buffer.find_first_of('/')-1);
292 const string cmd = buffer.substr(0, buffer.find_first_of('|'));
293
294 // Add name the the list
295 list.push_back(name);
296
297 // Add format to the list
298 fFormatList[cmd] = fmt;
299 }
300 }
301 else
302 {
303 fDescriptionMap.clear();
304
305 stringstream stream(str);
306 while (getline(stream, buffer, '\n'))
307 {
308 if (buffer.empty())
309 continue;
310
311 const vector<Description> v = Description::SplitDescription(buffer);
312
313 const string svc = v[0].name;
314
315 fDescriptionMap[svc] = v[0].comment;
316 fDescriptionList[svc] = vector<Description>(v.begin()+1, v.end());
317 }
318 }
319}
320
321// --------------------------------------------------------------------------
322//
323//! @returns
324//! the list of servers as a vector of strings.
325//
326vector<string> ServiceList::GetServerList() const
327{
328 vector<string> v;
329 for (ServerMap::const_iterator i=fServerList.begin(); i!=fServerList.end(); i++)
330 v.push_back(i->first);
331
332 return v;
333}
334
335vector<string> ServiceList::GetServiceList(const std::string &server) const
336{
337 const ServiceMap::const_iterator m = fServiceList.find(server);
338 return m==end() ? vector<string>() : m->second;
339}
340
341vector<string> ServiceList::GetServiceList() const
342{
343 vector<string> vec;
344 for (ServerMap::const_iterator i=fServerList.begin(); i!=fServerList.end(); i++)
345 {
346 const string server = i->first;
347
348 const vector<string> v = GetServiceList(server);
349
350 for (vector<string>::const_iterator s=v.begin(); s<v.end(); s++)
351 vec.push_back(server+"/"+*s);
352 }
353
354 return vec;
355}
356
357// --------------------------------------------------------------------------
358//
359//! Get the format of a command or service
360//!
361//! @param service
362//! full qualified service name, e.g. SERVER/EXIT
363//!
364//! @returns
365//! the format corresponding to the given service. If the service is not
366//! found an empty string is returned.
367//
368string ServiceList::GetFormat(const string &service) const
369{
370 const StringMap::const_iterator i = fFormatList.find(service);
371 return i==fFormatList.end() ? "" : i->second;
372}
373
374// --------------------------------------------------------------------------
375//
376//! Get the format of a command or service
377//!
378//! @param server
379//! the server name, e.g. SERVER
380//!
381//! @param name
382//! the service name, e.g. EXIT
383//!
384//! @returns
385//! the format corresponding to the given service. If the service is not
386//! found an empty string is returned.
387//
388string ServiceList::GetFormat(const string &server, const string &name) const
389{
390 return GetFormat(server+"/"+name);
391}
392
393// --------------------------------------------------------------------------
394//
395//! Get the Description vector of a command or service
396//!
397//! @param service
398//! full qualified service name, e.g. SERVER/EXIT
399//!
400//! @returns
401//! a vector of Description objects corresponding to the arguments
402//!
403//
404vector<Description> ServiceList::GetDescriptions(const string &service) const
405{
406 const DescriptionMap::const_iterator i = fDescriptionList.find(service);
407 return i==fDescriptionList.end() ? vector<Description>() : i->second;
408}
409
410// --------------------------------------------------------------------------
411//
412//! Get the Description vector of a command or service
413//!
414//! @param server
415//! the server name, e.g. SERVER
416//!
417//! @param name
418//! the service name, e.g. EXIT
419//!
420//! @returns
421//! a vector of Description objects corresponding to the arguments
422//
423vector<Description> ServiceList::GetDescriptions(const string &server, const string &name) const
424{
425 return GetDescriptions(server+"/"+name);
426}
427
428// --------------------------------------------------------------------------
429//
430//! Get a description describing the given command or service if available.
431//!
432//! @param service
433//! full qualified service name, e.g. SERVER/EXIT
434//!
435//! @returns
436//! string with the stored comment
437//!
438//
439string ServiceList::GetComment(const string &service) const
440{
441 const StringMap::const_iterator i = fDescriptionMap.find(service);
442 return i==fDescriptionMap.end() ? "" : i->second;
443}
444
445// --------------------------------------------------------------------------
446//
447//! Get a description describing the given command or service if available.
448//!
449//! @param server
450//! the server name, e.g. SERVER
451//!
452//! @param name
453//! the service name, e.g. EXIT
454//!
455//! @returns
456//! string with the stored comment
457//
458string ServiceList::GetComment(const string &server, const string &name) const
459{
460 return GetComment(server+"/"+name);
461}
462
463// --------------------------------------------------------------------------
464//
465//! Checks if a server is existing.
466//!
467//! @param name
468//! Name of a server, e.g. DIS_DNS
469//!
470//! @returns
471//! true if the server is found in fServiceList, false otherwise
472//
473bool ServiceList::HasServer(const string &name) const
474{
475 return fServiceList.find(name)!=end();
476}
477
478// --------------------------------------------------------------------------
479//
480//! Checks if a given service is existing.
481//!
482//! @param server
483//! Name of a server, e.g. DIS_DNS
484//!
485//! @param service
486//! Name of a service, e.g. EXIT
487//!
488//! @returns
489//! true if the service is found in fServiceList, false otherwise.
490//
491bool ServiceList::HasService(const string &server, const string &service) const
492{
493 ServiceMap::const_iterator v = fServiceList.find(server);
494 if (v==end())
495 return false;
496
497 const vector<string> &w = v->second;
498 return find(w.begin(), w.end(), service)!=w.end();
499}
500
501bool ServiceList::HasService(const string &svc) const
502{
503 const size_t p = svc.find_first_of('/');
504 if (p==string::npos)
505 return false;
506
507 return HasService(svc.substr(0, p), svc.substr(p+1));
508}
509
510// --------------------------------------------------------------------------
511//
512//! Returns an iterator to the begin of ta vector<string> which will
513//! contain the service names of the given server.
514//!
515//! @param server
516//! Name of a server, e.g. DIS_DNS
517//!
518//! @returns
519//! an iterator to the vector of strings with the service names
520//! for the given server. If none is found it returns
521//! vector<string>().end()
522//
523vector<string>::const_iterator ServiceList::begin(const string &server) const
524{
525 ServiceMap::const_iterator i = fServiceList.find(server);
526 if (i==end())
527 return vector<string>().end();
528
529 return i->second.begin();
530}
531
532// --------------------------------------------------------------------------
533//
534//! Returns an iterator to the end of ta vector<string> which will
535//! contain the service names of the given server.
536//!
537//! @param server
538//! Name of a server, e.g. DIS_DNS
539//!
540//! @returns
541//! an iterator to the vector of strings with the service names
542//! for the given server. If none is found it returns
543//! vector<string>().end()
544//
545vector<string>::const_iterator ServiceList::end(const string &server) const
546{
547 ServiceMap::const_iterator i = fServiceList.find(server);
548 if (i==end())
549 return vector<string>().end();
550
551 return i->second.end();
552}
553
554
555// --------------------------------------------------------------------------
556//
557//! Print the stored list of servers to the given stream.
558//!
559//! @param out
560//! ostream to which the server names stored in fServerList are dumped
561//!
562void ServiceList::PrintServerList(ostream &out) const
563{
564 out << endl << kBold << "ServerList:" << endl;
565
566 for (ServerMap::const_iterator i=fServerList.begin(); i!=fServerList.end(); i++)
567 {
568 const string &server = i->first;
569 DimInfo *ptr1 = i->second.first;
570 DimInfo *ptr2 = i->second.second;
571
572 out << " " << server << " " << ptr1->getName() << "|" << ptr2->getName() << endl;
573 }
574 out << endl;
575}
576
577// --------------------------------------------------------------------------
578//
579//! Print the stored list of services to the given stream.
580//!
581//! @param out
582//! ostream to which the services names stored in fServiceList are dumped
583//!
584void ServiceList::PrintServiceList(ostream &out) const
585{
586 out << endl << kBold << "ServiceList:" << endl;
587
588 for (ServiceMap::const_iterator i=fServiceList.begin(); i!=fServiceList.end(); i++)
589 {
590 const string &server = i->first;
591 const vector<string> &lst = i->second;
592
593 out << " " << server << endl;
594
595 for (vector<string>::const_iterator j=lst.begin(); j!=lst.end(); j++)
596 out << " " << *j << " [" << GetFormat(server, *j) << "]" << endl;
597 }
598 out << endl;
599}
600
601// --------------------------------------------------------------------------
602//
603//! Print the full available documentation (description) of all available
604//! services or comments to the the given stream.
605//!
606//! @param out
607//! ostream to which the output is send.
608//!
609//! @param serv
610//! if a server is given, only the information for this server is printed
611//!
612//! @param service
613//! if a service is given, only information for this service is printed
614//
615int ServiceList::PrintDescription(std::ostream &out, const string &serv, const string &service) const
616{
617 int rc = 0;
618 for (ServiceMap::const_iterator i=fServiceList.begin(); i!=fServiceList.end(); i++)
619 {
620 const string &server = i->first;
621
622 if (!serv.empty() && server!=serv)
623 continue;
624
625 out << "" << kRed << "----- " << server << " -----" << endl;
626
627 const vector<string> &lst = i->second;
628 for (vector<string>::const_iterator s=lst.begin(); s!=lst.end(); s++)
629 {
630 if (!service.empty() && *s!=service)
631 continue;
632
633 rc++;
634
635 out << " " << *s;
636
637 const string fmt = GetFormat(server, *s);
638 if (!fmt.empty())
639 out << "[" << fmt << "]";
640
641 const string svc = server + "/" + *s;
642
643 const DescriptionMap::const_iterator v = fDescriptionList.find(svc);
644 if (v==fDescriptionList.end())
645 {
646 out << endl;
647 continue;
648 }
649
650 for (vector<Description>::const_iterator j=v->second.begin();
651 j!=v->second.end(); j++)
652 out << " <" << j->name << ">";
653 out << endl;
654
655 const StringMap::const_iterator d = fDescriptionMap.find(svc);
656 if (d!=fDescriptionMap.end() && !d->second.empty())
657 out << " " << d->second << endl;
658
659 for (vector<Description>::const_iterator j=v->second.begin();
660 j!=v->second.end(); j++)
661 {
662 out << " " << kGreen << j->name;
663 if (!j->comment.empty())
664 out << kReset << ": " << kBlue << j->comment;
665 if (!j->unit.empty())
666 out << kYellow << " [" << j->unit << "]";
667 out << endl;
668 }
669 }
670 out << endl;
671 }
672
673 return rc;
674}
675
676// --------------------------------------------------------------------------
677//
678//! Request a SERVER_LIST from the name server and a SERVICE_LIST from all
679//! servers in the list. Dumps the result to the given ostream.
680//!
681//! @param out
682//! ostream to which the received info is redirected
683//!
684void ServiceList::DumpServiceList(ostream &out)
685{
686 DimCurrentInfo info1("DIS_DNS/SERVER_LIST", const_cast<char*>(""));
687
688 stringstream stream(info1.getString());
689
690 string buffer;
691 while (getline(stream, buffer, '|'))
692 {
693 const string server = buffer.substr(0, buffer.find_first_of('@'));
694 if (server.empty())
695 continue;
696
697 out << kBold << " " << server << endl;
698
699 DimCurrentInfo info2(Form("%s/SERVICE_LIST", server.c_str()).c_str(), const_cast<char*>(""));
700
701 string buffer2;
702
703 stringstream stream2(info2.getString());
704 while (getline(stream2, buffer2, '\n'))
705 {
706 if (buffer2.empty())
707 continue;
708
709 out << " " << buffer2 << endl;
710 }
711 }
712}
713
714// --------------------------------------------------------------------------
715//
716//! Tries to send a dim command according to the arguments.
717//! The command given is evaluated according to the available format string.
718//!
719//! @param lout
720//! the ostream to which errors and debug output is redirected
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//! @returns
729//! If parsing the string was successfull and the command exists in the
730//! network true is returned, false otherwise.
731//!
732bool ServiceList::SendDimCommand(ostream &lout, const string &server, const string &str) const
733{
734 // Find the delimiter between the command name and the data
735 size_t p0 = str.find_first_of(' ');
736 if (p0==string::npos)
737 p0 = str.length();
738
739 // Get just the command name separated from the data
740 const string name = str.substr(0, p0);
741
742 // Compile the command which will be sent to the state-machine
743 const string cmd = server + "/" + name;
744
745 if (!HasService(server, name))
746 {
747 lout << kRed << "Unkown command '" << cmd << "'" << endl;
748 return false;
749 }
750
751 // Get the format of the event data
752 const string fmt = GetFormat(cmd);
753
754 // Convert the user entered data according to the format string
755 // into a data block which will be attached to the event
756 const Converter conv(lout, fmt, false);
757 if (!conv)
758 {
759 lout << kRed << "Couldn't properly parse the format... ignored." << endl;
760 return false;
761 }
762
763 try
764 {
765 lout << kBlue << cmd;
766 const vector<char> v = conv.GetVector(str.substr(p0));
767 lout << endl;
768
769 const int rc = DimClient::sendCommand(cmd.c_str(), (void*)v.data(), v.size());
770 if (rc)
771 lout << kGreen << "Command " << cmd << " emitted successfully to DimClient." << endl;
772 else
773 lout << kRed << "ERROR - Sending command " << cmd << " failed." << endl;
774 }
775 catch (const std::runtime_error &e)
776 {
777 lout << endl << kRed << e.what() << endl;
778 return false;
779 }
780
781 return true;
782}
Note: See TracBrowser for help on using the repository browser.