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

Last change on this file since 17873 was 17191, checked in by tbretz, 11 years ago
There is no need to wiat for the commad to be sent.
File size: 23.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//! @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 fList->RemoveAllServices(s);
124
125 const ServiceInfoList::iterator v = fServiceInfoList.find(s);
126 if (v==fServiceInfoList.end())
127 return;
128 /*
129 {
130 stringstream err;
131 err << "DimServiceInfoList: Server '" << s << "' not in list as it ought to be.";
132 throw runtime_error(err.str());
133 }*/
134
135 // Remove the server from the server list
136 delete v->second[0];
137 delete v->second[1];
138 delete v->second[2];
139
140 fServiceInfoList.erase(v);
141 fServiceList.erase(s);
142}
143
144// --------------------------------------------------------------------------
145//
146//! Removes the service subscription for SERVICE_LIST, SERVICE_DESC and
147//! STATE_LIST for all servers, as well as all stored informations.
148//! Don't forget to call this function if it is overwritten in a derived
149//! class.
150//!
151void DimServiceInfoList::RemoveAllServers()
152{
153 while (!fServiceInfoList.empty())
154 RemoveServer(fServiceInfoList.begin()->first);
155}
156
157
158// --------------------------------------------------------------------------
159//
160//! This function processes the update of the SERVICE_LIST, SERVICE_DESC,
161//! and STATE_LIST updates.
162//!
163//! Whenever a service is added or removed or all services of a server are
164//! removed (the list is newly sent completely) the virtual functions
165//! AddService(), RemoveService() and RemoveAllServices() aee called.
166//!
167//! If a new description or a new state is added, the virtual functions
168//! AddDescription() and AddStates() respectively are called.
169//
170void DimServiceInfoList::infoHandler()
171{
172 // Get the name of the service
173 const string svc = getInfo()->getName();
174
175 // Get the server name from the service name
176 const string server = svc.substr(0, svc.find_first_of('/'));
177 const string service = svc.substr(svc.find_first_of('/')+1);
178
179 if (service=="SERVICE_LIST")
180 {
181 // For easy and fast access get the corresponding reference
182 TypeList &list = fServiceList[server].first;
183
184 const string str = getInfo()->getString();
185
186 // WHAT's THIS???
187 if (str.length()==0)
188 return;
189
190 // Initialize the entry with an empty list
191 if (str[0]!='+' && str[0]!='-')
192 {
193 fList->RemoveAllServices(server);
194 list.clear();
195 }
196
197 string buffer;
198
199 // Tokenize the stream into lines
200 stringstream stream(str);
201 while (getline(stream, buffer, '\n'))
202 {
203 if (buffer.empty())
204 continue;
205
206 // Get the type and compare it with fType
207 const string type = buffer.substr(buffer.find_last_of('|')+1);
208 if (type=="RPC")
209 continue;
210
211 /*
212 const bool iscmd = type=="CMD";
213 if (type!=fType && fType!="*")
214 continue;
215 */
216
217 // Get format, name and command name
218 const string fmt = buffer.substr(buffer.find_first_of('|')+1, buffer.find_last_of('|')-buffer.find_first_of('|')-1);
219 const string name = buffer.substr(buffer.find_first_of('/')+1, buffer.find_first_of('|')-buffer.find_first_of('/')-1);
220 //const string cmd = buffer.substr(0, buffer.find_first_of('|'));
221
222 const bool iscmd = type=="CMD";
223
224 // FIXME: Do we need to check that the buffer starts with SERVER ?
225
226 if (buffer[0]=='-')
227 {
228 // Check if this server is not found in the list.
229 // This should never happen if Dim works reliable
230 const TypeList::iterator v = list.find(name);
231 /*
232 if (v==list.end())
233 {
234 stringstream err;
235 err << "DimServiceInfoList: Service '" << server << "/" << name << "' not in list as it ought to be.";
236 // Seems to happen why more than one client is subscribed
237 // and e.g. the datalogger is immediately quit
238 throw runtime_error(err.str());
239 }*/
240
241 fList->RemoveService(server, name, iscmd);
242 if (v!=list.end())
243 list.erase(v);
244
245 continue;
246 }
247
248 if (buffer[0]=='+')
249 {
250 // Check if this server is not found in the list.
251 // This should never happen if Dim works reliable
252 const TypeList::iterator v = list.find(name);
253 if (v!=list.end())
254 {
255 stringstream err;
256 err << "DimServiceInfoList: Service '" << server << "/" << name << "' already in list not as it ought to be.";
257 throw runtime_error(err.str());
258 }
259
260 list[name] = make_pair(fmt, iscmd);
261 fList->AddService(server, name, fmt, iscmd);
262
263 continue;
264 }
265
266 // Add name the the list
267 list[name] = make_pair(fmt, iscmd);
268 fList->AddService(server, name, fmt, iscmd);
269 }
270
271 return;
272 }
273
274 if (service=="SERVICE_DESC")
275 {
276 // For easy and fast access get the corresponding reference
277 DescriptionList &list = fServiceList[server].second;
278
279 list.clear();
280
281 string buffer;
282
283 stringstream stream(getInfo()->getString());
284 while (getline(stream, buffer, '\n'))
285 {
286 if (buffer.empty())
287 continue;
288
289 const vector<Description> v = Description::SplitDescription(buffer);
290
291 const string name = v[0].name.substr(v[0].name.find_first_of('/')+1);
292 const string comment = v[0].comment;
293
294 list[name] = make_pair(comment, vector<Description>(v.begin()+1, v.end()));
295
296 fList->AddDescription(server, name, v);
297 }
298
299 return;
300 }
301
302 if (service=="STATE_LIST")
303 {
304 vector<State> &vec = fServiceList[server].third;
305 vec = State::SplitStates(getInfo()->getString());
306 fList->AddStates(server, vec);
307
308 return;
309 }
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.empty())
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 // Avoid compiler warning of unused parameter
677 lout << flush;
678
679 // Convert the user entered data according to the format string
680 // into a data block which will be attached to the event
681#ifndef DEBUG
682 ostringstream sout;
683 const Converter conv(sout, fmt, false);
684#else
685 const Converter conv(lout, fmt, false);
686#endif
687 if (!conv)
688 throw runtime_error("Couldn't properly parse the format... ignored.");
689
690#ifdef DEBUG
691 lout << kBlue << cmd;
692#endif
693 const vector<char> v = conv.GetVector(str.substr(p0));
694#ifdef DEBUG
695 lout << kBlue << " [" << v.size() << "]" << endl;
696#endif
697
698 DimClient::sendCommandNB(cmd.c_str(), (void*)v.data(), v.size());
699}
700
701// --------------------------------------------------------------------------
702//
703//! Catches the runtime_erros thrown by
704//! SendDimCommand(const string &, string, ostream &)
705//! and redirects the error message to the output stream.
706//!
707//! @param lout
708//! the ostream to which errors and debug output is redirected
709//!
710//! @param server
711//! The name of the server to which the command should be send, e.g. DRIVE
712//!
713//! @param str
714//! Command and data, eg "TRACK 12.5 13.8"
715//!
716//! @returns
717//! true if SendDimComment didn't throw an exception, false otherwise
718//!
719bool DimServiceInfoList::SendDimCommand(ostream &lout, const string &server, const string &str) const
720{
721 try
722 {
723 SendDimCommand(server, str, lout);
724 //lout << kGreen << "Command emitted successfully to " << server << "." << endl;
725 return true;
726 }
727 catch (const runtime_error &e)
728 {
729 lout << kRed << e.what() << endl;
730 return false;
731 }
732}
733
734// --------------------------------------------------------------------------
735//
736//! Calls SendDimCommand(const string &, string, ostream &) and dumps
737//! the output.
738//!
739//! @param server
740//! The name of the server to which the command should be send, e.g. DRIVE
741//!
742//! @param str
743//! Command and data, eg "TRACK 12.5 13.8"
744//!
745//! @throws
746//! see SendDimCommand(const string &, string, ostream &)
747//
748void DimServiceInfoList::SendDimCommand(const std::string &server, const std::string &str) const
749{
750 ostringstream dummy;
751 SendDimCommand(server, str, dummy);
752}
753
754DimServiceInfoList::DimServiceInfoList(DimServiceInfoListImp *list) : DimServerList(list), fList(list) { }
Note: See TracBrowser for help on using the repository browser.