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

Last change on this file since 10183 was 10183, checked in by tbretz, 9 years ago
New import.
File size: 17.7 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            delete i->second;
90}
91
92// --------------------------------------------------------------------------
93//
94//! The infoHandler which is called when an update to one of our subscribed
95//! services is available. If it is the service list, it calls
96//! ProcessServerList() and ProcessServiceList() otherwise.
97//!
98//! After the update has been processed the infoHandler() of fHandler
99//! is called if fHandler is available to signal an update to a parent
100//! class.
101//
102void ServiceList::infoHandler()
103{
104    if (getInfo()==&fDimServers)
105        ProcessServerList();
106    else
107        ProcessServiceList(*getInfo());
108
109    if (fHandler)
110        fHandler->infoHandler();
111}
112
113// --------------------------------------------------------------------------
114//
115//! A helper to shorten the call to create a DimInfo.
116//!
117//! @param str
118//!    name of the service to which we want to subscribe
119//!
120//! @returns
121//!    a pointer to the newly created DimInfo
122//!
123DimInfo *ServiceList::CreateDimInfo(const string &str) const
124{
125    return new DimInfo((str+"/SERVICE_LIST").c_str(),
126                       const_cast<char*>(""),
127                       const_cast<ServiceList*>(this));
128}
129
130// --------------------------------------------------------------------------
131//
132//! This function processes the update of the DIS_DNS/SERVER_LIST update.
133//! After this function the server list should be up-to-date again.
134//!
135//! For each new server a SERVER/SERVICE_LIST service subscription is
136//! created and stored in the fServerList. For each removed server
137//! the corresponding object are deleted, as well as the corresponding
138//! entries from the fServiceList.
139//!
140void ServiceList::ProcessServerList()
141{
142    // Get the received string from the handler
143    const string str = fDimServers.getString();
144
145    // Check if it starts with + or -
146    if (str[0]!='-' && str[0]!='+')
147    {
148        // If it doesn't start with + or - remove all existing servers
149        // we have received a full server list
150        for (ServerMap::iterator i=fServerList.begin(); i!=fServerList.end(); i++)
151        {
152            delete i->second;
153
154            ServiceMap::iterator x = fServiceList.find(i->first);
155            fServiceList.erase(x);
156            //wout << "Delete: " << i->first << endl;
157        }
158        fServerList.clear();
159    }
160
161    // Create a stringstream to tokenize the received string
162    stringstream stream(str);
163
164    // Loop over the seperating tokens
165    string buffer;
166    while (getline(stream, buffer, '|'))
167    {
168        // The first part before the first @ is the server name
169        const string server = buffer.substr(0, buffer.find_first_of('@'));
170        if (server.empty())
171            continue;
172
173        // If it starts with a - we have to remove an entry
174        if (server[0]=='-')
175        {
176            const string trunc = server.substr(1);
177
178            // Check if this server is not found in the list.
179            // This should never happen if Dim works reliable
180            const ServerMap::iterator v = fServerList.find(trunc);
181            if (v==fServerList.end())
182            {
183                wout << kRed << "Server '" << trunc << "' not in list as it ought to be." << endl;
184                continue;
185            }
186
187            // Remove the server from the server list
188            delete v->second;
189            fServerList.erase(v);
190
191            // Remove the server from the command list
192            ServiceMap::iterator w = fServiceList.find(trunc);
193            fServiceList.erase(w);
194
195            wout << " -> " << Time().GetAsStr() << " - " << trunc << "/SERVICE_LIST: Disconnected." << endl;
196            //wout << "Remove: " << server << endl;
197            continue;
198        }
199
200        // If it starts with a + we have to add an entry
201        if (server[0]=='+')
202        {
203            const string trunc = server.substr(1);
204
205            // Check if this server is already in the list.
206            // This should never happen if Dim works reliable
207            const ServerMap::iterator v = fServerList.find(trunc);
208            if (v!=fServerList.end())
209            {
210                wout << kRed << "Server '" << trunc << "' in list not as it ought to be." << endl;
211                continue;
212            }
213
214            // Add the new server to the server list
215            fServerList[trunc] = CreateDimInfo(trunc);
216
217            wout << " -> " << Time().GetAsStr() << " - " << trunc << "/SERVICE_LIST: Connected." << endl;
218            //wout << "Add   : " << server << endl;
219            continue;
220        }
221
222        // In any other case we just add the entry to the list
223        fServerList[server] = CreateDimInfo(server);
224        //wout << "Add  0: " << server << endl;
225    }
226}
227
228// --------------------------------------------------------------------------
229//
230//! Process an update of the SERVICE_LIST service of the given DimInfo
231//!
232//! All services found are stored in the fServiceList map to be accessible
233//! through the server name. Their format is format is stored in the
234//! fFormatList. Note, that the list if only updated. So it will also
235//! contain services which are not available anymore. For an up-to-date
236//! list of service use fServiceList
237//!
238//! Only entries matching the fType data member are stored.
239//!
240//! @todo
241//!    Make sure that we do not receive +/- updates on the SERVICE_LIST
242//!    like on the SERVER_LIST
243//!
244void ServiceList::ProcessServiceList(DimInfo &info)
245{
246    const string str = info.getString();
247    if (str.empty())
248        return;
249
250    // Get the name of the service
251    string buffer = info.getName();
252
253    // Get the server name from the service name
254    const string server = buffer.substr(0, buffer.find_first_of('/'));
255
256    // Initialize the entry with an empty list
257    if (str[0]!='+')
258        fServiceList[server] = vector<string>();
259
260    // For easy and fast access get the corresponding reference
261    vector<string> &list = fServiceList[server];
262
263    // Tokenize the stream into lines
264    stringstream stream(str);
265    while (getline(stream, buffer, '\n'))
266    {
267        if (buffer.empty())
268            continue;
269
270        // Get the type and compare it with fType
271        const string type = buffer.substr(buffer.find_last_of('|')+1);
272        if (type!=fType)
273            continue;
274
275        // Get format, name and command name
276        const string fmt  = buffer.substr(buffer.find_first_of('|')+1, buffer.find_last_of('|')-buffer.find_first_of('|')-1);
277        const string name = buffer.substr(buffer.find_first_of('/')+1, buffer.find_first_of('|')-buffer.find_first_of('/')-1);
278        const string cmd  = buffer.substr(0, buffer.find_first_of('|'));
279
280        // Add name the the list
281        list.push_back(name);
282
283        // Add format to the list
284        fFormatList[cmd] = fmt;
285    }
286}
287
288// --------------------------------------------------------------------------
289//
290//! @returns
291//!    the list of servers as a vector of strings.
292//
293vector<string> ServiceList::GetServerList() const
294{
295    vector<string> v;
296    for (ServerMap::const_iterator i=fServerList.begin(); i!=fServerList.end(); i++)
297        v.push_back(i->first);
298
299    return v;
300}
301
302vector<string> ServiceList::GetServiceList(const std::string &server) const
303{
304    const ServiceMap::const_iterator m = fServiceList.find(server);
305    return m==end() ? vector<string>() : m->second;
306}
307
308
309// --------------------------------------------------------------------------
310//
311//! Get the format of a command or service
312//!
313//! @param service
314//!    full qualified service name, e.g. SERVER/EXIT
315//!
316//! @returns
317//!    the format corresponding to the given service. If the service is not
318//!    found an empty string is returned.
319//
320string ServiceList::GetFormat(const string &service) const
321{
322    FormatMap::const_iterator i = fFormatList.find(service);
323    return i==fFormatList.end() ? "" : i->second;
324}
325
326// --------------------------------------------------------------------------
327//
328//! Get the format of a command or service
329//!
330//! @param server
331//!     the server name, e.g. SERVER
332//!
333//! @param name
334//!     the service name, e.g. EXIT
335//!
336//! @returns
337//!    the format corresponding to the given service. If the service is not
338//!    found an empty string is returned.
339//
340string ServiceList::GetFormat(const string &server, const string &name) const
341{
342    return GetFormat(server+"/"+name);
343}
344
345// --------------------------------------------------------------------------
346//
347//! Checks if a server is existing.
348//!
349//! @param name
350//!     Name of a server, e.g. DIS_DNS
351//!
352//! @returns
353//!    true if the server is found in fServiceList, false otherwise
354//
355bool ServiceList::HasServer(const string &name) const
356{
357    return fServiceList.find(name)!=end();
358}
359
360// --------------------------------------------------------------------------
361//
362//! Checks if a given service is existing.
363//!
364//! @param server
365//!     Name of a server, e.g. DIS_DNS
366//!
367//! @param service
368//!     Name of a service, e.g. EXIT
369//!
370//! @returns
371//!    true if the service is found in fServiceList, false otherwise.
372//
373bool ServiceList::HasService(const string &server, const string &service) const
374{
375    ServiceMap::const_iterator v = fServiceList.find(server);
376    if (v==end())
377        return false;
378
379    const vector<string> &w = v->second;
380    return find(w.begin(), w.end(), service)!=w.end();
381}
382
383// --------------------------------------------------------------------------
384//
385//! Returns an iterator to the begin of ta vector<string> which will
386//! contain the service names of the given server.
387//!
388//! @param server
389//!     Name of a server, e.g. DIS_DNS
390//!
391//! @returns
392//!    an iterator to the vector of strings with the service names
393//!    for the given server. If none is found it returns
394//!    vector<string>().end()
395//
396vector<string>::const_iterator ServiceList::begin(const string &server) const
397{
398    ServiceMap::const_iterator i = fServiceList.find(server);
399    if (i==end())
400        return vector<string>().end();
401
402    return i->second.begin();
403}
404
405// --------------------------------------------------------------------------
406//
407//! Returns an iterator to the end of ta vector<string> which will
408//! contain the service names of the given server.
409//!
410//! @param server
411//!     Name of a server, e.g. DIS_DNS
412//!
413//! @returns
414//!     an iterator to the vector of strings with the service names
415//!    for the given server. If none is found it returns
416//!    vector<string>().end()
417//
418vector<string>::const_iterator ServiceList::end(const string &server) const
419{
420    ServiceMap::const_iterator i = fServiceList.find(server);
421    if (i==end())
422        return vector<string>().end();
423
424    return i->second.end();
425}
426
427
428// --------------------------------------------------------------------------
429//
430//! Print the stored list of servers to the given stream.
431//!
432//! @param out
433//!    ostream to which the server names stored in fServerList are dumped
434//!
435void ServiceList::PrintServerList(ostream &out) const
436{
437    out << endl << kBold << "ServerList:" << endl;
438
439    for (ServerMap::const_iterator i=fServerList.begin(); i!=fServerList.end(); i++)
440    {
441        const string &server = i->first;
442        DimInfo *ptr = i->second;
443
444        out << " " << server << " " << ptr->getName() << endl;
445    }
446    out << endl;
447}
448
449// --------------------------------------------------------------------------
450//
451//! Print the stored list of services to the given stream.
452//!
453//! @param out
454//!    ostream to which the services names stored in fServiceList are dumped
455//!
456void ServiceList::PrintServiceList(ostream &out) const
457{
458    out << endl << kBold << "ServiceList:" << endl;
459
460    for (ServiceMap::const_iterator i=fServiceList.begin(); i!=fServiceList.end(); i++)
461    {
462        const string &server = i->first;
463        const vector<string> &lst = i->second;
464
465        out << " " << server << endl;
466
467        for (vector<string>::const_iterator j=lst.begin(); j!=lst.end(); j++)
468            out << "  " << *j << " [" << GetFormat(server, *j) << "]" << endl;
469    }
470    out << endl;
471}
472
473// --------------------------------------------------------------------------
474//
475//! Request a SERVER_LIST from the name server and a SERVICE_LISZ from all
476//! servers in the list. Dumps the result to the given ostream.
477//!
478//! @param out
479//!    ostream to which the received info is redirected
480//!
481void ServiceList::DumpServiceList(ostream &out)
482{
483    DimCurrentInfo info1("DIS_DNS/SERVER_LIST", const_cast<char*>(""));
484
485    stringstream stream(info1.getString());
486
487    string buffer;
488    while (getline(stream, buffer, '|'))
489    {
490        const string server = buffer.substr(0, buffer.find_first_of('@'));
491        if (server.empty())
492            continue;
493
494        out << kBold << " " << server << endl;
495
496        DimCurrentInfo info2(Form("%s/SERVICE_LIST", server.c_str()).c_str(), const_cast<char*>(""));
497
498        string buffer2;
499
500        stringstream stream2(info2.getString());
501        while (getline(stream2, buffer2, '\n'))
502        {
503            if (buffer2.empty())
504                continue;
505
506            out << "  " << buffer2 << endl;
507        }
508    }
509}
510
511// --------------------------------------------------------------------------
512//
513//! Tries to send a dim command according to the arguments.
514//! The command given is evaluated according to the available format string.
515//!
516//! @param lout
517//!    the ostream to which errors and debug output is redirected
518//!
519//! @param server
520//!    The name of the server to which the command should be send, e.g. DRIVE
521//!
522//! @param str
523//!    Command and data, eg "TRACK 12.5 13.8"
524//!
525//! @returns
526//!    If parsing the string was successfull and the command exists in the
527//!    network true is returned, false otherwise.
528//!
529bool ServiceList::SendDimCommand(ostream &lout, const string &server, const string &str) const
530{
531    // Find the delimiter between the command name and the data
532    size_t p0 = str.find_first_of(' ');
533    if (p0==string::npos)
534        p0 = str.length();
535
536    // Get just the command name separated from the data
537    const string name = str.substr(0, p0);
538
539    // Compile the command which will be sent to the state-machine
540    const string cmd = server + "/" + name;
541
542    if (!HasService(server, name))
543    {
544        lout << kRed << "Unkown command '" << cmd << "'" << endl;
545        return false;
546    }
547
548    // Get the format of the event data
549    const string fmt = GetFormat(cmd);
550
551    // Convert the user enetered data according to the format string
552    // into a data block which will be attached to the event
553    lout << kBlue << cmd;
554    Converter c(lout, fmt, str.substr(p0));
555
556    // Parsing was not successfull
557    if (!c.GetRc())
558    {
559        lout << kRed << "Couldn't properly parse the input... ignored." << endl;
560        return false;
561    }
562
563    const int rc = DimClient::sendCommand(cmd.c_str(), c.Ptr(), c.Size());
564    if (rc)
565        lout << kGreen << "Command " << cmd << " emitted successfully to DimClient." << endl;
566    else
567        lout << kRed << "ERROR - Sending command " << cmd << " failed." << endl;
568    return true;
569}
Note: See TracBrowser for help on using the repository browser.