OpenWalnut  1.3.1
WModule.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #include <algorithm>
26 #include <set>
27 #include <string>
28 #include <sstream>
29 
30 #include <boost/shared_ptr.hpp>
31 
32 #include "WModuleInputConnector.h"
33 #include "WModuleOutputConnector.h"
34 #include "WModuleInputData.h"
35 #include "WModuleOutputData.h"
36 #include "WModuleConnectorSignals.h"
37 #include "WModuleContainer.h"
38 #include "WModuleFactory.h"
39 #include "WModuleMetaInformation.h"
40 #include "exceptions/WModuleConnectorInitFailed.h"
41 #include "exceptions/WModuleConnectorNotFound.h"
42 #include "exceptions/WModuleUninitialized.h"
43 #include "exceptions/WModuleRequirementNotMet.h"
44 #include "../common/WException.h"
45 #include "../common/exceptions/WNameNotUnique.h"
46 #include "../common/exceptions/WSignalUnknown.h"
47 #include "../common/exceptions/WSignalSubscriptionFailed.h"
48 #include "../common/WLogger.h"
49 #include "../common/WCondition.h"
50 #include "../common/WConditionOneShot.h"
51 #include "../common/WConditionSet.h"
52 #include "../common/WPathHelper.h"
53 #include "../common/WProgressCombiner.h"
54 #include "../common/WPredicateHelper.h"
55 
56 #include "WModule.h"
57 
60  WPrototyped(),
61  m_initialized( new WCondition(), false ),
62  m_isAssociated( new WCondition(), false ),
63  m_isUsable( new WCondition(), false ),
64  m_isReady( new WConditionOneShot(), false ),
65  m_isReadyOrCrashed( new WConditionSet(), false ),
66  m_isRunning( new WCondition(), false ),
67  m_readyProgress( boost::shared_ptr< WProgress >( new WProgress( "Initializing Module" ) ) ),
68  m_moduleState(),
69  m_localPath( WPathHelper::getSharePath() )
70 {
71  // initialize members
72  m_properties = boost::shared_ptr< WProperties >( new WProperties( "Properties", "Module's properties" ) );
73  m_infoProperties = boost::shared_ptr< WProperties >( new WProperties( "Informational Properties", "Module's information properties" ) );
74  m_infoProperties->setPurpose( PV_PURPOSE_INFORMATION );
75 
76  m_runtimeName = m_properties->addProperty( "Name", "The name of the module defined by the user. This is, by default, the module name but "
77  "can be changed by the user to provide some kind of simple identification upon many modules.",
78  std::string( "" ), false );
79 
80  m_active = m_properties->addProperty( "active", "Determines whether the module should be activated.", true, true );
81  m_active->getCondition()->subscribeSignal( boost::bind( &WModule::activate, this ) );
82 
83  // the isReadyOrCrashed condition set needs to be set up here
84  WConditionSet* cs = static_cast< WConditionSet* >( m_isReadyOrCrashed.getCondition().get() ); // NOLINT
85  cs->setResetable( true, false );
86  cs->add( m_isReady.getCondition() );
87  cs->add( m_isCrashed.getCondition() );
88 
89  m_container = boost::shared_ptr< WModuleContainer >();
90  m_progress = boost::shared_ptr< WProgressCombiner >( new WProgressCombiner() );
91 
92  // add a progress indicator which finishes on "ready()"
93  m_progress->addSubProgress( m_readyProgress );
94 
95  // our internal state consist out of two conditions: data changed and the exit flag from WThreadedRunner.
97 }
98 
100 {
101  // cleanup
102 }
103 
104 void WModule::addConnector( boost::shared_ptr< WModuleInputConnector > con )
105 {
106  size_t c = std::count_if( m_inputConnectors.begin(), m_inputConnectors.end(),
108  );
109  // well ... we want it to be unique in both:
110  c += std::count_if( m_outputConnectors.begin(), m_outputConnectors.end(),
112  );
113 
114  // if there already is one ... exception
115  if( c )
116  {
117  throw WNameNotUnique( std::string( "Could not add the connector " + con->getCanonicalName() + " since names must be unique." ) );
118  }
119 
120  m_inputConnectors.push_back( con );
121 }
122 
123 void WModule::addConnector( boost::shared_ptr< WModuleOutputConnector > con )
124 {
125  size_t c = std::count_if( m_inputConnectors.begin(), m_inputConnectors.end(),
127  );
128  // well ... we want it to be unique in both:
129  c += std::count_if( m_outputConnectors.begin(), m_outputConnectors.end(),
131  );
132 
133  // if there already is one ... exception
134  if( c )
135  {
136  throw WNameNotUnique( std::string( "Could not add the connector " + con->getCanonicalName() + " since names must be unique." ) );
137  }
138 
139  m_outputConnectors.push_back( con );
140 }
141 
143 {
144  // remove connections and their signals
145  for( InputConnectorList::iterator listIter = m_inputConnectors.begin();
146  listIter != m_inputConnectors.end(); ++listIter )
147  {
148  ( *listIter )->disconnectAll();
149  }
150  for( OutputConnectorList::iterator listIter = m_outputConnectors.begin();
151  listIter != m_outputConnectors.end(); ++listIter )
152  {
153  ( *listIter )->disconnectAll();
154  }
155 }
156 
157 WCombinerTypes::WDisconnectList WModule::getPossibleDisconnections()
158 {
159  WCombinerTypes::WDisconnectList discons;
160 
161  // iterate inputs
162  for( InputConnectorList::iterator listIter = m_inputConnectors.begin(); listIter != m_inputConnectors.end(); ++listIter )
163  {
164  // get all connections of the current connector:
165  WCombinerTypes::WDisconnectGroup g = WCombinerTypes::WDisconnectGroup( ( *listIter )->getName(),
166  ( *listIter )->getPossibleDisconnections() );
167 
168  if( g.second.size() )
169  {
170  discons.push_back( g );
171  }
172  }
173 
174  // iterate outputs
175  for( OutputConnectorList::iterator listIter = m_outputConnectors.begin(); listIter != m_outputConnectors.end(); ++listIter )
176  {
177  // get all connections of the current connector:
178  WCombinerTypes::WDisconnectGroup g = WCombinerTypes::WDisconnectGroup( ( *listIter )->getName(),
179  ( *listIter )->getPossibleDisconnections() );
180 
181  if( g.second.size() )
182  {
183  discons.push_back( g );
184  }
185  }
186 
187  return discons;
188 }
189 
191 {
192  m_initialized( false );
194 
195  // remove connections and their signals, this is flat removal. The module container can do deep removal
196  disconnect();
197 
198  // clean up list
199  // this should delete the connector since nobody else *should* have another shared_ptr to them
200  m_inputConnectors.clear();
201  m_outputConnectors.clear();
202 }
203 
205 {
206 }
207 
209 {
210 }
211 
213 {
214 }
215 
217 {
218 }
219 
220 std::string WModule::deprecated() const
221 {
222  return "";
223 }
224 
226 {
227  return m_meta;
228 }
229 
231 {
232  // doing it twice is not allowed
233  if( isInitialized()() )
234  {
235  throw WModuleConnectorInitFailed( std::string( "Could not initialize connectors for Module " ) + getName() +
236  std::string( ". Reason: already initialized." ) );
237  }
238 
239  // set the module name as default runtime name
240  m_runtimeName->set( getName() );
241 
242  // initialize module meta information
243  m_meta = WModuleMetaInformation::SPtr( new WModuleMetaInformation( shared_from_this() ) );
244 
245  // initialize connectors and properties
246  requirements();
247  connectors();
248  properties();
249 
250  // now, the module is initialized but not necessarily usable (if not associated with a container)
251  m_initialized( true );
253 
254  // also set thread name
255  setThreadName( getName() );
256 }
257 
259 {
260  // currently just removes connectors
262 }
263 
264 boost::shared_ptr< WModuleContainer > WModule::getAssociatedContainer() const
265 {
266  return m_container;
267 }
268 
269 void WModule::setAssociatedContainer( boost::shared_ptr< WModuleContainer > container )
270 {
271  m_container = container;
272 
273  // true if the pointer is set
274  m_isAssociated( m_container != boost::shared_ptr< WModuleContainer >() );
276 }
277 
278 MODULE_TYPE WModule::getType() const
279 {
280  return MODULE_ARBITRARY;
281 }
282 
284 {
285  return m_inputConnectors;
286 }
287 
289 {
290  return m_outputConnectors;
291 }
292 
293 boost::shared_ptr< WModuleInputConnector > WModule::findInputConnector( std::string name )
294 {
295  // simply search
296  for( InputConnectorList::const_iterator listIter = m_inputConnectors.begin();
297  listIter != m_inputConnectors.end(); ++listIter )
298  {
299  // try the canonical name
300  if( ( name == ( *listIter )->getCanonicalName() ) || ( name == ( *listIter )->getName() ) )
301  {
302  return ( *listIter );
303  }
304  }
305 
306  return boost::shared_ptr< WModuleInputConnector >();
307 }
308 
309 boost::shared_ptr< WModuleInputConnector > WModule::getInputConnector( std::string name )
310 {
311  boost::shared_ptr< WModuleInputConnector > p = findInputConnector( name );
312 
313  if( !p )
314  {
315  throw WModuleConnectorNotFound( std::string( "The connector \"" ) + name +
316  std::string( "\" does not exist in the module \"" ) + getName() + std::string( "\"." ) );
317  }
318 
319  return p;
320 }
321 
322 boost::shared_ptr< WModuleOutputConnector > WModule::findOutputConnector( std::string name )
323 {
324  // simply search
325  for( OutputConnectorList::const_iterator listIter = m_outputConnectors.begin();
326  listIter != m_outputConnectors.end(); ++listIter )
327  {
328  // try the canonical name
329  if( ( name == ( *listIter )->getCanonicalName() ) || ( name == ( *listIter )->getName() ) )
330  {
331  return ( *listIter );
332  }
333  }
334 
335  return boost::shared_ptr< WModuleOutputConnector >();
336 }
337 
338 boost::shared_ptr< WModuleOutputConnector > WModule::getOutputConnector( std::string name )
339 {
340  boost::shared_ptr< WModuleOutputConnector > p = findOutputConnector( name );
341 
342  if( !p )
343  {
344  throw WModuleConnectorNotFound( std::string( "The connector \"" ) + name +
345  std::string( "\" does not exist in the module \"" ) + getName() +
346  std::string( "\"." ) );
347  }
348 
349  return p;
350 }
351 
352 boost::shared_ptr< WModuleConnector > WModule::findConnector( std::string name )
353 {
354  // simply search both
355  boost::shared_ptr< WModuleConnector > p = findInputConnector( name );
356  if( p ) // found?
357  {
358  return p;
359  }
360 
361  // search in output list
362  return findOutputConnector( name );
363 }
364 
365 boost::shared_ptr< WModuleConnector > WModule::getConnector( std::string name )
366 {
367  boost::shared_ptr< WModuleConnector > p = findConnector( name );
368 
369  if( !p )
370  {
371  throw WModuleConnectorNotFound( std::string( "The connector \"" ) + name +
372  std::string( "\" does not exist in the module \"" ) + getName() +
373  std::string( "\"." ) );
374  }
375 
376  return p;
377 }
378 
379 boost::signals2::connection WModule::subscribeSignal( MODULE_SIGNAL signal, t_ModuleGenericSignalHandlerType notifier )
380 {
381  switch( signal )
382  {
383  case WM_READY:
384  return signal_ready.connect( notifier );
385  default:
386  std::ostringstream s;
387  s << "Could not subscribe to unknown signal.";
388  throw WSignalSubscriptionFailed( s.str() );
389  break;
390  }
391 }
392 
393 boost::signals2::connection WModule::subscribeSignal( MODULE_SIGNAL signal, t_ModuleErrorSignalHandlerType notifier )
394 {
395  switch( signal)
396  {
397  case WM_ERROR:
398  return signal_error.connect( notifier );
399  default:
400  std::ostringstream s;
401  s << "Could not subscribe to unknown signal.";
402  throw WSignalSubscriptionFailed( s.str() );
403  break;
404  }
405 }
406 
407 const t_GenericSignalHandlerType WModule::getSignalHandler( MODULE_CONNECTOR_SIGNAL signal )
408 {
409  switch( signal )
410  {
411  case CONNECTION_ESTABLISHED:
412  return boost::bind( &WModule::notifyConnectionEstablished, this, _1, _2 );
413  case CONNECTION_CLOSED:
414  return boost::bind( &WModule::notifyConnectionClosed, this, _1, _2 );
415  case DATA_CHANGED:
416  return boost::bind( &WModule::notifyDataChange, this, _1, _2 );
417  default:
418  std::ostringstream s;
419  s << "Could not subscribe to unknown signal. You need to implement this signal type explicitly in your module.";
420  throw WSignalUnknown( s.str() );
421  break;
422  }
423 }
424 
426 {
427  return m_initialized;
428 }
429 
431 {
432  return m_isAssociated;
433 }
434 
436 {
437  return m_isUsable;
438  //return isInitialized() && isAssociated();
439 }
440 
442 {
443  return m_isReady;
444 }
445 
447 {
448  return m_isReadyOrCrashed;
449 }
450 
452 {
453  return m_isRunning;
454 }
455 
456 void WModule::notifyConnectionEstablished( boost::shared_ptr< WModuleConnector > /*here*/,
457  boost::shared_ptr< WModuleConnector > /*there*/ )
458 {
459  // By default this callback does nothing. Overwrite it in your module.
460 }
461 
462 void WModule::notifyConnectionClosed( boost::shared_ptr< WModuleConnector > /*here*/,
463  boost::shared_ptr< WModuleConnector > /*there*/ )
464 {
465  // By default this callback does nothing. Overwrite it in your module.
466 }
467 
468 void WModule::notifyDataChange( boost::shared_ptr< WModuleConnector > /*input*/,
469  boost::shared_ptr< WModuleConnector > /*output*/ )
470 {
471  // By default this callback does nothing. Overwrite it in your module.
472 }
473 
474 boost::shared_ptr< WProperties > WModule::getProperties() const
475 {
476  return m_properties;
477 }
478 
479 boost::shared_ptr< WProperties > WModule::getInformationProperties() const
480 {
481  return m_infoProperties;
482 }
483 
484 boost::shared_ptr< WProgressCombiner > WModule::getRootProgressCombiner()
485 {
486  return m_progress;
487 }
488 
489 const char** WModule::getXPMIcon() const
490 {
491  // return empty 1x1 icon by default.
492  static const char * o_xpm[] =
493  {
494  "1 1 1 1",
495  " c None",
496  " "
497  };
498  return o_xpm;
499 }
500 
502 {
503  m_isReady( true );
504  m_readyProgress->finish();
505  signal_ready( shared_from_this() );
506 }
507 
509 {
510  // simply iterate all requirements and return the first found that is not fulfilled
511  for( Requirements::const_iterator i = m_requirements.begin(); i != m_requirements.end(); ++i )
512  {
513  if( !( *i )->isComplied() )
514  {
515  return *i;
516  }
517  }
518 
519  return NULL;
520 }
521 
523 {
524  WLogger::getLogger()->addLogMessage( "Starting module main method.", "Module (" + getName() + ")", LL_INFO );
525 
526  // check requirements
527  const WRequirement* failedReq = checkRequirements();
528  if( failedReq )
529  {
530  throw WModuleRequirementNotMet( failedReq );
531  }
532 
533  // call main thread function
534  m_isRunning( true );
535  moduleMain();
536 
537  // NOTE: if there is any exception in the module thread, WThreadedRunner calls onThreadException for us. We can then disconnect the
538  // module and call our own error notification mechanism.
539 
540  // remove all pending connections. This is important as connections that still exists after module deletion can cause segfaults when they get
541  // disconnected in the connector destructor.
542  disconnect();
543  m_isRunning( false );
544 }
545 
547 {
548  // use our own error callback which includes the exact module pointer which caused the problem
549  signal_error( shared_from_this(), e );
550 
551  // ensure the module is properly disconnected
552  disconnect();
553 
554  // module is not running anymore.
555  m_isRunning( false );
556 
557  // let WThreadedRunner do the remaining tasks.
558  handleDeadlyException( e, "Module (" + getName() +")" );
559 }
560 
562 {
563  return wlog::info( getName() );
564 }
565 
567 {
568  return wlog::error( getName() );
569 }
570 
572 {
573  return wlog::debug( getName() );
574 }
575 
577 {
578  return wlog::warn( getName() );
579 }
580 
581 void WModule::setLocalPath( boost::filesystem::path path )
582 {
583  m_localPath = path;
584 }
585 
586 boost::filesystem::path WModule::getLocalPath() const
587 {
588  return m_localPath;
589 }
590 
591 void WModule::setLibPath( boost::filesystem::path path )
592 {
593  m_libPath = path;
594 }
595 
596 boost::filesystem::path WModule::getLibPath() const
597 {
598  return m_libPath;
599 }
600 
601 void WModule::setPackageName( std::string name )
602 {
603  m_packageName = name;
604 }
605 
606 std::string WModule::getPackageName() const
607 {
608  return m_packageName;
609 }
610 
612 {
613  return !deprecated().empty();
614 }
615 
617 {
618  return deprecated();
619 }
620