OpenWalnut  1.5.0dev
WMTemplateContainers.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 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
26 // This is a tutorial on how to re-use other modules and how to combine them into a new module, a so called container-module. Lets assume the
27 // following scenatio: we want a module to get a scalar dataset as input. It then Gauss-filters the data (smoothes it), and combines this with
28 // the original data again (multiply, add, substract,...). The result will be rendered as isosurface.
29 //
30 // Would you program this from scratch even though you already have a gauss module, a dataset arithmetic
31 // module and an isosurface module? In this example we will implement this scenario by re-using the existing tools of OpenWalnut.
32 //
33 // You will need the knowledge of these tutorials before you can go on:
34 // * WMTemplate
35 //
36 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
37 
38 #include <memory>
39 #include <string>
40 
41 #include "WMTemplateContainers.h"
42 #include "core/kernel/WKernel.h"
43 #include "core/kernel/WModuleFactory.h"
44 #include "core/kernel/WPrototypeRequirement.h"
45 
46 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
47 // Setting up the representation of THIS module to the outside world. So basically, this module's interface (properties and connectors).
48 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
49 
50 // Unlike a standard WModule, you have to specify the name and description of the container module in its constructor:
52  WModuleContainer( "Template Containers",
53  "Show the possibilities of combining and re-using existing modules in container modules." )
54 {
55  // Thats it. Nothing else is needed or different from WModule. You now have a container module.
56  //
57  // WARNING: As in WModule, creating connectors and properties inside the constructor will fail.
58 }
59 
61 {
62 }
63 
64 std::shared_ptr< WModule > WMTemplateContainers::factory() const
65 {
66  // NOTE: Refer to WMTemplate.cpp if you do not understand these commands.
67  // Same as in standard modules. Create your instance.
68  return std::shared_ptr< WModule >( new WMTemplateContainers() );
69 }
70 
72 {
73  // NOTE: Refer to WMTemplate.cpp if you do not understand these commands.
74 
75  // Remember the above scenario description? We want an scalar dataset to be modified and rendered. Thus we need an input where the user can
76  // define the scalar data. In a normal module, you would use WModuleInputData< WDataSetScalar >. But our arithmetic module already has this
77  // input. We just need to forward it:
78  m_input = WModuleInputForwardData< WDataSetScalar >::createAndAdd( shared_from_this(), "in", "The dataset to modify and display" );
79 
80  // So, now we have a connector which only forwards data without ever storing it. Imagine it as a gate for connections from the outside world
81  // to your own little world of modules.
82 
83  // Additionally, we would like to output the gaussed and recombined input data again. Maybe someone needs it. For this purpose, we also
84  // create a forwarding output connector:
85  m_output = WModuleOutputForwardData< WDataSetScalar >::createAndAdd( shared_from_this(), "out", "The noised input" );
86 
87  // HINT: always keep in mind that a module container itself is a module. You can add as much connectors of any type as you like. In this
88  // example, we only demonstrate forwarding connectors. For more details on standard connectors, have a look at WMTemplate.cpp
89 
90  // We do not need any connectors. Have a look at WMTemplate.cpp if you want to know what this means.
92 }
93 
95 {
96  // NOTE: Refer to WMTemplate.cpp if you do not understand these commands.
97 
98  // Add properties here. But please note, that we will also forward properties of nested modules. But this has to be done in the moduleMain
99  // method.
101 }
102 
104 {
105  // NOTE: Refer to WMTemplate.cpp if you do not understand these commands.
106 
107  // You already know this method from WMTemplate. We define the requirements of this module. In our case, we want to combine other modules
108  // in our container, so we need to tell OpenWalnut that we require them:
109  m_requirements.push_back( new WPrototypeRequirement( "Scalar Operator" ) );
110  m_requirements.push_back( new WPrototypeRequirement( "Gauss Filtering" ) );
111  m_requirements.push_back( new WPrototypeRequirement( "Isosurface" ) );
112 
113  // Thats it. Although the Isosurface module requires a running graphics engine, we are not needed to add a WGERequirement here, as the
114  // requirements of the nested modules get propagated automatically.
115 }
116 
117 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
118 // We now setup the contents of the container.
119 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
120 
122 {
123  m_moduleState.setResetable( true, true );
124  //m_moduleState.add( m_propCondition );
125 
126  // Now it gets interesting. At this point, we create the nested modules and wire them up.
127 
128  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
129  // 1) Create all modules
130  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
131 
132  //////////////////////////////////////////////////////////////////////////////////
133  // Gauss filter
134 
135  // Lets create our noise generator. We use the module factory to create it for us:
136  WModule::SPtr gauss = WModuleFactory::getModuleFactory()->create( "Gauss Filtering" );
137  // Please note that the specified module might not exist. BUT as we specified this module as a requirement, our module would not be run in
138  // this case. So, we can safely assume that we get a proper module pointer here.
139 
140  // Add the module to THIS container
141  add( gauss );
142 
143  // Wait for the module to finish initialization. This is very important. You are not allowed to use or access the module before it has called
144  // its ready() method.
145  gauss->isReady().wait();
146 
147  //////////////////////////////////////////////////////////////////////////////////
148  // Scalar Operator
149 
150  // Create the scalar operator. This is all the same as above.
151  // 1) Create
152  WModule::SPtr scalarOp = WModuleFactory::getModuleFactory()->create( "Scalar Operator" );
153  // 2) Add
154  add( scalarOp );
155  // 3) Wait
156  scalarOp->isReady().wait();
157 
158  //////////////////////////////////////////////////////////////////////////////////
159  // Isosurface
160 
161  // Create the scalar operator. This is all the same as above.
162  // 1) Create
163  WModule::SPtr iso = WModuleFactory::getModuleFactory()->create( "Isosurface" );
164  // 2) Add
165  add( iso );
166  // 3) Wait
167  iso->isReady().wait();
168 
169  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
170  // 2) Setup the properties
171  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
172 
173  // Ok. Let us summarize. We have created our modules, added them to THIS container and ensured that they are ready.
174  // We can go on and configure the modules using their properties and forward some of them to the user of this module.
175 
176  // We start by changing the amount of smoothing in the gauss module:
177  gauss->getProperties()->getProperty( "Iterations" )->set( 5 );
178  // Let us analyze the above line:
179  // 1) we query all properties of the module
180  // 2) we query a property called "Iterations" in the module's main property group
181  // 3) we set the value
182  // Be warned! There might be one problem: the name of the property might be wrong. In this case, getProperty thows an exception. You should
183  // take care about this. If not, your module will forward the exception to the top-level container -> marking your module as crashed.
184  //
185  // So basically you have three options:
186  // 1) risk the exception and let module fail in this case
187  // 2) catch the exception and try to circumvent the parameter (might not always be possible, good option if this only is an non-important
188  // parameter)
189  // 3) use WPropertyBase::SPtr iter = gauss->getProperties()->findProperty( "Iterations" ); and check this for NULL. Same
190  // advantages/disadvantages as 2).
191 
192  // We now want the user to be able to choose the way the original data and the gaussed one get merged. For this, we forward the properties of
193  // the scalar operator module:
194  m_properties->addProperty( scalarOp->getProperties()->getProperty( "Operation" ) );
195  // and we want another default op:
196  scalarOp->getProperties()->getProperty( "Operation" )->set( 1 ); // the index 1 operation is A-B
197 
198  // The above line showed how to add single properties. But as the WPropGroup also is a property, you can add all properties of a module too:
199  m_properties->addProperty( iso->getProperties() );
200  iso->getProperties()->getProperty( "Iso value" )->set( 1 );
201 
202  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
203  // 3) Wire them up
204  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
205 
206  // We now have modules inside our container and configured them as we like. Now, it is time to connect the modules to each other and to the
207  // outside world via our forwarding connectors we set up in the connectors() method.
208 
209  // First, connect the modules with each other:
210 
211  // Set the gauss version as the first operant by querying the connectors and connecting them:
212  scalarOp->getInputConnector( "operandA" )->connect( gauss->getOutputConnector( "out" ) );
213  // This is the same as doing it the other way around.
214  // gauss->getOutputConnector( "out" )->connect( scalarOp->getInputConnector( "operandA" ) );
215  // It is important to understand that the connectors need to be compatible and connectable.
216  // * compatibility means that the transfer type (the type you specify as template parameter for the connectors) can be cast into each other
217  // * connectible means that
218  // * you can only connect inputs to outputs (or outputs to inputs)
219  // * you can only connect one output to a single input
220  //
221  // Keep this in mind.
222 
223  // Use the arithmetic result as input for the isosurface:
224  iso->getInputConnector( "values" )->connect( scalarOp->getOutputConnector( "result" ) );
225 
226  // Now, we connect our forwarding connectors:
227  m_input->forward( gauss->getInputConnector( "in" ) );
228  m_input->forward( scalarOp->getInputConnector( "operandB" ) );
229 
230  // And also forward the result:
231  m_output->forward( scalarOp->getOutputConnector( "result" ) );
232 
233  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
234  // 4) Running and stopping
235  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
236 
237  // You remember that this container is a module for itself? The you will remember to say "ready":
238  ready();
239  // You are allowed to do this before you add/configure/wire the modules as above. BUT there is one problem. OpenWalnut assumes that you have
240  // set up all properties of your modules before calling ready(). So if we would have signalled ready before adding all properties, then a
241  // project file could not be loaded properly due to the missing properties.
242 
243  // In a standard module, you would now start the main loop. If you are not interested in any further interaction with the modules or your own
244  // properties, you can simply wait until OpenWalnut tells your module to stop:
245  waitForStop();
246 
247  // BUT you can also implement your module main loop here. Set properties, react on property changes, change connections between module and so
248  // on. Please refer to WMTemplate.cpp for details on how this works.
249  //
250  // KEEP IN MIND: properties, connectors and modules provide WConditions and signals for nearly everything. You can register to them and thus
251  // get notified about the things you are interested in. This allows a maximum of possibilities to interact with modules and to build highly
252  // dynamic module graphs inside your container module.
253 
254 
255  // This is an important, additional step. We need to tell our nested modules to stop working:
256  debugLog() << "Shutting down ...";
257  stop();
258  // If you would leave this out, you run into a serious problem: your modules are still running while they get destroyed at the end of this
259  // function (as they are shared_ptr defined in this scope). So if you experience crashes at the end of your container module, you probably
260  // missed the stop call!
261 }
262 
void setResetable(bool resetable=true, bool autoReset=true)
Sets the resetable flag.
WModuleOutputForwardData< WDataSetScalar >::SPtr m_output
Define an output connector, which only forwards the data.
virtual std::shared_ptr< WModule > factory() const
Due to the prototype design pattern used to build modules, this method returns a new instance of this...
virtual ~WMTemplateContainers()
Destructor.
virtual void connectors()
Initialize the connectors this module is using.
virtual void requirements()
Initialize requirements for this module.
virtual void properties()
Initialize the properties for this module.
virtual void moduleMain()
Entry point after loading the module.
WModuleInputForwardData< WDataSetScalar >::SPtr m_input
Define an input connector, which only forwards the data.
Class able to contain other modules.
virtual void stop()
Stops all modules inside this container.
virtual void add(std::shared_ptr< WModule > module, bool run=true)
Add a module to this container and start it.
static SPtr getModuleFactory()
Returns instance of the module factory to use to create modules.
static PtrType createAndAdd(std::shared_ptr< WModule > module, std::string name="", std::string description="")
Convenience method to create a new instance of this in forward data connector with proper type and ad...
virtual void forward(std::shared_ptr< WModuleConnector > to)
Forward the input to the specified input.
virtual void forward(std::shared_ptr< WModuleConnector > from)
Forward the output to the specified output.
static PtrType createAndAdd(std::shared_ptr< WModule > module, std::string name="", std::string description="")
Convenience method to create a new instance of this out data connector with proper type and add it to...
std::shared_ptr< WModule > SPtr
Shared pointer to a WModule.
Definition: WModule.h:106
Requirements m_requirements
The list of requirements.
Definition: WModule.h:754
virtual void properties()
Initialize properties in this function.
Definition: WModule.cpp:212
wlog::WStreamedLogger debugLog() const
Logger instance for comfortable debug logging.
Definition: WModule.cpp:575
std::shared_ptr< WProperties > m_properties
The property object for the module.
Definition: WModule.h:640
void ready()
Call this whenever your module is ready and can react on property changes.
Definition: WModule.cpp:505
WConditionSet m_moduleState
The internal state of the module.
Definition: WModule.h:703
virtual void connectors()
Initialize connectors in this function.
Definition: WModule.cpp:208
This requirement ensures that the specified prototype exists in the factory.
void waitForStop()
Let the thread sleep until a stop request was given.