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

Last change on this file since 10319 was 10306, checked in by tbretz, 14 years ago
Implemented GetComment and GetDescription member functions.
File size: 23.3 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 service to which we want to subscribe
125//!
126//! @returns
127//! a pointer to the newly created DimInfo
128//!
129DimInfo *ServiceList::CreateDimInfo(const string &str, const string &svc) const
130{
131 return new DimInfo((str+"/"+svc).c_str(),
132 const_cast<char*>(""),
133 const_cast<ServiceList*>(this));
134}
135
136// --------------------------------------------------------------------------
137//
138//! This function processes the update of the DIS_DNS/SERVER_LIST update.
139//! After this function the server list should be up-to-date again.
140//!
141//! For each new server a SERVER/SERVICE_LIST service subscription is
142//! created and stored in the fServerList. For each removed server
143//! the corresponding object are deleted, as well as the corresponding
144//! entries from the fServiceList.
145//!
146void ServiceList::ProcessServerList()
147{
148 // Get the received string from the handler
149 const string str = fDimServers.getString();
150
151 // Check if it starts with + or -
152 if (str[0]!='-' && str[0]!='+')
153 {
154 // If it doesn't start with + or - remove all existing servers
155 // we have received a full server list
156 for (ServerMap::iterator i=fServerList.begin(); i!=fServerList.end(); i++)
157 {
158 delete i->second.first;
159 delete i->second.second;
160
161 ServiceMap::iterator x = fServiceList.find(i->first);
162 fServiceList.erase(x);
163 //wout << "Delete: " << i->first << endl;
164 }
165
166 fServerList.clear();
167 }
168
169 // Create a stringstream to tokenize the received string
170 stringstream stream(str);
171
172 // Loop over the seperating tokens
173 string buffer;
174 while (getline(stream, buffer, '|'))
175 {
176 // The first part before the first @ is the server name
177 const string server = buffer.substr(0, buffer.find_first_of('@'));
178 if (server.empty())
179 continue;
180
181 // If it starts with a - we have to remove an entry
182 if (server[0]=='-')
183 {
184 const string trunc = server.substr(1);
185
186 // Check if this server is not found in the list.
187 // This should never happen if Dim works reliable
188 const ServerMap::iterator v = fServerList.find(trunc);
189 if (v==fServerList.end())
190 {
191 wout << kRed << "Server '" << trunc << "' not in list as it ought to be." << endl;
192 continue;
193 }
194
195 // Remove the server from the server list
196 delete v->second.first;
197 delete v->second.second;
198 fServerList.erase(v);
199
200 // Remove the server from the command list
201 ServiceMap::iterator w = fServiceList.find(trunc);
202 fServiceList.erase(w);
203
204 wout << " -> " << Time().GetAsStr() << " - " << trunc << "/SERVICE_LIST: Disconnected." << endl;
205 //wout << "Remove: " << server << endl;
206 continue;
207 }
208
209 // If it starts with a + we have to add an entry
210 if (server[0]=='+')
211 {
212 const string trunc = server.substr(1);
213
214 // Check if this server is already in the list.
215 // This should never happen if Dim works reliable
216 const ServerMap::iterator v = fServerList.find(trunc);
217 if (v!=fServerList.end())
218 {
219 wout << kRed << "Server '" << trunc << "' in list not as it ought to be." << endl;
220 continue;
221 }
222
223 // Add the new server to the server list
224 fServerList[trunc] = make_pair(CreateSL(trunc), CreateFMT(trunc));
225
226 wout << " -> " << Time().GetAsStr() << " - " << trunc << "/SERVICE_LIST: Connected." << endl;
227 //wout << "Add : " << server << endl;
228 continue;
229 }
230
231 // In any other case we just add the entry to the list
232 fServerList[server] = make_pair(CreateSL(server), CreateFMT(server));
233 //wout << "Add 0: " << server << endl;
234 }
235}
236
237// --------------------------------------------------------------------------
238//
239//! Process an update of the SERVICE_LIST service of the given DimInfo
240//!
241//! All services found are stored in the fServiceList map to be accessible
242//! through the server name. Their format is format is stored in the
243//! fFormatList. Note, that the list if only updated. So it will also
244//! contain services which are not available anymore. For an up-to-date
245//! list of service use fServiceList
246//!
247//! Only entries matching the fType data member are stored.
248//!
249//! @todo
250//! Make sure that we do not receive +/- updates on the SERVICE_LIST
251//! like on the SERVER_LIST
252//!
253void ServiceList::ProcessServiceList(DimInfo &info)
254{
255 const string str = info.getString();
256 if (str.empty())
257 return;
258
259 // Get the name of the service
260 string buffer = info.getName();
261
262 if (buffer.find("SERVICE_DESC")!=buffer.length()-12)
263 {
264 // Get the server name from the service name
265 const string server = buffer.substr(0, buffer.find_first_of('/'));
266
267 // Initialize the entry with an empty list
268 if (str[0]!='+')
269 fServiceList[server] = vector<string>();
270
271 // For easy and fast access get the corresponding reference
272 vector<string> &list = fServiceList[server];
273
274 // Tokenize the stream into lines
275 stringstream stream(str);
276 while (getline(stream, buffer, '\n'))
277 {
278 if (buffer.empty())
279 continue;
280
281 // Get the type and compare it with fType
282 const string type = buffer.substr(buffer.find_last_of('|')+1);
283 if (type!=fType)
284 continue;
285
286 // Get format, name and command name
287 const string fmt = buffer.substr(buffer.find_first_of('|')+1, buffer.find_last_of('|')-buffer.find_first_of('|')-1);
288 const string name = buffer.substr(buffer.find_first_of('/')+1, buffer.find_first_of('|')-buffer.find_first_of('/')-1);
289 const string cmd = buffer.substr(0, buffer.find_first_of('|'));
290
291 // Add name the the list
292 list.push_back(name);
293
294 // Add format to the list
295 fFormatList[cmd] = fmt;
296 }
297 }
298 else
299 {
300 fDescriptionMap.clear();
301
302 wout << str << endl;
303
304 stringstream stream(str);
305 while (getline(stream, buffer, '\n'))
306 {
307 if (buffer.empty())
308 continue;
309
310 const vector<Description> v = Description::SplitDescription(buffer);
311
312 const string svc = v[0].name;
313
314 fDescriptionMap[svc] = v[0].comment;
315 fDescriptionList[svc] = vector<Description>(v.begin()+1, v.end());
316 }
317 }
318}
319
320// --------------------------------------------------------------------------
321//
322//! @returns
323//! the list of servers as a vector of strings.
324//
325vector<string> ServiceList::GetServerList() const
326{
327 vector<string> v;
328 for (ServerMap::const_iterator i=fServerList.begin(); i!=fServerList.end(); i++)
329 v.push_back(i->first);
330
331 return v;
332}
333
334vector<string> ServiceList::GetServiceList(const std::string &server) const
335{
336 const ServiceMap::const_iterator m = fServiceList.find(server);
337 return m==end() ? vector<string>() : m->second;
338}
339
340vector<string> ServiceList::GetServiceList() const
341{
342 vector<string> vec;
343 for (ServerMap::const_iterator i=fServerList.begin(); i!=fServerList.end(); i++)
344 {
345 const string server = i->first;
346
347 const vector<string> v = GetServiceList(server);
348
349 for (vector<string>::const_iterator s=v.begin(); s<v.end(); s++)
350 vec.push_back(server+"/"+*s);
351 }
352
353 return vec;
354}
355
356// --------------------------------------------------------------------------
357//
358//! Get the format of a command or service
359//!
360//! @param service
361//! full qualified service name, e.g. SERVER/EXIT
362//!
363//! @returns
364//! the format corresponding to the given service. If the service is not
365//! found an empty string is returned.
366//
367string ServiceList::GetFormat(const string &service) const
368{
369 const StringMap::const_iterator i = fFormatList.find(service);
370 return i==fFormatList.end() ? "" : i->second;
371}
372
373// --------------------------------------------------------------------------
374//
375//! Get the format of a command or service
376//!
377//! @param server
378//! the server name, e.g. SERVER
379//!
380//! @param name
381//! the service name, e.g. EXIT
382//!
383//! @returns
384//! the format corresponding to the given service. If the service is not
385//! found an empty string is returned.
386//
387string ServiceList::GetFormat(const string &server, const string &name) const
388{
389 return GetFormat(server+"/"+name);
390}
391
392// --------------------------------------------------------------------------
393//
394//! Get the Description vector of a command or service
395//!
396//! @param service
397//! full qualified service name, e.g. SERVER/EXIT
398//!
399//! @returns
400//! a vector of Description objects corresponding to the arguments
401//!
402//
403vector<Description> ServiceList::GetDescriptions(const string &service) const
404{
405 const DescriptionMap::const_iterator i = fDescriptionList.find(service);
406 return i==fDescriptionList.end() ? vector<Description>() : i->second;
407}
408
409// --------------------------------------------------------------------------
410//
411//! Get the Description vector of a command or service
412//!
413//! @param server
414//! the server name, e.g. SERVER
415//!
416//! @param name
417//! the service name, e.g. EXIT
418//!
419//! @returns
420//! a vector of Description objects corresponding to the arguments
421//
422vector<Description> ServiceList::GetDescriptions(const string &server, const string &name) const
423{
424 return GetDescriptions(server+"/"+name);
425}
426
427// --------------------------------------------------------------------------
428//
429//! Get a description describing the given command or service if available.
430//!
431//! @param service
432//! full qualified service name, e.g. SERVER/EXIT
433//!
434//! @returns
435//! string with the stored comment
436//!
437//
438string ServiceList::GetComment(const string &service) const
439{
440 const StringMap::const_iterator i = fDescriptionMap.find(service);
441 return i==fDescriptionMap.end() ? "" : i->second;
442}
443
444// --------------------------------------------------------------------------
445//
446//! Get a description describing the given command or service if available.
447//!
448//! @param server
449//! the server name, e.g. SERVER
450//!
451//! @param name
452//! the service name, e.g. EXIT
453//!
454//! @returns
455//! string with the stored comment
456//
457string ServiceList::GetComment(const string &server, const string &name) const
458{
459 return GetComment(server+"/"+name);
460}
461
462// --------------------------------------------------------------------------
463//
464//! Checks if a server is existing.
465//!
466//! @param name
467//! Name of a server, e.g. DIS_DNS
468//!
469//! @returns
470//! true if the server is found in fServiceList, false otherwise
471//
472bool ServiceList::HasServer(const string &name) const
473{
474 return fServiceList.find(name)!=end();
475}
476
477// --------------------------------------------------------------------------
478//
479//! Checks if a given service is existing.
480//!
481//! @param server
482//! Name of a server, e.g. DIS_DNS
483//!
484//! @param service
485//! Name of a service, e.g. EXIT
486//!
487//! @returns
488//! true if the service is found in fServiceList, false otherwise.
489//
490bool ServiceList::HasService(const string &server, const string &service) const
491{
492 ServiceMap::const_iterator v = fServiceList.find(server);
493 if (v==end())
494 return false;
495
496 const vector<string> &w = v->second;
497 return find(w.begin(), w.end(), service)!=w.end();
498}
499
500bool ServiceList::HasService(const string &svc) const
501{
502 const size_t p = svc.find_first_of('/');
503 if (p==string::npos)
504 return false;
505
506 return HasService(svc.substr(0, p), svc.substr(p+1));
507}
508
509// --------------------------------------------------------------------------
510//
511//! Returns an iterator to the begin of ta vector<string> which will
512//! contain the service names of the given server.
513//!
514//! @param server
515//! Name of a server, e.g. DIS_DNS
516//!
517//! @returns
518//! an iterator to the vector of strings with the service names
519//! for the given server. If none is found it returns
520//! vector<string>().end()
521//
522vector<string>::const_iterator ServiceList::begin(const string &server) const
523{
524 ServiceMap::const_iterator i = fServiceList.find(server);
525 if (i==end())
526 return vector<string>().end();
527
528 return i->second.begin();
529}
530
531// --------------------------------------------------------------------------
532//
533//! Returns an iterator to the end of ta vector<string> which will
534//! contain the service names of the given server.
535//!
536//! @param server
537//! Name of a server, e.g. DIS_DNS
538//!
539//! @returns
540//! an iterator to the vector of strings with the service names
541//! for the given server. If none is found it returns
542//! vector<string>().end()
543//
544vector<string>::const_iterator ServiceList::end(const string &server) const
545{
546 ServiceMap::const_iterator i = fServiceList.find(server);
547 if (i==end())
548 return vector<string>().end();
549
550 return i->second.end();
551}
552
553
554// --------------------------------------------------------------------------
555//
556//! Print the stored list of servers to the given stream.
557//!
558//! @param out
559//! ostream to which the server names stored in fServerList are dumped
560//!
561void ServiceList::PrintServerList(ostream &out) const
562{
563 out << endl << kBold << "ServerList:" << endl;
564
565 for (ServerMap::const_iterator i=fServerList.begin(); i!=fServerList.end(); i++)
566 {
567 const string &server = i->first;
568 DimInfo *ptr1 = i->second.first;
569 DimInfo *ptr2 = i->second.second;
570
571 out << " " << server << " " << ptr1->getName() << "|" << ptr2->getName() << endl;
572 }
573 out << endl;
574}
575
576// --------------------------------------------------------------------------
577//
578//! Print the stored list of services to the given stream.
579//!
580//! @param out
581//! ostream to which the services names stored in fServiceList are dumped
582//!
583void ServiceList::PrintServiceList(ostream &out) const
584{
585 out << endl << kBold << "ServiceList:" << endl;
586
587 for (ServiceMap::const_iterator i=fServiceList.begin(); i!=fServiceList.end(); i++)
588 {
589 const string &server = i->first;
590 const vector<string> &lst = i->second;
591
592 out << " " << server << endl;
593
594 for (vector<string>::const_iterator j=lst.begin(); j!=lst.end(); j++)
595 out << " " << *j << " [" << GetFormat(server, *j) << "]" << endl;
596 }
597 out << endl;
598}
599
600// --------------------------------------------------------------------------
601//
602//! Print the full available documentation (description) of all available
603//! services or comments to the the given stream.
604//!
605//! @param out
606//! ostream to which the output is send.
607//
608void ServiceList::PrintDescription(std::ostream &out) const
609{
610 for (ServiceMap::const_iterator i=fServiceList.begin(); i!=fServiceList.end(); i++)
611 {
612 const string &server = i->first;
613 const vector<string> &lst = i->second;
614
615 out << "" << kRed << "----- " << server << " -----" << endl;
616
617 for (vector<string>::const_iterator s=lst.begin(); s!=lst.end(); s++)
618 {
619 out << " " << *s;
620
621 const string fmt = GetFormat(server, *s);
622 if (!fmt.empty())
623 out << "[" << fmt << "]";
624
625 const string svc = server + "/" + *s;
626
627 const DescriptionMap::const_iterator v = fDescriptionList.find(svc);
628 if (v==fDescriptionList.end())
629 {
630 out << endl;
631 continue;
632 }
633
634 for (vector<Description>::const_iterator j=v->second.begin();
635 j!=v->second.end(); j++)
636 out << " <" << j->name << ">";
637 out << endl;
638
639 const StringMap::const_iterator d = fDescriptionMap.find(svc);
640 if (d!=fDescriptionMap.end() && !d->second.empty())
641 out << " " << d->second << endl;
642
643 for (vector<Description>::const_iterator j=v->second.begin();
644 j!=v->second.end(); j++)
645 {
646 out << " " << kGreen << j->name;
647 if (!j->comment.empty())
648 out << kReset << ": " << kBlue << j->comment;
649 if (!j->unit.empty())
650 out << kYellow << " [" << j->unit << "]";
651 out << endl;
652 }
653 }
654 out << endl;
655 }
656}
657
658// --------------------------------------------------------------------------
659//
660//! Request a SERVER_LIST from the name server and a SERVICE_LIST from all
661//! servers in the list. Dumps the result to the given ostream.
662//!
663//! @param out
664//! ostream to which the received info is redirected
665//!
666void ServiceList::DumpServiceList(ostream &out)
667{
668 DimCurrentInfo info1("DIS_DNS/SERVER_LIST", const_cast<char*>(""));
669
670 stringstream stream(info1.getString());
671
672 string buffer;
673 while (getline(stream, buffer, '|'))
674 {
675 const string server = buffer.substr(0, buffer.find_first_of('@'));
676 if (server.empty())
677 continue;
678
679 out << kBold << " " << server << endl;
680
681 DimCurrentInfo info2(Form("%s/SERVICE_LIST", server.c_str()).c_str(), const_cast<char*>(""));
682
683 string buffer2;
684
685 stringstream stream2(info2.getString());
686 while (getline(stream2, buffer2, '\n'))
687 {
688 if (buffer2.empty())
689 continue;
690
691 out << " " << buffer2 << endl;
692 }
693 }
694}
695
696// --------------------------------------------------------------------------
697//
698//! Tries to send a dim command according to the arguments.
699//! The command given is evaluated according to the available format string.
700//!
701//! @param lout
702//! the ostream to which errors and debug output is redirected
703//!
704//! @param server
705//! The name of the server to which the command should be send, e.g. DRIVE
706//!
707//! @param str
708//! Command and data, eg "TRACK 12.5 13.8"
709//!
710//! @returns
711//! If parsing the string was successfull and the command exists in the
712//! network true is returned, false otherwise.
713//!
714bool ServiceList::SendDimCommand(ostream &lout, const string &server, const string &str) const
715{
716 // Find the delimiter between the command name and the data
717 size_t p0 = str.find_first_of(' ');
718 if (p0==string::npos)
719 p0 = str.length();
720
721 // Get just the command name separated from the data
722 const string name = str.substr(0, p0);
723
724 // Compile the command which will be sent to the state-machine
725 const string cmd = server + "/" + name;
726
727 if (!HasService(server, name))
728 {
729 lout << kRed << "Unkown command '" << cmd << "'" << endl;
730 return false;
731 }
732
733 // Get the format of the event data
734 const string fmt = GetFormat(cmd);
735
736 // Convert the user enetered data according to the format string
737 // into a data block which will be attached to the event
738 lout << kBlue << cmd;
739
740 const Converter conv(lout, fmt);
741 if (!conv)
742 {
743 lout << kRed << "Couldn't properly parse the format... ignored." << endl;
744 return false;
745 }
746
747 try
748 {
749 const vector<char> v = conv.GetVector(str.substr(p0));
750
751 const int rc = DimClient::sendCommand(cmd.c_str(), (void*)v.data(), v.size());
752 if (rc)
753 lout << kGreen << "Command " << cmd << " emitted successfully to DimClient." << endl;
754 else
755 lout << kRed << "ERROR - Sending command " << cmd << " failed." << endl;
756 }
757 catch (const std::runtime_error &e)
758 {
759 lout << kRed << e.what() << endl;
760 return false;
761 }
762
763 return true;
764}
Note: See TracBrowser for help on using the repository browser.