Examples
This chapter gathers a series of macro examples distributed with Sardana.
The example modules shown below can be found in the
sardana.macroserver.macros.examples package.
Macro examples
Macro parameter examples
The following examples demonstrate how to declare macros which receive parameter(s). See Adding parameters to your macro for a tutorial on macro parameters.
1##############################################################################
2##
3# This file is part of Sardana
4##
5# http://www.sardana-controls.org/
6##
7# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
8##
9# Sardana is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Lesser General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13##
14# Sardana is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Lesser General Public License for more details.
18##
19# You should have received a copy of the GNU Lesser General Public License
20# along with Sardana. If not, see <http://www.gnu.org/licenses/>.
21##
22##############################################################################
23
24"""This module contains macros that demonstrate the usage of macro parameters"""
25
26from sardana.macroserver.macro import Macro, Type
27
28__all__ = [
29 "pt0",
30 "pt1",
31 "pt2",
32 "pt3",
33 "pt3d",
34 "pt4",
35 "pt5",
36 "pt6",
37 "pt7",
38 "pt7d1",
39 "pt7d2",
40 "pt8",
41 "pt9",
42 "pt10",
43 "pt11",
44 "pt12",
45 "pt13",
46 "pt14",
47 "pt14d",
48 "twice",
49]
50
51
52class pt0(Macro):
53 """Macro without parameters. Pretty dull.
54 Usage from Spock, ex.:
55 pt0
56 """
57
58 param_def = []
59
60 def run(self):
61 pass
62
63
64class pt1(Macro):
65 """Macro with one float parameter: Each parameter is described in the
66 param_def sequence as being a sequence of four elements: name, type,
67 default value and description.
68 Usage from Spock, ex.:
69 pt1 1
70 """
71
72 param_def = [["value", Type.Float, None, "some bloody float"]]
73
74 def run(self, f):
75 pass
76
77
78class pt1d(Macro):
79 """Macro with one float parameter with default value..
80 Usage from Spock, ex.:
81 pt1d 1
82 pt1d
83 """
84
85 param_def = [["value", Type.Float, None, "some bloody float"]]
86
87 def run(self, f):
88 pass
89
90
91class pt2(Macro):
92 """Macro with one Motor parameter: Each parameter is described in the
93 param_def sequence as being a sequence of four elements: name, type,
94 default value and description.
95 Usage from Spock, ex.
96 pt2 mot1
97 """
98
99 param_def = [["motor", Type.Motor, None, "some bloody motor"]]
100
101 def run(self, m):
102 pass
103
104
105class pt3(Macro):
106 """Macro with a list of numbers as parameter: the type is a sequence of
107 parameter types which is repeated. In this case it is a repetition of a
108 float so only one parameter is defined.
109 By default the repetition as a semantics of 'at least one'
110 Usages from Spock, ex.:
111 pt3 [1 34 15]
112 pt3 1 34 15
113 """
114
115 param_def = [
116 ["numb_list", [["pos", Type.Float, None, "value"]], None, "List of values"],
117 ]
118
119 def run(self, *args, **kwargs):
120 pass
121
122
123class pt3d(Macro):
124 """Macro with a list of numbers as parameter: the type is a sequence of
125 parameter types which is repeated. In this case it is a repetition of a
126 float so only one parameter is defined. The parameter has a default value.
127 By default the repetition as a semantics of 'at least one'
128 Usages from Spock, ex.:
129 pt3d [1 34 15]
130 pt3d 1 34 15
131 Usage taken the default value, ex.:
132 pt3d [1 [] 15]
133 """
134
135 param_def = [
136 ["numb_list", [["pos", Type.Float, 21, "value"]], None, "List of values"],
137 ]
138
139 def run(self, *args, **kwargs):
140 pass
141
142
143class pt4(Macro):
144 """Macro with a list of motors as parameter: the type is a sequence of
145 parameter types which is repeated. In this case it is a repetition of a
146 motor so only one parameter is defined.
147 By default the repetition as a semantics of 'at least one'.
148 Usages from Spock, ex.:
149 pt4 [mot1 mot2 mot3]
150 pt4 mot1 mot2 mot3
151 """
152
153 param_def = [
154 [
155 "motor_list",
156 [["motor", Type.Motor, None, "motor name"]],
157 None,
158 "List of motors",
159 ],
160 ]
161
162 def run(self, *args, **kwargs):
163 pass
164
165
166class pt5(Macro):
167 """Macro with a motor parameter followed by a list of numbers.
168 Usages from Spock, ex.:
169 pt5 mot1 [1 3]
170 pt5 mot1 1 3
171 """
172
173 param_def = [
174 ["motor", Type.Motor, None, "Motor to move"],
175 ["numb_list", [["pos", Type.Float, None, "value"]], None, "List of values"],
176 ]
177
178 def run(self, *args, **kwargs):
179 pass
180
181
182class pt6(Macro):
183 """Macro with a motor parameter followed by a list of numbers. The list as
184 explicitly stated an optional last element which is a dictionary that defines the
185 min and max values for repetitions.
186 Usages from Spock, ex.:
187 pt6 mot1 [1 34 1]
188 pt6 mot1 1 34 1
189 """
190
191 param_def = [
192 ["motor", Type.Motor, None, "Motor to move"],
193 [
194 "numb_list",
195 [["pos", Type.Float, None, "value"], {"min": 1, "max": None}],
196 None,
197 "List of values",
198 ],
199 ]
200
201 def run(self, *args, **kwargs):
202 pass
203
204
205class pt7(Macro):
206 """Macro with a list of pair Motor,Float.
207 Usages from Spock, ex.:
208 pt7 [[mot1 1] [mot2 3]]
209 pt7 mot1 1 mot2 3
210 """
211
212 param_def = [
213 [
214 "m_p_pair",
215 [
216 ["motor", Type.Motor, None, "Motor to move"],
217 ["pos", Type.Float, None, "Position to move to"],
218 ],
219 None,
220 "List of motor/position pairs",
221 ]
222 ]
223
224 def run(self, *args, **kwargs):
225 pass
226
227
228class pt7d1(Macro):
229 """Macro with a list of pair Motor,Float. Default value for last
230 repeat parameter element.
231 Usages from Spock, ex.:
232 pt7d1 [[mot1 1] [mot2 3]]
233 pt7d1 mot1 1 mot2 3
234 Using default value, ex.:
235 pt7d1 [[mot1] [mot2 3]] # at any repetition
236 pt7d1 mot1 # if only one repetition
237
238 """
239
240 param_def = [
241 [
242 "m_p_pair",
243 [
244 ["motor", Type.Motor, None, "Motor to move"],
245 ["pos", Type.Float, 2, "Position to move to"],
246 ],
247 None,
248 "List of motor/position pairs",
249 ]
250 ]
251
252 def run(self, *args, **kwargs):
253 pass
254
255
256class pt7d2(Macro):
257 """Macro with a list of pair Motor,Float. Default value for both
258 repeat parameters elements.
259 Usages from Spock, ex.:
260 pt7d2 [[mot1 1] [mot2 3]]
261 pt7d2 mot1 1 mot2 3
262 Using both default values, ex.:
263 pt7d2 [[] [mot2 3] []] # at any repetition
264 """
265
266 param_def = [
267 [
268 "m_p_pair",
269 [
270 ["motor", Type.Motor, "mot1", "Motor to move"],
271 ["pos", Type.Float, 2, "Position to move to"],
272 ],
273 None,
274 "List of motor/position pairs",
275 ]
276 ]
277
278 def run(self, *args, **kwargs):
279 pass
280
281
282class pt8(Macro):
283 """Macro with a list of pair Motor,Float. The min and max elements have been
284 explicitly stated.
285 Usages from Spock, ex.:
286 pt8 [[mot1 1] [mot2 3]]
287 pt8 mot1 1 mot2 3
288 """
289
290 param_def = [
291 [
292 "m_p_pair",
293 [
294 ["motor", Type.Motor, None, "Motor to move"],
295 ["pos", Type.Float, None, "Position to move to"],
296 {"min": 1, "max": 2},
297 ],
298 None,
299 "List of motor/position pairs",
300 ]
301 ]
302
303 def run(self, *args, **kwargs):
304 pass
305
306
307class pt9(Macro):
308 """Same as macro pt7 but with min and max number of repetitions of the
309 repeat parameter.
310 Usages from Spock, ex.:
311 pt9 [[mot1 1][mot2 3]]
312 pt9 mot1 1 mot2 3
313 """
314
315 param_def = [
316 [
317 "m_p_pair",
318 [
319 ["motor", Type.Motor, None, "Motor to move"],
320 ["pos", Type.Float, None, "Position to move to"],
321 {"min": 1, "max": 2},
322 ],
323 None,
324 "List of motor/position pairs",
325 ],
326 ]
327
328 def run(self, *args, **kwargs):
329 pass
330
331
332class pt10(Macro):
333 """Macro with list of numbers followed by a motor parameter. The repeat
334 parameter may be defined as first one.
335 Usage from Spock, ex.:
336 pt10 [1 3] mot1
337 pt10 1 mot1 # if only one repetition
338 """
339
340 param_def = [
341 ["numb_list", [["pos", Type.Float, None, "value"]], None, "List of values"],
342 ["motor", Type.Motor, None, "Motor to move"],
343 ]
344
345 def run(self, *args, **kwargs):
346 pass
347
348
349class pt11(Macro):
350 """Macro with counter parameter followed by a list of numbers, followed by
351 a motor parameter. The repeat parameter may be defined in between other
352 parameters.
353 Usages from Spock, ex.:
354 pt11 ct1 [1 3] mot1
355 pt11 ct1 1 mot1 # if only one repetition
356 """
357
358 param_def = [
359 ["counter", Type.ExpChannel, None, "Counter to count"],
360 ["numb_list", [["pos", Type.Float, None, "value"]], None, "List of values"],
361 ["motor", Type.Motor, None, "Motor to move"],
362 ]
363
364 def run(self, *args, **kwargs):
365 pass
366
367
368class pt12(Macro):
369 """Macro with list of motors followed by list of numbers. Two repeat
370 parameters may defined.
371 Usage from Spock, ex.:
372 pt12 [1 3 4] [mot1 mot2]
373 pt12 1 mot1 # if only one repetition for each repeat parameter
374 """
375
376 param_def = [
377 ["numb_list", [["pos", Type.Float, None, "value"]], None, "List of values"],
378 [
379 "motor_list",
380 [["motor", Type.Motor, None, "Motor to move"]],
381 None,
382 "List of motors",
383 ],
384 ]
385
386 def run(self, *args, **kwargs):
387 pass
388
389
390class pt13(Macro):
391 """Macro with list of motors groups, where each motor group is a list of
392 motors. Repeat parameters may be defined as nested.
393 Usage from Spock, ex.:
394 pt13 [[mot1 mot2] [mot3 mot4]]
395 """
396
397 param_def = [
398 [
399 "motor_group_list",
400 [
401 [
402 "motor_list",
403 [["motor", Type.Motor, None, "Motor to move"]],
404 None,
405 "List of motors",
406 ]
407 ],
408 None,
409 "Motor groups",
410 ]
411 ]
412
413 def run(self, *args, **kwargs):
414 pass
415
416
417class pt14(Macro):
418 """Macro with list of motors groups, where each motor group is a list of
419 motors and a float. Repeat parameters may be defined as nested.
420 Usage from Spock, ex.:
421 pt14 [[[mot1 mot2] 3] [[mot3] 5]]
422 """
423
424 param_def = [
425 [
426 "motor_group_list",
427 [
428 [
429 "motor_list",
430 [["motor", Type.Motor, None, "Motor to move"]],
431 None,
432 "List of motors",
433 ],
434 ["float", Type.Float, None, "Number"],
435 ],
436 None,
437 "Motor groups",
438 ]
439 ]
440
441 def run(self, *args, **kwargs):
442 pass
443
444
445class pt14d(Macro):
446 """Macro with list of motors groups, where each motor group is a list of
447 motors and a float. Repeat parameters may be defined as nested.
448 Default values can be used.
449 Usages taken default values, ex.:
450 pt14d [[[mot1 mot2] 3] [[mot3] []]]
451 pt14d [[[mot1 []] 3] [[mot3] []]]
452 pt14d [[[[]] 3] [[mot3] []]]
453 """
454
455 param_def = [
456 [
457 "motor_group_list",
458 [
459 [
460 "motor_list",
461 [["motor", Type.Motor, "mot1", "Motor to move"]],
462 None,
463 "List of motors",
464 ],
465 ["float", Type.Float, 33, "Number"],
466 ],
467 None,
468 "Motor groups",
469 ]
470 ]
471
472 def run(self, *args, **kwargs):
473 pass
474
475
476class twice(Macro):
477 """A macro that returns a float that is twice its input. It also sets its
478 data to be a dictionary with 'in','out' as keys and value,result
479 as values, respectively"""
480
481 # uncomment the following lines as necessary. Otherwise you may delete them
482 param_def = [["value", Type.Float, 23, "value to be doubled"]]
483 result_def = [["result", Type.Float, None, "the double of the given value"]]
484 # hints = {}
485 # env = (,)
486
487 # uncomment the following lines if need prepare. Otherwise you may delete them
488 # def prepare(self):
489 # pass
490
491 def run(self, n):
492 ret = 2 * n
493 self.setData({"in": n, "out": ret})
494 return ret
Macro call examples
The following examples demonstrate how to call macros from inside a macro. See Calling other macros from inside your macro for a tutorial on calling macros.
1##############################################################################
2##
3# This file is part of Sardana
4##
5# http://www.sardana-controls.org/
6##
7# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
8##
9# Sardana is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Lesser General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13##
14# Sardana is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Lesser General Public License for more details.
18##
19# You should have received a copy of the GNU Lesser General Public License
20# along with Sardana. If not, see <http://www.gnu.org/licenses/>.
21##
22##############################################################################
23
24"""
25A macro package to show examples on how to run a macro from inside another macro
26"""
27
28__all__ = ["call_wa", "call_wm", "subsubm", "subm", "mainmacro", "runsubs"]
29
30__docformat__ = "restructuredtext"
31
32from sardana.macroserver.macro import Macro, Type
33
34# -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
35# First example:
36# A 'mainmacro' that executes a 'subm' that in turn executes a 'subsubm'.
37# The 'subsubm' macro itself calls a short ascan macro
38# -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~--~-~-
39
40
41class call_wa(Macro):
42 def run(self):
43 self.macros.wa()
44
45
46class call_wm(Macro):
47 param_def = [
48 [
49 "motor_list",
50 [["motor", Type.Motor, None, "Motor to move"]],
51 None,
52 "List of motor to show",
53 ],
54 ]
55
56 def run(self, m):
57 self.macros.wm(m)
58
59
60class subsubm(Macro):
61 """this macro just calls the 'subm' macro
62 This macro is part of the examples package. It was written for demonstration purposes"""
63
64 def run(self):
65 self.output("Starting %s" % self.getName())
66 m = self.macros
67 motors = self.getObjs(".*", type_class=Type.Motor)
68 m.ascan(motors[0], 0, 100, 10, 0.2)
69 self.output("Finished %s" % self.getName())
70
71
72class subm(Macro):
73 """this macro just calls the 'subsubm' macro
74 This macro is part of the examples package. It was written for demonstration purposes"""
75
76 def run(self):
77 self.output("Starting %s" % self.getName())
78 self.macros.subsubm()
79 self.output("Finished %s" % self.getName())
80
81
82class mainmacro(Macro):
83 """this macro just calls the 'subm' macro
84 This macro is part of the examples package. It was written for demonstration purposes"""
85
86 def run(self):
87 self.output("Starting %s" % self.getName())
88 self.macros.subm()
89 self.output("Finished %s" % self.getName())
90
91
92# -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
93# Second example:
94# a 'runsubs' macro that shows the different ways to call a macro from inside
95# another macro
96# -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~--~-~-
97
98
99class runsubs(Macro):
100 """A macro that calls a ascan macro using the motor given as first parameter.
101
102 This macro is part of the examples package. It was written for demonstration purposes
103
104 Call type will allow to choose to format in which the ascan macro is called
105 from this macro:
106 1 - m.ascan(motor.getName(), '0', '10', '4', '0.2')
107 2 - m.ascan(motor, 0, 10, 4, 0.2)
108 3 - self.execMacro('ascan', motor.getName(), '0', '10', '4', '0.2')
109 4 - self.execMacro(['ascan', motor, 0, 10, 4, 0.2])
110 5 - params = 'ascan', motor, 0, 10, 4, 0.2
111 self.execMacro(params)
112 6 - self.execMacro("ascan %s 0 10 4 0.2" % motor.getName())
113 7 - macro, prep = self.createMacro("ascan %s 0 10 4 0.2" % motor.getName())
114 macro.hooks = [ self.hook ]
115 self.runMacro(macro)
116 8 - macro, prep = self.createMacro('ascan', motor, 0, 10, 4, 0.2)
117 macro.hooks = [ self.hook ]
118 self.runMacro(macro)
119 9 - params = 'ascan', motor, 0, 10, 4, 0.2
120 macro, prep = self.createMacro(params)
121 macro.hooks = [ self.hook ]
122 self.runMacro(macro)
123
124 Options 7,8 and 9 use the lower level macro API in order to be able to
125 attach hooks to the ascan macro."""
126
127 param_def = [
128 ["motor", Type.Motor, None, "Motor to move"],
129 ["call_type", Type.Integer, 2, "type of run to execute internally"],
130 ]
131
132 def hook(self):
133 self.info("executing hook in a step of a scan...")
134
135 def run(self, motor, call_type):
136 m = self.macros
137 self.output("Using type %d" % call_type)
138 if call_type == 1:
139 m.ascan(motor.getName(), "0", "10", "4", "0.2")
140 elif call_type == 2:
141 m.ascan(motor, 0, 10, 4, 0.2)
142 elif call_type == 3:
143 self.execMacro("ascan", motor.getName(), "0", "10", "4", "0.2")
144 elif call_type == 4:
145 self.execMacro("ascan", motor, 0, 10, 4, 0.2)
146 elif call_type == 5:
147 params = "ascan", motor, 0, 10, 4, 0.2
148 self.execMacro(params)
149 elif call_type == 6:
150 self.execMacro("ascan %s 0 10 4 0.2" % motor.getName())
151 elif call_type == 7:
152 macro, prep = self.createMacro("ascan %s 0 10 4 0.2" % motor.getName())
153 macro.hooks = [self.hook]
154 self.runMacro(macro)
155 elif call_type == 8:
156 macro, prep = self.createMacro("ascan", motor, 0, 10, 4, 0.2)
157 macro.hooks = [self.hook]
158 self.runMacro(macro)
159 elif call_type == 9:
160 params = "ascan", motor, 0, 10, 4, 0.2
161 macro, prep = self.createMacro(params)
162 macro.hooks = [self.hook]
163 self.runMacro(macro)
164
165
166class get_data(Macro):
167 """A macro that executes another macro from within it, get its data,
168 and calculates a result using this data.
169
170 This macro is part of the examples package. It was written for
171 demonstration purposes"""
172
173 param_def = [["mot", Type.Moveable, None, "moveable to be moved"]]
174 result_def = [["middle", Type.Float, None, "the middle motor position"]]
175
176 def run(self, mot):
177 start = 0
178 end = 2
179 intervals = 2
180 integtime = 0.1
181 positions = []
182 dscan, _ = self.createMacro("dscan", mot, start, end, intervals, integtime)
183 self.runMacro(dscan)
184
185 data = dscan.data
186 len_data = len(data)
187 for point_nb in range(len_data):
188 position = data[point_nb].data[mot.getName()]
189 positions.append(position)
190
191 middle_pos = max(positions) - min(positions) / len_data
192 return middle_pos
Macro hooks examples
The following examples demonstrate how to define hookable macros and how to programmatically attach hooks to macros. See Macro Hooks and Adding hooks support for more information.
1##############################################################################
2##
3# This file is part of Sardana
4##
5# http://www.sardana-controls.org/
6##
7# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
8##
9# Sardana is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Lesser General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13##
14# Sardana is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Lesser General Public License for more details.
18##
19# You should have received a copy of the GNU Lesser General Public License
20# along with Sardana. If not, see <http://www.gnu.org/licenses/>.
21##
22##############################################################################
23
24"""This module contains macros that demonstrate the usage of hooks"""
25
26__all__ = ["captain_hook", "captain_hook2", "loop", "hooked_scan", "hooked_dummyscan"]
27
28__docformat__ = "restructuredtext"
29
30from sardana.macroserver.macro import ExecMacroHook, Hookable, Macro, Type
31
32
33class loop(Macro, Hookable):
34 """A macro that executes a for loop. It accepts hooks.
35 This macro is part of the examples package. It was written for
36 demonstration purposes"""
37
38 hints = {"allowsHooks": ("pre-move", "post-move", "pre-acq", "post-acq")}
39
40 param_def = [
41 ["start", Type.Integer, None, "start point"],
42 ["stop", Type.Integer, None, "end point"],
43 ["step", Type.Integer, 1, "step"],
44 ]
45
46 def run(self, start, stop, step):
47 self.info("Starting loop")
48 for i in range(start, stop, step):
49 self.output("At step %d" % i)
50 self.flushOutput()
51 for hook, hints in self.hooks:
52 self.info("running hook with hints=" + repr(hints))
53 hook()
54 self.info("Finished loop")
55
56
57class captain_hook(Macro):
58 """A macro that executes a loop macro. A hook was attached so that in each
59 step of the loop this hook is executed.
60 This macro is part of the examples package. It was written for
61 demonstration purposes"""
62
63 param_def = [
64 ["start", Type.Integer, None, "start point"],
65 ["stop", Type.Integer, None, "end point"],
66 ["step", Type.Integer, 1, "step"],
67 ]
68
69 def hook(self):
70 self.info("\thook execution")
71
72 def run(self, start, stop, step):
73 loop_macro, _ = self.createMacro("loop", start, stop, step)
74 loop_macro.hooks = [(self.hook, ["pre-acq"])]
75 self.runMacro(loop_macro)
76
77
78class captain_hook2(Macro):
79 """A macro that executes a loop macro. A hook was attached so that in each
80 step of the loop this hook is executed.
81 This macro is part of the examples package. It was written for
82 demonstration purposes"""
83
84 param_def = [
85 ["start", Type.Integer, None, "start point"],
86 ["stop", Type.Integer, None, "end point"],
87 ["step", Type.Integer, 1, "step"],
88 ]
89
90 def hook(self):
91 self.execMacroStr(["lsm"])
92
93 def run(self, start, stop, step):
94 loop_macro, _ = self.createMacro("loop", start, stop, step)
95 # h = self.createExecMacroHook(["lsm"])
96 # it gives the "pre-acq" hint to the hook
97 loop_macro.hooks = [self.hook]
98 self.runMacro(loop_macro)
99
100
101class hooked_scan(Macro):
102 """An example on how to attach hooks to the various hook points of a scan.
103 This macro is part of the examples package. It was written for
104 demonstration purposes"""
105
106 param_def = [
107 ["motor", Type.Moveable, None, "Motor to move"],
108 ["start_pos", Type.Float, None, "Scan start position"],
109 ["final_pos", Type.Float, None, "Scan final position"],
110 ["nr_interv", Type.Integer, None, "Number of scan intervals"],
111 ["integ_time", Type.Float, None, "Integration time"],
112 ]
113
114 def hook1(self):
115 self.info("\thook1 execution")
116
117 def hook2(self):
118 self.info("\thook2 execution")
119
120 def hook3(self):
121 self.info("\thook3 execution")
122
123 def hook4(self):
124 self.info("\thook4 execution")
125
126 def hook5(self):
127 self.info("\thook5 execution")
128
129 def hook6(self):
130 self.info("\thook6 execution")
131
132 def run(self, motor, start_pos, final_pos, nr_interv, integ_time):
133 ascan, pars = self.createMacro(
134 "ascan", motor, start_pos, final_pos, nr_interv, integ_time
135 )
136 ascan.hooks = [
137 (self.hook1, ["pre-acq"]),
138 (self.hook2, ["pre-acq", "post-acq", "pre-move", "post-move", "aaaa"]),
139 self.hook3,
140 (self.hook4, ["pre-scan"]),
141 (self.hook5, ["pre-scan", "post-scan"]),
142 (self.hook6, ["post-step"]),
143 ]
144 self.runMacro(ascan)
145
146
147class hooked_scan_with_macro(Macro):
148 """An example on how to attach macro (in this case without parameters)
149 as a hook to the various hook points of a scan.
150
151 This macro is part of the examples package. It was written for
152 demonstration purposes"""
153
154 param_def = [
155 ["motor", Type.Moveable, None, "Motor to move"],
156 ["start_pos", Type.Float, None, "Scan start position"],
157 ["final_pos", Type.Float, None, "Scan final position"],
158 ["nr_interv", Type.Integer, None, "Number of scan intervals"],
159 ["integ_time", Type.Float, None, "Integration time"],
160 ["macro", Type.Macro, None, "Macro to be used as hook"],
161 [
162 "hook_places",
163 [["hook_place", Type.String, None, "Hook place"]],
164 None,
165 "Hook places where macro will be called",
166 ],
167 ]
168
169 def run(
170 self, motor, start_pos, final_pos, nr_interv, integ_time, macro, hook_places
171 ):
172 ascan, _ = self.createMacro(
173 "ascan", motor, start_pos, final_pos, nr_interv, integ_time
174 )
175 macro_hook = ExecMacroHook(self, "umv", [["mot01", 1]])
176 ascan.hooks = [(macro_hook, hook_places)]
177 self.runMacro(ascan)
178
179
180class hooked_dummyscan(Macro):
181 """An example on how to attach hooks to the various hook points of a scan.
182 This macro is part of the examples package. It was written for
183 demonstration purposes"""
184
185 param_def = [
186 ["start_pos", Type.Float, None, "Scan start position"],
187 ["final_pos", Type.Float, None, "Scan final position"],
188 ["nr_interv", Type.Integer, None, "Number of scan intervals"],
189 ["integ_time", Type.Float, None, "Integration time"],
190 ]
191
192 def hook1(self):
193 self.info("\thook1 execution")
194
195 def hook2(self):
196 self.info("\thook2 execution")
197
198 def hook3(self):
199 self.info("\thook3 execution")
200
201 def run(self, start_pos, final_pos, nr_interv, integ_time):
202 dummyscan, pars = self.createMacro(
203 "dummyscan", start_pos, final_pos, nr_interv, integ_time
204 )
205 dummyscan.hooks = [
206 (self.hook1, ["pre-scan"]),
207 (self.hook2, ["pre-acq", "post-acq", "pre-move", "post-move", "aaaa"]),
208 (self.hook3, ["post-scan"]),
209 ]
210 self.runMacro(dummyscan)
Macro scans examples
The following examples demonstrate specific features or tricks for programming custom scan macros using the Sardana scan framework.
1##############################################################################
2##
3# This file is part of Sardana
4##
5# http://www.sardana-controls.org/
6##
7# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
8##
9# Sardana is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Lesser General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13##
14# Sardana is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Lesser General Public License for more details.
18##
19# You should have received a copy of the GNU Lesser General Public License
20# along with Sardana. If not, see <http://www.gnu.org/licenses/>.
21##
22##############################################################################
23
24"""
25 Macro library containning examples demonstrating specific features or tricks
26 for programming macros for Sardana.
27
28Available Macros are:
29 ascanr
30 toothedtriangle
31
32"""
33
34__all__ = ["ascan_demo", "ascanr", "toothedtriangle", "a2scan_mod"]
35
36__docformat__ = "restructuredtext"
37
38import numpy
39
40from sardana.macroserver.macro import Hookable, Macro, Type
41from sardana.macroserver.scan import ColumnDesc, CScan, MoveableDesc, SScan
42
43
44class ascan_demo(Macro):
45 """
46 This is a basic reimplementation of the ascan` macro for demonstration
47 purposes of the Generic Scan framework. The "real" implementation of
48 :class:`sardana.macroserver.macros.ascan` derives from
49 :class:`sardana.macroserver.macros.aNscan` and provides some extra features.
50 """
51
52 # this is used to indicate other codes that the macro is a scan
53 hints = {"scan": "ascan_demo"}
54 # this hints that the macro requires the ActiveMntGrp environment variable
55 # to be set
56 env = ("ActiveMntGrp",)
57
58 param_def = [
59 ["motor", Type.Moveable, None, "Motor to move"],
60 ["start_pos", Type.Float, None, "Scan start position"],
61 ["final_pos", Type.Float, None, "Scan final position"],
62 ["nr_interv", Type.Integer, None, "Number of scan intervals"],
63 ["integ_time", Type.Float, None, "Integration time"],
64 ]
65
66 def prepare(self, motor, start_pos, final_pos, nr_interv, integ_time, **opts):
67 # parse the user parameters
68 self.start = numpy.array([start_pos], dtype="d")
69 self.final = numpy.array([final_pos], dtype="d")
70 self.integ_time = integ_time
71
72 self.nb_points = nr_interv + 1
73 if nr_interv:
74 self.interv_size = (self.final - self.start) / nr_interv
75 else:
76 self.interv_size = self.final - self.start
77 self.name = "ascan_demo"
78 # the "env" dictionary may be passed as an option
79 env = opts.get("env", {})
80
81 # create an instance of GScan (in this case, of its child, SScan
82 self._gScan = SScan(self, generator=self._generator, moveables=[motor], env=env)
83
84 def _generator(self):
85 step = {}
86 # integ_time is the same for all steps
87 step["integ_time"] = self.integ_time
88 for point_no in range(self.nb_points):
89 step["positions"] = (
90 self.start + point_no * self.interv_size
91 ) # note that this is a numpy array
92 step["point_id"] = point_no
93 yield step
94
95 def run(self, *args):
96 for step in self._gScan.step_scan(): # just go through the steps
97 yield step
98
99 @property
100 def data(self):
101 return self._gScan.data # the GScan provides scan data
102
103 def _get_nr_points(self):
104 msg = "nr_points is deprecated since version 3.0.3. Use nb_points instead."
105 self.warning(msg)
106 return self.nb_points
107
108 nr_points = property(_get_nr_points)
109
110
111class ascanr(Macro, Hookable):
112 """This is an example of how to handle adding extra info columns in a scan.
113 Does the same than ascan but repeats the acquisitions "repeat" times for each step.
114 It could be implemented deriving from aNscan, but I do it like this for clarity.
115 Look for the comments with "!!!" for tips specific to the extra info columns
116 I do not support constrains in this one for simplicity (see ascan for that)
117
118 Do an absolute scan of the specified motor, repeating measurements in each step.
119 ascanr scans one motor, as specified by motor. The motor starts at the
120 position given by start_pos and ends at the position given by final_pos.
121 At each step, the acquisition will be repeated "repeat" times
122 The step size is (start_pos-final_pos)/nr_interv. The number of data points collected
123 will be (nr_interv+1)*repeat. Count time for each acquisition is given by time which if positive,
124 specifies seconds and if negative, specifies monitor counts."""
125
126 hints = {
127 "scan": "ascanr",
128 "allowsHooks": ("pre-move", "post-move", "pre-acq", "post-acq", "post-step"),
129 }
130 env = ("ActiveMntGrp",)
131
132 param_def = [
133 ["motor", Type.Moveable, None, "Motor to move"],
134 ["start_pos", Type.Float, None, "Scan start position"],
135 ["final_pos", Type.Float, None, "Scan final position"],
136 ["nr_interv", Type.Integer, None, "Number of scan intervals"],
137 ["integ_time", Type.Float, None, "Integration time"],
138 ["repeat", Type.Integer, None, "Number of Repetitions"],
139 ]
140
141 def prepare(
142 self, motor, start_pos, final_pos, nr_interv, integ_time, repeat, **opts
143 ):
144 self.starts = numpy.array([start_pos], dtype="d")
145 self.finals = numpy.array([final_pos], dtype="d")
146 self.nr_interv = nr_interv
147 self.integ_time = integ_time
148 self.repeat = repeat
149 self.opts = opts
150
151 self.nb_points = (nr_interv + 1) * repeat
152 if nr_interv:
153 self.interv_sizes = (self.finals - self.starts) / nr_interv
154 else:
155 self.interv_sizes = self.finals - self.starts
156 self.name = "ascanr"
157
158 generator = self._generator
159 moveables = [motor]
160 env = opts.get("env", {})
161 constrains = []
162 extrainfodesc = [ColumnDesc(name="repetition", dtype="int64", shape=())] # !!!
163
164 self._gScan = SScan(
165 self, generator, moveables, env, constrains, extrainfodesc
166 ) # !!!
167
168 def _generator(self):
169 step = {}
170 step["integ_time"] = self.integ_time
171 step["pre-move-hooks"] = self.getHooks("pre-move")
172 step["post-move-hooks"] = self.getHooks("post-move")
173 step["pre-acq-hooks"] = self.getHooks("pre-acq")
174 step["post-acq-hooks"] = self.getHooks("post-acq") + self.getHooks("_NOHINTS_")
175 step["post-step-hooks"] = self.getHooks("post-step")
176 step["check_func"] = []
177 for point_no in range(self.nb_points // self.repeat):
178 extrainfo = {"repetition": 0} # !!!
179 step["extrainfo"] = extrainfo # !!!
180 step["positions"] = self.starts + point_no * self.interv_sizes
181 step["point_id"] = point_no
182 yield step
183 for i in range(1, self.repeat):
184 extrainfo["repetition"] = i # !!!
185 step["positions"] = [None]
186 yield step
187
188 def run(self, *args):
189 for step in self._gScan.step_scan():
190 yield step
191
192 @property
193 def data(self):
194 return self._gScan.data
195
196 def _get_nr_points(self):
197 msg = "nr_points is deprecated since version 3.0.3. Use nb_points instead."
198 self.warning(msg)
199 return self.nb_points
200
201 nr_points = property(_get_nr_points)
202
203
204class toothedtriangle(Macro, Hookable):
205 """toothedtriangle macro implemented with the gscan framework.
206 It performs nr_cycles cycles, each consisting of two stages: the first half
207 of the cycle it behaves like the ascan macro (from start_pos to stop_pos in
208 nr_interv+1 steps).For the second half of the cycle it steps back until
209 it undoes the first half and is ready for the next cycle.
210 At each step, nr_samples acquisitions are performed.
211 The total number of points in the scan is nr_interv*2*nr_cycles*nr_samples+1"""
212
213 hints = {
214 "scan": "toothedtriangle",
215 "allowsHooks": (
216 "pre-scan",
217 "pre-move",
218 "post-move",
219 "pre-acq",
220 "post-acq",
221 "post-step",
222 "post-scan",
223 ),
224 }
225 env = ("ActiveMntGrp",)
226
227 param_def = [
228 ["motor", Type.Moveable, None, "Motor to move"],
229 ["start_pos", Type.Float, None, "start position"],
230 ["final_pos", Type.Float, None, "position after half cycle"],
231 ["nr_interv", Type.Integer, None, "Number of intervals in half cycle"],
232 ["integ_time", Type.Float, None, "Integration time"],
233 ["nr_cycles", Type.Integer, None, "Number of cycles"],
234 ["nr_samples", Type.Integer, 1, "Number of samples at each point"],
235 ]
236
237 def prepare(
238 self,
239 motor,
240 start_pos,
241 final_pos,
242 nr_interv,
243 integ_time,
244 nr_cycles,
245 nr_samples,
246 **opts,
247 ):
248 self.start_pos = start_pos
249 self.final_pos = final_pos
250 self.nr_interv = nr_interv
251 self.integ_time = integ_time
252 self.nr_cycles = nr_cycles
253 self.nr_samples = nr_samples
254 self.opts = opts
255 cycle_nb_points = self.nr_interv + 1 + (self.nr_interv + 1) - 2
256 self.nb_points = cycle_nb_points * nr_samples * nr_cycles + nr_samples
257
258 self.interv_size = (self.final_pos - self.start_pos) / nr_interv
259 self.name = "toothedtriangle"
260
261 generator = self._generator
262 moveables = []
263 moveable = MoveableDesc(
264 moveable=motor,
265 is_reference=True,
266 min_value=min(start_pos, final_pos),
267 max_value=max(start_pos, final_pos),
268 )
269 moveables = [moveable]
270 env = opts.get("env", {})
271 constrains = []
272 extrainfodesc = [
273 ColumnDesc(name="cycle", dtype="int64", shape=()),
274 ColumnDesc(name="interval", dtype="int64", shape=()),
275 ColumnDesc(name="sample", dtype="int64", shape=()),
276 ] # !!!
277
278 self._gScan = SScan(
279 self, generator, moveables, env, constrains, extrainfodesc
280 ) # !!!
281
282 def _generator(self):
283 step = {}
284 step["integ_time"] = self.integ_time
285 step["pre-move-hooks"] = self.getHooks("pre-move")
286 step["post-move-hooks"] = self.getHooks("post-move")
287 step["pre-acq-hooks"] = self.getHooks("pre-acq")
288 step["post-acq-hooks"] = self.getHooks("post-acq") + self.getHooks("_NOHINT_")
289 step["post-step-hooks"] = self.getHooks("post-step")
290 step["check_func"] = []
291 extrainfo = {
292 "cycle": None,
293 "interval": None,
294 "sample": None,
295 }
296 step["extrainfo"] = extrainfo
297 halfcycle1 = list(range(self.nr_interv + 1))
298 halfcycle2 = halfcycle1[1:-1]
299 halfcycle2.reverse()
300 intervallist = halfcycle1 + halfcycle2
301 point_no = 0
302 for cycle in range(self.nr_cycles):
303 extrainfo["cycle"] = cycle
304 for interval in intervallist:
305 extrainfo["interval"] = interval
306 step["positions"] = numpy.array(
307 [self.start_pos + (interval) * self.interv_size], dtype="d"
308 )
309 for sample in range(self.nr_samples):
310 extrainfo["sample"] = sample
311 step["point_id"] = point_no
312 yield step
313 point_no += 1
314
315 # last step for closing the loop
316 extrainfo["interval"] = 0
317 step["positions"] = numpy.array([self.start_pos], dtype="d")
318 for sample in range(self.nr_samples):
319 extrainfo["sample"] = sample
320 step["point_id"] = point_no
321 yield step
322 point_no += 1
323
324 def run(self, *args):
325 for step in self._gScan.step_scan():
326 yield step
327
328 @property
329 def data(self):
330 return self._gScan.data
331
332 def _get_nr_points(self):
333 msg = "nr_points is deprecated since version 3.0.3. Use nb_points instead."
334 self.warning(msg)
335 return self.nb_points
336
337 nr_points = property(_get_nr_points)
338
339
340class a2scan_mod(Macro):
341 """a2scan_mod.
342 Do an a2scan with the particularity of different intervals per motor: int_mot1, int_mot2.
343 If int_mot2 < int_mot1, mot2 will change position every int(int_mot1/int_mot2) steps of mot1.
344 It uses the gscan framework.
345 """
346
347 hints = {"scan": "a2scan_mod"}
348 env = ("ActiveMntGrp",)
349
350 param_def = [
351 ["motor1", Type.Moveable, None, "Motor 1 to move"],
352 ["start_pos1", Type.Float, None, "Scan start position 1"],
353 ["final_pos1", Type.Float, None, "Scan final position 1"],
354 ["nr_interv1", Type.Integer, None, "Number of scan intervals of Motor 1"],
355 ["motor2", Type.Moveable, None, "Motor 2 to move"],
356 ["start_pos2", Type.Float, None, "Scan start position 2"],
357 ["final_pos2", Type.Float, None, "Scan final position 2"],
358 ["nr_interv2", Type.Integer, None, "Number of scan intervals of Motor 2"],
359 ["integ_time", Type.Float, None, "Integration time"],
360 ]
361
362 def prepare(
363 self,
364 motor1,
365 start_pos1,
366 final_pos1,
367 nr_interv1,
368 motor2,
369 start_pos2,
370 final_pos2,
371 nr_interv2,
372 integ_time,
373 **opts,
374 ):
375 self.name = "a2scan_mod"
376 self.integ_time = integ_time
377 self.start_pos1 = start_pos1
378 self.final_pos1 = final_pos1
379 self.nr_interv1 = nr_interv1
380 self.start_pos2 = start_pos2
381 self.final_pos2 = final_pos2
382 self.nr_interv2 = nr_interv2
383
384 generator = self._generator
385 moveables = [motor1, motor2]
386 env = opts.get("env", {})
387 constraints = []
388 self._gScan = SScan(self, generator, moveables, env, constraints)
389
390 def _generator(self):
391 step = {}
392 step["integ_time"] = self.integ_time
393
394 start1, end1, interv1 = self.start_pos1, self.final_pos1, self.nr_interv1
395 start2, end2, interv2 = self.start_pos2, self.final_pos2, self.nr_interv2
396
397 # Prepare the positions
398 positions_m1 = numpy.linspace(start1, end1, interv1 + 1)
399 positions_m2 = numpy.linspace(start2, end2, interv2 + 1)
400
401 if interv1 > interv2:
402 positions_m2 = start2 + ((end2 - start2) / interv2) * (
403 numpy.arange(interv1 + 1) // (interv1 / interv2)
404 )
405 elif interv2 > interv1:
406 positions_m1 = start1 + ((end1 - start1) / interv1) * (
407 numpy.arange(interv2 + 1) // (interv2 / interv1)
408 )
409
410 point_id = 0
411 for pos1, pos2 in zip(positions_m1, positions_m2):
412 step["point_id"] = point_id
413 step["positions"] = [pos1, pos2]
414 yield step
415 point_id += 1
416
417 def run(self, *args):
418 for step in self._gScan.step_scan():
419 yield step
420
421
422class ascanc_demo(Macro):
423 """
424 This is a basic reimplementation of the ascanc` macro for demonstration
425 purposes of the Generic Scan framework. The "real" implementation of
426 :class:`sardana.macroserver.macros.ascanc` derives from
427 :class:`sardana.macroserver.macros.aNscan` and provides some extra features.
428 """
429
430 # this is used to indicate other codes that the macro is a scan
431 hints = {"scan": "ascanc_demo"}
432 # this hints that the macro requires the ActiveMntGrp environment variable
433 # to be set
434 env = ("ActiveMntGrp",)
435
436 param_def = [
437 ["motor", Type.Moveable, None, "Motor to move"],
438 ["start_pos", Type.Float, None, "Scan start position"],
439 ["final_pos", Type.Float, None, "Scan final position"],
440 ["integ_time", Type.Float, None, "Integration time"],
441 ]
442
443 def prepare(self, motor, start_pos, final_pos, integ_time, **opts):
444 self.name = "ascanc_demo"
445 # parse the user parameters
446 self.start = numpy.array([start_pos], dtype="d")
447 self.final = numpy.array([final_pos], dtype="d")
448 self.integ_time = integ_time
449 # the "env" dictionary may be passed as an option
450 env = opts.get("env", {})
451
452 # create an instance of GScan (in this case, of its child, CScan
453 self._gScan = CScan(
454 self,
455 waypointGenerator=self._waypoint_generator,
456 periodGenerator=self._period_generator,
457 moveables=[motor],
458 env=env,
459 )
460
461 def _waypoint_generator(self):
462 # a very simple waypoint generator! only start and stop points!
463 yield {"positions": self.start, "waypoint_id": 0}
464 yield {"positions": self.final, "waypoint_id": 1}
465
466 def _period_generator(self):
467 step = {}
468 step["integ_time"] = self.integ_time
469 point_no = 0
470 while True: # infinite generator. The acquisition loop is started/stopped at begin and end of each waypoint
471 point_no += 1
472 step["point_id"] = point_no
473 yield step
474
475 def run(self, *args):
476 for step in self._gScan.step_scan():
477 yield step
478
479
480class ascan_with_addcustomdata(ascan_demo):
481 """
482 example of an ascan-like macro where we demonstrate how to pass custom data to the data handler.
483 This is an extension of the ascan_demo macro. Wemake several calls to `:meth:DataHandler.addCustomData`
484 exemplifying different features.
485 At least the following recorders will act on custom data:
486 - OutputRecorder (this will ignore array data)
487 - NXscan_FileRecorder
488 - SPEC_FileRecorder (this will ignore array data)
489 """
490
491 def run(self, motor, start_pos, final_pos, nr_interv, integ_time, **opts):
492 # we get the datahandler
493 dh = self._gScan._data_handler
494 # at this point the entry name is not yet set, so we give it explicitly
495 # (otherwise it would default to "entry")
496 dh.addCustomData(
497 "Hello world1",
498 "dummyChar1",
499 nxpath="/custom_entry:NXentry/customdata:NXcollection",
500 )
501 # this is the normal scan loop
502 for step in self._gScan.step_scan():
503 yield step
504 # the entry number is known and the default nxpath is used
505 # "/<currententry>/custom_data") if none given
506 dh.addCustomData("Hello world1", "dummyChar1")
507 # you can pass arrays (but not all recorders will handle them)
508 dh.addCustomData(list(range(10)), "dummyArray1")
509 # you can pass a custom nxpath *relative* to the current entry
510 dh.addCustomData("Hello world2", "dummyChar2", nxpath="sample:NXsample")
511
512 # calculate a linear fit to the timestamps VS motor positions and store
513 # it
514 x = [r.data[motor.getName()] for r in self.data.records]
515 y = [r.data["timestamp"] for r in self.data.records]
516 fitted_y = numpy.polyval(numpy.polyfit(x, y, 1), x)
517 dh.addCustomData(fitted_y, "fittedtime", nxpath="measurement:NXcollection")
518
519 # as a bonus, plot the fit
520 self.pyplot.plot(x, y, "ro")
521 self.pyplot.plot(x, fitted_y, "b-")
522 self.pyplot.draw()
523
524
525class ascanct_midtrigger(Macro):
526 """This macro demonstrates how to add an extra scan column with
527 the shifted positions of the motor corresponding to the middle of the
528 space interval. Be aware that the space interval does not
529 necessarily correspond to the middle of the acquisition interval
530 (remember about the latency time).
531
532 This macro does not export all the ascanct features e.g. hooks.
533 """
534
535 param_def = [
536 ["motor", Type.Moveable, None, "Moveable name"],
537 ["start_pos", Type.Float, None, "Scan start position"],
538 ["final_pos", Type.Float, None, "Scan final position"],
539 ["nr_interv", Type.Integer, None, "Number of scan intervals"],
540 ["integ_time", Type.Float, None, "Integration time"],
541 ["latency_time", Type.Float, 0, "Latency time"],
542 ]
543
544 def run(self, *args, **kwargs):
545 motor = args[0]
546 scan_macro = self.execMacro("ascanct", *args, **kwargs)
547 # we get the datahandler
548 # TODO: use public api to GScan object whenever
549 # https://gitlab.com/sardana-org/sardana/-/issues/784 gets solved
550 dh = scan_macro._gScan.data_handler
551 # calculate position corresponding to the middle space interval
552 positions = [r.data[motor.getName()] for r in scan_macro.data.records]
553 first_position = positions[0]
554 second_position = positions[1]
555 positive_direction = second_position > first_position
556 shift = abs(second_position - first_position) / 2
557 if positive_direction:
558 mid_positions = positions + shift
559 else:
560 mid_positions = positions - shift
561 # add custom data column to the measurement HDF5 group with the
562 # <motor_name>_mid name
563 dh.addCustomData(
564 mid_positions, motor.getName() + "_mid", nxpath="measurement:NXcollection"
565 )
Macro plotting examples
The following examples demonstrate how to plot graphics from inside a macro.
See Plotting for a tutorial on macro plotting.
The complete set of pyplot examples can be found in the
matplotlib gallery.
1import math
2
3import numpy
4from numpy import linspace, random
5from scipy.integrate import quad
6from scipy.special import j0
7
8from sardana.macroserver.macro import Type, macro
9
10
11def j0i(x):
12 """Integral form of J_0(x)"""
13
14 def integrand(phi):
15 return math.cos(x * math.sin(phi))
16
17 return (1 / math.pi) * quad(integrand, 0, math.pi)[0]
18
19
20@macro()
21def J0_plot(self):
22 """Sample J0 at linspace(0, 20, 200)"""
23 x = linspace(0, 20, 200)
24 y = j0(x)
25 x1 = x[::10]
26 y1 = list(map(j0i, x1))
27 self.pyplot.plot(x, y, label=r"$J_0(x)$")
28 self.pyplot.plot(x1, y1, "ro", label=r"$J_0^{integ}(x)$")
29 self.pyplot.title(
30 r"Verify $J_0(x)=\frac{1}{\pi}\int_0^{\pi}\cos(x \sin\phi)\,d\phi$"
31 )
32 self.pyplot.xlabel("$x$")
33 self.pyplot.legend()
34 self.pyplot.draw()
35
36
37@macro()
38def random_image(self):
39 """Shows a random image 32x32"""
40 img = random.random((32, 32))
41 self.pyplot.matshow(img)
42 self.pyplot.draw()
43
44
45@macro([["interactions", Type.Integer, None, ""], ["density", Type.Integer, None, ""]])
46def mandelbrot(self, interactions, density):
47 x_min, x_max = -2, 1
48 y_min, y_max = -1.5, 1.5
49
50 x, y = numpy.meshgrid(
51 numpy.linspace(x_min, x_max, density), numpy.linspace(y_min, y_max, density)
52 )
53
54 c = x + 1j * y
55 z = c.copy()
56
57 fractal = numpy.zeros(z.shape, dtype=numpy.uint8) + 255
58
59 for n in range(interactions):
60 z *= z
61 z += c
62 mask = (fractal == 255) & (abs(z) > 10)
63 fractal[mask] = 254 * n / interactions
64 self.pyplot.imshow(fractal)
65 self.pyplot.draw()
Macro input examples
The following examples demonstrate how to ask for user input inside macros.
A tutorial on macro input parameter can be found
here.
The API documentation: input().
1from sardana.macroserver.macro import Type, imacro
2
3
4@imacro()
5def ask_number_of_points(self):
6 """asks user for the number of points"""
7
8 nb_points = self.input("How many points?", data_type=Type.Integer)
9 self.input("How many points?", data_type=Type.Integer)
10 self.output("Points: %i", nb_points)
11
12
13@imacro()
14def ask_for_moveable(self):
15 """asks user for a motor"""
16
17 moveable = self.input("Which moveable?", data_type=Type.Moveable)
18 self.output("You selected %s which is at %f", moveable, moveable.getPosition())
19
20
21@imacro()
22def ask_for_car_brand(self):
23 """asks user for a car brand"""
24
25 car_brands = "Mazda", "Citroen", "Renault"
26 car_brand = self.input("Which car brand?", data_type=car_brands)
27 self.output("You selected %s", car_brand)
28
29
30@imacro()
31def ask_for_multiple_car_brands(self):
32 """asks user for several car brands"""
33
34 car_brands = "Mazda", "Citroen", "Renault", "Ferrari", "Porche", "Skoda"
35 car_brands = self.input(
36 "Which car brand(s)?",
37 data_type=car_brands,
38 allow_multiple=True,
39 title="Favorites",
40 )
41 self.output("You selected %s", ", ".join(car_brands))
42
43
44@imacro()
45def ask_peak(self):
46 """asks user for peak current of points with a custom title"""
47
48 peak = self.input(
49 "What is the peak current?", data_type=Type.Float, title="Peak selection"
50 )
51 self.output("You selected a peak of %f A", peak)
52
53
54@imacro()
55def ask_peak_v2(self):
56 """asks user for peak current of points with a custom title,
57 default value, label and units"""
58
59 label, unit = "peak", "mA"
60 peak = self.input(
61 "What is the peak current?",
62 data_type=Type.Float,
63 title="Peak selection",
64 key=label,
65 unit=unit,
66 default_value=123.4,
67 )
68 self.output("You selected a %s of %f %s", label, peak, unit)
69
70
71@imacro()
72def ask_peak_v3(self):
73 """asks user for peak current of points with a custom title,
74 default value, label, units and ranges"""
75
76 label, unit = "peak", "mA"
77 peak = self.input(
78 "What is the peak current?",
79 data_type=Type.Float,
80 title="Peak selection",
81 key=label,
82 unit=unit,
83 default_value=123.4,
84 minimum=0.0,
85 maximum=200.0,
86 )
87 self.output("You selected a %s of %f %s", label, peak, unit)
88
89
90@imacro()
91def ask_peak_v4(self):
92 """asks user for peak current of points with a custom title,
93 default value, label, units, ranges and step size"""
94
95 label, unit = "peak", "mA"
96 peak = self.input(
97 "What is the peak current?",
98 data_type=Type.Float,
99 title="Peak selection",
100 key=label,
101 unit=unit,
102 default_value=123.4,
103 minimum=0.0,
104 maximum=200.0,
105 step=5,
106 )
107 self.output("You selected a %s of %f %s", label, peak, unit)
108
109
110@imacro()
111def ask_peak_v5(self):
112 """asks user for peak current of points with a custom title,
113 default value, label, units, ranges, step size and decimal places"""
114
115 label, unit = "peak", "mA"
116 peak = self.input(
117 "What is the peak current?",
118 data_type=Type.Float,
119 title="Peak selection",
120 key=label,
121 unit=unit,
122 default_value=123.4,
123 minimum=0.0,
124 maximum=200.0,
125 step=5,
126 decimals=2,
127 )
128 self.output("You selected a %s of %f %s", label, peak, unit)
Controller examples
Controller examples are discussed together with each controller type in the Writing controllers guides. The Springfield example controllers used throughout those guides can be found next to the guide sources.