source: pacpusframework/trunk/src/PacpusLib/ComponentBase.cpp@ 289

Last change on this file since 289 was 288, checked in by Marek Kurdej, 11 years ago

Using boost::shared_ptr for storing components.

  • Property svn:executable set to *
File size: 16.2 KB
Line 
1// %pacpus:license{
2// This file is part of the PACPUS framework distributed under the
3// CECILL-C License, Version 1.0.
4// %pacpus:license}
5/// @version $Id: ComponentBase.cpp 76 2013-01-10 17:05:10Z kurdejma $
6
7#include <Pacpus/kernel/ComponentBase.h>
8
9#include <Pacpus/kernel/ComponentManager.h>
10#include <Pacpus/kernel/Log.h>
11#include <Pacpus/kernel/PacpusException.h>
12
13#include <boost/bind/bind.hpp>
14#include <boost/exception/detail/exception_ptr.hpp>
15#include <boost/exception/diagnostic_information.hpp>
16#include <boost/exception/exception.hpp>
17#include <boost/program_options/parsers.hpp>
18#include <boost/program_options/variables_map.hpp>
19//#include <boost/thread/thread.hpp>
20#include <ostream>
21#include <string>
22#include <vector>
23
24namespace po = boost::program_options;
25using namespace pacpus;
26using namespace std;
27
28vector<string> convertAttributesToArgumentVector(const QDomNamedNodeMap & attributes);
29
30namespace std
31{
32
33template <typename _Elem, typename _Traits>
34std::basic_ostream<_Elem, _Traits> & operator<<(std::basic_ostream<_Elem, _Traits> & os, const boost::program_options::variables_map & vm)
35{
36 for (po::variables_map::const_iterator i = vm.begin(); i != vm.end(); ++i) {
37 const po::variable_value & v = i->second;
38 if (v.empty()) {
39 continue;
40 }
41 const type_info & type = v.value().type();
42 if (type == typeid(string)) {
43 const string & val = v.as<string>();
44 os << i->first << "=" << val;
45 } else if (type == typeid(long)) {
46 int val = v.as<long>();
47 os << i->first << "=" << val;
48 } else if (type == typeid(int)) {
49 int val = v.as<int>();
50 os << i->first << "=" << val;
51 } else if (type == typeid(unsigned long)) {
52 int val = v.as<unsigned long>();
53 os << i->first << "=" << val;
54 } else if (type == typeid(unsigned int)) {
55 int val = v.as<unsigned int>();
56 os << i->first << "=" << val;
57 } else if (type == typeid(double)) {
58 int val = v.as<double>();
59 os << i->first << "=" << val;
60 } else if (type == typeid(float)) {
61 int val = v.as<float>();
62 os << i->first << "=" << val;
63 } else if (type == typeid(bool)) {
64 int val = v.as<bool>();
65 os << i->first << "=" << val;
66 } else {
67 // unknown value type
68 os << i->first;
69 }
70 os << "\n";
71 }
72 return os;
73}
74
75} // namespace std
76
77DECLARE_STATIC_LOGGER("pacpus.core.ComponentBase");
78
79ComponentBase::ComponentBase(QString const& componentName)
80 : m_componentName(componentName)
81 , m_isActive(false)
82 , mIsRecording(true)
83 , m_manager(NULL)
84 , m_ui(NULL)
85 , m_componentState(NOT_MONITORED)
86 , mOptionsDescription("Component parameters")
87{
88 LOG_TRACE("constructor");
89 // Get a pointer on the instance of ComponentManager.
90 m_manager = ComponentManager::getInstance();
91 LOG_INFO("component " << getName() << " was created");
92
93 addParameters()
94 ("name", po::value<string>(&mName)->required(), "component name")
95 ("type", po::value<string>(&mTypeName)->required(), "component type")
96 ("ui", po::value<bool>(&mHasGui)->default_value(false), "whether to show GUI")
97 ("verbose", po::value<bool>(&mVerbose)->default_value(false), "set output verbose")
98 ("verbosity-level", po::value<int>(&mVerbosityLevel)->default_value(0), "set verbosity level")
99 ("recording", po::value<bool>(&mIsRecording)->default_value(false), "whether to record data")
100 ;
101}
102
103ComponentBase::~ComponentBase()
104{
105 LOG_TRACE("destructor");
106}
107
108bool ComponentBase::isActive() const
109{
110 return m_isActive;
111}
112
113void ComponentBase::setActive(bool isActive)
114{
115 m_isActive = isActive;
116}
117
118bool ComponentBase::isRecording() const
119{
120 return mIsRecording;
121}
122
123void ComponentBase::setRecording(bool isRecording)
124{
125 mIsRecording = isRecording;
126}
127
128const XmlComponentConfig ComponentBase::xmlParameters() const
129{
130 return param;
131}
132
133//void ComponentBase::startComponentWithException(boost::exception_ptr& error)
134//{
135// try {
136// startActivity();
137// error = boost::exception_ptr();
138// } catch (...) {
139// error = boost::current_exception();
140// }
141//}
142//
143//void ComponentBase::startComponentInThread()
144//{
145// boost::exception_ptr error;
146// boost::thread t(
147// boost::bind(
148// &ComponentBase::startComponentWithException,
149// this,
150// boost::ref(error)
151// )
152// );
153// t.join();
154// if (error) {
155// try {
156// boost::rethrow_exception(error);
157// } catch (boost::exception& e) {
158// LOG_FATAL("[" << getName() << "]" << "\tboost::exception thrown: " << boost::diagnostic_information(e));
159// //throw;
160// }
161// }
162//}
163
164int ComponentBase::startComponent()
165{
166 if (isActive()) {
167 LOG_DEBUG("component already started, cannot (re-)start");
168 return false;
169 }
170
171 setActive(true);
172 //boost::thread worker(&ComponentBase::startComponentInThread, this);
173 startActivity();
174 //moveToThread(&mThread);
175 //mThread.start();
176 return true;
177}
178
179int ComponentBase::stopComponent()
180{
181 if (!isActive()) {
182 LOG_DEBUG("component already stopped, cannot (re-)stop");
183 return false;
184 }
185
186 setActive(false);
187 stopActivity();
188 //QMetaObject::invokeMethod(&mThread, "quit");
189 return true;
190}
191
192void ComponentBase::setState(const COMPONENT_STATE state)
193{
194 m_componentState = state;
195}
196
197// FIXME: this should be const.
198ComponentBase::COMPONENT_STATE ComponentBase::getState()
199{
200 COMPONENT_STATE state = m_componentState;
201 if (ComponentBase::NOT_MONITORED != m_componentState) {
202 m_componentState = ComponentBase::MONITOR_NOK;
203 }
204 return state;
205}
206
207ComponentBase::COMPONENT_CONFIGURATION ComponentBase::configurationState() const
208{
209 return m_configurationState;
210}
211
212void ComponentBase::setConfigurationState(COMPONENT_CONFIGURATION state)
213{
214 m_configurationState = state;
215}
216
217bool ComponentBase::isConfigured() const
218{
219 return (m_configurationState == CONFIGURED_OK);
220}
221
222QString ComponentBase::getName() const
223{
224 return m_componentName;
225}
226
227ComponentBase::InputsMap & ComponentBase::inputs()
228{
229 return m_inputs;
230}
231
232const ComponentBase::InputsMap & ComponentBase::inputs() const
233{
234 return m_inputs;
235}
236
237ComponentBase::OutputsMap & ComponentBase::outputs()
238{
239 return m_outputs;
240}
241
242const ComponentBase::OutputsMap & ComponentBase::outputs() const
243{
244 return m_outputs;
245}
246
247InputInterfaceBase * ComponentBase::getInput(QString inputName) const
248{
249 if (inputs().contains(inputName)) {
250 return inputs()[inputName];
251 }
252 LOG_WARN("Component " << getName() << " does not contain input " << inputName);
253 return NULL;
254}
255
256OutputInterfaceBase * ComponentBase::getOutput(QString outputName) const
257{
258 if (outputs().contains(outputName)) {
259 return outputs()[outputName];
260 }
261 LOG_WARN("Component " << getName() << " does not contain output " << outputName);
262 return NULL;
263}
264
265bool ComponentBase::hasGui() const
266{
267 return mHasGui;
268}
269
270bool ComponentBase::isOutputVerbose() const
271{
272 return mVerbose || (getVerbosityLevel() > 0);
273}
274
275int ComponentBase::getVerbosityLevel() const
276{
277 return mVerbosityLevel;
278}
279
280po::options_description_easy_init ComponentBase::addParameters()
281{
282 return mOptionsDescription.add_options();
283}
284
285class DomElementParser
286 : boost::noncopyable
287{
288public:
289 DomElementParser(QDomElement const& args);
290
291 /** Sets options descriptions to use. */
292 DomElementParser& options(const boost::program_options::options_description& desc);
293
294 /** Parses the options and returns the result of parsing.
295 Throws on error.
296 */
297 boost::program_options::basic_parsed_options<char> run();
298
299 /** Specifies that unregistered options are allowed and should
300 be passed though. For each command like token that looks
301 like an option but does not contain a recognized name, an
302 instance of basic_option<charT> will be added to result,
303 with 'unrecognized' field set to 'true'. It's possible to
304 collect all unrecognized options with the 'collect_unrecognized'
305 funciton.
306 */
307 DomElementParser& allow_unregistered();
308
309private:
310 boost::program_options::basic_parsed_options<char> parseDomElement(
311 const QDomElement& dom_element,
312 const boost::program_options::options_description& desc,
313 bool allow_unregistered = false);
314
315private:
316 boost::program_options::options_description const* m_desc;
317 QDomElement const& m_dom_element;
318 bool m_allow_unregistered;
319};
320
321DomElementParser::DomElementParser(const QDomElement& dom_element)
322 : m_dom_element(dom_element)
323 , m_allow_unregistered(false)
324{
325}
326
327DomElementParser& DomElementParser::options(const boost::program_options::options_description& desc)
328{
329 m_desc = &desc;
330 return *this;
331}
332
333DomElementParser& DomElementParser::allow_unregistered()
334{
335 m_allow_unregistered = true;
336 return *this;
337}
338
339#include <boost/iterator/iterator_facade.hpp>
340#include <boost/program_options/errors.hpp>
341
342namespace detail
343{
344template <typename charT>
345class basic_dom_element_iterator
346 : public boost::iterator_facade<
347 basic_dom_element_iterator<charT>
348 , const boost::program_options::option
349 , boost::random_access_traversal_tag
350 >
351{
352public:
353 typedef boost::program_options::option ValueType;
354 typedef basic_dom_element_iterator<charT> self_type;
355 typedef typename self_type::iterator_facade_ base_type;
356 typedef typename self_type::difference_type difference_type;
357
358 basic_dom_element_iterator<charT>(QDomElement const& dom_element)
359 : m_dom_element(NULL)
360 , m_at_eof(true)
361 , m_i(dom_element.attributes().size())
362 {
363 }
364
365 basic_dom_element_iterator<charT>(QDomElement const& dom_element,
366 std::set<std::string> const& allowed_options,
367 bool allow_unregistered = false)
368 : m_dom_element(&dom_element)
369 , m_allowed_options(allowed_options)
370 , m_allow_unregistered(allow_unregistered)
371 , m_i(0)
372 {
373 m_attrs = m_dom_element->attributes();
374 m_at_eof = !(m_i < m_attrs.size());
375 if (!m_at_eof) {
376 get();
377 }
378 }
379
380private:
381 friend class ::boost::iterator_core_access;
382
383 bool equal(const basic_dom_element_iterator<charT>& other) const
384 {
385 if (m_at_eof && other.m_at_eof) {
386 return true;
387 }
388 return false;
389 }
390
391 void increment()
392 {
393 ++m_i;
394 m_at_eof = !(m_i < m_attrs.size());
395 if (!m_at_eof) {
396 get();
397 }
398 }
399
400 const ValueType& dereference() const
401 {
402 return m_value;
403 }
404
405 void advance(size_t n)
406 {
407 m_i += n;
408 m_at_eof = !(m_i < m_attrs.size());
409 if (!m_at_eof) {
410 get();
411 }
412 }
413
414 difference_type distance_to(const basic_dom_element_iterator<charT>& other) const
415 {
416 return other.m_i - this->m_i;
417 }
418
419private:
420 ValueType& value()
421 {
422 return m_value;
423 }
424
425 void get()
426 {
427 using namespace boost::program_options;
428
429 QDomNode node = m_attrs.item(m_i);
430 QDomAttr attr = node.toAttr();
431
432 string name = attr.name().toStdString();
433 string value = attr.value().toStdString();
434
435 bool registered = allowed_option(name);
436 if (!registered && !m_allow_unregistered) {
437 boost::throw_exception(unknown_option(name));
438 }
439
440 this->value().string_key = name;
441 this->value().value.clear();
442 this->value().value.push_back(value);
443 this->value().unregistered = !registered;
444 //this->value().original_tokens.push_back(name);
445 //this->value().original_tokens.push_back(value);
446 }
447
448 bool allowed_option(const std::string& s) const
449 {
450 set<string>::const_iterator it = m_allowed_options.find(s);
451 if (it != m_allowed_options.end()) {
452 return true;
453 }
454 return false;
455 }
456
457private:
458 const QDomElement* m_dom_element;
459 std::set<std::string> m_allowed_options;
460 bool m_allow_unregistered;
461
462 QDomNamedNodeMap m_attrs;
463 int m_i;
464 bool m_at_eof;
465 ValueType m_value;
466};
467
468typedef basic_dom_element_iterator<char> dom_element_iterator;
469typedef basic_dom_element_iterator<wchar_t> wdom_element_iterator;
470
471}
472
473boost::program_options::basic_parsed_options<char> DomElementParser::run()
474{
475 assert(m_desc);
476 return parseDomElement(m_dom_element, *m_desc, m_allow_unregistered);
477}
478
479boost::program_options::basic_parsed_options<char> DomElementParser::parseDomElement(
480 const QDomElement& dom_element,
481 const boost::program_options::options_description& desc,
482 bool allow_unregistered)
483{
484 // TODO: use XPath paths
485
486 typedef char charT;
487
488 using boost::program_options::error;
489 using boost::shared_ptr;
490 using namespace boost::program_options;
491 using ::detail::basic_dom_element_iterator;
492
493 set<string> allowed_options;
494
495 const vector<shared_ptr<option_description> >& options = desc.options();
496 for (unsigned i = 0; i < options.size(); ++i) {
497 const option_description& d = *options[i];
498
499 if (d.long_name().empty()) {
500 boost::throw_exception(
501 error("abbreviated option names are not permitted when parsing DOM elements"));
502 }
503
504 allowed_options.insert(d.long_name());
505 }
506
507 // Parser returns char strings
508 parsed_options result(&desc);
509 copy(basic_dom_element_iterator<charT>(dom_element, allowed_options, allow_unregistered),
510 basic_dom_element_iterator<charT>(dom_element),
511 back_inserter(result.options));
512
513 // Convert char strings into desired type.
514 return basic_parsed_options<charT>(result);
515}
516
517DomElementParser parseDomElement();
518
519/** Creates instance of 'command_line_parser', passes parameters to it,
520 and returns the result of calling the 'run' method.
521 */
522boost::program_options::basic_parsed_options<char>
523parseDomElement(QDomElement const& domElement, const boost::program_options::options_description&);
524
525boost::program_options::basic_parsed_options<char>
526parseDomElement(QDomElement const& domElement, const boost::program_options::options_description& desc)
527{
528 return DomElementParser(domElement)
529 .options(desc)
530 .run();
531}
532
533void ComponentBase::parseParameters(XmlComponentConfig const& cfg)
534{
535 LOG_INFO("Parsing parameters...");
536 LOG_INFO(mOptionsDescription);
537
538 po::variables_map vm;
539 try {
540 po::store(
541 DomElementParser(cfg.getDomElement())
542 .options(mOptionsDescription)
543 .allow_unregistered() // FIXME: temporary only, at term all the components specify all parameters
544 .run()
545 , vm);
546 po::notify(vm);
547 } catch (po::error& e) {
548 LOG_WARN(e.what());
549 BOOST_THROW_EXCEPTION(PacpusException(e.what()));
550 }
551
552 LOG_INFO("Parsed parameter values:\n" << vm);
553}
554
555vector<string> convertAttributesToArgumentVector(const QDomNamedNodeMap & attributes)
556{
557 vector<string> xargs;
558 xargs.reserve(attributes.size());
559
560 for (int i = 0; i < attributes.size(); ++i) {
561 QDomAttr parameter = attributes.item(i).toAttr();
562 if (parameter.isNull()) {
563 LOG_WARN("node is not a parameter");
564 continue;
565 }
566
567 QString arg = QString("--") + parameter.name() + "=";
568
569 bool shouldAddQuotes = parameter.value().contains(' ');
570 if (shouldAddQuotes) {
571 arg += '\"';
572 arg += parameter.value();
573 arg += '\"';
574 } else {
575 arg += parameter.value();
576 }
577
578 LOG_DEBUG("parameter: " << arg);
579 xargs.push_back(arg.toStdString());
580 }
581
582 return xargs;
583}
Note: See TracBrowser for help on using the repository browser.