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

Last change on this file since 10298 was 10298, checked in by tbretz, 9 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.