OpenWalnut  1.2.5
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
WModuleProjectFileCombiner.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 <iostream>
26 #include <map>
27 #include <set>
28 #include <list>
29 #include <string>
30 #include <utility>
31 
32 #include <boost/regex.hpp>
33 #include <boost/lexical_cast.hpp>
34 
35 #include "../WKernel.h"
36 #include "../WModuleCombiner.h"
37 #include "../WModuleFactory.h"
38 #include "../WModuleConnector.h"
39 #include "../WModule.h"
40 #include "../WDataModule.h"
41 #include "../WModuleInputConnector.h"
42 #include "../WModuleOutputConnector.h"
43 #include "../exceptions/WModuleConnectorNotFound.h"
44 
45 #include "../../common/exceptions/WFileNotFound.h"
46 #include "../../common/WProperties.h"
47 #include "../../common/WPropertyBase.h"
48 #include "../../common/WPropertyVariable.h"
49 #include "../../common/WPropertyTypes.h"
50 #include "../../common/WLogger.h"
51 #include "../../common/math/linearAlgebra/WLinearAlgebra.h"
52 
53 #include "WModuleProjectFileCombiner.h"
54 
55 WModuleProjectFileCombiner::WModuleProjectFileCombiner( boost::shared_ptr< WModuleContainer > target ):
56  WModuleCombiner( target ),
58 {
59 }
60 
62  WModuleCombiner( WKernel::getRunningKernel()->getRootContainer() ),
64 {
65 }
66 
68 {
69  // cleanup
70 }
71 
72 bool WModuleProjectFileCombiner::parse( std::string line, unsigned int lineNumber )
73 {
74  // this is the proper regular expression for modules
75  static const boost::regex modRe( "^ *MODULE:([0-9]*):(.*)$" );
76  static const boost::regex dataRe( "^ *DATA:([0-9]*):(.*)$" );
77  static const boost::regex conRe( "^ *CONNECTION:\\(([0-9]*),(.*)\\)->\\(([0-9]*),(.*)\\)$" );
78  static const boost::regex propRe( "^ *PROPERTY:\\(([0-9]*),(.*)\\)=(.*)$" );
79 
80  boost::smatch matches; // the list of matches
81  if( boost::regex_match( line, matches, modRe ) )
82  {
83  // it is a module line
84  // matches[1] is the ID
85  // matches[2] is the name of the module
86 
87  wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Module \"" << matches[2] << "\" with ID " << matches[1];
88 
89  // create a module instance
90  boost::shared_ptr< WModule > proto = WModuleFactory::getModuleFactory()-> isPrototypeAvailable( matches[2] );
91 
92  // data modules are not allowed here
93  if( !proto )
94  {
95  wlog::error( "Project Loader" ) << "There is no prototype available for module \"" << matches[2] << "\". Skipping.";
96  }
97  else if( proto->getType() == MODULE_DATA )
98  {
99  wlog::error( "Project Loader" ) << "Data modules are not allowed to be specified in a \"MODULE\" Statement." <<
100  " Use the \"DATA\" statement instead. Skipping.";
101  }
102  else
103  {
104  boost::shared_ptr< WModule > module = WModuleFactory::getModuleFactory()->create( proto );
105  m_modules.insert( ModuleID( boost::lexical_cast< unsigned int >( matches[1] ), module ) );
106  }
107  }
108  else if( boost::regex_match( line, matches, dataRe ) )
109  {
110  // it is a dataset line
111  // matches[1] is the ID
112  // matches[2] is the filename
113  wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Data \"" << matches[2] << "\" with ID " << matches[1];
114 
115  // create a module instance
116  boost::shared_ptr< WModule > proto = WModuleFactory::getModuleFactory()-> isPrototypeAvailable( "Data Module" );
117  if( !proto )
118  {
119  wlog::error( "Project Loader" ) << "There is no prototype available for module \"" << "Data Module" << "\"."
120  << " This should not happen!. Skipping.";
121  }
122  else
123  {
124  std::string parameter = std::string( matches[2] );
125  boost::shared_ptr< WModule > module = WModuleFactory::getModuleFactory()->create( proto );
126  if( parameter.empty() )
127  {
128  wlog::error( "Project Loader" ) << "Data modules need an additional filename parameter. Skipping.";
129  }
130  else
131  {
132  boost::shared_static_cast< WDataModule >( module )->setFilename( parameter );
133  m_modules.insert( ModuleID( boost::lexical_cast< unsigned int >( matches[1] ), module ) );
134  }
135  }
136  }
137  else if( boost::regex_match( line, matches, conRe ) )
138  {
139  // it is a connector line
140  // matches[1] and [2] are the module ID and connector name of the output connector
141  // matches[3] and [4] are the module ID and connector name of the target input connector
142 
143  wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Connection between \"" << matches[2] << "\" of module "
144  << matches[1] << " and \"" << matches[4] << "\" of module " << matches[3] << ".";
145 
146  // now we search in modules[ matches[1] ] for an output connector named matches[2]
147  m_connections.push_back( Connection( Connector( boost::lexical_cast< unsigned int >( matches[1] ), matches[2] ),
148  Connector( boost::lexical_cast< unsigned int >( matches[3] ), matches[4] ) ) );
149  }
150  else if( boost::regex_match( line, matches, propRe ) )
151  {
152  // it is a property line
153  // matches[1] is the module ID
154  // matches[2] is the property name
155  // matches[3] is the property value
156 
157  wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Property \"" << matches[2] << "\" of module " << matches[1]
158  << " set to " << matches[3];
159 
160  m_properties.push_back( PropertyValue( Property( boost::lexical_cast< unsigned int >( matches[1] ), matches[2] ), matches[3] ) );
161  }
162  else
163  {
164  return false;
165  }
166 
167  return true;
168 }
169 
171 {
172  // now add each module to the target container
173  for( std::map< unsigned int, boost::shared_ptr< WModule > >::const_iterator iter = m_modules.begin(); iter != m_modules.end(); ++iter )
174  {
175  m_container->add( ( *iter ).second );
176  }
177 
178  // now wait for the modules to get ready. We could have waited for this in the previous loop, but a long loading module would block others.
179  // -> so we wait after adding and starting them
180  for( std::map< unsigned int, boost::shared_ptr< WModule > >::iterator iter = m_modules.begin(); iter != m_modules.end(); ++iter )
181  {
182  ( *iter ).second->isReadyOrCrashed().wait();
183 
184  // if isReady now is false, the module has crashed before it got ready -> remove the module from the list
185  if( ( *iter ).second->isCrashed()() )
186  {
187  wlog::warn( "Project Loader" ) << "In the module with ID "
188  << ( *iter ).first
189  << " a problem occurred. Connections and properties relating to this"
190  << " module will fail.";
191  m_modules.erase( iter );
192  }
193  }
194 
195  // now, as we have created the modules, we need to set the properties for each of it.
196  for( std::list< PropertyValue >::const_iterator iter = m_properties.begin(); iter != m_properties.end(); ++iter )
197  {
198  // grab corresponding module
199  if( !m_modules.count( ( *iter ).first.first ) )
200  {
201  wlog::error( "Project Loader" ) << "There is no module with ID \"" << ( *iter ).first.first << "\" to set the property \"" <<
202  ( *iter ).first.second << "\" for. Skipping.";
203  continue;
204  }
205  boost::shared_ptr< WModule > m = m_modules[ ( *iter ).first.first ];
206 
207  // has this module the specified property?
208  boost::shared_ptr< WPropertyBase > prop = m->getProperties()->findProperty( ( *iter ).first.second );
209  if( !prop )
210  {
211  wlog::error( "Project Loader" ) << "The module \"" << m->getName() << "\" has no property named \"" <<
212  ( *iter ).first.second << "\". Skipping.";
213  continue;
214  }
215  else
216  {
217  if( prop->getPurpose() != PV_PURPOSE_INFORMATION )
218  {
219  // set the property here
220  bool result = prop->setAsString( ( *iter ).second );
221  if( !result )
222  {
223  wlog::error( "Project Loader" ) << "Failed to set property " << ( *iter ).first.second << " in module \"" <<
224  m->getName() << "\".";
225  }
226  }
227  else
228  {
229  wlog::error( "Project Loader" ) << "The module \"" << m->getName() << "\" has a property named \"" <<
230  ( *iter ).first.second << "\" which is an INFORMATION property. Skipping.";
231  }
232  }
233  }
234 
235  // and finally, connect them all together
236  for( std::list< Connection >::const_iterator iter = m_connections.begin(); iter != m_connections.end(); ++iter )
237  {
238  // each connection contains two connectors
239  Connector c1 = ( *iter ).first;
240  Connector c2 = ( *iter ).second;
241 
242  // each of these connectors contains the module ID and the connector name
243  // grab corresponding module 1
244  boost::shared_ptr< WModule > m1;
245  if( !m_modules.count( c1.first ) )
246  {
247  wlog::error( "Project Loader" ) << "There is no module with ID \"" << c1.first << "\" for the connection "
248  << "(" << c1.first << "," << c1.second << ")->(" << c2.first << "," << c2.second << "). Skipping.";
249 
250  continue;
251  }
252  m1 = m_modules[ c1.first ];
253 
254  boost::shared_ptr< WModule > m2;
255  if( !m_modules.count( c2.first ) )
256  {
257  wlog::error( "Project Loader" ) << "There is no module with ID \"" << c2.first << "\" for the connection "
258  << "(" << c1.first << "," << c1.second << ")->(" << c2.first << "," << c2.second << "). Skipping.";
259 
260  continue;
261  }
262  m2 = m_modules[ c2.first ];
263 
264  // now we have the modules referenced by the ID
265  // -> query the connectors
266  // NOTE: we assume the first connector to be an output connector!
267  boost::shared_ptr< WModuleOutputConnector > con1;
268  try
269  {
270  con1 = m1->getOutputConnector( c1.second );
271  }
272  catch( const WModuleConnectorNotFound& e )
273  {
274  wlog::error( "Project Loader" ) << "There is no output connector \"" << c1.second << "\" in module \"" << m1->getName() << "\"";
275  continue;
276  }
277  boost::shared_ptr< WModuleInputConnector > con2;
278  try
279  {
280  con2 = m2->getInputConnector( c2.second );
281  }
282  catch( const WModuleConnectorNotFound& e )
283  {
284  wlog::error( "Project Loader" ) << "There is no input connector \"" << c2.second << "\" in module \"" << m2->getName() << "\"";
285  continue;
286  }
287 
288  // finally, connect them
289  try
290  {
291  con1->connect( con2 );
292  }
293  catch( const WException& e )
294  {
295  wlog::error( "Project Loader" ) << "Connection " << "(" << c1.first << "," << c1.second << ")->(" << c2.first << "," << c2.second <<
296  ") could not be created. Incompatible connectors?. Skipping.";
297  continue;
298  }
299  }
300 
301  // clear all our lists (deref all contained pointers)
302  m_modules.clear();
303  m_connections.clear();
304  m_properties.clear();
305 }
306 
308 {
309  apply();
310 }
311 
312 void WModuleProjectFileCombiner::printProperties( std::ostream& output, boost::shared_ptr< WProperties > props, std::string indent, //NOLINT
313  std::string prefix, unsigned int module )
314 {
315  // lock, unlocked if l looses focus
316  WProperties::PropertySharedContainerType::ReadTicket l = props->getProperties();
317 
318  output << indent << "// Property Group: " << props->getName() << std::endl;
319 
320  // iterate of them and print them to output
321  for( WProperties::PropertyConstIterator iter = l->get().begin(); iter != l->get().end(); ++iter )
322  {
323  // information properties do not get written
324  if( ( *iter )->getPurpose () == PV_PURPOSE_INFORMATION )
325  {
326  continue;
327  }
328  if( ( *iter )->getType() != PV_GROUP )
329  {
330  output << indent + " " << "PROPERTY:(" << module << "," << prefix + ( *iter )->getName() << ")="
331  << ( *iter )->getAsString() << std::endl;
332  }
333  else
334  {
335  // it is a group -> recursively print it
336  if( prefix.empty() )
337  {
338  printProperties( output, ( *iter )->toPropGroup(), indent + " ", ( *iter )->getName() + "/", module );
339  }
340  else
341  {
342  printProperties( output, ( *iter )->toPropGroup(), indent + " ", prefix + ( *iter )->getName() + "/", module );
343  }
344  }
345  }
346 
347  output << indent << "// Property Group END: " << props->getName() << std::endl;
348 }
349 
350 void WModuleProjectFileCombiner::save( std::ostream& output ) // NOLINT
351 {
352  // grab access object of root container
354 
355  std::map< boost::shared_ptr< WModule >, unsigned int > moduleToIDMap;
356 
357  output << "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////" << std::endl <<
358  "// Modules and Properties" << std::endl <<
359  "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////" << std::endl <<
360  std::endl;
361 
362  // iterate all modules:
363  unsigned int i = 0;
364  for( WModuleContainer::ModuleConstIterator iter = container->get().begin(); iter != container->get().end(); ++iter )
365  {
366  // store the mapping of ptr to ID
367  moduleToIDMap[ ( *iter ) ] = i;
368 
369  // handle data modules separately
370  if( ( *iter )->getType() == MODULE_DATA )
371  {
372  output << "DATA:" << i << ":" << boost::shared_static_cast< WDataModule >( ( *iter ) )->getFilename() << std::endl;
373  }
374  else
375  {
376  output << "MODULE:" << i << ":" << ( *iter )->getName() << std::endl;
377  }
378 
379  // the properties:
380  printProperties( output, ( *iter )->getProperties(), "", "", i );
381 
382  // some readability:
383  output << std::endl;
384  ++i;
385  }
386 
387  // finally, process all connections for each module
388  output << "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////" << std::endl <<
389  "// Connections" << std::endl <<
390  "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////" << std::endl <<
391  std::endl;
392 
393 
394  // iterate over all modules
395  for( WModuleContainer::ModuleConstIterator iter = container->get().begin(); iter != container->get().end(); ++iter )
396  {
397  // iterate over all outputs
398  const WModule::OutputConnectorList& outs = ( *iter )->getOutputConnectors();
399  for( WModule::OutputConnectorList::const_iterator citer = outs.begin(); citer != outs.end(); ++citer )
400  {
401  // iterate over all connections:
402  // TODO(ebaum): iterating over a protected member variable? Thats ugly. This should be adopted to WSharedObject
403  boost::unique_lock<boost::shared_mutex> lock( ( *citer )->m_connectionListLock );
404  for( std::set<boost::shared_ptr<WModuleConnector> >::const_iterator iciter = ( *citer )->m_connected.begin();
405  iciter != ( *citer )->m_connected.end(); ++iciter )
406  {
407  // as the module is a weak_ptr -> lock and get access to it
408  boost::shared_ptr< WModule > theOtherModule = ( *iciter )->m_module.lock();
409  output << "CONNECTION:(" << moduleToIDMap[ ( *iter ) ] << "," << ( *citer )->getName() << ")->(" <<
410  moduleToIDMap[ theOtherModule ] << "," << ( *iciter )->getName() << ")" << std::endl;
411  }
412  lock.unlock();
413  }
414  }
415 }
416