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.