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

Last change on this file since 290 was 290, checked in by Marek Kurdej, 10 years ago

Some clean-up.

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