OpenWalnut  1.5.0dev
WMTemplateDataLoader.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 create custom data loading modules.
27 //
28 // You will need the knowledge of these tutorials before you can go on:
29 // * WMTemplate
30 //
31 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
32 
33 #include <memory>
34 #include <string>
35 #include <vector>
36 
37 #include "WMTemplateDataLoader.h"
38 #include "core/kernel/WDataModuleInputFile.h"
39 #include "core/kernel/WDataModuleInputFilterFile.h"
40 #include "core/kernel/WKernel.h"
41 
42 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
43 // All the basic setup ... You already know this from WMTemplate.
44 //
45 // BUT: there is one difference: you derive from WDataModule instead of WModule. Accordingly, you have to change the constructor initializer list a
46 // bit. WDataModule itself is derived from WModule, so you basically have a module with some additional functionality to load data. Also the
47 // life-cycle of WDataModule is the same. But it is important to understand the way OpenWalnut starts WDataModules. So we assume, the user wants to
48 // load something. He either drags a file into the OpenWalnut window, uses the file-open dialog or simply starts your WDataModule:
49 //
50 // 1) OpenWalnut uses the method getInputFilter of the prototype of your WDataModule. The module is NOT instanceiated at this point.
51 // -> return a list of filters that define the allowed types of input
52 // 2) OpenWalnut creates an instance of your WDataModule whenever one of the input filters match
53 // -> this happens exactly as you know it from WModule
54 // -> your module starts to run
55 // 3) The final step is that OpenWalnut sets the WDataModuleInput, which then triggers your handleInputChange method
56 // -> this is done from another thread, not your module-thread!
57 // --> so this happens sooner or later.
58 // -> use this to notify your module thread to load something
59 // -> this might be done multiple times (the user changes the file for the module)
60 // --> write proper code to support loading multiple times!
61 //
62 // Thats it. To summarize: OW asks your module for a list of input filters. These filters are used to find a proper WDataModuleInput. OW creates
63 // your module, adds it to the module root container, starts it and sets the WDataModuleInput sooner or later.
64 //
65 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
66 
68  : WDataModule() // NOTE: thats new.
69 {
70 }
71 
73 {
74 }
75 
76 std::shared_ptr< WModule > WMTemplateDataLoader::factory() const
77 {
78  // NOTE: Refer to WMTemplate.cpp if you do not understand these commands.
79  return std::shared_ptr< WModule >( new WMTemplateDataLoader() );
80 }
81 
82 const std::string WMTemplateDataLoader::getName() const
83 {
84  // NOTE: Refer to WMTemplate.cpp if you do not understand these commands.
85  return "Template Data Loader";
86 }
87 
88 const std::string WMTemplateDataLoader::getDescription() const
89 {
90  // NOTE: Refer to WMTemplate.cpp if you do not understand these commands.
91  return "Load a dummy file from disk.";
92 }
93 
94 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
95 // ATTENTION: now it gets interesting ...
96 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
97 
99 {
100  // As WDataModule is also a WModule, you can create output connectors as you are used to.
101  // Data modules of course should have at least one output connectors. This is not enforced, which might come in handy
102  // when loading data directly to the colormapper without providing it to other modules.
103 
104  // NOTE: Refer to WMTemplate.cpp if you do not understand these commands.
105  m_output = WModuleOutputData < WDataSetScalar >::createAndAdd( shared_from_this(), "out", "The loaded dataset" );
107 }
108 
110 {
111  // Again, as WDataModule is a standard module, you can define arbitrary properties here. They can modify the loaded data
112  // or something similar. But be aware that these properties are not shown to the user before the data module was added and
113  // got its filename/path/url/whatever to load. So it is not useful for configure the loader before loading. There is another
114  // way to define properties which are used before creating and starting the WDataModule. Read on!
115 
116  // NOTE: Refer to WMTemplate.cpp if you do not understand these commands.
117  m_propCondition = std::shared_ptr< WCondition >( new WCondition() );
118  // Add your properties as you are used to:
119  // m_enableFeature = m_properties->addProperty( "Enable feature", "Description.", true );
120 
121  // Never forget to call this:
123 }
124 
125 // You will need to implement this function. It defines a list of filters of inputs your module supports. This method is called
126 // before instancing the module (called for your module prototype instance).
127 std::vector< WDataModuleInputFilter::ConstSPtr > WMTemplateDataLoader::getInputFilter() const
128 {
129  // The list of filters:
130  std::vector< WDataModuleInputFilter::ConstSPtr > filters;
131 
132  // Now, let us add some filters. OpenWalnut already provides a file-based filter for convenience.
133  filters.push_back( WDataModuleInputFilter::ConstSPtr(
135  "owfun", // the extension of the file, without "."
136  "OpenWalnut Demo File Type" // a description shown in OpenWalnut for this kind of files
137  ) ) );
138 
139  // thats it. OpenWalnut will use this filter whenever the user tries to load some data. If it matches, your module
140  // will be created and started.
141 
142  // But wait! There is more. How about writing your own input filters? This can be interesting if you only allow
143  // a certain subset of files with a given extension. Maybe you are only able to load scalar fields from files that support
144  // a wide range of internal formats? Then writing your own input filter, derived from WDataModuleInputFilterFile is the
145  // best choice.
146 
147  // IMPORTANT: right now (OpenWalnut 1.4), there are some limitations that will be resolved soon:
148  //
149  // * there is NO support for other input filters. We are working hard to add this.
150  // * two modules which match the same file type: OpenWalnut chooses the last loaded module -> yes, ugly.
151  // * no support for properties in input filters
152  //
153  // Please stay tuned. It requires some work in the GUI to support generic input filter types. You can follow the progress on
154  // feature #32 in the OpenWalnut bug tracker.
155 
156  return filters;
157 }
158 
159 // So let us assume, the user has chosen a file/path/url/... to load. OpenWalnut creates and runs an instance of this data module
160 // and sets the new WDataModuleInput. This causes this method to be called. It is NOT called from within a certain thread, like the module thread.
161 // You should strictly avoid actually loading data in here. This might freeze other threads. Instead, use conditions, flags, properties or
162 // more to wake up your module thread and load the data there.
163 //
164 // IMPORTANT: please keep in mind that this can be called multiple times, whenever the user changes the WDataModuleInput! A typical use-case
165 // is when the user changes the file in an already existing data module instance OR the user triggers a reload button. So take care that your
166 // data module is able to load multiple times.
168 {
169  // A very simple way of achieving this is to define a flag. We call it m_reload. This loads the stuff defined in the WDataModuleInput again. No
170  // matter whether this is a new input or not.
171 
172  // Set flag.
173  m_reload = true;
174 
175  // wakeup the thread
177 }
178 
180 {
181  // NOTE: an easy way to wake up the thread, even if it is not waiting: use resetable condition sets:
182  m_moduleState.setResetable( true, true );
183 
184  // Now, we can mark the module ready.
185  ready();
186 
187  // Wait until the project file loader finished.
188  waitRestored();
189 
190  // Now the remaining module code. In our case, this is empty.
191  while( !m_shutdownFlag() )
192  {
193  // Usually, this waits. But IF there was a call to handleInputChange already, this immediately wakes up again and does the first load.
194  // This behaviour can be changed using the setResetable method of the condition set.
196  if( m_shutdownFlag() )
197  {
198  break;
199  }
200 
201  // Are we forced to load something?
202  if( m_reload )
203  {
204  load();
205  }
206  }
207 
208  // Never miss to clean up.
209  debugLog() << "Shutting down ...";
210 }
211 
212 // Finally, this is our load routine. This is called from within the moduleMain method. Never call this from handleInputChange!
214 {
215  // Reset the flag.
216  m_reload = false;
217 
218  // Query the new input. We defined a file input filter, thus we get a WDataModuleInputFile. If you implement your own input filters that create
219  // custom WDataModuleInput instances, you can query them in the same fashion.
220  WDataModuleInputFile::SPtr inputFile = getInputAs< WDataModuleInputFile >();
221  // Be aware, that the input might also be NULL or cannot be cast.
222  if( !inputFile )
223  {
224  // No input? Reset output too. You should do this. The user might want to remove the input and make the output empty again.
225  m_output->updateData( WDataSetScalar::SPtr() );
226  return;
227  }
228 
229  // Now implement your loading .......
230  boost::filesystem::path p = inputFile->getFilename();
231  infoLog() << "Start Loading from \"" << p.string() << "\"";
232 
233  // also add some progress to give feedback:
234  std::shared_ptr< WProgress > progress( new WProgress( "Loading" ) );
235  m_progress->addSubProgress( progress );
236 
237  // LOAD LOAD LOAD LOAD
238 
239  progress->finish();
240  m_progress->removeSubProgress( progress );
241  infoLog() << "Done";
242 }
243 
virtual void wait() const
Wait for the condition.
void setResetable(bool resetable=true, bool autoReset=true)
Sets the resetable flag.
virtual void notify()
Notifies all waiting threads.
Class to encapsulate boost::condition_variable_any.
Definition: WCondition.h:42
std::shared_ptr< WDataModuleInputFile > SPtr
Convenience typedef for a std::shared_ptr< WDataModuleInputFile >.
Checks a given WDataModuleInput against a file type.
std::shared_ptr< const WDataModuleInputFilter > ConstSPtr
Convenience typedef for a std::shared_ptr< const WDataModuleInputFilter >.
Base for all data loader modules.
Definition: WDataModule.h:47
std::shared_ptr< WDataSetScalar > SPtr
shared_ptr abbreviation
virtual void handleInputChange()
Handle a newly set input.
virtual void connectors()
Initialize the connectors this module is using.
virtual ~WMTemplateDataLoader()
Destructor.
virtual void load()
This contains the code to load the data.
virtual std::vector< WDataModuleInputFilter::ConstSPtr > getInputFilter() const
Define a list of file filters we support.
std::shared_ptr< WCondition > m_propCondition
A condition used to notify about changes in several properties.
virtual void properties()
Initialize the properties for this module.
bool m_reload
True if the load function needs to be called.
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 void moduleMain()
Entry point after loading the module.
std::shared_ptr< WModuleOutputData< WDataSetScalar > > m_output
The output connector for the data.
virtual const std::string getName() const
Gives back the name of this module.
virtual const std::string getDescription() const
Gives back a description of this module.
Class offering an instantiate-able data connection between modules.
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
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
std::shared_ptr< WProgressCombiner > m_progress
Progress indicator used as parent for all progress' of this module.
Definition: WModule.h:652
wlog::WStreamedLogger infoLog() const
Logger instance for comfortable info logging.
Definition: WModule.cpp:565
virtual void connectors()
Initialize connectors in this function.
Definition: WModule.cpp:208
void waitRestored()
This method waits for the module to be restored completely.
Definition: WModule.cpp:625
Class managing progress inside of modules.
Definition: WProgress.h:42
WBoolFlag m_shutdownFlag
Condition getting fired whenever the thread should quit.