source: pacpusframework/trunk/src/PacpusLib/ComponentManager.cpp@ 270

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

Major: DomElementParser (direct parsing of XML attributes).

  • Property svn:executable set to *
File size: 17.4 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/// @author Gerald Dherbomez <firstname.surname@utc.fr>
6/// @version $Id: ComponentManager.cpp 76 2013-01-10 17:05:10Z kurdejma $
7
8#include <Pacpus/kernel/ComponentManager.h>
9
10#include <Pacpus/kernel/ComponentFactoryBase.h>
11#include <Pacpus/kernel/ComponentBase.h>
12#include <Pacpus/kernel/ConnectionBase.h>
13#include <Pacpus/kernel/InputOutputBase.h>
14#include <Pacpus/kernel/Log.h>
15#include <Pacpus/kernel/PacpusException.h>
16
17#include <QDomNodeList>
18#include <QObject>
19#include <QList>
20#include <ostream>
21
22using namespace pacpus;
23
24template <typename _Elem, typename _Traits, typename _ListElem>
25std::basic_ostream<_Elem, _Traits> & operator<<(std::basic_ostream<_Elem, _Traits> & os, const QList<_ListElem> & list)
26{
27 typedef QList<_ListElem> ListType;
28 for (typename ListType::const_iterator it = list.begin(), itend = list.end(); it != itend; ++it) {
29 os << *it << "\n";
30 }
31 return os;
32}
33
34DECLARE_STATIC_LOGGER("pacpus.core.ComponentManager");
35
36////////////////////////////////////////////////////////////////////////////////
37
38/// Connects OutputInterfaceBase @b out to InputInterfaceBase @b in using given priority and reading mode.
39/// @returns @b true if connection has been added successfully, @b false otherwise.
40bool connectInterface(OutputInterfaceBase * out, InputInterfaceBase * in, int priority, InputInterfaceBase::ReadingMode mode = InputInterfaceBase::GetLast);
41
42////////////////////////////////////////////////////////////////////////////////
43
44bool connectInterface(OutputInterfaceBase * out, InputInterfaceBase * in, int priority, InputInterfaceBase::ReadingMode mode)
45{
46 // check connections
47 if (!out || !in) {
48 LOG_WARN("null connection");
49 return false;
50 }
51
52 // check if connection type are compatible
53 // FIXME: it should permit an OutputType to be a type conversible to InputType
54 if (out->getDataType() != in->getDataType()) {
55 LOG_WARN("connection types do not match: "
56 << out->getSignature() << "." << out->getDataType().name()
57 << " -> "
58 << in->getSignature() << "." << in->getDataType().name()
59 );
60 return false;
61 }
62 //if ((out->getDataType() == in->getDataType())
63 // || (out->getDataType() == typeid(QByteArray))
64 // || (in->getDataType() == typeid(QByteArray))) {
65
66 // add connection
67 out->addConnection(ConnectionBase(in, priority)); // TODO make connect function
68 in->addConnection(ConnectionBase(out, priority));
69 in->setReadingMode(mode);
70 //LOG_INFO("connection : Output " << out->getSignature() << " => Input " << in->getSignature());
71 return true;
72}
73
74////////////////////////////////////////////////////////////////////////////////
75
76ComponentManager * ComponentManager::mInstance = NULL;
77
78ComponentManager * ComponentManager::create()
79{
80 return getInstance();
81}
82
83ComponentManager * ComponentManager::getInstance()
84{
85 LOG_TRACE("getInstance()");
86 LOG_TRACE("before: mInstance = " << mInstance);
87
88 if (!mInstance) {
89 LOG_INFO("creating new instance...");
90 mInstance = new ComponentManager();
91 LOG_DEBUG("mInstance = " << mInstance);
92 }
93
94 LOG_TRACE("after : mInstance = " << mInstance);
95 return mInstance;
96}
97
98void ComponentManager::destroy()
99{
100 LOG_TRACE("destroy");
101
102 LOG_TRACE("before: mInstance = " << mInstance);
103 delete mInstance;
104 mInstance = NULL;
105 LOG_TRACE("after : mInstance = " << mInstance);
106}
107
108ComponentManager::ComponentManager()
109{
110 LOG_TRACE("constructor");
111
112 xmlTree_ = XmlConfigFile::create();
113 LOG_DEBUG("component manager was created");
114}
115
116ComponentManager::~ComponentManager()
117{
118 LOG_TRACE("destructor");
119
120 for (ComponentMap::iterator it = componentMap_.begin(), itend = componentMap_.end(); it != itend; ++it) {
121 bool unregisteredSuccessfully = unregisterComponent(it.key());
122 }
123
124 LOG_DEBUG("component manager was deleted");
125}
126
127bool ComponentManager::registerComponentFactory(ComponentFactoryBase* addr, const QString& type)
128{
129 LOG_TRACE("registerComponentFactory(type="<< type << ")");
130
131 if (factoryMap_.contains(type))
132 {
133 LOG_WARN("cannot register a component factory of type '" << type << "'. It already belongs to the manager");
134 return false;
135 }
136
137 factoryMap_[type] = addr;
138 LOG_INFO("registered component factory '" << type << "'");
139
140 return true;
141}
142
143bool ComponentManager::unregisterComponentFactory(const QString& type)
144{
145 LOG_TRACE("unregisterComponentFactory(type="<< type << ")");
146
147 if (!factoryMap_.contains(type)) {
148 LOG_WARN("cannot unregister component factory '" << type << "'. It was not registered");
149 return false;
150 }
151
152 factoryMap_.remove(type);
153 LOG_INFO("unregistered component factory '" << type << "'");
154
155 return true;
156}
157
158bool ComponentManager::registerComponent(ComponentBase* addr, const QString& name)
159{
160 LOG_TRACE("registerComponent(name="<< name << ")");
161
162 if (componentMap_.contains(name)) {
163 LOG_WARN("cannot register component '" << name << "'. A component with the same name exists already");
164 return false;
165 }
166
167 componentMap_[name] = addr;
168 LOG_INFO("registered component " << name);
169
170 return true;
171}
172
173bool ComponentManager::unregisterComponent(const QString& name)
174{
175 LOG_TRACE("unregisterComponent(name="<< name << ")");
176
177 if (!componentMap_.contains(name)) {
178 LOG_WARN("cannot unregister component '" << name << "'. It was not registered");
179 return false;
180 }
181
182 // FIXME: delete component
183 ComponentBase* component = componentMap_.value(name, NULL);
184 //delete component;
185
186 // FIXME: remove from map (causes segfault in QMap::qMapLessThanKey on Windows)
187 //componentMap_.remove(name);
188 LOG_INFO("unregistered component '" << name << "'");
189
190 return true;
191}
192
193bool ComponentManager::createComponent(const QString& type, const QString& name)
194{
195 LOG_TRACE("createComponent(type=" << type << ", " << "name="<< name << ")");
196
197 if (factoryMap_.contains(type)) {
198 ComponentFactoryBase* factory = factoryMap_.value(type);
199 assert(factory);
200 factory->addComponent(name);
201 return true;
202 }
203
204 LOG_WARN("cannot create component '" << name << "'"
205 << ". Component factory for type '" << type << "'"
206 << " does not exist or was not registered"
207 );
208 return false;
209}
210
211bool ComponentManager::loadPlugin(const QString& filename)
212{
213 LOG_TRACE("loadPlugin(filename=" << filename << ")");
214
215 pluginLoader_.setFileName(filename);
216
217 if (!pluginLoader_.load()) {
218 LOG_ERROR("cannot load plugin '" << filename << "'"
219 << ". Plugin loader returned error: " << pluginLoader_.errorString()
220 );
221 return false;
222 }
223
224 QObject * plugin = pluginLoader_.instance();
225 if (NULL == plugin) {
226 LOG_WARN("cannot create an instance of the plugin '" << filename << "'"
227 << ". Plugin loader returned error: " << pluginLoader_.errorString()
228 );
229 return false;
230 }
231 pluginList_.append(plugin);
232 LOG_INFO("loaded plugin '" << qobject_cast<PacpusPluginInterface*>(plugin)->name() << "'"
233 << " from file '" << pluginLoader_.fileName() << "'"
234 );
235 return true;
236}
237
238bool ComponentManager::checkComponent(const QString & componentName)
239{
240 if (NULL == getComponent(componentName)) {
241 LOG_WARN("component " << componentName << " does not exist");
242 return false;
243 }
244 return true;
245}
246
247bool ComponentManager::checkComponentInput(const QString & componentName, const QString & inputName)
248{
249 if (!checkComponent(componentName)) {
250 return false;
251 }
252 if (NULL == getComponent(componentName)->getInput(inputName)) {
253 LOG_WARN("cannot make connection: component " << componentName << " does not have input " << inputName);
254 return false;
255 }
256 return true;
257}
258
259bool ComponentManager::checkComponentOutput(const QString & componentName, const QString & outputName)
260{
261 if (!checkComponent(componentName)) {
262 return false;
263 }
264 if (NULL == getComponent(componentName)->getOutput(outputName)) {
265 LOG_WARN("cannot make connection: component " << componentName << " does not have output " << outputName);
266 return false;
267 }
268 return true;
269}
270
271bool ComponentManager::createConnection(const QString & outputSignature, const QString & inputSignature, const QString & type, int priority = 0)
272{
273 // FIXME: use 2 signatures (output component + output connection) instead of 1 separated by a (".") dot
274 QStringList output = outputSignature.split(".");
275 QStringList input = inputSignature.split(".");
276
277 if (!checkComponentOutput(output[0], output[1])) {
278 return false;
279 }
280 if (!checkComponentInput(input[0], input[1])) {
281 return false;
282 }
283 // FIXME: Create communicationInterface if needed ??
284 return connectInterface(
285 getComponent(output[0])->getOutput(output[1]),
286 getComponent(input[0])->getInput(input[1]),
287 priority
288 );
289}
290
291std::size_t ComponentManager::loadComponents(const QString& configFilename)
292{
293 LOG_TRACE("loadComponents(filename=" << configFilename << ")");
294
295 // load the components tree in memory
296 xmlTree_->loadFile(configFilename);
297
298 {
299 // Load the plugins containing the components
300 QStringList plugins = xmlTree_->getAllPluginsNames();
301 Q_FOREACH (QString plugin, plugins) {
302 if (!loadPlugin(plugin)) {
303 LOG_WARN("cannot load plugin '" << plugin << "'");
304 } else {
305 LOG_INFO("successfully loaded plugin '" << plugin << "'");
306 }
307 }
308 }
309
310 QDomNodeList componentsNodeList = xmlTree_->getAllComponents();
311 XmlComponentConfig cfg;
312
313 // First, create all the components in the XML list
314 for (int i = 0; i < componentsNodeList.size(); ++i) {
315 cfg.localCopy(componentsNodeList.item(i).toElement());
316 QString componentType = cfg.getComponentType();
317 QString componentName = cfg.getComponentName();
318 LOG_DEBUG("try to create component '" << componentName << "'");
319
320 // create the component and automatically add it to the component manager list
321 if (!createComponent(componentType, componentName)) {
322 LOG_ERROR("cannot create component '" << componentName << "'");
323 continue;
324 }
325 }
326
327 int componentsToConfigureCount = componentMap_.count();
328
329 // Second, try to configure the components without regarding the dependencies
330 for (int i = 0; i < componentsNodeList.size(); ++i) {
331 cfg.localCopy(componentsNodeList.item(i).toElement());
332 QString componentName = cfg.getComponentName();
333 LOG_DEBUG("try to configure component '" << componentName << "'");
334
335 // copy locally the config parameters of the component
336 ComponentBase * component = getComponent(componentName);
337 if (NULL == component) {
338 LOG_WARN("component '" << componentName << "' does not exist");
339 continue;
340 }
341
342 component->param.localCopy(cfg.getDomElement());
343 try {
344 component->parseParameters(cfg);
345 component->setConfigurationState(component->configureComponent(cfg));
346 } catch (PacpusException & e) {
347 LOG_ERROR("component '" << componentName << "' has thrown an exception");
348 LOG_ERROR(e.what());
349 component->setConfigurationState(ComponentBase::CONFIGURED_FAILED);
350 }
351 } // for
352
353 // Third, if some components requested a delayed configuration, retry
354 for (int i = 0, iend = componentsNodeList.size(); i < iend; ++i) {
355 cfg.localCopy(componentsNodeList.item(i).toElement());
356 QString componentName = cfg.getComponentName();
357
358 ComponentBase * component = getComponent(componentName);
359 if (NULL == component) {
360 LOG_WARN("component '" << componentName << "' does not exist");
361 continue;
362 }
363
364 // add inputs and outputs
365 component->addInputs();
366 component->addOutputs();
367 // print inputs and outputs
368 LOG_INFO("Inputs for component '" << componentName << "':\n" << component->inputs().keys());
369 LOG_INFO("Outputs for component '" << componentName << "':\n" << component->outputs().keys());
370
371 if (ComponentBase::CONFIGURATION_DELAYED == component->configurationState()) {
372 LOG_DEBUG("try to configure component '" << componentName << "'");
373
374 // copy locally the config parameters of the component
375 component->param.localCopy(cfg.getDomElement());
376 component->setConfigurationState(component->configureComponent(cfg));
377 }
378
379 if (ComponentBase::CONFIGURED_OK == component->configurationState()) {
380 --componentsToConfigureCount;
381 } else {
382 LOG_ERROR("cannot configure component '" << componentName << "'"
383 << ". Dependencies with other components are too complex"
384 << ". It was not configured, please review your configuration and/or your component"
385 );
386 component->setConfigurationState(ComponentBase::CONFIGURED_FAILED);
387 }
388 } // for
389
390 LOG_INFO(componentMap_.count() << " component(s) were loaded");
391 if (componentsToConfigureCount > 0) {
392 LOG_WARN(componentsToConfigureCount << " component(s) were not configured");
393 }
394
395 // Fourthly, create connections find in the XML list
396 QDomNodeList connectionsNodeList = xmlTree_->getAllConnections();
397
398 for (int i = 0, iend = connectionsNodeList.size(); i < iend; ++i) {
399 cfg.localCopy(connectionsNodeList.item(i).toElement());
400 QString connectionInput = cfg.getConnectionInput();
401 QString connectionOutput = cfg.getConnectionOutput();
402 QString connectionType = cfg.getConnectionType();
403 int connectionPriority = cfg.getConnectionPriority();
404
405 //TODO set connection mode from string
406
407 //InputInterfaceBase::GetLast;
408 //InputInterfaceBase::NeverSkip;
409 //InputInterfaceBase::TimeBounded;
410
411 if (!createConnection(connectionOutput, connectionInput, connectionType,connectionPriority)) {
412 LOG_ERROR("cannot create connection '" << connectionOutput + " => " + connectionInput << "'");
413 continue;
414 }
415 } // for
416
417 return componentMap_.count();
418}
419
420bool ComponentManager::start()
421{
422 LOG_TRACE("start()");
423
424 bool result = true;
425 for (ComponentMap::iterator it = componentMap_.begin(), itend = componentMap_.end(); it != itend; ++it) {
426 result &= start(it.key());
427 }
428
429 return result;
430}
431
432bool ComponentManager::start(const QString & componentName)
433{
434 LOG_TRACE("start(component=" << componentName << ")");
435
436 ComponentBase* component = getComponent(componentName);
437 if (!component) {
438 LOG_WARN("cannot start component '" << componentName << "'. It does not exist!");
439 return false;
440 }
441
442 if (ComponentBase::CONFIGURED_OK != component->configurationState()) {
443 LOG_WARN("cannot start component '" << componentName << "'. It has not been configured properly!");
444 return false;
445 }
446
447 LOG_INFO("starting component '" << componentName << "'...");
448 if (!component->startComponent()) {
449 LOG_WARN("cannot start component '" << componentName << "'. It can already be started");
450 }
451 LOG_INFO("successfully started component '" << componentName << "'");
452 return true;
453}
454
455bool ComponentManager::stop()
456{
457 LOG_TRACE("stop()");
458
459 bool result = true;
460 for (ComponentMap::iterator it = componentMap_.begin(), itend = componentMap_.end(); it != itend; ++it) {
461 result &= stop(*it);
462 }
463
464 return result;
465}
466
467bool ComponentManager::stop(ComponentBase * component) const
468{
469 if (!component) {
470 LOG_WARN("NULL component pointer");
471 return false;
472 }
473 if (!component->stopComponent()) {
474 return false;
475 }
476 return true;
477}
478
479bool ComponentManager::stop(const QString & componentName)
480{
481 LOG_TRACE("stop(component=" << componentName << ")");
482
483 ComponentBase* component = getComponent(componentName);
484 if (!component) {
485 LOG_WARN("cannot stop component '" << componentName << "'" << ". It does not exist");
486 return false;
487 }
488
489 LOG_INFO("stopping component '" << componentName << "'...");
490 if (!stop(component)) {
491 LOG_WARN("cannot stop component '" << componentName << "'" << ". It can be already stopped");
492 }
493
494 return true;
495}
496
497ComponentBase * ComponentManager::getComponent(const QString & name)
498{
499 LOG_TRACE("getComponent(name=" << name << ")");
500
501 ComponentMap::iterator it = componentMap_.find(name);
502 if (it != componentMap_.end()) {
503 return *it;
504 }
505
506 LOG_WARN("cannot retrieve component '" << name << "'" << ". It does not exist");
507 return NULL;
508}
509
510QStringList ComponentManager::getAllComponentsName() const
511{
512 LOG_TRACE("getAllComponentsName()");
513 return xmlTree_->getAllComponentsNames();
514}
Note: See TracBrowser for help on using the repository browser.