summaryrefslogtreecommitdiff
path: root/doc/source/devel/overview/overview_controller.rst
blob: 3062457d545a89c05f478359c1da82590d8832da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
.. currentmodule:: sardana.pool.controller

.. _sardana-controller-overview:

===================
Controller overview
===================

Each different hardware object is directly controlled by a software object
called *controller*. This object is responsible for mapping the communication
between a set of hardware objects (example motors) and the underlying hardware
(example: a motor controller crate). The *controller* object is also exposed as
a Tango_ device.

Usually a controller is capable of handling several hardware objects.
For example, a motor controller crate is capable of controlling several motors
(generally called *axis* [#]_).

The controller objects can be created/deleted/renamed dynamically in a running
pool.

A specific type of controller needs to be created to handle each specific type
of hardware. Therefore, to each type of hardware controller there must be
associated a specific controller software component. You can write a
specific controller software component (:term:`plug-in`) that is able to
communicate with the specific hardware. You can this way extend the initial
pool capabilities to talk to all kinds of different hardware.

.. figure:: /_static/sardana_server_np200.png
    :width: 500
    :align: center
    
    A diagram representing a sardana server with a controller class 
    *NSC200Controller*, an instance of that controller *np200ctrl_1* "connected"
    to a real hardware and a single motor *npm_1*.

A sardana controller is responsible for it's sardana element(s). Example: an
Icepap hardware motor controller can *control* up to 128 individual motor axis.
In the same way, the coresponding software motor controller *IcepapController*
will *own* the individual motor axises.

.. figure:: /_static/sardana_server_icepap.png
    :width: 500
    :align: center
    
    A diagram representing a sardana server with a controller class 
    *IcepapController*, an instance of that controller *icectrl_1* "connected"
    to a real hardware and motors *icem_[1..5]*.

    
These are the different types of controllers recognized by sardana:

:class:`MotorController`
    You should use/write a :class:`MotorController` sardana :term:`plug-in` if
    the the device you want to control has a *moveable* interface.
    The :class:`MotorController` actually fullfils a *changeable* interface.
    This means that, for example, a power supply that has a current which you
    want to *ramp* could also be implemented as a :class:`MotorController`.
    
    Example: the Newport NSC200 motor controller

:class:`CounterTimerController`
    This controller type is designed to control a device capable of counting
    scalar values (and, optionaly have a timer).
    
    Example: The National Instruments 6602 8-Channel Counter/Timer

:class:`ZeroDController`
    This controller type is designed to control a device capable of supplying
    scalar values. The :term:`API` provides a way to obtain a value over a
    certain acquisition time through different algorithms (average, maximum,
    integration).
    
    Example: an electrometer 

:class:`OneDController`
    This controller type is designed to control a device capable of supplying
    1D values. It has a very similar :term:`API` to :class:`CounterTimerController` 
    
    Example: an :term:`MCA`

:class:`TwoDController`
    This controller type is designed to control a device capable of supplying
    2D values. It has a very similar :term:`API` to :class:`CounterTimerController` 
    
    Example: a :term:`CCD`
    
:class:`PseudoMotorController`
    A controller designed to export *virtual motors* that represent a new view
    over the actual physical motors.
    
    Example: A slit pseudo motor controller provides *gap* and *offset* virtual
    motors over the physical blades
        
:class:`PseudoCounterController`
    A controller designed to export *virtual counters* that represent a new view
    over the actual physical counters/0Ds.

:class:`IORegisterController`
    A controller designed to control hardware registers.

Controller plug-ins can be written in Python_ (and in the future in C++).
Each controller code is basically a Python_ class that needs to obey a
specific :term:`API`.

Here is an a extract of the pertinent part of a Python_ motor controller code
that is able to talk to a Newport motor controller::

    from sardana.pool.controller import MotorController, \
        Type, Description, DefaultValue

    class NSC200Controller(MotorController):
        """This class is the Tango Sardana motor controller for the Newport NewStep
        handheld motion controller NSC200.
        This controller communicates through a Device Pool serial communication
        channel."""

        ctrl_properties = \
            { 'SerialCh' : { Type : str,
                             Description : 'Communication channel name for the serial line' },
              'SwitchBox': { Type : bool,
                             Description : 'Using SwitchBox',
                             DefaultValue : False},
              'ControllerNumber' : { Type : int, 
                                     Description : 'Controller number',
                                     DefaultValue : 1 } }

        def __init__(self, inst, props, *args, **kwargs):
            MotorController.__init__(self, inst, props, *args, **kwargs)
                
            self.serial = None
            self.serial_state_event_id = -1

            if self.SwitchBox:
                self.MaxDevice = 8

        def AddDevice(self, axis):
            if axis > 1 and not self.SwitchBox:
                raise Exception("Without using a Switchbox only axis 1 is allowed")
            
            if self.SwitchBox:
                self._setCommand("MX", axis)

        def DeleteDevice(self, axis):
            pass
        
        _STATE_MAP = { NSC200.MOTOR_OFF : State.Off, NSC200.MOTOR_ON : State.On,
                       NSC200.MOTOR_MOVING : State.Moving }
        
        def StateOne(self, axis):
            if self.SwitchBox:
                self._setCommand("MX", axis)
                
            status = int(self._queryCommand("TS"))
            status = self._STATE_MAP.get(status, State.Unknown)
            register = int(self._queryCommand("PH"))
            lower = int(NSC200.getLimitNegative(register))
            upper = int(NSC200.getLimitPositive(register))

            switchstate = 0
            if lower == 1 and upper == 1: switchstate = 6
            elif lower == 1: switchstate = 4
            elif upper == 1: switchstate = 2
            return status, "OK", switchstate

        def ReadOne(self, axis):
            try:
                if self.SwitchBox:
                    self._setCommand("MX", axis)
                return float(self._queryCommand("TP"))
            except:
                raise Exception("Error reading position, axis not available")

        def PreStartOne(self, axis, pos):
            return True

        def StartOne(self, axis, pos):
            if self.SwitchBox:
                self._setCommand("MX", axis)
            status = int(self._queryCommand("TS"))
            if status == NSC200.MOTOR_OFF:
                self._setCommand("MO","")
            self._setCommand("PA", pos)
            self._log.debug("[DONE] sending position")
                
        def StartAll(self):
            pass

        def AbortOne(self, axis):
            if self.SwitchBox:
                self._setCommand("MX", axis)
            self._setCommand("ST", "")

.. seealso:: 
    
    :ref:`sardana-controller-howto`
        How to write controller :term:`plug-in`\s in sardana
    
    :ref:`sardana-controller-api`
        the controller :term:`API` 
    
    :class:`~sardana.tango.pool.Controller.Controller`
        the controller tango device :term:`API`
        
.. rubric:: Footnotes

.. [#] The term *axis* will be used from here on to refer to the ID of
       a specific hardware object (like a motor) with respect to its *controller*.

.. _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/
.. _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/