.. _sardana-tutorial-first-macro: ======================= Write your first macro ======================= Macros are the procedures that the Sardana MacroServer executes: every scan, every motion command you typed so far is a macro. In this lesson you will write a macro of your own, run it from Spock, give it parameters and make it return a result. You need the simulated beamline from :ref:`sardana-tutorial-demo-beamline` up and running. Macros live in macro libraries ------------------------------ A macro is plain Python code. A Python file containing macros is called a *macro library*. The MacroServer discovers macro libraries in a list of directories configured through the ``MacroPath`` property of the MacroServer Tango device. First, create a directory for your macro libraries, for example:: mkdir -p /home/homer/sardana/macros Then add it to the ``MacroPath`` of your MacroServer. From any Python or itango console: .. code-block:: python import tango ms = tango.DeviceProxy("MacroServer/demo1/1") ms.put_property({"MacroPath": ["/home/homer/sardana/macros"]}) Finally restart the ``Sardana demo1`` server (stop it with :kbd:`Ctrl+C` and start it again) so that the new property takes effect. This configuration only has to be done once. Hello, world! ------------- Create the file ``/home/homer/sardana/macros/salute.py`` with this content: .. code-block:: python from sardana.macroserver.macro import macro @macro() def hello_world(self): """This is a hello world macro""" self.output("Hello, World!") That is a complete macro: - The ``@macro()`` decorator labels the function as a macro -- without it, Sardana ignores the function. - The first (and here only) parameter, conventionally called ``self``, is the macro *execution context*. Through it you can send output, ask for motors, run other macros, and much more. - The docstring documents the macro; always write one, it is shown by the command line and graphical interfaces. - ``self.output(...)`` sends text to the client. A plain ``print()`` would print in the *server* terminal -- remember your code runs in the MacroServer, possibly on another computer. Load and run it --------------- If you just (re)started the server, the new library was discovered automatically. If the server was already running when you created the file, load it with the ``addmaclib`` macro: .. sourcecode:: spock Door_demo1_1 [1]: addmaclib salute salute successfully loaded (found 1 macros) Now run your macro like any other: .. sourcecode:: spock Door_demo1_1 [2]: hello_world Hello, World! Whenever you *edit* the file afterwards, reload it with ``relmaclib``: .. sourcecode:: spock Door_demo1_1 [3]: relmaclib salute salute successfully (re)loaded (found 1 macros) .. hint:: Spock can also create and edit macros for you: ``edmac hello_world salute`` opens your favorite editor (the one in the ``EDITOR`` environment variable) on a macro template and loads the result into the server when you are done. Adding parameters ----------------- Macro parameters must be *declared*: each one has a name, a type, a default value (``None`` meaning no default) and a description. In exchange, Sardana rejects calls with wrong types, Spock offers tab-completion of parameter values, and GUIs can build forms automatically. Add this to ``salute.py``: .. code-block:: python from sardana.macroserver.macro import macro, Type @macro([["moveable", Type.Moveable, None, "moveable to move"], ["position", Type.Float, None, "absolute position"]]) def move(self, moveable, position): """This macro moves a moveable to the specified position""" moveable.move(position) self.output("%s is now at %s", moveable.getName(), moveable.getPosition()) Note that the ``moveable`` parameter arrives as a full-blown motor object you can operate on directly. Reload and run it on a demo motor: .. sourcecode:: spock Door_demo1_1 [4]: relmaclib salute salute successfully (re)loaded (found 2 macros) Door_demo1_1 [5]: move mot01 3 mot01 is now at 3.0 Common parameter types include ``Integer``, ``Float``, ``Boolean``, ``String``, ``Moveable`` and ``Motor``. Returning a result ------------------ A macro can also return one or more results, declared in the same fashion with ``result_def``. Macros come in two equivalent forms and both of them support results. The first one is the *class* form: instead of decorating a function, you inherit from ``Macro`` and implement the ``run`` method: .. code-block:: python from sardana.macroserver.macro import Macro, Type class twice(Macro): """A macro that returns twice its input.""" param_def = [["value", Type.Float, 23, "value to be doubled"]] result_def = [["result", Type.Float, None, "the double of the given value"]] def run(self, value): return 2 * value The second one is the decorator form you have been using so far -- the ``@macro`` decorator takes ``result_def`` as its second argument and the value returned by the function is the result: .. code-block:: python from sardana.macroserver.macro import macro, Type @macro([["value", Type.Float, 23, "value to be doubled"]], [["result", Type.Float, None, "the double of the given value"]]) def twice(self, value): """A macro that returns twice its input.""" return 2 * value Pick whichever reads better for the macro at hand. Either way, other macros can now call ``twice`` and read its result, e.g. with ``self.execMacro("twice 21").getResult()``. Next steps ---------- You can now extend Sardana with your own procedures. Continue with :ref:`sardana-tutorial-first-controller` to learn how Sardana talks to hardware. When you need more -- repeat parameters, calling macros from macros, environment, hooks, plotting -- the complete guide is :ref:`sardana-macros-howto`.