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

Last change on this file was 294, checked in by Marek Kurdej, 11 years ago

Fixed: GCC compilation problems with shared_ptr.

  • 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 created: source=" << out->getSignature() << ", destination=" << 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, QString const& 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(QString const& 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(boost::shared_ptr<ComponentBase> addr, QString const& 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(QString const& 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 ComponentSharedPointer component = componentMap_.value(name, ComponentSharedPointer());
183
184 // FIXME: remove from map (causes segfault in QMap::qMapLessThanKey on Windows)
185 //componentMap_.remove(name);
186 LOG_INFO("unregistered component '" << name << "'");
187 return true;
188}
189
190bool ComponentManager::createComponent(QString const& type, QString const& name)
191{
192 LOG_TRACE("createComponent(type=" << type << ", " << "name="<< name << ")");
193
194 if (factoryMap_.contains(type)) {
195 ComponentFactoryBase* factory = factoryMap_.value(type);
196 assert(factory);
197 factory->createComponent(name);
198 return true;
199 }
200
201 LOG_WARN("cannot create component '" << name << "'"
202 << ". Component factory for type '" << type << "'"
203 << " does not exist or was not registered"
204 );
205 return false;
206}
207
208bool ComponentManager::loadPlugin(QString const& filename)
209{
210 LOG_TRACE("loadPlugin(filename=" << filename << ")");
211
212 pluginLoader_.setFileName(filename);
213
214 if (!pluginLoader_.load()) {
215 LOG_ERROR("cannot load plugin '" << filename << "'"
216 << ". Plugin loader returned error: " << pluginLoader_.errorString()
217 );
218 return false;
219 }
220
221 QObject * plugin = pluginLoader_.instance();
222 if (NULL == plugin) {
223 LOG_WARN("cannot create an instance of the plugin '" << filename << "'"
224 << ". Plugin loader returned error: " << pluginLoader_.errorString()
225 );
226 return false;
227 }
228 pluginList_.append(plugin);
229 LOG_INFO("loaded plugin '" << qobject_cast<PacpusPluginInterface*>(plugin)->name() << "'"
230 << " from file '" << pluginLoader_.fileName() << "'"
231 );
232 return true;
233}
234
235bool ComponentManager::checkComponent(QString const& componentName)
236{
237 if (NULL == getComponent(componentName)) {
238 LOG_WARN("component " << componentName << " does not exist");
239 return false;
240 }
241 return true;
242}
243
244bool ComponentManager::checkComponentInput(QString const& componentName, QString const& inputName)
245{
246 if (!checkComponent(componentName)) {
247 return false;
248 }
249 if (NULL == getComponent(componentName)->getInput(inputName)) {
250 LOG_WARN("cannot make connection: component " << componentName << " does not have input " << inputName);
251 return false;
252 }
253 return true;
254}
255
256bool ComponentManager::checkComponentOutput(QString const& componentName, QString const& outputName)
257{
258 if (!checkComponent(componentName)) {
259 return false;
260 }
261 if (NULL == getComponent(componentName)->getOutput(outputName)) {
262 LOG_WARN("cannot make connection: component " << componentName << " does not have output " << outputName);
263 return false;
264 }
265 return true;
266}
267
268bool ComponentManager::createConnection(QString const& outputSignature, QString const& inputSignature, QString const& type, int priority = 0)
269{
270 // FIXME: use 2 signatures (output component + output connection) instead of 1 separated by a (".") dot
271 QStringList output = outputSignature.split(".");
272 QStringList input = inputSignature.split(".");
273
274 if (!checkComponentOutput(output[0], output[1])) {
275 return false;
276 }
277 if (!checkComponentInput(input[0], input[1])) {
278 return false;
279 }
280 // FIXME: Create communicationInterface if needed ??
281 return connectInterface(
282 getComponent(output[0])->getOutput(output[1]),
283 getComponent(input[0])->getInput(input[1]),
284 priority
285 );
286}
287
288std::size_t ComponentManager::loadComponents(QString const& configFilename)
289{
290 LOG_TRACE("loadComponents(filename=" << configFilename << ")");
291
292 // load the components tree in memory
293 xmlTree_->loadFile(configFilename);
294
295 {
296 // Load the plugins containing the components
297 QStringList plugins = xmlTree_->getAllPluginsNames();
298 Q_FOREACH (QString plugin, plugins) {
299 if (!loadPlugin(plugin)) {
300 LOG_WARN("cannot load plugin '" << plugin << "'");
301 } else {
302 LOG_INFO("successfully loaded plugin '" << plugin << "'");
303 }
304 }
305 }
306
307 QDomNodeList componentsNodeList = xmlTree_->getAllComponents();
308 XmlComponentConfig cfg;
309
310 // First, create all the components in the XML list
311 for (int i = 0; i < componentsNodeList.size(); ++i) {
312 cfg.localCopy(componentsNodeList.item(i).toElement());
313 QString componentType = cfg.getComponentType();
314 QString componentName = cfg.getComponentName();
315 LOG_DEBUG("try to create component '" << componentName << "'");
316
317 // create the component and automatically add it to the component manager list
318 if (!createComponent(componentType, componentName)) {
319 LOG_ERROR("cannot create component '" << componentName << "'");
320 continue;
321 }
322 }
323
324 int componentsToConfigureCount = componentMap_.count();
325
326 // Second, try to configure the components without regarding the dependencies
327 for (int i = 0; i < componentsNodeList.size(); ++i) {
328 cfg.localCopy(componentsNodeList.item(i).toElement());
329 QString componentName = cfg.getComponentName();
330 LOG_DEBUG("try to configure component '" << componentName << "'");
331
332 // copy locally the config parameters of the component
333 ComponentSharedPointer component = getComponent(componentName);
334 if (NULL == component) {
335 LOG_WARN("component '" << componentName << "' does not exist");
336 continue;
337 }
338
339 component->param.localCopy(cfg.getDomElement());
340 try {
341 component->parseParameters(cfg);
342 component->setConfigurationState(component->configureComponent(cfg));
343 } catch (PacpusException & e) {
344 LOG_ERROR("component '" << componentName << "' has thrown an exception");
345 LOG_ERROR(e.what());
346 component->setConfigurationState(ComponentBase::CONFIGURED_FAILED);
347 }
348 } // for
349
350 // Third, if some components requested a delayed configuration, retry
351 for (int i = 0, iend = componentsNodeList.size(); i < iend; ++i) {
352 cfg.localCopy(componentsNodeList.item(i).toElement());
353 QString componentName = cfg.getComponentName();
354
355 ComponentSharedPointer component = getComponent(componentName);
356 if (!component) {
357 LOG_WARN("component '" << componentName << "' does not exist");
358 continue;
359 }
360
361 // add inputs and outputs
362 component->addInputs();
363 component->addOutputs();
364 // print inputs and outputs
365 LOG_INFO("Inputs for component '" << componentName << "':\n" << component->inputs().keys());
366 LOG_INFO("Outputs for component '" << componentName << "':\n" << component->outputs().keys());
367
368 if (ComponentBase::CONFIGURATION_DELAYED == component->configurationState()) {
369 LOG_DEBUG("try to configure component '" << componentName << "'");
370
371 // copy locally the config parameters of the component
372 component->param.localCopy(cfg.getDomElement());
373 component->setConfigurationState(component->configureComponent(cfg));
374 }
375
376 if (ComponentBase::CONFIGURED_OK == component->configurationState()) {
377 --componentsToConfigureCount;
378 } else {
379 LOG_ERROR("cannot configure component '" << componentName << "'"
380 << ". Dependencies with other components are too complex"
381 << ". It was not configured, please review your configuration and/or your component"
382 );
383 component->setConfigurationState(ComponentBase::CONFIGURED_FAILED);
384 }
385 } // for
386
387 LOG_INFO(componentMap_.count() << " component(s) were loaded");
388 if (componentsToConfigureCount > 0) {
389 LOG_WARN(componentsToConfigureCount << " component(s) were not configured");
390 }
391
392 // Fourthly, create connections find in the XML list
393 QDomNodeList connectionsNodeList = xmlTree_->getAllConnections();
394
395 for (int i = 0, iend = connectionsNodeList.size(); i < iend; ++i) {
396 cfg.localCopy(connectionsNodeList.item(i).toElement());
397 QString connectionInput = cfg.getConnectionInput();
398 QString connectionOutput = cfg.getConnectionOutput();
399 QString connectionType = cfg.getConnectionType();
400 int connectionPriority = cfg.getConnectionPriority();
401
402 //TODO set connection mode from string
403
404 //InputInterfaceBase::GetLast;
405 //InputInterfaceBase::NeverSkip;
406 //InputInterfaceBase::TimeBounded;
407
408 if (!createConnection(connectionOutput, connectionInput, connectionType,connectionPriority)) {
409 LOG_ERROR("cannot create connection '" << connectionOutput + " => " + connectionInput << "'");
410 continue;
411 }
412 } // for
413
414 return componentMap_.count();
415}
416
417bool ComponentManager::start()
418{
419 LOG_TRACE("start()");
420
421 bool result = true;
422 for (ComponentMap::iterator it = componentMap_.begin(), itend = componentMap_.end(); it != itend; ++it) {
423 result &= start(it.key());
424 }
425
426 return result;
427}
428
429bool ComponentManager::start(QString const& componentName)
430{
431 LOG_TRACE("start(component=" << componentName << ")");
432
433 ComponentSharedPointer component = getComponent(componentName);
434 if (!component) {
435 LOG_WARN("cannot start component '" << componentName << "'. It does not exist!");
436 return false;
437 }
438
439 if (ComponentBase::CONFIGURED_OK != component->configurationState()) {
440 LOG_WARN("cannot start component '" << componentName << "'. It has not been configured properly!");
441 return false;
442 }
443
444 LOG_INFO("starting component '" << componentName << "'...");
445 if (!component->startComponent()) {
446 LOG_WARN("cannot start component '" << componentName << "'. It can already be started");
447 }
448 LOG_INFO("successfully started component '" << componentName << "'");
449 return true;
450}
451
452bool ComponentManager::stop()
453{
454 LOG_TRACE("stop()");
455
456 bool result = true;
457 for (ComponentMap::iterator it = componentMap_.begin(), itend = componentMap_.end(); it != itend; ++it) {
458 result &= stop(*it);
459 }
460
461 return result;
462}
463
464bool ComponentManager::stop(ComponentSharedPointer component) const
465{
466 if (!component) {
467 LOG_WARN("NULL component pointer");
468 return false;
469 }
470 if (!component->stopComponent()) {
471 return false;
472 }
473 return true;
474}
475
476bool ComponentManager::stop(QString const& componentName)
477{
478 LOG_TRACE("stop(component=" << componentName << ")");
479
480 ComponentSharedPointer component = getComponent(componentName);
481 if (!component) {
482 LOG_WARN("cannot stop component '" << componentName << "'" << ". It does not exist");
483 return false;
484 }
485
486 LOG_INFO("stopping component '" << componentName << "'...");
487 if (!stop(component)) {
488 LOG_WARN("cannot stop component '" << componentName << "'" << ". It can be already stopped");
489 }
490
491 return true;
492}
493
494ComponentSharedPointer ComponentManager::getComponent(QString const& name)
495{
496 LOG_TRACE("getComponent(name=" << name << ")");
497
498 ComponentMap::iterator it = componentMap_.find(name);
499 if (it != componentMap_.end()) {
500 return *it;
501 }
502
503 // FIXME: throw
504 LOG_WARN("cannot retrieve component '" << name << "'" << ". It does not exist");
505 return ComponentSharedPointer();
506}
507
508QStringList ComponentManager::getAllComponentsName() const
509{
510 LOG_TRACE("getAllComponentsName()");
511 return xmlTree_->getAllComponentsNames();
512}
Note: See TracBrowser for help on using the repository browser.