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

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