comparison m-toolbox/html_help/help/ug/extensions_intro_content.html @ 0:f0afece42f48

Import.
author Daniele Nicolodi <nicolodi@science.unitn.it>
date Wed, 23 Nov 2011 19:22:13 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:f0afece42f48
1 <p>
2 As of Version 2.4, LTPDA now supports extension modules. This should allow
3 users to extend LTPDA to provide more specific functionalities for their own
4 context.
5 </p>
6 <p>
7 <ul>
8 <li><a href="#whatis">What is an Extension Module?</a></li>
9 <li><a href="#building">Building your own Extension Module</a></li>
10 <li><a href="#installing">Installing Extension Modules</a></li>
11 <li><a href="#newmethods">Adding New Methods</a></li>
12 <li><a href="#userclasses">New User Classes</a></li>
13 <li><a href="#unittests">Unit Tests</a></li>
14 </ul>
15 </p>
16 <br><br>
17 <h2><a name="whatis">What is an Extension Module?</a></h2>
18 <p>
19 Extension modules are a collection of class methods, new user classes, built-in models,
20 utility functions, examples, source files, and unit tests. All extension modules have the
21 following structure on disk:
22 <pre>
23 My_Module/
24 |-- README.txt
25 |-- classes
26 | |-- README_classes.txt
27 |-- examples
28 |-- functions
29 | |-- README_functions.txt
30 |-- jar
31 | |-- README_jar.txt
32 |-- models
33 | |-- README_models.txt
34 |-- moduleinfo.xml
35 |-- pipelines
36 | |-- README_pipelines.txt
37 |-- tests
38 |-- README_tests.txt
39 |-- classes
40 | |-- README_class_tests.txt
41 |-- models
42 |-- README_model_tests.txt
43 </pre>
44 The file <tt>moduleinfo.xml</tt> contains the name and version of the module. It is
45 not necessary that the module name and the containing directory are the same, though
46 they may be. To build an extension module, see Section <a href="#building">Building your own Extension Module</a>. The various
47 directories and their uses are described in the following sections.
48 </p>
49
50 <h3>classes</h3>
51 <p>
52 The <tt>classes</tt> directory can contain either new LTPDA user classes (see Section <a href="#userclasses">New User Classes</a>)
53 or by adding methods to existing LTPDA user classes (see Section <a href="#newmethods">Adding New Methods</a>).
54 </p>
55
56 <h3>examples</h3>
57 <p>
58 The <tt>examples</tt> directory is meant to contain useful examples for users.
59 </p>
60
61 <h3>jar</h3>
62 <p>
63 Since MATLAB is built on top of java, one useful way to extend the functionalities is to create java classes and methods, or even
64 graphical user interfaces. The LTPDA startup script (<tt>ltpda_startup</tt>) will take care of properly installing any jar files (java archive files)
65 contained within this directory.
66 </p>
67
68 <h3>models</h3>
69 <p>
70 Here you should place any built-in models, either for existing LTPDA user classes, or for new user classes defined in this module. The LTPDA
71 built-in model framework looks in this directory (and any sub-directories) for built-in models.
72 </p>
73
74 <h3>pipelines</h3>
75 <p>
76 This directory is meant to hold any LTPDA pipelines or analysis workflows which you want to distribute to users.
77 </p>
78
79 <h3>tests</h3>
80 <p>
81 The <tt>tests</tt> directory should contain unit-tests for all new class methods, user classes and built-in models in this module. For help in writing
82 unit tests see Section <a href="#unittests">Unit Tests</a>.
83 </p>
84
85
86 <br><br>
87 <h2><a name="building">Building your own Extension Module</a></h2>
88
89 <p>
90 Building your own extension modules starts by preparing the directory structure on disk. For this
91 we have a convenient utility method:
92 </p>
93 <div class="fragment"><pre>
94 >> utils.modules.buildModule(<span class="string">'/some/path/'</span>, <span class="string">'My_Module'</span>)
95 </pre></div>
96
97 <br><br>
98 <h2><a name="installing">Installing Extension Modules</a></h2>
99 <p>
100 Installing an LTPDA Extension Module is straightforward. Start the LTPDA Preferences:
101 </p>
102 <div class="fragment"><pre>
103 >> LTPDAprefs
104 </pre></div>
105 <p>
106 Select the 'Extensions' tab. Click the 'Browse' button to locate the module directory on disk, or directly type the path to the module in the input text field.
107 Click the 'plus' button to add this extension to the list. You should see some activity on the MATLAB terminal as LTPDA will start installing any
108 extension methods for existing user classes. Removing an extension module is just a case of selecting the module in the list and clicking the 'minus'
109 button.
110 </p>
111 <p>
112 <table cellspacing="0" class="note" summary="Note" cellpadding="5" border="1">
113 <tr width="90%">
114 <td>
115 Note: after installing an extension module, in order to make new methods available to the workbench, you need to rebuild the
116 library from the workbench menu: "Tools -> Rebuild LTPDA Library". This will take a couple of minutes, but afterwards
117 the new methods from the extension module should be available on the workbench.
118 </td>
119 </tr>
120 </table>
121 </p>
122
123 <br><br>
124 <h2><a name="newmethods">Adding New Methods</a></h2>
125 <p>
126 New methods can be added to existing LTPDA user classes. For example, you can add a new method to the Analysis Object class (<tt>ao</tt>)
127 by creating a sub-directory of the <tt>classes</tt> directory called <tt>ao</tt> then put your new method in there. For example, suppose
128 we want to create a new AO method called <tt>myCalibration</tt>. We create a directory <tt>ao</tt> in <tt>My_Module/classes</tt> then add
129 the new file <tt>myCalibration.m</tt> to that directory. In order for the new method to work, the LTPDA startup function <tt>ltpda_startup</tt>
130 copies all methods for core LTPDA user classes in to their correct class directories. In order to write a correct LTPDA user-class method, it
131 is recommended to look at some examples, such as <tt>ao/abs</tt>, <tt>ao/average</tt>, or <tt>ao/psd</tt>. You can also extend other existing user
132 classes in the same way. Just make a directory of the correct class name (remember to leave off the <tt>@</tt> from the directory name; this is not supposed to
133 be a 'normal' MATLAB class directory) and put your new methods in there.
134 </p>
135
136 <br><br>
137 <h2><a name="userclasses">New User Classes</a></h2>
138 <p>
139 This is an advanced topic, and it is assumed that you are familiary with
140 creating MATLAB classes already. To get familiar, read the MATLAB documentation on Object-Oriented Programming in the user manual.
141 </p>
142 <p>
143 You can create new user classes in the <tt>classes</tt> directory. These follow MATLAB rules for classes, i.e., the directory name begins with a <tt>@</tt>
144 and contains a constructor file with the same name. For example, suppose we want to create a new user class which stores trigger events from some experiment.
145 Each trigger event has the following properties: a trigger time, an amplitude, and a frequency. We would create a new directory under <tt>classes</tt> called
146 <tt>@Trigger</tt> and an associated constructor file like this:
147
148 </p>
149 <div class="fragment"><pre>
150 >> cd <span class="string">'My_Module/classes'</span>
151 >> mkdir <span class="string">@Trigger</span>
152 >> edit <span class="string">@Trigger/Trigger.m</span>
153 </pre></div>
154 <p>
155 The constructor file should declare that this new Trigger class is a subclass of the LTPDA user-object base class <tt>ltpda_uoh</tt>.
156 </p>
157 <div class="fragment"><pre>
158 <span class="comment">% TRIGGER constructor for Trigger class.</span>
159 <span class="comment">%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%</span>
160 <span class="comment">%</span>
161 <span class="comment">% DESCRIPTION: TRIGGER constructor for Trigger class.</span>
162 <span class="comment">%</span>
163 <span class="comment">% CONSTRUCTOR:</span>
164 <span class="comment">%</span>
165 <span class="comment">% t = Trigger() - creates an empty trigger object</span>
166 <span class="comment">%</span>
167 <span class="comment">% <a href="matlab:utils.helper.displayMethodInfo('Trigger', 'Trigger')">Parameter Sets</a></span>
168 <span class="comment">%</span>
169 <span class="comment">% VERSION: $Id: extensions_intro_content.html,v 1.2 2011/04/29 07:23:31 hewitson Exp $</span>
170 <span class="comment">%</span>
171 <span class="comment">%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%</span>
172
173 classdef Trigger < ltpda_uoh
174
175 %---------- Public (read/write) Properties ----------
176 properties
177 time = time();
178 amplitude = [];
179 frequency = [];
180 end
181
182 methods
183 function obj = Trigger(varargin)
184
185 import utils.const.*
186 utils.helper.msg(msg.PROC3, <span class="string">'running %s/%s'</span>, mfilename(<span class="string">'class'</span>), mfilename);
187
188 <span class="comment">% do some initialisation of the object</span>
189
190 end <span class="comment">% End constructor</span>
191
192 end
193
194 end
195 </pre></div>
196 <p>
197 Various methods need to be defined by any new user class, in particular, the following abstract methods need to be created:
198 <table cellspacing="0" class="body" cellpadding="4" border="2">
199 <colgroup>
200 <col width="20%">
201 <col width="80%">
202 </colgroup>
203 <thead>
204 <tr valign="top">
205 <th bgcolor="#B2B2B2">Method name</th>
206 <th bgcolor="#B2B2B2">Description</th>
207 </tr>
208 </thead>
209 <tbody>
210 <!-- attachToDom -->
211 <tr valign="top">
212 <td bgcolor="#F2F2F2">
213 <p>attachToDom</p>
214 </td>
215 <td bgcolor="#F2F2F2">
216 <p>Defines how the object is serialized to an XML DOM.</p>
217 </td>
218 </tr>
219 <!-- char -->
220 <tr valign="top">
221 <td bgcolor="#F2F2F2">
222 <p>char</p>
223 </td>
224 <td bgcolor="#F2F2F2">
225 <p>Creates a character representation of the object, typically for use in display.</p>
226 </td>
227 </tr>
228 <!-- copy -->
229 <tr valign="top">
230 <td bgcolor="#F2F2F2">
231 <p>copy</p>
232 </td>
233 <td bgcolor="#F2F2F2">
234 <p>Makes a deep or shallow copy of the object, depending on the passed argument.</p>
235 </td>
236 </tr>
237 <!-- display -->
238 <tr valign="top">
239 <td bgcolor="#F2F2F2">
240 <p>display</p>
241 </td>
242 <td bgcolor="#F2F2F2">
243 <p>Defines how the object is displayed on the MATLAB terminal.</p>
244 </td>
245 </tr>
246 <!-- fromDom -->
247 <tr valign="top">
248 <td bgcolor="#F2F2F2">
249 <p>fromDom</p>
250 </td>
251 <td bgcolor="#F2F2F2">
252 <p>Constructs an object from an XML DOM.</p>
253 </td>
254 </tr>
255 <!-- loadobj -->
256 <tr valign="top">
257 <td bgcolor="#F2F2F2">
258 <p>loadobj</p>
259 </td>
260 <td bgcolor="#F2F2F2">
261 <p>This function is called is MATLAB is unable to load an object from a MAT file, for example if the class structure changes between versions. This gives an opportunity to update the loaded structure before trying to create an object from it. For more details, see MATLAB's help topic <tt>>>help loadobj</tt></p>
262 </td>
263 </tr>
264 <!-- update_struct -->
265 <tr valign="top">
266 <td bgcolor="#F2F2F2">
267 <p>update_struct</p>
268 </td>
269 <td bgcolor="#F2F2F2">
270 <p>Define rules how to update structure representation of an object between versions.</p>
271 </td>
272 </tr>
273 <!-- generateConstructorPlist -->
274 <tr valign="top">
275 <td bgcolor="#F2F2F2">
276 <p>generateConstructorPlist</p>
277 </td>
278 <td bgcolor="#F2F2F2">
279 <p>Given an instance of the user object, this method generates a plist which can be used to construct an object with the same properties.</p>
280 </td>
281 </tr>
282 </tbody>
283 </table>
284 In most cases, copying these methods from an existing LTPDA user class, for example, <tt>ao</tt>, is a good start.
285 </p>
286 <p>
287 In addition to defining these abstract methods, you typically need to overload some static methods (which we usually place inside the class constructor file). The following
288 code fragment shows the necessary methods needed to complete our Trigger example.
289 </p>
290
291 <div class="fragment"><pre>
292 methods (Static)
293
294 <span class="comment">% This provides the hook for the command <tt>&lt;class&gt;.getBuiltInModels</tt>. </span>
295 function mdls = getBuiltInModels(varargin)
296 mdls = ltpda_uo.getBuiltInModels(<span class="string">'Trigger'</span>);
297 end
298
299 <span class="comment">% Here we typically return the CVS version or some other version string </span>
300 function out = VEROUT()
301 out = <span class="string">'$Id: extensions_intro_content.html,v 1.2 2011/04/29 07:23:31 hewitson Exp $'</span>;
302 end
303
304 <span class="comment">% This provides the hook for the command <tt>&lt;class&gt;.getInfo</tt>. </span>
305 function ii = getInfo(varargin)
306 ii = utils.helper.generic_getInfo(varargin{:}, <span class="string">'Trigger'</span>);
307 end
308
309 <span class="comment">% Here we return a list of parameter sets that this constructor can handle. </span>
310 function out = SETS()
311 out = [SETS@ltpda_uoh, ...
312 {'<span class="string">Default'</span>} ...
313 ];
314 end
315
316
317 <span class="comment">% This returns a parameter list for a given parameter set. </span>
318 <span class="comment">% The use of the MATLAB 'persistent' keyword means we don't repeatedly build the same plist.</span>
319 function plout = getDefaultPlist(set)
320 persistent pl;
321 persistent lastset;
322 if exist(<span class="string">'pl'</span>, <span class="string">'var'</span>)==0 || isempty(pl) || ~strcmp(lastset, set)
323 pl = Trigger.buildplist(set);
324 lastset = set;
325 end
326 plout = pl;
327 end
328
329 <span class="comment">% This builds a parameter list for the given set name. </span>
330 function out = buildplist(set)
331
332 if ~utils.helper.ismember(lower(Trigger.SETS), lower(set))
333 error(<span class="string">'### Unknown set [%s]'</span>, set);
334 end
335
336 out = plist();
337 out = Trigger.addGlobalKeys(out);
338 out = buildplist@ltpda_uoh(out, set);
339
340 <span class="comment">% Build the requested parameter list.</span>
341 switch lower(set)
342 case <span class="string">'Default'</span>
343 % time
344 p = param({<span class="string">'time'</span>,<span class="string">'The time of the trigger. Give either a string representation or a time object.'</span>}, paramValue.EMPTY_STRING);
345 out.append(p);
346
347 % amplitude
348 p = param({<span class="string">'amplitude'</span>,<span class="string">'The trigger amplitude.'</span>}, paramValue.EMPTY_DOUBLE);
349 out.append(p);
350
351 % frequency
352 p = param({<span class="string">'frequency'</span>,<span class="string">'The trigger frequency.'</span>}, paramValue.EMPTY_DOUBLE);
353 out.append(p);
354
355 end
356 end % function out = getDefaultPlist(varargin)
357
358 <span class="comment">% This creates arrays of the given size containing empty Trigger objects.</span>
359 function obj = initObjectWithSize(n,m)
360 obj = Trigger.newarray([n m]);
361 for ii = 1:numel(obj)
362 obj(ii).UUID = char(java.util.UUID.randomUUID);
363 end
364 end
365
366 end <span class="comment">% End static methods</span>
367
368 methods (Static, Access=protected)
369
370 <span class="comment">% Global keys are added to all parameter lists so that properties common to all</span>
371 <span class="comment">% user objects can be set.</span>
372 function pl = removeGlobalKeys(pl)
373 pl.remove(<span class="string">'name'</span>);
374 pl.remove(<span class="string">'description'</span>);
375 end
376
377 function pl = addGlobalKeys(pl)
378 % Name
379 p = param({<span class="string">'Name'</span>,<span class="string">'The name of the constructed trigger object.'</span>}, paramValue.STRING_VALUE(<span class="string">'None'</span>));
380 pl.append(p);
381
382 % Description
383 p = param({<span class="string">'Description'</span>,<span class="string">'The description of the constructed trigger object.'</span>}, paramValue.EMPTY_STRING);
384 pl.append(p);
385 end
386
387 end <span class="comment">% End static, private methods</span>
388
389 methods (Static = true, Hidden = true)
390 varargout = loadobj(varargin)
391 varargout = update_struct(varargin);
392 end
393
394 methods
395 varargout = char(varargin)
396 varargout = display(varargin)
397 varargout = copy(varargin)
398 end
399
400 methods (Hidden = true)
401 varargout = attachToDom(varargin)
402 end
403
404 methods (Access = protected)
405 obj = fromStruct(obj, a_struct)
406 varargout = fromDom(varargin)
407 end
408 </pre></div>
409
410
411 <br><br>
412 <h2><a name="unittests">Unit Tests</a></h2>
413
414 <p>
415 LTPDA provides a unit test framework that aims to make it easy to test your new methods, classes and built-in models. Unit tests are methods of a
416 unit test class. These unit test classes should inherit from one of the base classes <tt>ltpda_utp</tt> or <tt>ltpda_builtin_model_utp</tt>. The
417 directory <tt>ltpda_toolbox/ltpda/classes/tests/</tt> contains these test classes and examples for testing classes and built-in models. To run
418 the unit tests you can use the unit test runner class <tt>ltpda_test_runner</tt>. For example,
419 </p>
420 <div class="fragment"><pre>
421 >> ltpda_test_runner.RUN_ALL_TESTS
422 >> ltpda_test_runner.RUN_TESTS(<span class="string">'@my_class_tests'</span>)
423 >> ltpda_test_runner.RUN_TESTS(<span class="string">'@my_class_tests'</span>, <span class="string">'test_a_particular_method_feature'</span>)
424 </pre></div>
425
426
427