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

Last change on this file since 10329 was 10329, checked in by tbretz, 14 years ago
Removed some unintentional debug output.
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 stringstream stream(str);
303 while (getline(stream, buffer, '\n'))
304 {
305 if (buffer.empty())
306 continue;
307
308 const vector<Description> v = Description::SplitDescription(buffer);
309
310 const string svc = v[0].name;
311
312 fDescriptionMap[svc] = v[0].comment;
313 fDescriptionList[svc] = vector<Description>(v.begin()+1, v.end());
314 }
315 }
316}
317
318// --------------------------------------------------------------------------
319//
320//! @returns
321//! the list of servers as a vector of strings.
322//
323vector<string> ServiceList::GetServerList() const
324{
325 vector<string> v;
326 for (ServerMap::const_iterator i=fServerList.begin(); i!=fServerList.end(); i++)
327 v.push_back(i->first);
328
329 return v;
330}
331
332vector<string> ServiceList::GetServiceList(const std::string &server) const
333{
334 const ServiceMap::const_iterator m = fServiceList.find(server);
335 return m==end() ? vector<string>() : m->second;
336}
337
338vector<string> ServiceList::GetServiceList() const
339{
340 vector<string> vec;
341 for (ServerMap::const_iterator i=fServerList.begin(); i!=fServerList.end(); i++)
342 {
343 const string server = i->first;
344
345 const vector<string> v = GetServiceList(server);
346
347 for (vector<string>::const_iterator s=v.begin(); s<v.end(); s++)
348 vec.push_back(server+"/"+*s);
349 }
350
351 return vec;
352}
353
354// --------------------------------------------------------------------------
355//
356//! Get the format of a command or service
357//!
358//! @param service
359//! full qualified service name, e.g. SERVER/EXIT
360//!
361//! @returns
362//! the format corresponding to the given service. If the service is not
363//! found an empty string is returned.
364//
365string ServiceList::GetFormat(const string &service) const
366{
367 const StringMap::const_iterator i = fFormatList.find(service);
368 return i==fFormatList.end() ? "" : i->second;
369}
370
371// --------------------------------------------------------------------------
372//
373//! Get the format of a command or service
374//!
375//! @param server
376//! the server name, e.g. SERVER
377//!
378//! @param name
379//! the service name, e.g. EXIT
380//!
381//! @returns
382//! the format corresponding to the given service. If the service is not
383//! found an empty string is returned.
384//
385string ServiceList::GetFormat(const string &server, const string &name) const
386{
387 return GetFormat(server+"/"+name);
388}
389
390// --------------------------------------------------------------------------
391//
392//! Get the Description vector of a command or service
393//!
394//! @param service
395//! full qualified service name, e.g. SERVER/EXIT
396//!
397//! @returns
398//! a vector of Description objects corresponding to the arguments
399//!
400//
401vector<Description> ServiceList::GetDescriptions(const string &service) const
402{
403 const DescriptionMap::const_iterator i = fDescriptionList.find(service);
404 return i==fDescriptionList.end() ? vector<Description>() : i->second;
405}
406
407// --------------------------------------------------------------------------
408//
409//! Get the Description vector of a command or service
410//!
411//! @param server
412//! the server name, e.g. SERVER
413//!
414//! @param name
415//! the service name, e.g. EXIT
416//!
417//! @returns
418//! a vector of Description objects corresponding to the arguments
419//
420vector<Description> ServiceList::GetDescriptions(const string &server, const string &name) const
421{
422 return GetDescriptions(server+"/"+name);
423}
424
425// --------------------------------------------------------------------------
426//
427//! Get a description describing the given command or service if available.
428//!
429//! @param service
430//! full qualified service name, e.g. SERVER/EXIT
431//!
432//! @returns
433//! string with the stored comment
434//!
435//
436string ServiceList::GetComment(const string &service) const
437{
438 const StringMap::const_iterator i = fDescriptionMap.find(service);
439 return i==fDescriptionMap.end() ? "" : i->second;
440}
441
442// --------------------------------------------------------------------------
443//
444//! Get a description describing the given command or service if available.
445//!
446//! @param server
447//! the server name, e.g. SERVER
448//!
449//! @param name
450//! the service name, e.g. EXIT
451//!
452//! @returns
453//! string with the stored comment
454//
455string ServiceList::GetComment(const string &server, const string &name) const
456{
457 return GetComment(server+"/"+name);
458}
459
460// --------------------------------------------------------------------------
461//
462//! Checks if a server is existing.
463//!
464//! @param name
465//! Name of a server, e.g. DIS_DNS
466//!
467//! @returns
468//! true if the server is found in fServiceList, false otherwise
469//
470bool ServiceList::HasServer(const string &name) const
471{
472 return fServiceList.find(name)!=end();
473}
474
475// --------------------------------------------------------------------------
476//
477//! Checks if a given service is existing.
478//!
479//! @param server
480//! Name of a server, e.g. DIS_DNS
481//!
482//! @param service
483//! Name of a service, e.g. EXIT
484//!
485//! @returns
486//! true if the service is found in fServiceList, false otherwise.
487//
488bool ServiceList::HasService(const string &server, const string &service) const
489{
490 ServiceMap::const_iterator v = fServiceList.find(server);
491 if (v==end())
492 return false;
493
494 const vector<string> &w = v->second;
495 return find(w.begin(), w.end(), service)!=w.end();
496}
497
498bool ServiceList::HasService(const string &svc) const
499{
500 const size_t p = svc.find_first_of('/');
501 if (p==string::npos)
502 return false;
503
504 return HasService(svc.substr(0, p), svc.substr(p+1));
505}
506
507// --------------------------------------------------------------------------
508//
509//! Returns an iterator to the begin of ta vector<string> which will
510//! contain the service names of the given server.
511//!
512//! @param server
513//! Name of a server, e.g. DIS_DNS
514//!
515//! @returns
516//! an iterator to the vector of strings with the service names
517//! for the given server. If none is found it returns
518//! vector<string>().end()
519//
520vector<string>::const_iterator ServiceList::begin(const string &server) const
521{
522 ServiceMap::const_iterator i = fServiceList.find(server);
523 if (i==end())
524 return vector<string>().end();
525
526 return i->second.begin();
527}
528
529// --------------------------------------------------------------------------
530//
531//! Returns an iterator to the end of ta vector<string> which will
532//! contain the service names of the given server.
533//!
534//! @param server
535//! Name of a server, e.g. DIS_DNS
536//!
537//! @returns
538//! an iterator to the vector of strings with the service names
539//! for the given server. If none is found it returns
540//! vector<string>().end()
541//
542vector<string>::const_iterator ServiceList::end(const string &server) const
543{
544 ServiceMap::const_iterator i = fServiceList.find(server);
545 if (i==end())
546 return vector<string>().end();
547
548 return i->second.end();
549}
550
551
552// --------------------------------------------------------------------------
553//
554//! Print the stored list of servers to the given stream.
555//!
556//! @param out
557//! ostream to which the server names stored in fServerList are dumped
558//!
559void ServiceList::PrintServerList(ostream &out) const
560{
561 out << endl << kBold << "ServerList:" << endl;
562
563 for (ServerMap::const_iterator i=fServerList.begin(); i!=fServerList.end(); i++)
564 {
565 const string &server = i->first;
566 DimInfo *ptr1 = i->second.first;
567 DimInfo *ptr2 = i->second.second;
568
569 out << " " << server << " " << ptr1->getName() << "|" << ptr2->getName() << endl;
570 }
571 out << endl;
572}
573
574// --------------------------------------------------------------------------
575//
576//! Print the stored list of services to the given stream.
577//!
578//! @param out
579//! ostream to which the services names stored in fServiceList are dumped
580//!
581void ServiceList::PrintServiceList(ostream &out) const
582{
583 out << endl << kBold << "ServiceList:" << endl;
584
585 for (ServiceMap::const_iterator i=fServiceList.begin(); i!=fServiceList.end(); i++)
586 {
587 const string &server = i->first;
588 const vector<string> &lst = i->second;
589
590 out << " " << server << endl;
591
592 for (vector<string>::const_iterator j=lst.begin(); j!=lst.end(); j++)
593 out << " " << *j << " [" << GetFormat(server, *j) << "]" << endl;
594 }
595 out << endl;
596}
597
598// --------------------------------------------------------------------------
599//
600//! Print the full available documentation (description) of all available
601//! services or comments to the the given stream.
602//!
603//! @param out
604//! ostream to which the output is send.
605//
606void ServiceList::PrintDescription(std::ostream &out) const
607{
608 for (ServiceMap::const_iterator i=fServiceList.begin(); i!=fServiceList.end(); i++)
609 {
610 const string &server = i->first;
611 const vector<string> &lst = i->second;
612
613 out << "" << kRed << "----- " << server << " -----" << endl;
614
615 for (vector<string>::const_iterator s=lst.begin(); s!=lst.end(); s++)
616 {
617 out << " " << *s;
618
619 const string fmt = GetFormat(server, *s);
620 if (!fmt.empty())
621 out << "[" << fmt << "]";
622
623 const string svc = server + "/" + *s;
624
625 const DescriptionMap::const_iterator v = fDescriptionList.find(svc);
626 if (v==fDescriptionList.end())
627 {
628 out << endl;
629 continue;
630 }
631
632 for (vector<Description>::const_iterator j=v->second.begin();
633 j!=v->second.end(); j++)
634 out << " <" << j->name << ">";
635 out << endl;
636
637 const StringMap::const_iterator d = fDescriptionMap.find(svc);
638 if (d!=fDescriptionMap.end() && !d->second.empty())
639 out << " " << d->second << endl;
640
641 for (vector<Description>::const_iterator j=v->second.begin();
642 j!=v->second.end(); j++)
643 {
644 out << " " << kGreen << j->name;
645 if (!j->comment.empty())
646 out << kReset << ": " << kBlue << j->comment;
647 if (!j->unit.empty())
648 out << kYellow << " [" << j->unit << "]";
649 out << endl;
650 }
651 }
652 out << endl;
653 }
654}
655
656// --------------------------------------------------------------------------
657//
658//! Request a SERVER_LIST from the name server and a SERVICE_LIST from all
659//! servers in the list. Dumps the result to the given ostream.
660//!
661//! @param out
662//! ostream to which the received info is redirected
663//!
664void ServiceList::DumpServiceList(ostream &out)
665{
666 DimCurrentInfo info1("DIS_DNS/SERVER_LIST", const_cast<char*>(""));
667
668 stringstream stream(info1.getString());
669
670 string buffer;
671 while (getline(stream, buffer, '|'))
672 {
673 const string server = buffer.substr(0, buffer.find_first_of('@'));
674 if (server.empty())
675 continue;
676
677 out << kBold << " " << server << endl;
678
679 DimCurrentInfo info2(Form("%s/SERVICE_LIST", server.c_str()).c_str(), const_cast<char*>(""));
680
681 string buffer2;
682
683 stringstream stream2(info2.getString());
684 while (getline(stream2, buffer2, '\n'))
685 {
686 if (buffer2.empty())
687 continue;
688
689 out << " " << buffer2 << endl;
690 }
691 }
692}
693
694// --------------------------------------------------------------------------
695//
696//! Tries to send a dim command according to the arguments.
697//! The command given is evaluated according to the available format string.
698//!
699//! @param lout
700//! the ostream to which errors and debug output is redirected
701//!
702//! @param server
703//! The name of the server to which the command should be send, e.g. DRIVE
704//!
705//! @param str
706//! Command and data, eg "TRACK 12.5 13.8"
707//!
708//! @returns
709//! If parsing the string was successfull and the command exists in the
710//! network true is returned, false otherwise.
711//!
712bool ServiceList::SendDimCommand(ostream &lout, const string &server, const string &str) const
713{
714 // Find the delimiter between the command name and the data
715 size_t p0 = str.find_first_of(' ');
716 if (p0==string::npos)
717 p0 = str.length();
718
719 // Get just the command name separated from the data
720 const string name = str.substr(0, p0);
721
722 // Compile the command which will be sent to the state-machine
723 const string cmd = server + "/" + name;
724
725 if (!HasService(server, name))
726 {
727 lout << kRed << "Unkown command '" << cmd << "'" << endl;
728 return false;
729 }
730
731 // Get the format of the event data
732 const string fmt = GetFormat(cmd);
733
734 // Convert the user entered data according to the format string
735 // into a data block which will be attached to the event
736 const Converter conv(lout, fmt, false);
737 if (!conv)
738 {
739 lout << kRed << "Couldn't properly parse the format... ignored." << endl;
740 return false;
741 }
742
743 try
744 {
745 lout << kBlue << cmd;
746 const vector<char> v = conv.GetVector(str.substr(p0));
747 lout << endl;
748
749 const int rc = DimClient::sendCommand(cmd.c_str(), (void*)v.data(), v.size());
750 if (rc)
751 lout << kGreen << "Command " << cmd << " emitted successfully to DimClient." << endl;
752 else
753 lout << kRed << "ERROR - Sending command " << cmd << " failed." << endl;
754 }
755 catch (const std::runtime_error &e)
756 {
757 lout << endl << kRed << e.what() << endl;
758 return false;
759 }
760
761 return true;
762}
Note: See TracBrowser for help on using the repository browser.