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

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