source: trunk/FACT++/src/DimServiceInfoList.cc@ 12536

Last change on this file since 12536 was 12349, checked in by tbretz, 13 years ago
Removed the warning in case of double delete... so far no problems weere encountered.
File size: 23.5 KB
Line 
1// **************************************************************************
2/** @class DimServiceInfoList
3
4@brief Maintains a list of all services available in the Dim network
5
6The idea of this class is to maintain a list of services available
7in the Dim network as well as state descriptions if available.
8
9Therefore, it subscribes to the SERVICE_LIST, SERVICE_DESC and STATE_LIST
10services of all servers.
11
12To maintain the list it derives from DimServerList which maintains
13a list of all servers.
14
15To maintain the subscriptions it overwrites:
16
17- void DimServerList::AddServer(const std::string &s)
18- void DimServerList::RemoveServer(const std::string &s)
19- void DimServerList::RemoveAllServers()
20
21If a derived class also overwrites these functions it must be ensured that
22the member functions of DimServiceInfoList are still called properly.
23
24Whenever a service is added or removed, or all services of one server
25is removed the following virtual functions are called:
26
27- virtual void AddService(const std::string &server, const std::string &service, const std::string &fmt, bool iscmd)
28- virtual void RemoveService(const std::string &server, const std::string &service, bool iscmd)
29- virtual void RemoveAllServices(const std::string &server)
30
31Note, that these functions are not called from the RemoveServer() and
32RemoveAllServer() functions. It might be a difference whether all services
33were removed but the server is still online or the server went offline.
34
35If a description or a state was added, this is signaled though:
36
37- virtual void AddDescription(const std::string &server, const std::string &service, const std::vector<Description> &vec)
38- virtual void AddStates(const std::string &server, const std::vector<State> &vec)
39
40Note, that Descriptions and States are never removed except a service or
41server goes offline. It is expected that if a service comes online also
42the list of descritions is sent again.
43
44*/
45// **************************************************************************
46#include "DimServiceInfoList.h"
47
48#include <sstream>
49
50#include "WindowLog.h"
51#include "Converter.h"
52
53#include "tools.h"
54#include "Time.h"
55
56using namespace std;
57
58// --------------------------------------------------------------------------
59//
60//! A helper to shorten the call to create a DimInfo.
61//!
62//! @param str
63//! name of the server to which we want to subscribe
64//!
65//! @param svc
66//! name of the servic on the server to which we want to subscribe
67//!
68//! @returns
69//! a pointer to the newly created DimInfo
70//!
71DimInfo *DimServiceInfoList::CreateDimInfo(const string &str, const string &svc) const
72{
73 return new DimInfo((str+'/'+svc).c_str(),
74 const_cast<char*>(""),
75 const_cast<DimServiceInfoList*>(this));
76}
77
78// --------------------------------------------------------------------------
79//
80//! Adds the service subscription for SERVICE_LIST, SERVICE_DESC and
81//! STATE_LIST for the given server. Don't forget to call this function
82//! if it is overwritten in a derived class.
83//!
84//! @param s
85//! server which should be added
86//!
87//! @throws
88//! a runtime_error is the server is already in the list
89//
90void DimServiceInfoList::AddServer(const string &s)
91{
92 // Check if this server is already in the list.
93 // This should never happen if Dim works reliable
94 const ServiceInfoList::iterator v = fServiceInfoList.find(s);
95 if (v!=fServiceInfoList.end())
96 {
97 stringstream err;
98 err << "DimServiceInfoList: Server '" << s << "' in list not as it ought to be.";
99 throw runtime_error(err.str());
100 }
101
102 fServiceInfoList[s].push_back(CreateSL(s));
103 fServiceInfoList[s].push_back(CreateFMT(s));
104 fServiceInfoList[s].push_back(CreateDS(s));
105}
106
107// --------------------------------------------------------------------------
108//
109//! Removes the service subscription for SERVICE_LIST, SERVICE_DESC and
110//! STATE_LIST for the given server, as well as the stored informations.
111//! Don't forget to call this function if it is overwritten in a derived
112//! class.
113//!
114//! @param s
115//! server which should be removed. We need to make a copy, otherwise
116//! RemoveServer will destroy the staring the reference is pointing to
117//!
118//! @throws
119//! a runtime_error is the server to be removed is not in the list
120//
121void DimServiceInfoList::RemoveServer(const string s)
122{
123 const ServiceInfoList::iterator v = fServiceInfoList.find(s);
124 if (v==fServiceInfoList.end())
125 {
126 stringstream err;
127 err << "DimServiceInfoList: Server '" << s << "' not in list as it ought to be.";
128 throw runtime_error(err.str());
129 }
130
131 RemoveAllServices(s);
132
133 // Remove the server from the server list
134 delete v->second[0];
135 delete v->second[1];
136 delete v->second[2];
137
138 fServiceInfoList.erase(v);
139 fServiceList.erase(s);
140}
141
142// --------------------------------------------------------------------------
143//
144//! Removes the service subscription for SERVICE_LIST, SERVICE_DESC and
145//! STATE_LIST for all servers, as well as all stored informations.
146//! Don't forget to call this function if it is overwritten in a derived
147//! class.
148//!
149void DimServiceInfoList::RemoveAllServers()
150{
151 while (fServiceInfoList.size()>0)
152 RemoveServer(fServiceInfoList.begin()->first);
153}
154
155
156// --------------------------------------------------------------------------
157//
158//! This function processes the update of the SERVICE_LIST, SERVICE_DESC,
159//! and STATE_LIST updates.
160//!
161//! Whenever a service is added or removed or all services of a server are
162//! removed (the list is newly sent completely) the virtual functions
163//! AddService(), RemoveService() and RemoveAllServices() aee called.
164//!
165//! If a new description or a new state is added, the virtual functions
166//! AddDescription() and AddStates() respectively are called.
167//
168void DimServiceInfoList::infoHandler()
169{
170 // Get the name of the service
171 const string svc = getInfo()->getName();
172
173 // Get the server name from the service name
174 const string server = svc.substr(0, svc.find_first_of('/'));
175 const string service = svc.substr(svc.find_first_of('/')+1);
176
177 if (service=="SERVICE_LIST")
178 {
179 // For easy and fast access get the corresponding reference
180 TypeList &list = fServiceList[server].first;
181
182 const string str = getInfo()->getString();
183
184 // WHAT's THIS???
185 if (str.length()==0)
186 return;
187
188 // Initialize the entry with an empty list
189 if (str[0]!='+' && str[0]!='-')
190 {
191 RemoveAllServices(server);
192 list.clear();
193 }
194
195 string buffer;
196
197 // Tokenize the stream into lines
198 stringstream stream(str);
199 while (getline(stream, buffer, '\n'))
200 {
201 if (buffer.empty())
202 continue;
203
204 // Get the type and compare it with fType
205 const string type = buffer.substr(buffer.find_last_of('|')+1);
206 if (type=="RPC")
207 continue;
208
209 /*
210 const bool iscmd = type=="CMD";
211 if (type!=fType && fType!="*")
212 continue;
213 */
214
215 // Get format, name and command name
216 const string fmt = buffer.substr(buffer.find_first_of('|')+1, buffer.find_last_of('|')-buffer.find_first_of('|')-1);
217 const string name = buffer.substr(buffer.find_first_of('/')+1, buffer.find_first_of('|')-buffer.find_first_of('/')-1);
218 //const string cmd = buffer.substr(0, buffer.find_first_of('|'));
219
220 const bool iscmd = type=="CMD";
221
222 // FIXME: Do we need to check that the buffer starts with SERVER ?
223
224 if (buffer[0]=='-')
225 {
226 // Check if this server is not found in the list.
227 // This should never happen if Dim works reliable
228 const TypeList::iterator v = list.find(name);
229 /*
230 if (v==list.end())
231 {
232 stringstream err;
233 err << "DimServiceInfoList: Service '" << server << "/" << name << "' not in list as it ought to be.";
234 // Seems to happen why more than one client is subscribed
235 // and e.g. the datalogger is immediately quit
236 throw runtime_error(err.str());
237 }*/
238
239 RemoveService(server, name, iscmd);
240 if (v!=list.end())
241 list.erase(v);
242
243 continue;
244 }
245
246 if (buffer[0]=='+')
247 {
248 // Check if this server is not found in the list.
249 // This should never happen if Dim works reliable
250 const TypeList::iterator v = list.find(name);
251 if (v!=list.end())
252 {
253 stringstream err;
254 err << "DimServiceInfoList: Service '" << server << "/" << name << "' already in list not as it ought to be.";
255 throw runtime_error(err.str());
256 }
257
258 list[name] = make_pair(fmt, iscmd);
259 AddService(server, name, fmt, iscmd);
260
261 continue;
262 }
263
264 // Add name the the list
265 list[name] = make_pair(fmt, iscmd);
266 AddService(server, name, fmt, iscmd);
267 }
268
269 return;
270 }
271
272 if (service=="SERVICE_DESC")
273 {
274 // For easy and fast access get the corresponding reference
275 DescriptionList &list = fServiceList[server].second;
276
277 list.clear();
278
279 string buffer;
280
281 stringstream stream(getInfo()->getString());
282 while (getline(stream, buffer, '\n'))
283 {
284 if (buffer.empty())
285 continue;
286
287 const vector<Description> v = Description::SplitDescription(buffer);
288
289 const string name = v[0].name.substr(v[0].name.find_first_of('/')+1);
290 const string comment = v[0].comment;
291
292 list[name] = make_pair(comment, vector<Description>(v.begin()+1, v.end()));
293
294 AddDescription(server, name, v);
295 }
296
297 return;
298 }
299
300 if (service=="STATE_LIST")
301 {
302 vector<State> &vec = fServiceList[server].third;
303 vec = State::SplitStates(getInfo()->getString());
304 AddStates(server, vec);
305
306 return;
307 }
308
309 DimServerList::infoHandler();
310}
311
312// --------------------------------------------------------------------------
313//
314//! Returns a list of all services available for the given server.
315//! Depending on iscmd either only services or only commands are returned.
316//!
317//! @param server
318//! server for which the list should be returned
319//!
320//! @param iscmd
321//! true if only commands should be returned, false for services
322//!
323//! @returns
324//! a vector<string> which contains all the service or command names for
325//! the given server. The names returned are always SERVER/SERVICE
326//! If the server was not fund an empty vector is returned.
327//
328vector<string> DimServiceInfoList::GetServiceList(const std::string &server, bool iscmd) const
329{
330 const ServiceList::const_iterator m = fServiceList.find(server);
331 if (m==fServiceList.end())
332 return vector<string>();
333
334 const TypeList &list = m->second.first;
335
336 vector<string> vec;
337 for (TypeList::const_iterator i=list.begin(); i!=list.end(); i++)
338 if (i->second.second==iscmd)
339 vec.push_back(server+'/'+i->first);
340
341 return vec;
342}
343
344// --------------------------------------------------------------------------
345//
346//! Returns a list of all services available in the network.
347//! Depending on iscmd either only services or only commands are returned.
348//!
349//! @param iscmd
350//! true if only commands should be returned, false for services
351//!
352//! @returns
353//! a vector<string> which contains all the service or command names in
354//! the network. The names returned are always SERVER/SERVICE
355//
356vector<string> DimServiceInfoList::GetServiceList(bool iscmd) const
357{
358 vector<string> vec;
359 for (ServiceList::const_iterator m=fServiceList.begin(); m!=fServiceList.end(); m++)
360 {
361 const TypeList &list = m->second.first;
362
363 for (TypeList::const_iterator i=list.begin(); i!=list.end(); i++)
364 if (i->second.second==iscmd)
365 vec.push_back(m->first+'/'+i->first);
366 }
367
368 return vec;
369}
370
371// --------------------------------------------------------------------------
372//
373//! Returns a list of all descriptions for the given service on the
374//! given server. Service in this context can also be a command.
375//!
376//! @param server
377//! Server name to look for
378//!
379//! @param service
380//! Service/command name to look for
381//!
382//! @returns
383//! a vector<Description> which contains all argument descriptions for
384//! the given service or command. The first entry contains the name
385//! and the general description for the given service. If the server
386//! or service was not found an empty vector is returned.
387//
388std::vector<Description> DimServiceInfoList::GetDescription(const std::string &server, const std::string &service) const
389{
390 const ServiceList::const_iterator s = fServiceList.find(server);
391 if (s==fServiceList.end())
392 return vector<Description>();
393
394 const DescriptionList &descs = s->second.second;
395
396 const DescriptionList::const_iterator d = descs.find(service);
397 if (d==descs.end())
398 return vector<Description>();
399
400 vector<Description> vec;
401 vec.push_back(Description(service, d->second.first));
402 vec.insert(vec.end(), d->second.second.begin(), d->second.second.end());
403
404 return vec;
405}
406
407// --------------------------------------------------------------------------
408//
409//! Returns a list of all states associated with the given server.
410//!
411//! @param server
412//! Server name to look for
413//!
414//! @returns
415//! a vector<State> which contains all state descriptions for
416//! the given server. If the server or service was not found an
417//! empty vector is returned.
418//
419vector<State> DimServiceInfoList::GetStates(const std::string &server) const
420{
421 const ServiceList::const_iterator s = fServiceList.find(server);
422 if (s==fServiceList.end())
423 return vector<State>();
424
425 return s->second.third;
426}
427
428// --------------------------------------------------------------------------
429//
430//! Returns the Description of the state as defined by the arguments.
431//! given server. Service in this context can also be a command.
432//!
433//! @param server
434//! Server name to look for
435//!
436//! @param state
437//! The state index to look for (e.g. 1)
438//!
439//! @returns
440//! The State object containing the description. If the server was
441//! not found the State object will contain the index -3, if the
442//! state was not found -2.
443//
444State DimServiceInfoList::GetState(const std::string &server, int state) const
445{
446 const ServiceList::const_iterator s = fServiceList.find(server);
447 if (s==fServiceList.end())
448 {
449 stringstream str;
450 str << "DimServiceInfoList::GetState: Searching for state #" << state << " server " << server << " not found.";
451 return State(-3, "Server not found", str.str());
452 }
453
454 const std::vector<State> &v = s->second.third;
455
456 for (vector<State>::const_iterator i=v.begin(); i!=v.end(); i++)
457 if (i->index==state)
458 return *i;
459
460 stringstream str;
461 str << "DimServiceInfoList::GetState: State #" << state << " not found on server " << server << ".";
462 return State(-2, "State not found", str.str());
463}
464
465// --------------------------------------------------------------------------
466//
467//! Returns whether the given service on the given server is a command
468//! or not.
469//!
470//! @param server
471//! Server name to look for
472//!
473//! @param service
474//! The service name to look for
475//!
476//! @returns
477//! 1 if it is a command, 0 if it is a service, -1 if the service
478//! was not found on the server, -2 if the server was not found.
479//
480int DimServiceInfoList::IsCommand(const std::string &server, const std::string &service) const
481{
482 const ServiceList::const_iterator s = fServiceList.find(server);
483 if (s==fServiceList.end())
484 return -2;
485
486 const TypeList &list = s->second.first;
487
488 const TypeList::const_iterator t = list.find(service);
489 if (t==list.end())
490 return -1;
491
492 return t->second.second;
493}
494
495
496// --------------------------------------------------------------------------
497//
498//! Print the full available documentation (description) of all available
499//! services or comments to the the given stream.
500//!
501//! @param out
502//! ostream to which the output is send.
503//!
504//! @param iscmd
505//! true if all commands should be printed, false for services.
506//!
507//! @param serv
508//! if a server is given, only the information for this server is printed
509//!
510//! @param service
511//! if a service is given, only information for this service is printed
512//!
513//! @returns
514//! the number of descriptions found
515//
516int DimServiceInfoList::PrintDescription(std::ostream &out, bool iscmd, const string &serv, const string &service) const
517{
518 int rc = 0;
519 for (ServiceList::const_iterator i=fServiceList.begin(); i!=fServiceList.end(); i++)
520 {
521 const string &server = i->first;
522
523 if (!serv.empty() && server!=serv)
524 continue;
525
526 out << kRed << "----- " << server << " -----" << endl;
527
528 const TypeList &types = i->second.first;
529 const DescriptionList &descs = i->second.second;
530
531 for (TypeList::const_iterator t=types.begin(); t!=types.end(); t++)
532 {
533 if (!service.empty() && t->first!=service)
534 continue;
535
536 if (t->second.second!=iscmd)
537 continue;
538
539 rc++;
540
541 out << " " << t->first;
542
543 // Check t->second->first for command or service
544 const string fmt = t->second.first;
545 if (!fmt.empty())
546 out << '[' << fmt << ']';
547
548 const DescriptionList::const_iterator d = descs.find(t->first);
549 if (d==descs.end())
550 {
551 out << endl;
552 continue;
553 }
554
555 const string comment = d->second.first;
556 const vector<Description> &v = d->second.second;
557
558 for (vector<Description>::const_iterator j=v.begin(); j!=v.end(); j++)
559 out << " <" << j->name << ">";
560 out << endl;
561
562 if (!comment.empty())
563 out << " " << comment << endl;
564
565 for (vector<Description>::const_iterator j=v.begin(); j!=v.end(); j++)
566 {
567 out << " " << kGreen << j->name;
568 if (!j->comment.empty())
569 out << kReset << ": " << kBlue << j->comment;
570 if (!j->unit.empty())
571 out << kYellow << " [" << j->unit << "]";
572 out << endl;
573 }
574 }
575 out << endl;
576 }
577
578 return rc;
579}
580
581// --------------------------------------------------------------------------
582//
583//! Print the full list of stated for the given server.
584//!
585//! @param out
586//! ostream to which the output is send.
587//!
588//! @param serv
589//! if a server is given, only the information for this server is printed
590//!
591//! @returns
592//! the number of states found
593//
594int DimServiceInfoList::PrintStates(std::ostream &out, const string &serv) const
595{
596 int rc = 0;
597 for (ServiceList::const_iterator i=fServiceList.begin(); i!=fServiceList.end(); i++)
598 {
599 const string &server = i->first;
600
601 if (!serv.empty() && server!=serv)
602 continue;
603
604 out << kRed << "----- " << server << " -----" << endl;
605
606 const vector<State> &v = i->second.third;
607
608 if (v.size()==0)
609 out << " <no states>" << endl;
610 else
611 rc++;
612
613 for (vector<State>::const_iterator s=v.begin(); s!=v.end(); s++)
614 {
615 out << kBold << setw(5) << s->index << kReset << ": ";
616 out << kYellow << s->name;
617 out << kBlue << " (" << s->comment << ")" << endl;
618 }
619 out << endl;
620 }
621
622 return rc;
623}
624
625
626// --------------------------------------------------------------------------
627//
628//! Tries to send a dim command according to the arguments.
629//! The command given is evaluated according to the available format string.
630//!
631//! @param server
632//! The name of the server to which the command should be send, e.g. DRIVE
633//!
634//! @param str
635//! Command and data, eg "TRACK 12.5 13.8"
636//!
637//! @param lout
638//! the ostream to which errors and debug output is redirected
639//!
640//! @throws
641//! runtime_error if the server or command was not found, or if the
642//! format associated with the command could not be properly parsed,
643//! or if the command could not successfully be emitted.
644//!
645void DimServiceInfoList::SendDimCommand(const string &server, string str, ostream &lout) const
646{
647 str = Tools::Trim(str);
648
649 // Find the delimiter between the command name and the data
650 size_t p0 = str.find_first_of(' ');
651 if (p0==string::npos)
652 p0 = str.length();
653
654 // Get just the command name separated from the data
655 const string name = str.substr(0, p0);
656
657 // Compile the command which will be sent to the state-machine
658 const string cmd = server + '/' + name;
659
660 const ServiceList::const_iterator m = fServiceList.find(server);
661 if (m==fServiceList.end())
662 throw runtime_error("Unkown server '"+server+"'");
663
664 const TypeList &services = m->second.first;
665
666 const TypeList::const_iterator t = services.find(name);
667 if (t==services.end())
668 throw runtime_error("Command '"+name+"' not known on server '"+server+"'");
669
670 if (!t->second.second)
671 throw runtime_error("'"+server+"/"+name+" not a command.");
672
673 // Get the format of the event data
674 const string fmt = t->second.first;
675
676 // Convert the user entered data according to the format string
677 // into a data block which will be attached to the event
678 const Converter conv(lout, fmt, false);
679 if (!conv)
680 throw runtime_error("Couldn't properly parse the format... ignored.");
681
682 lout << kBlue << cmd;
683 const vector<char> v = conv.GetVector(str.substr(p0));
684 lout << endl;
685
686 const int rc = DimClient::sendCommand(cmd.c_str(), (void*)v.data(), v.size());
687 if (!rc)
688 throw runtime_error("ERROR - Sending command "+cmd+" failed.");
689}
690
691// --------------------------------------------------------------------------
692//
693//! Catches the runtime_erros thrown by
694//! SendDimCommand(const string &, string, ostream &)
695//! and redirects the error message to the output stream.
696//!
697//! @param lout
698//! the ostream to which errors and debug output is redirected
699//!
700//! @param server
701//! The name of the server to which the command should be send, e.g. DRIVE
702//!
703//! @param str
704//! Command and data, eg "TRACK 12.5 13.8"
705//!
706//! @returns
707//! true if SendDimComment didn't throw an exception, false otherwise
708//!
709bool DimServiceInfoList::SendDimCommand(ostream &lout, const string &server, const string &str) const
710{
711 try
712 {
713 SendDimCommand(server, str, lout);
714 lout << kGreen << "Command emitted successfully to " << server << "." << endl;
715 return true;
716 }
717 catch (const runtime_error &e)
718 {
719 lout << kRed << e.what() << endl;
720 return false;
721 }
722}
723
724// --------------------------------------------------------------------------
725//
726//! Calls SendDimCommand(const string &, string, ostream &) and dumps
727//! the output.
728//!
729//! @param server
730//! The name of the server to which the command should be send, e.g. DRIVE
731//!
732//! @param str
733//! Command and data, eg "TRACK 12.5 13.8"
734//!
735//! @throws
736//! see SendDimCommand(const string &, string, ostream &)
737//
738void DimServiceInfoList::SendDimCommand(const std::string &server, const std::string &str) const
739{
740 ostringstream dummy;
741 SendDimCommand(server, str, dummy);
742}
Note: See TracBrowser for help on using the repository browser.