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

Last change on this file since 10290 was 10289, checked in by tbretz, 13 years ago
The Converter functions now throw exceptions. The previous way of reporting a conversion error was not convenient.
File size: 17.9 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
555 const Converter conv(lout, fmt);
556 if (!conv)
557 {
558 lout << kRed << "Couldn't properly parse the format... ignored." << endl;
559 return false;
560 }
561
562 try
563 {
564 const vector<char> v = conv.GetVector(str.substr(p0));
565
566 const int rc = DimClient::sendCommand(cmd.c_str(), (void*)v.data(), v.size());
567 if (rc)
568 lout << kGreen << "Command " << cmd << " emitted successfully to DimClient." << endl;
569 else
570 lout << kRed << "ERROR - Sending command " << cmd << " failed." << endl;
571 }
572 catch (const std::runtime_error &e)
573 {
574 lout << kRed << e.what() << endl;
575 return false;
576 }
577
578 return true;
579}
Note: See TracBrowser for help on using the repository browser.