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

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