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

Last change on this file since 10305 was 10298, checked in by tbretz, 14 years ago
Added the automatic retrieval of the description strings as send by DimDescriptionService; added a GetServiceList() member function returning all service names.
File size: 21.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 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//! Checks if a server is existing.
395//!
396//! @param name
397//! Name of a server, e.g. DIS_DNS
398//!
399//! @returns
400//! true if the server is found in fServiceList, false otherwise
401//
402bool ServiceList::HasServer(const string &name) const
403{
404 return fServiceList.find(name)!=end();
405}
406
407// --------------------------------------------------------------------------
408//
409//! Checks if a given service is existing.
410//!
411//! @param server
412//! Name of a server, e.g. DIS_DNS
413//!
414//! @param service
415//! Name of a service, e.g. EXIT
416//!
417//! @returns
418//! true if the service is found in fServiceList, false otherwise.
419//
420bool ServiceList::HasService(const string &server, const string &service) const
421{
422 ServiceMap::const_iterator v = fServiceList.find(server);
423 if (v==end())
424 return false;
425
426 const vector<string> &w = v->second;
427 return find(w.begin(), w.end(), service)!=w.end();
428}
429
430bool ServiceList::HasService(const string &svc) const
431{
432 const size_t p = svc.find_first_of('/');
433 if (p==string::npos)
434 return false;
435
436 return HasService(svc.substr(0, p), svc.substr(p+1));
437}
438
439// --------------------------------------------------------------------------
440//
441//! Returns an iterator to the begin of ta vector<string> which will
442//! contain the service names of the given server.
443//!
444//! @param server
445//! Name of a server, e.g. DIS_DNS
446//!
447//! @returns
448//! an iterator to the vector of strings with the service names
449//! for the given server. If none is found it returns
450//! vector<string>().end()
451//
452vector<string>::const_iterator ServiceList::begin(const string &server) const
453{
454 ServiceMap::const_iterator i = fServiceList.find(server);
455 if (i==end())
456 return vector<string>().end();
457
458 return i->second.begin();
459}
460
461// --------------------------------------------------------------------------
462//
463//! Returns an iterator to the end of ta vector<string> which will
464//! contain the service names of the given server.
465//!
466//! @param server
467//! Name of a server, e.g. DIS_DNS
468//!
469//! @returns
470//! an iterator to the vector of strings with the service names
471//! for the given server. If none is found it returns
472//! vector<string>().end()
473//
474vector<string>::const_iterator ServiceList::end(const string &server) const
475{
476 ServiceMap::const_iterator i = fServiceList.find(server);
477 if (i==end())
478 return vector<string>().end();
479
480 return i->second.end();
481}
482
483
484// --------------------------------------------------------------------------
485//
486//! Print the stored list of servers to the given stream.
487//!
488//! @param out
489//! ostream to which the server names stored in fServerList are dumped
490//!
491void ServiceList::PrintServerList(ostream &out) const
492{
493 out << endl << kBold << "ServerList:" << endl;
494
495 for (ServerMap::const_iterator i=fServerList.begin(); i!=fServerList.end(); i++)
496 {
497 const string &server = i->first;
498 DimInfo *ptr1 = i->second.first;
499 DimInfo *ptr2 = i->second.second;
500
501 out << " " << server << " " << ptr1->getName() << "|" << ptr2->getName() << endl;
502 }
503 out << endl;
504}
505
506// --------------------------------------------------------------------------
507//
508//! Print the stored list of services to the given stream.
509//!
510//! @param out
511//! ostream to which the services names stored in fServiceList are dumped
512//!
513void ServiceList::PrintServiceList(ostream &out) const
514{
515 out << endl << kBold << "ServiceList:" << endl;
516
517 for (ServiceMap::const_iterator i=fServiceList.begin(); i!=fServiceList.end(); i++)
518 {
519 const string &server = i->first;
520 const vector<string> &lst = i->second;
521
522 out << " " << server << endl;
523
524 for (vector<string>::const_iterator j=lst.begin(); j!=lst.end(); j++)
525 out << " " << *j << " [" << GetFormat(server, *j) << "]" << endl;
526 }
527 out << endl;
528}
529
530// --------------------------------------------------------------------------
531//
532//! Print the full available documentation (description) of all available
533//! services or comments to the the given stream.
534//!
535//! @param out
536//! ostream to which the output is send.
537//
538void ServiceList::PrintDescription(std::ostream &out) const
539{
540 for (ServiceMap::const_iterator i=fServiceList.begin(); i!=fServiceList.end(); i++)
541 {
542 const string &server = i->first;
543 const vector<string> &lst = i->second;
544
545 out << "" << kRed << "----- " << server << " -----" << endl;
546
547 for (vector<string>::const_iterator s=lst.begin(); s!=lst.end(); s++)
548 {
549 out << " " << *s;
550
551 const string fmt = GetFormat(server, *s);
552 if (!fmt.empty())
553 out << "[" << fmt << "]";
554
555 const string svc = server + "/" + *s;
556
557 const DescriptionMap::const_iterator v = fDescriptionList.find(svc);
558 if (v==fDescriptionList.end())
559 {
560 out << endl;
561 continue;
562 }
563
564 for (vector<Description>::const_iterator j=v->second.begin();
565 j!=v->second.end(); j++)
566 out << " <" << j->name << ">";
567 out << endl;
568
569 const StringMap::const_iterator d = fDescriptionMap.find(svc);
570 if (d!=fDescriptionMap.end() && !d->second.empty())
571 out << " " << d->second << endl;
572
573 for (vector<Description>::const_iterator j=v->second.begin();
574 j!=v->second.end(); j++)
575 {
576 out << " " << kGreen << j->name;
577 if (!j->comment.empty())
578 out << kReset << ": " << kBlue << j->comment;
579 if (!j->unit.empty())
580 out << kYellow << " [" << j->unit << "]";
581 out << endl;
582 }
583 }
584 out << endl;
585 }
586}
587
588// --------------------------------------------------------------------------
589//
590//! Request a SERVER_LIST from the name server and a SERVICE_LIST from all
591//! servers in the list. Dumps the result to the given ostream.
592//!
593//! @param out
594//! ostream to which the received info is redirected
595//!
596void ServiceList::DumpServiceList(ostream &out)
597{
598 DimCurrentInfo info1("DIS_DNS/SERVER_LIST", const_cast<char*>(""));
599
600 stringstream stream(info1.getString());
601
602 string buffer;
603 while (getline(stream, buffer, '|'))
604 {
605 const string server = buffer.substr(0, buffer.find_first_of('@'));
606 if (server.empty())
607 continue;
608
609 out << kBold << " " << server << endl;
610
611 DimCurrentInfo info2(Form("%s/SERVICE_LIST", server.c_str()).c_str(), const_cast<char*>(""));
612
613 string buffer2;
614
615 stringstream stream2(info2.getString());
616 while (getline(stream2, buffer2, '\n'))
617 {
618 if (buffer2.empty())
619 continue;
620
621 out << " " << buffer2 << endl;
622 }
623 }
624}
625
626// --------------------------------------------------------------------------
627//
628//! Tries to send a dim command according to the arguments.
629//! The command given is evaluated according to the available format string.
630//!
631//! @param lout
632//! the ostream to which errors and debug output is redirected
633//!
634//! @param server
635//! The name of the server to which the command should be send, e.g. DRIVE
636//!
637//! @param str
638//! Command and data, eg "TRACK 12.5 13.8"
639//!
640//! @returns
641//! If parsing the string was successfull and the command exists in the
642//! network true is returned, false otherwise.
643//!
644bool ServiceList::SendDimCommand(ostream &lout, const string &server, const string &str) const
645{
646 // Find the delimiter between the command name and the data
647 size_t p0 = str.find_first_of(' ');
648 if (p0==string::npos)
649 p0 = str.length();
650
651 // Get just the command name separated from the data
652 const string name = str.substr(0, p0);
653
654 // Compile the command which will be sent to the state-machine
655 const string cmd = server + "/" + name;
656
657 if (!HasService(server, name))
658 {
659 lout << kRed << "Unkown command '" << cmd << "'" << endl;
660 return false;
661 }
662
663 // Get the format of the event data
664 const string fmt = GetFormat(cmd);
665
666 // Convert the user enetered data according to the format string
667 // into a data block which will be attached to the event
668 lout << kBlue << cmd;
669
670 const Converter conv(lout, fmt);
671 if (!conv)
672 {
673 lout << kRed << "Couldn't properly parse the format... ignored." << endl;
674 return false;
675 }
676
677 try
678 {
679 const vector<char> v = conv.GetVector(str.substr(p0));
680
681 const int rc = DimClient::sendCommand(cmd.c_str(), (void*)v.data(), v.size());
682 if (rc)
683 lout << kGreen << "Command " << cmd << " emitted successfully to DimClient." << endl;
684 else
685 lout << kRed << "ERROR - Sending command " << cmd << " failed." << endl;
686 }
687 catch (const std::runtime_error &e)
688 {
689 lout << kRed << e.what() << endl;
690 return false;
691 }
692
693 return true;
694}
Note: See TracBrowser for help on using the repository browser.