.. _sardana-macroserver-overview: ================ The Macro Server ================ The Macro Server object is the sardana server object which manages all high level sardana objects related to macro execution, namely doors, macro libraries and macros themselves. The main purpose of the Macro Server is to run *macros*. Macros are just pieces of Python_ code (functions or classes) which reside in a macro library (Python_ file). Macros can be written by anyone with knowledge of Python_. The Macro Server is exposed on the sardana server as a Tango_ device. Through configuration, the Macro Server can be told to connect to a Pool device. This is the most common configuration. You can, however, tell the Macro Server to connect to more than one Pool device or to no Pool devices at all. When connected to a Pool device(s), the Macro Server uses the Pool device introspection :term:`API` to discover which elements are available. The existing macros will be able to access these elements (through parameters passed to the macro or using the macro :term:`API`) and act on them. In order to be able to run macros, you must first connect to the Macro Server entry point object called *Door*. A single Macro Server can have many active Doors at the same time but a Door can only run one macro at a time. Each Door is exposed on the sardana server as a Tango_ device. You are not in any way restricted to the standard macros provided by the sardana system. You can write as many macros as you need. Writing your own macros is easy. The macro equivalent of Python_\'s *Hello, World!* example:: from sardana.macroserver.macro import macro @macro() def hello_world(self): self.output("Hello, World!") Information on how to write your own sardana macros can be found :ref:`here `. The complete macro :term:`API` can be found :ref:`here `. .. _sardana-door-overview: Door ==== *Door* is an entry point to the MacroServer. Door exposes an interface to run macros and interact with the macro currently being run. A single Macro Server can have many active Doors at the same time but a Door can only run one macro at a time. Each Door is exposed on the sardana server as a Tango_ device. There exists a possibility to define :ref:`macro environment ` applied at the door level i.e. not visible on other doors. Macro execution model ===================== Each Door owns a *macro executor* which runs macros asynchronously in a dedicated thread (internally, a thread pool of size one per Door). This design has two consequences: * the Door device itself stays responsive while a macro runs — clients can query its state, receive its output and logs as events, and request a stop, abort or pause at any time; * macros within one Door are strictly serialized, while different Doors run their macros concurrently and independently. The total number of macros that can execute concurrently in a Macro Server is limited by its ``MaxParallelMacros`` Tango_ device property. Macros may call other macros (e.g. a scan macro moving motors via the ``mv`` macro). A sub-macro does not get a new thread: it runs synchronously inside its parent's thread, and the executor maintains a *macro stack* recording the parent-child chain. Every macro object knows its parent macro, which is used, among other things, for cascading interruption requests, log propagation and environment lookup in the parent macro's scope. Hooks ===== Macros which inherit from the `~sardana.macroserver.macro.Hookable` class advertise *hook places* — named points in their execution (e.g. ``pre-scan``, ``post-acq``, ``post-scan`` in the scan macros) where arbitrary user code, a Python callable or another macro, can be attached without modifying the hooked macro. This is the standard extension mechanism for customizing standard procedures: instead of copying and editing a scan macro, a user attaches behavior to the places the macro exposes. The concept and the ways of attaching hooks (programmatically, via the general hooks mechanism or in sequences) are described in :ref:`sardana-macros-hooks`. Stop, abort and pause semantics =============================== A running macro can be interrupted at any time, with two clearly distinguished flavors. *Stop* is the gentle one: it is supposed to bring the system to a stable state according to a particular protocol, respecting the nominal configuration — e.g. stopping motors with their nominal deceleration. *Abort* is foreseen for emergency situations and brings the system to a stable state as fast as possible — e.g. stopping motors instantly, possibly losing track of position. The interruption acts on two levels. Objects *reserved* by the macro (e.g. motors obtained through the macro :term:`API`) are stopped or aborted immediately. The macro code itself, however, only terminates when its thread reaches the next macro :term:`API` call acting as a *checkpoint*; the interruption is then delivered as a Python exception raised inside the macro thread. Macros can install custom cleanup behavior by overriding the ``on_stop`` / ``on_abort`` methods. The full semantics, including the interplay with exception handling in macro code, are described in :ref:`sardana-macro-handling-macro-stop-and-abort`; how to issue the interruption from Spock is described in :ref:`sardana-spock-stopping`. Macros can also be *paused* at checkpoints and later resumed, e.g. to let a user inspect an intermediate state before continuing. Environment =========== The Macro Server provides a persistent key-value store, the *environment*, which macros use to share state and configuration across executions (e.g. the active measurement group, scan file settings). Environment variables can be scoped globally, per door and per macro. The concept and its usage are described in :ref:`macro-environment`; the design of the pluggable storage backends (shelve, Redis, Tango database) is discussed in :ref:`sardana-explanation-environment-backends`. .. _Tango: http://www.tango-controls.org/ .. _Python: http://www.python.org/