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

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

Fixed: allow_unregistered.

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