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

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

Transporting and catching exceptions in threads.

  • Property svn:executable set to *
File size: 16.1 KB
RevLine 
[89]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>
[182]8
[89]9#include <Pacpus/kernel/ComponentManager.h>
10#include <Pacpus/kernel/Log.h>
[201]11#include <Pacpus/kernel/PacpusException.h>
[89]12
[281]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>
[176]17#include <boost/program_options/parsers.hpp>
18#include <boost/program_options/variables_map.hpp>
[279]19#include <boost/thread/thread.hpp>
[202]20#include <ostream>
[176]21#include <string>
22#include <vector>
23
24namespace po = boost::program_options;
[89]25using namespace pacpus;
[176]26using namespace std;
[89]27
[182]28vector<string> convertAttributesToArgumentVector(const QDomNamedNodeMap & attributes);
[176]29
[272]30namespace std
31{
[202]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{
[231]36 for (po::variables_map::const_iterator i = vm.begin(); i != vm.end(); ++i) {
[202]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;
[263]66 } else {
67 // unknown value type
68 os << i->first;
69 }
[204]70 os << "\n";
[202]71 }
72 return os;
73}
74
75} // namespace std
76
[89]77DECLARE_STATIC_LOGGER("pacpus.core.ComponentBase");
78
[182]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")
[89]87{
88 LOG_TRACE("constructor");
89 // Get a pointer on the instance of ComponentManager.
[152]90 m_manager = ComponentManager::getInstance();
[176]91 LOG_INFO("component " << getName() << " was created");
92
[179]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")
[176]100 ;
[89]101}
102
103ComponentBase::~ComponentBase()
104{
105 LOG_TRACE("destructor");
106}
107
[152]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{
[177]120 return mIsRecording;
[152]121}
122
123void ComponentBase::setRecording(bool isRecording)
124{
[177]125 mIsRecording = isRecording;
[152]126}
127
128const XmlComponentConfig ComponentBase::xmlParameters() const
129{
130 return param;
131}
132
[281]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
[89]164int ComponentBase::startComponent()
165{
[152]166 if (isActive()) {
167 LOG_DEBUG("component already started, cannot (re-)start");
168 return false;
169 }
170
171 setActive(true);
[281]172 boost::thread worker(&ComponentBase::startComponentInThread, this);
173 //boost::thread worker(&ComponentBase::startActivity, this);
[279]174 //startActivity();
[89]175 return true;
176}
177
178int ComponentBase::stopComponent()
179{
[152]180 if (!isActive()) {
181 LOG_DEBUG("component already stopped, cannot (re-)stop");
182 return false;
183 }
184
185 setActive(false);
[89]186 stopActivity();
187
188 return true;
189}
190
191void ComponentBase::setState(const COMPONENT_STATE state)
192{
[152]193 m_componentState = state;
[89]194}
195
196// FIXME: this should be const.
197ComponentBase::COMPONENT_STATE ComponentBase::getState()
198{
[152]199 COMPONENT_STATE state = m_componentState;
200 if (ComponentBase::NOT_MONITORED != m_componentState) {
201 m_componentState = ComponentBase::MONITOR_NOK;
[89]202 }
203 return state;
204}
205
[152]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
[89]216bool ComponentBase::isConfigured() const
217{
[152]218 return (m_configurationState == CONFIGURED_OK);
[89]219}
220
221QString ComponentBase::getName() const
222{
[152]223 return m_componentName;
[89]224}
225
[152]226ComponentBase::InputsMap & ComponentBase::inputs()
[89]227{
[152]228 return m_inputs;
229}
[89]230
[152]231const ComponentBase::InputsMap & ComponentBase::inputs() const
232{
233 return m_inputs;
[89]234}
235
[152]236ComponentBase::OutputsMap & ComponentBase::outputs()
[89]237{
[152]238 return m_outputs;
239}
[89]240
[152]241const ComponentBase::OutputsMap & ComponentBase::outputs() const
242{
243 return m_outputs;
[89]244}
[120]245
[152]246InputInterfaceBase * ComponentBase::getInput(QString inputName) const
[120]247{
[152]248 if (inputs().contains(inputName)) {
249 return inputs()[inputName];
250 }
[176]251 LOG_WARN("Component " << getName() << " does not contain input " << inputName);
[152]252 return NULL;
[120]253}
254
[152]255OutputInterfaceBase * ComponentBase::getOutput(QString outputName) const
[120]256{
[152]257 if (outputs().contains(outputName)) {
258 return outputs()[outputName];
259 }
[206]260 LOG_WARN("Component " << getName() << " does not contain output " << outputName);
[152]261 return NULL;
[120]262}
[176]263
[181]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
[176]279po::options_description_easy_init ComponentBase::addParameters()
280{
281 return mOptionsDescription.add_options();
282}
283
[270]284class DomElementParser
[176]285{
[270]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;
[276]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;
[270]355
[278]356 basic_dom_element_iterator<charT>(QDomElement const& dom_element)
[270]357 : m_dom_element(NULL)
358 , m_at_eof(true)
[278]359 , m_i(dom_element.attributes().size())
[270]360 {
361 }
362
[278]363 basic_dom_element_iterator<charT>(QDomElement const& dom_element,
364 std::set<std::string> const& allowed_options,
[270]365 bool allow_unregistered = false)
366 : m_dom_element(&dom_element)
367 , m_allowed_options(allowed_options)
[271]368 , m_allow_unregistered(allow_unregistered)
[270]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
[271]378private:
379 friend class ::boost::iterator_core_access;
[270]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
[274]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
[270]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;
[275]442 //this->value().original_tokens.push_back(name);
443 //this->value().original_tokens.push_back(value);
[270]444 }
445
446 bool allowed_option(const std::string& s) const
447 {
[278]448 set<string>::const_iterator it = m_allowed_options.find(s);
449 if (it != m_allowed_options.end()) {
[270]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{
[271]482 // TODO: use XPath paths
483
[270]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),
[278]508 basic_dom_element_iterator<charT>(dom_element),
[270]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{
[176]533 LOG_INFO("Parsing parameters...");
534 LOG_INFO(mOptionsDescription);
535
[201]536 po::variables_map vm;
[176]537 try {
538 po::store(
[270]539 DomElementParser(cfg.getDomElement())
[176]540 .options(mOptionsDescription)
[180]541 .allow_unregistered() // FIXME: temporary only, at term all the components specify all parameters
[176]542 .run()
543 , vm);
544 po::notify(vm);
[270]545 } catch (po::error& e) {
[201]546 LOG_WARN(e.what());
[280]547 BOOST_THROW_EXCEPTION(PacpusException(e.what()));
[176]548 }
549
[204]550 LOG_INFO("Parsed parameter values:\n" << vm);
[176]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.