.. currentmodule:: sardana.pool.controller .. _sardana-2dcontroller-howto: ============================ How to write a 2D controller ============================ This chapter provides the necessary information to write a two dimensional (2D) experimental channel controller in Sardana. .. contents:: Table of contents :depth: 3 :backlinks: entry .. _sardana-2dcontroller-general-guide: General guide ------------- :ref:`2D experimental channels ` together with :ref:`1D experimental channels ` and :ref:`counter/timers ` belong to the same family of *timerable* experimental channels. To write a 2D controller class you can follow the :ref:`sardana-countertimercontroller` guide keeping in mind differences explained in continuation. .. _sardana-2dcontroller-general-guide-shape: Get 2D shape ~~~~~~~~~~~~ 2D controller should provide a shape of the image which will be produced by acquisition. The shape can be either static e.g. defined by the detector's sensor size or dynamic e.g. depending on the detector's (or an intermediate control software layer e.g. `LImA`_) configuration like :term:`RoI` or binning. In any case you should provide the shape in the format of a two-element sequence with horizontal and vertical dimensions using the :meth:`~sardana.pool.controller.Controller.GetAxisPar` method. Here is an example of the possible implementation of :meth:`~sardana.pool.controller.Controller.GetAxisPar`: .. code-block:: python class SpringfieldTwoDController(TwoDController): def GetAxisPar(self, axis, par): if par == "shape": return self.springfield.getShape(axis) For backwards compatibility, in case of not implementing the ``shape`` axis parameter, shape will be determined frm the ``MaxDimSize`` of the ``Value`` attribute, currently (4096, 4096). .. _sardana-2dcontroller-differences-countertimer: Differences with counter/timer controller ----------------------------------------- Class definition ~~~~~~~~~~~~~~~~ :ref:`The basics of the counter/timer controller ` chapter explains how to define the counter/timer controller class. Here you need to simply inherit from the `~sardana.pool.controller.TwoDController` class: .. code-block:: python :emphasize-lines: 3 from sardana.pool.controller import TwoDController class SpringfieldTwoDController(TwoDController): def __init__(self, inst, props, *args, **kwargs): super().__init__(inst, props, *args, **kwargs) .. _sardana-2dcontroller-getvalue: Get 2D value ~~~~~~~~~~~~ :ref:`Get counter value ` chapter explains how to read a counter/timer value using the :meth:`~sardana.pool.controller.Readable.ReadOne` method. Here you need to implement the same method but: * its return value must be a two-dimensional `numpy.array` (or eventually a `~sardana.sardanavalue.SardanaValue` object) containing an image instead of a scalar value * sardana will not call :meth:`~sardana.pool.controller.Readable.ReadOne` at a given frequency during the acquisition to get the still changing result .. _sardana-2dcontroller-getvalues: Get 2D values ~~~~~~~~~~~~~ :ref:`Get counter values ` chapter explains how to read counter/timer values using the :meth:`~sardana.pool.controller.Readable.ReadOne` method while acquiring with external (hardware) synchronization. Here you need to implement the same method but its return value must be a sequence with two-dimensional `numpy.array` objects (or eventually with :obj:`~sardana.sardanavalue.SardanaValue` objects) containing the images instead of a scalar values. Advanced topics --------------- .. _sardana-2dcontroller-valuereferencing: Working with value referencing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2D experimental channels may produce big arrays of data at high frame rate. Reading this data and storing it using sardana is not always optimal. `SEP2`_ introduced data saving duality, optionally, leaving the data storage at the responsibility of the detector (or an intermediate software layer e.g. `LImA`_). In this case sardana just deals with the reference to the data. In order to announce the referencing capability the 2D controller must additionally inherit from the `~sardana.pool.controller.Referable` class: .. code-block:: python :emphasize-lines: 3 from sardana.pool.controller import TwoDController, Referable class SpringfieldTwoDController(TwoDController, Referable): def __init__(self, inst, props, *args, **kwargs): super().__init__(inst, props, *args, **kwargs) .. _sardana-2dcontroller-getvaluereference: Get 2D value reference """""""""""""""""""""" To get the 2D value reference, sardana calls the :meth:`~sardana.pool.controller.Referable.RefOne` method. This method receives an axis as parameter and should return a URI (`str`) pointing to the value. Here is an example of the possible implementation of :meth:`~sardana.pool.controller.Referable.RefOne`: .. code-block:: python :emphasize-lines: 3 class SpringfieldTwoDController(TwoDController): def RefOne(self, axis): value_ref = self.springfield.getValueRef(axis) return value_ref .. _sardana-2dcontroller-getvaluesreferences: Get 2D values references """""""""""""""""""""""" :ref:`Get counter values ` chapter explains how to read counter/timer values using the :meth:`~sardana.pool.controller.Readable.ReadOne` method while acquiring with external (hardware) synchronization. Here you need to implement the :meth:`~sardana.pool.controller.Referable.RefOne` method and its return value must be a sequence with URIs (`str`) pointing to the values. .. _sardana-2dcontroller-configvaluereference: Configure 2D value reference """""""""""""""""""""""""""" Two axis parameters: ``value_ref_pattern`` (`str`) and ``value_ref_enabled`` (`bool`) are foreseen for configuring where to store the values and whether to use the value referencing. Here you need to implement the :meth:`~sardana.pool.controller.Controller.SetAxisPar` method. Here is an example of the possible implementation of :meth:`~sardana.pool.controller.Controller.SetAxisPar`: .. code-block:: python class SpringfieldTwoDController(TwoDController): def SetAxisPar(self, axis, par, value): if par == "value_ref_pattern": self.springfield.setValueRefPattern(axis, value) elif par == "value_ref_enabled": self.springfield.setValueRefEnabled(axis, value) .. hint:: Use `Python Format String Syntax `_ e.g. ``file:///tmp/sample1_{index:02d}`` to configure a dynamic value referencing using the acquisition index or any other parameter (acquisition index can be reset in the :ref:`per measurement preparation `. phase) When value referencing is used """""""""""""""""""""""""""""" Sardana will :ref:`sardana-2dcontroller-getvaluereference` when: - channel has referencing capability and it is enabled Sardana will :ref:`sardana-2dcontroller-getvalue` when any of these conditions applies: - channel does not have referencing capability - channel has referencing capability but it is disabled - there is a pseudo counter based on this channel Hence, in some configurations, both methods may be used simultaneously. .. _ALBA: http://www.cells.es/ .. _ANKA: http://http://ankaweb.fzk.de/ .. _ELETTRA: http://http://www.elettra.trieste.it/ .. _ESRF: http://www.esrf.eu/ .. _FRMII: http://www.frm2.tum.de/en/index.html .. _HASYLAB: http://hasylab.desy.de/ .. _MAX-lab: http://www.maxlab.lu.se/maxlab/max4/index.html .. _SOLEIL: http://www.synchrotron-soleil.fr/ .. _Tango: http://www.tango-controls.org/ .. _Taco: http://www.esrf.eu/Infrastructure/Computing/TACO/ .. _PyTango: http://packages.python.org/PyTango/ .. _Taurus: http://packages.python.org/taurus/ .. _QTango: http://www.tango-controls.org/download/index_html#qtango3 .. _Qt: http://qt.nokia.com/products/ .. _PyQt: http://www.riverbankcomputing.co.uk/software/pyqt/ .. _PyQwt: http://pyqwt.sourceforge.net/ .. _Python: http://www.python.org/ .. _IPython: http://ipython.org/ .. _ATK: http://www.tango-controls.org/Documents/gui/atk/tango-application-toolkit .. _Qub: http://www.blissgarden.org/projects/qub/ .. _numpy: http://numpy.scipy.org/ .. _SPEC: http://www.certif.com/ .. _EPICS: http://www.aps.anl.gov/epics/ .. _SEP2: http://www.sardana-controls.org/sep/?SEP2.md .. _LImA: https://lima1.readthedocs.io/en/latest/