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

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