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