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

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

Transporting and catching exceptions in threads.

  • Property svn:executable set to *
File size: 16.1 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(const QString & 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
133void 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
143void 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 //boost::thread worker(&ComponentBase::startActivity, this);
174 //startActivity();
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
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];
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];
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{
286public:
287 DomElementParser(QDomElement const& args);
288
289 /** Sets options descriptions to use. */
290 DomElementParser& options(const boost::program_options::options_description& desc);
291
292 /** Parses the options and returns the result of parsing.
293 Throws on error.
294 */
295 boost::program_options::basic_parsed_options<char> run();
296
297 /** Specifies that unregistered options are allowed and should
298 be passed though. For each command like token that looks
299 like an option but does not contain a recognized name, an
300 instance of basic_option<charT> will be added to result,
301 with 'unrecognized' field set to 'true'. It's possible to
302 collect all unrecognized options with the 'collect_unrecognized'
303 funciton.
304 */
305 DomElementParser& allow_unregistered();
306
307private:
308 boost::program_options::basic_parsed_options<char> parseDomElement(
309 const QDomElement& dom_element,
310 const boost::program_options::options_description& desc,
311 bool allow_unregistered = false);
312
313private:
314 const boost::program_options::options_description* m_desc;
315 const QDomElement& m_dom_element;
316 bool m_allow_unregistered;
317};
318
319DomElementParser::DomElementParser(const QDomElement& dom_element)
320 : m_dom_element(dom_element)
321 , m_allow_unregistered(false)
322{
323}
324
325DomElementParser& DomElementParser::options(const boost::program_options::options_description& desc)
326{
327 m_desc = &desc;
328 return *this;
329}
330
331DomElementParser& DomElementParser::allow_unregistered()
332{
333 m_allow_unregistered = true;
334 return *this;
335}
336
337#include <boost/iterator/iterator_facade.hpp>
338#include <boost/program_options/errors.hpp>
339
340namespace detail
341{
342template <typename charT>
343class basic_dom_element_iterator
344 : public boost::iterator_facade<
345 basic_dom_element_iterator<charT>
346 , const boost::program_options::option
347 , boost::random_access_traversal_tag
348 >
349{
350public:
351 typedef boost::program_options::option ValueType;
352 typedef basic_dom_element_iterator<charT> self_type;
353 typedef typename self_type::iterator_facade_ base_type;
354 typedef typename self_type::difference_type difference_type;
355
356 basic_dom_element_iterator<charT>(QDomElement const& dom_element)
357 : m_dom_element(NULL)
358 , m_at_eof(true)
359 , m_i(dom_element.attributes().size())
360 {
361 }
362
363 basic_dom_element_iterator<charT>(QDomElement const& dom_element,
364 std::set<std::string> const& allowed_options,
365 bool allow_unregistered = false)
366 : m_dom_element(&dom_element)
367 , m_allowed_options(allowed_options)
368 , m_allow_unregistered(allow_unregistered)
369 , m_i(0)
370 {
371 m_attrs = m_dom_element->attributes();
372 m_at_eof = !(m_i < m_attrs.size());
373 if (!m_at_eof) {
374 get();
375 }
376 }
377
378private:
379 friend class ::boost::iterator_core_access;
380
381 bool equal(const basic_dom_element_iterator<charT>& other) const
382 {
383 if (m_at_eof && other.m_at_eof) {
384 return true;
385 }
386 return false;
387 }
388
389 void increment()
390 {
391 ++m_i;
392 m_at_eof = !(m_i < m_attrs.size());
393 if (!m_at_eof) {
394 get();
395 }
396 }
397
398 const ValueType& dereference() const
399 {
400 return m_value;
401 }
402
403 void advance(size_t n)
404 {
405 m_i += n;
406 m_at_eof = !(m_i < m_attrs.size());
407 if (!m_at_eof) {
408 get();
409 }
410 }
411
412 difference_type distance_to(const basic_dom_element_iterator<charT>& other) const
413 {
414 return other.m_i - this->m_i;
415 }
416
417private:
418 ValueType& value()
419 {
420 return m_value;
421 }
422
423 void get()
424 {
425 using namespace boost::program_options;
426
427 QDomNode node = m_attrs.item(m_i);
428 QDomAttr attr = node.toAttr();
429
430 string name = attr.name().toStdString();
431 string value = attr.value().toStdString();
432
433 bool registered = allowed_option(name);
434 if (!registered && !m_allow_unregistered) {
435 boost::throw_exception(unknown_option(name));
436 }
437
438 this->value().string_key = name;
439 this->value().value.clear();
440 this->value().value.push_back(value);
441 this->value().unregistered = !registered;
442 //this->value().original_tokens.push_back(name);
443 //this->value().original_tokens.push_back(value);
444 }
445
446 bool allowed_option(const std::string& s) const
447 {
448 set<string>::const_iterator it = m_allowed_options.find(s);
449 if (it != m_allowed_options.end()) {
450 return true;
451 }
452 return false;
453 }
454
455private:
456 const QDomElement* m_dom_element;
457 std::set<std::string> m_allowed_options;
458 bool m_allow_unregistered;
459
460 QDomNamedNodeMap m_attrs;
461 int m_i;
462 bool m_at_eof;
463 ValueType m_value;
464};
465
466typedef basic_dom_element_iterator<char> dom_element_iterator;
467typedef basic_dom_element_iterator<wchar_t> wdom_element_iterator;
468
469}
470
471boost::program_options::basic_parsed_options<char> DomElementParser::run()
472{
473 assert(m_desc);
474 return parseDomElement(m_dom_element, *m_desc, m_allow_unregistered);
475}
476
477boost::program_options::basic_parsed_options<char> DomElementParser::parseDomElement(
478 const QDomElement& dom_element,
479 const boost::program_options::options_description& desc,
480 bool allow_unregistered)
481{
482 // TODO: use XPath paths
483
484 typedef char charT;
485
486 using boost::program_options::error;
487 using boost::shared_ptr;
488 using namespace boost::program_options;
489 using ::detail::basic_dom_element_iterator;
490
491 set<string> allowed_options;
492
493 const vector<shared_ptr<option_description> >& options = desc.options();
494 for (unsigned i = 0; i < options.size(); ++i) {
495 const option_description& d = *options[i];
496
497 if (d.long_name().empty()) {
498 boost::throw_exception(
499 error("abbreviated option names are not permitted when parsing DOM elements"));
500 }
501
502 allowed_options.insert(d.long_name());
503 }
504
505 // Parser returns char strings
506 parsed_options result(&desc);
507 copy(basic_dom_element_iterator<charT>(dom_element, allowed_options, allow_unregistered),
508 basic_dom_element_iterator<charT>(dom_element),
509 back_inserter(result.options));
510
511 // Convert char strings into desired type.
512 return basic_parsed_options<charT>(result);
513}
514
515DomElementParser parseDomElement();
516
517/** Creates instance of 'command_line_parser', passes parameters to it,
518 and returns the result of calling the 'run' method.
519 */
520boost::program_options::basic_parsed_options<char>
521parseDomElement(QDomElement const& domElement, const boost::program_options::options_description&);
522
523boost::program_options::basic_parsed_options<char>
524parseDomElement(QDomElement const& domElement, const boost::program_options::options_description& desc)
525{
526 return DomElementParser(domElement)
527 .options(desc)
528 .run();
529}
530
531void ComponentBase::parseParameters(XmlComponentConfig const& cfg)
532{
533 LOG_INFO("Parsing parameters...");
534 LOG_INFO(mOptionsDescription);
535
536 po::variables_map vm;
537 try {
538 po::store(
539 DomElementParser(cfg.getDomElement())
540 .options(mOptionsDescription)
541 .allow_unregistered() // FIXME: temporary only, at term all the components specify all parameters
542 .run()
543 , vm);
544 po::notify(vm);
545 } catch (po::error& e) {
546 LOG_WARN(e.what());
547 BOOST_THROW_EXCEPTION(PacpusException(e.what()));
548 }
549
550 LOG_INFO("Parsed parameter values:\n" << vm);
551}
552
553vector<string> convertAttributesToArgumentVector(const QDomNamedNodeMap & attributes)
554{
555 vector<string> xargs;
556 xargs.reserve(attributes.size());
557
558 for (int i = 0; i < attributes.size(); ++i) {
559 QDomAttr parameter = attributes.item(i).toAttr();
560 if (parameter.isNull()) {
561 LOG_WARN("node is not a parameter");
562 continue;
563 }
564
565 QString arg = QString("--") + parameter.name() + "=";
566
567 bool shouldAddQuotes = parameter.value().contains(' ');
568 if (shouldAddQuotes) {
569 arg += '\"';
570 arg += parameter.value();
571 arg += '\"';
572 } else {
573 arg += parameter.value();
574 }
575
576 LOG_DEBUG("parameter: " << arg);
577 xargs.push_back(arg.toStdString());
578 }
579
580 return xargs;
581}
Note: See TracBrowser for help on using the repository browser.