diff options
Diffstat (limited to 'doc/source/devel/howto_controllers')
13 files changed, 0 insertions, 2502 deletions
diff --git a/doc/source/devel/howto_controllers/howto_0dcontroller.rst b/doc/source/devel/howto_controllers/howto_0dcontroller.rst deleted file mode 100644 index 23c5af66..00000000 --- a/doc/source/devel/howto_controllers/howto_0dcontroller.rst +++ /dev/null @@ -1,37 +0,0 @@ -.. currentmodule:: sardana.pool.controller - -.. _sardana-0dcontroller-howto-basics: - -============================ -How to write a 0D controller -============================ - -The basics ----------- - -.. todo:: document 0D controller howto - -.. _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/ diff --git a/doc/source/devel/howto_controllers/howto_1dcontroller.rst b/doc/source/devel/howto_controllers/howto_1dcontroller.rst deleted file mode 100644 index 634654e7..00000000 --- a/doc/source/devel/howto_controllers/howto_1dcontroller.rst +++ /dev/null @@ -1,37 +0,0 @@ -.. currentmodule:: sardana.pool.controller - -.. _sardana-1dcontroller-howto-basics: - -============================ -How to write a 1D controller -============================ - -The basics ----------- - -.. todo:: document 1D controller howto - -.. _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/ diff --git a/doc/source/devel/howto_controllers/howto_2dcontroller.rst b/doc/source/devel/howto_controllers/howto_2dcontroller.rst deleted file mode 100644 index bc73fceb..00000000 --- a/doc/source/devel/howto_controllers/howto_2dcontroller.rst +++ /dev/null @@ -1,37 +0,0 @@ -.. currentmodule:: sardana.pool.controller - -.. _sardana-2dcontroller-howto-basics: - -============================ -How to write a 2D controller -============================ - -The basics ----------- - -.. todo:: document 2D controller howto - -.. _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/ diff --git a/doc/source/devel/howto_controllers/howto_controller.rst b/doc/source/devel/howto_controllers/howto_controller.rst deleted file mode 100644 index 508ca916..00000000 --- a/doc/source/devel/howto_controllers/howto_controller.rst +++ /dev/null @@ -1,533 +0,0 @@ -.. currentmodule:: sardana.pool.controller - -.. _sardana-controller-howto-whatis: - -==================== -What is a controller -==================== - -A controller in sardana is a piece of software capable of *translating* -between the sardana :term:`API` and a specific hardware :term:`API`. Sardana -expects a controller to obey a specific :term:`API` in order to be able to -properly configure and operate with it. The hardware :term:`API` used by the -controller could be anything, from a pure serial line to shared memory or a -remote server written in Tango_, Taco_ or even EPICS_. - -Controllers can only be written in Python_ (in future also C++ will be -possible). A controller **must** be a :term:`class` inheriting from one of the -existing controller types: - -.. hlist:: - :columns: 3 - - - :class:`MotorController` - - :class:`CounterTimerController` - - :class:`ZeroDController` - - :class:`OneDController` - - :class:`TwoDController` - - :class:`IORegisterController` - - - :class:`PseudoMotorController` - - :class:`PseudoCounterController` - -A controller is designed to incorporate a set of generic individual elements. -Each element has a corresponding *axis*. For example, in a motor -controller the elements will be motors, but in a counter/timer controller the -elements will be experimental channels. - -Some controller classes are designed to target a specific type of hardware. -Other classes of controllers, the *pseudo* classes, are designed to provide a -high level view over a set of underlying lower level controller elements. - -We will focus first on writing low level hardware controllers since they -share some of the :term:`API` and after on the *pseudo* controllers. - -.. _sardana-controller-howto-basics: - -Controller - The basics ------------------------ - -The first thing to do is to import the necessary symbols from sardana library. -As you will see, most symbols can be imported through the -:mod:`sardana.pool.controller` module: - -.. code-block:: python - - import springfieldlib - - from sardana.pool.controller import MotorController - - class SpringfieldMotorController(MotorController): - """A motor controller intended from demonstration purposes only""" - pass - -The common :term:`API` to all low level controllers includes the set of methods -to: - - #. construct the controller - #. add/delete a controller element [#f1]_ - #. obtain the state of controller element(s) [#f2]_ - #. define, set and get extra axis attributes - #. define, set and get extra controller attributes - #. define, set and get extra controller properties - -In the following chapters the examples will be based on a motor controller -scenario. - -The examples use a :mod:`springfieldlib` module which emulates a motor hardware -access library. - -The :mod:`springfieldlib` can be downloaded from -:download:`here <springfieldlib.py>`. - -The Springfield motor controller can be downloaded from -:download:`here <sf_motor_ctrl.py>`. - -.. _sardana-controller-howto-constructor: - -Constructor -~~~~~~~~~~~ - -The constructor consists of the -:meth:`~sardana.pool.controller.Controller.__init__` method. This method is -called when you create a new controller of that type and every time the sardana -server is started. It will also be called if the controller code has changed -on the file and the new code is reloaded into sardana. - -It is **NOT** mandatory to override the :meth:`~sardana.pool.controller.Controller.__init__` -from :class:`~sardana.pool.controller.MotorController` . Do it only -if you need to add some initialization code. If you do it, it is **very important** -to follow the two rules: - - #. use the method signature: ``__init__(self, inst, props, *args, **kwargs)`` - #. always call the super class constructor - -The example shows how to implement a constructor for a motor controller: - -.. code-block:: python - :emphasize-lines: 3 - - class SpringfieldMotorController(MotorController): - - def __init__(self, inst, props, *args, **kwargs): - super(SpringfieldMotorController, self).__init__(inst, props, *args, **kwargs) - - # initialize hardware communication - self.springfield = springfieldlib.SpringfieldMotorHW() - - # do some initialization - self._motors = {} - -.. _sardana-controller-howto-add-delete: - -Add/Delete axis -~~~~~~~~~~~~~~~ - -Each individual element in a controller is called *axis*. An axis is represented -by a number. A controller can support one or more axes. Axis numbers don't need -to be sequencial. For example, at one time you may have created for your motor -controller instance only axis 2 and 5. - -Two methods are called when creating or removing an element from a controller. -These methods are :meth:`~sardana.pool.controller.Controller.AddDevice` and -:meth:`~sardana.pool.controller.Controller.DeleteDevice`. The -:meth:`~sardana.pool.controller.Controller.AddDevice` method is called when a -new axis belonging to the controller is created in sardana. The -:meth:`~sardana.pool.controller.Controller.DeleteDevice` method is -called when an axis belonging to the controller is removed from sardana. -The example shows an example how to implement these methods on a motor -controller: - -.. code-block:: python - :emphasize-lines: 3, 6 - - class SpringfieldMotorController(MotorController): - - def AddDevice(self, axis): - self._motors[axis] = True - - def DeleteDevice(self, axis): - del self._motor[axis] - -.. _sardana-controller-howto-axis-state: - -Get axis state -~~~~~~~~~~~~~~ - -To get the state of an axis, sardana calls the -:meth:`~sardana.pool.controller.Controller.StateOne` method. This method -receives an axis as parameter and should return either: - - - state (:obj:`~sardana.sardanadefs.State`) or - - a sequence of two elements: - - state (:obj:`~sardana.sardanadefs.State`) - - status (:obj:`str`) - -(For motor controller see :ref:`get motor state <sardana-motorcontroller-howto-axis-state>` ): - -The state should be a member of :obj:`~sardana.sardanadefs.State` (For backward -compatibility reasons, it is also supported to return one of -:class:`PyTango.DevState`). The status could be any string. - -If you return a :obj:`~sardana.sardanadefs.State` object, sardana will compose a -status string with: - - <axis name> is in <state name> - -Here is an example of the possible implementation of -:meth:`~sardana.pool.controller.Controller.StateOne` : - -.. code-block:: python - :emphasize-lines: 11 - - from sardana import State - - class SpringfieldMotorController(MotorController): - - StateMap = { - 1 : State.On, - 2 : State.Moving, - 3 : State.Fault, - } - - def StateOne(self, axis): - springfield = self.springfield - state = self.StateMap[ springfield.getState(axis) ] - status = springfield.getStatus(axis) - return state, status - - -.. _sardana-controller-howto-axis-attributes: - -Extra axis attributes -~~~~~~~~~~~~~~~~~~~~~ - -Each axis is associated a set of standard attributes. These attributes depend -on the type of controller (example, a motor will have velocity, acceleration but -a counter won't). - -Additionally, you can specify an additional set of extra attributes on each axis. - -Lets suppose that a Springfield motor controller can do close loop on hardware. -We could define an extra motor attribute on each axis that (de)actives close -loop on demand. - -The first thing to do is to specify which are the extra attributes. -This is done through the :attr:`~sardana.pool.controller.Controller.axis_attributes`. -This is basically a dictionary were the keys are attribute names and the value -is a dictionary describing the folowing properties for each attribute: - -===================== ========= ============================================ ======================================================= =============================================== -config. parameter Mandatory Key Default value Example -===================== ========= ============================================ ======================================================= =============================================== -data type & format Yes :obj:`~sardana.pool.controller.Type` --- :obj:`int` -data access No :obj:`~sardana.pool.controller.Access` :obj:`~sardana.pool.controller.Access.ReadWrite` :obj:`~sardana.pool.controller.Access.ReadOnly` -description No :obj:`~sardana.pool.controller.Description` "" (empty string) "the motor encoder source" -default value No :obj:`~sardana.pool.controller.DefaultValue` --- 12345 -getter method name No :obj:`~sardana.pool.controller.FGet` "get" + <name> "getEncoderSource" -setter method name No :obj:`~sardana.pool.controller.FSet` "set" + <name> "setEncoderSource" -memorize value No :obj:`~sardana.pool.controller.Memorize` :obj:`~sardana.pool.controller.Memorized` :obj:`~sardana.pool.controller.NotMemorized` -max dimension size No :obj:`~sardana.pool.controller.MaxDimSize` Scalar: ``()``; 1D: ``(2048,)``; 2D: ``(2048, 2048)`` ``(2048,)`` -===================== ========= ============================================ ======================================================= =============================================== - -Here is an example of how to specify the scalar, boolean, read-write *CloseLoop* -extra attribute in a Springfield motor controller: - -.. code-block:: python - :emphasize-lines: 6, 14, 17 - - from sardana import DataAccess - from sardana.pool.controller import Type, Description, DefaultValue, Access, FGet, FSet - - class SpringfieldMotorController(MotorController): - - axis_attributes = { - "CloseLoop" : { - Type : bool, - Description : "(de)activates the motor close loop algorithm", - DefaultValue : False, - }, - } - - def getCloseLoop(self, axis): - return self.springfield.isCloseLoopActive(axis) - - def setCloseLoop(self, axis, value): - self.springfield.setCloseLoop(axis, value) - -When sardana needs to read the close loop value, it will first check if the -controller has the method specified by the :obj:`~sardana.pool.controller.FGet` -keyword (we didn't specify it in -:attr:`~sardana.pool.controller.Controller.axis_attributes` so it defaults to -*getCloseLoop*). It will then call this controller method which -should return a value compatible with the attribute data type. - -As an alternative, to avoid filling the controller code with pairs of get/set -methods, you can choose not to write the getCloseLoop and setCloseLoop methods. -This will trigger sardana to call the -:meth:`~sardana.pool.controller.Controller.GetAxisExtraPar` -/:meth:`~sardana.pool.controller.Controller.SetAxisExtraPar` pair of methods. -The disadvantage is you will end up with a forest of :keyword:`if` ... -:keyword:`elif` ... :keyword:`else` statements. Here is the alternative -implementation: - -.. code-block:: python - :emphasize-lines: 6, 14, 18 - - from sardana import DataAccess - from sardana.pool.controller import Type, Description, DefaultValue, Access, FGet, FSet - - class SpringfieldMotorController(MotorController): - - axis_attributes = { - "CloseLoop" : { - Type : bool, - Description : "(de)activates the motor close loop algorithm", - DefaultValue : False, - }, - } - - def GetAxisExtraPar(self, axis, parameter): - if parameter == 'CloseLoop': - return self.springfield.isCloseLoopActive(axis) - - def SetAxisExtraPar(self, axis, parameter, value): - if parameter == 'CloseLoop': - self.springfield.setCloseLoop(axis, value) - -Sardana gives you the choice: we leave it up to you to decide which is the -better option for your specific case. - -.. _sardana-controller-howto-controller-attributes: - -Extra controller attributes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Besides extra attributes per axis, you can also define extra attributes at the -controller level. -In order to do that you have to specify the extra controller attribute(s) within -the :attr:`~sardana.pool.controller.Controller.ctrl_attributes` member. The -syntax for this dictionary is the same as the one used for -:attr:`~sardana.pool.controller.Controller.axis_attributes`. - -Here is an example on how to specify a read-only float matrix attribute called -*ReflectionMatrix* at the controller level: - -.. code-block:: python - - class SpringfieldMotorController(MotorController): - - ctrl_attributes = { - "ReflectionMatrix" : { - Type : ( (float,), ), - Description : "The reflection matrix", - Access : DataAccess.ReadOnly, - }, - } - - def getReflectionMatrix(self): - return ( (1.0, 0.0), (0.0, 1.0) ) - -Or, similar to what you can do with axis attributes: - -.. code-block:: python - - class SpringfieldMotorController(MotorController): - - ctrl_attributes = \ - { - "ReflectionMatrix" : { - Type : ( (float,), ), - Description : "The reflection matrix", - Access : DataAccess.ReadOnly, - }, - } - - def GetCtrlPar(self, name): - if name == "ReflectionMatrix": - return ( (1.0, 0.0), (0.0, 1.0) ) - -.. _sardana-controller-howto-controller-properties: - -Extra controller properties -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A more static form of attributes can be defined at the controller level. -These *properties* are loaded into the controller at the time of object -construction. They are accessible to your controller at any time but it is -not possible for a user from outside to modify them. -The way to define :attr:`~sardana.pool.controller.Controller.ctrl_properties` is -very similar to the way you define extra axis attributes or extra controller -attributes. - -Here is an example on how to specify a host and port properties: - -.. code-block:: python - - class SpringfieldMotorController(MotorController): - - ctrl_properties = \ - { - "host" : { - Type : str, - Description : "host name" - }, - "port" : { - Type : int, - Description : "port number", - DefaultValue: springfieldlib.SpringfieldMotorHW.DefaultPort - }, - } - - def __init__(self, inst, props, *args, **kwargs): - super(SpringfieldMotorController, self).__init__(inst, props, *args, **kwargs) - - host = self.host - port = self.port - - # initialize hardware communication - self.springfield = springfieldlib.SpringfieldMotorHW(host=host, port=port) - - # do some initialization - self._motors = {} - -As you can see from lines 15 and 16, to access your controller properties -simply use ``self.<property name>``. Sardana assures that every property has a -value. In our case, when a SpringfieldMotorController is created, if port -property is not specified by the user (example: using the ``defctrl`` macro in -spock), sardana assignes the default value -``springfieldlib.SpringfieldMotorHW.DefaultPort``. On the other hand, since host -has no default value, if it is not specified by the user, sardana will complain -and fail to create and instance of SpringfieldMotorController. - -.. _sardana-controller-howto-error-handling: - -Error handling -~~~~~~~~~~~~~~ - -When you write a controller it is important to properly handle errors -(example: motor power overload, hit a limit switch, lost of communication with -the hardware). - -These are the two basic sardana rules you should have in mind: - -#. The exceptions which are not handled by the controller are handled by sardana, - usually by re-raising the exception (when sardana runs as a Tango_ DS a - translation is done from the Python_ exception to a Tango_ exception). - The :meth:`~sardana.pool.controller.Controller.StateOne` method is handled a - little differently: the state is set to ``Fault`` and the status will contain - the exception information. - -#. When the methods which are supposed to return a value (like - :meth:`~sardana.pool.controller.Controller.GetAxisPar`) don't return a value - compatible with the expected data type (including :obj:`None`) a - :exc:`TypeError` exception is thrown. - -In every method you should carefully choose how to do handle the possible -exceptions/errors. - -Usually, catch and handle is the best technique since it is the code of your -controller which knows exactly the workings of the hardware. You can -discriminate errors and decide a proper handle for each. Essencially, this -technique consists of: - -#. catching the error (if an exception: with :keyword:`try` ... :keyword:`except` - clause, if an expected return of a function: with a :keyword:`if` ... - :keyword:`elif` ... :keyword:`else` statement, etc) - -#. raise a proper exception (could be the same exception that has been catched) - or, if in :meth:`~sardana.pool.controller.Controller.StateOne`, return the - apropriate error state (``Fault``, ``Alarm``) and a descriptive status. - -Here is an example: if the documentation of the underlying library says that: - - `reading the motor closeloop raises CommunicationFailed if it is not - possible to communicate with the Springfield hardware` - - `reading the motor state raises MotorPowerOverload if the motors - has a power overload or a MotorTempTooHigh when the motor - temperature is too high` - -then you should handle the exception in the controller and return a proper -state information:: - - def getCloseLoop(self, axis): - # Here the "proper exception" to raise in case of error is actually the - # one that is raised from the springfield library so handling the - # exception is transparent. Nice! - return self.springfield.isCloseLoopActive(axis) - - def StateOne(self, axis): - springfield = self.springfield - - try: - state = self.StateMap[ springfield.getState(axis) ] - status = springfield.getStatus(axis) - except springfieldlib.MotorPowerOverload: - state = State.Fault - status = "Motor has a power overload" - except springfieldlib.MotorTempTooHigh: - temp = springfield.getTemperature(axis) - state = State.Alarm - status = "Motor temperature is too high (%f degrees)" % temp - - limit_switches = MotorController.NoLimitSwitch - hw_limit_switches = springfield.getLimits(axis) - if hw_limit_switches[0]: - limit_switches |= MotorController.HomeLimitSwitch - if hw_limit_switches[1]: - limit_switches |= MotorController.UpperLimitSwitch - if hw_limit_switches[2]: - limit_switches |= MotorController.LowerLimitSwitch - return state, status, limit_switches - -Hiding the exception is usually a **BAD** technique since it prevents the user -from finding what was the cause of the problem. You should only use it in -extreme cases (example: if there is a bug in sardana which crashes the server -if you try to properly raise an exception, then you can **temporarely** use -this technique until the bug is solved). - -Example:: - - def getCloseLoop(self, axis): - # BAD error handling technique - try: - return self.springfield.isCloseLoopActive(axis) - except: - pass - -.. rubric:: Footnotes - -.. [#f1] Pseudo controllers don't need to manage their individual axis. Therefore, - for pseudos you will not implement these methods - - -.. [#f2] For pseudo controllers, sardana will calculate the state of each pseudo - axis based on the state of the elements that serve as input to the - pseudo controller. Therefore, for pseudos you will not implement these - methods - -.. _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/ diff --git a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst b/doc/source/devel/howto_controllers/howto_countertimercontroller.rst deleted file mode 100644 index 22976064..00000000 --- a/doc/source/devel/howto_controllers/howto_countertimercontroller.rst +++ /dev/null @@ -1,105 +0,0 @@ -.. currentmodule:: sardana.pool.controller - -.. _sardana-countertimercontroller-howto-basics: - -======================================= -How to write a counter/timer controller -======================================= - -The basics ----------- - -An example of a hypothetical *Springfield* counter/timer controller will be build -incrementally from scratch to aid in the explanation. - -By now you should have read the general controller basics chapter. You should -be able to create a CounterTimerController with: - -- a proper constructor, -- add and delete axis methods -- get axis state - - -.. code-block:: python - - import springfieldlib - - from sardana.pool.controller import CounterTimerController - - class SpringfieldCounterTimerController(CounterTimerController): - - def __init__(self, inst, props, *args, **kwargs): - super(SpringfieldCounterTimerController, self).__init__(inst, props, *args, **kwargs) - - # initialize hardware communication - self.springfield = springfieldlib.SpringfieldCounterHW() - - # do some initialization - self._counters = {} - - def AddDevice(self, axis): - self._counters[axis] = True - - def DeleteDevice(self, axis): - del self._counters[axis] - - StateMap = { - 1 : State.On, - 2 : State.Moving, - 3 : State.Fault, - } - - def StateOne(self, axis): - springfield = self.springfield - state = self.StateMap[ springfield.getState(axis) ] - status = springfield.getStatus(axis) - return state, status - -The examples use a :mod:`springfieldlib` module which emulates a counter/timer -hardware access library. - -The :mod:`springfieldlib` can be downloaded from -:download:`here <springfieldlib.py>`. - -The Springfield counter/timer controller can be downloaded from -:download:`here <sf_ct_ctrl.py>`. - -The following code describes a minimal *Springfield* base counter/timer controller -which is able to return both the state and value of an individual counter as -well as to start an acquisition: - -.. literalinclude:: sf_ct_ctrl.py - :pyobject: SpringfieldBaseCounterTimerController - -This code is shown only to demonstrate the minimal controller :term:`API`. -The advanced counter/timer controller chapters describe how to account for more -complex behaviour like reducing the number of hardware accesses. - -.. todo:: finish counter/timer controller howto - - - -.. _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/ diff --git a/doc/source/devel/howto_controllers/howto_ioregistercontroller.rst b/doc/source/devel/howto_controllers/howto_ioregistercontroller.rst deleted file mode 100644 index ed669c03..00000000 --- a/doc/source/devel/howto_controllers/howto_ioregistercontroller.rst +++ /dev/null @@ -1,37 +0,0 @@ -.. currentmodule:: sardana.pool.controller - -.. _sardana-ioregistercontroller-howto-basics: - -======================================= -How to write an I/O register controller -======================================= - -The basics ----------- - -.. todo:: document IORegister controller howto - -.. _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/ diff --git a/doc/source/devel/howto_controllers/howto_motorcontroller.rst b/doc/source/devel/howto_controllers/howto_motorcontroller.rst deleted file mode 100644 index bb2aa272..00000000 --- a/doc/source/devel/howto_controllers/howto_motorcontroller.rst +++ /dev/null @@ -1,694 +0,0 @@ -.. currentmodule:: sardana.pool.controller - -.. _sardana-motorcontroller-howto-basics: - -=============================== -How to write a motor controller -=============================== - -The basics ----------- - -An example of a hypothetical *Springfield* motor controller will be build -incrementally from scratch to aid in the explanation. - -By now you should have read the general controller basics chapter. You should -now have a MotorController with a proper constructor, add and delete axis methods: - -.. code-block:: python - - import springfieldlib - - from sardana.pool.controller import MotorController - - class SpringfieldMotorController(MotorController): - - def __init__(self, inst, props, *args, **kwargs): - super(SpringfieldMotorController, self).__init__(inst, props, *args, **kwargs) - - # initialize hardware communication - self.springfield = springfieldlib.SpringfieldMotorHW() - - # do some initialization - self._motors = {} - - def AddDevice(self, axis): - self._motors[axis] = True - - def DeleteDevice(self, axis): - del self._motor[axis] - -The *get axis state* method has some details that will be explained below. - -The examples use a :mod:`springfieldlib` module which emulates a motor hardware -access library. - -The :mod:`springfieldlib` can be downloaded from -:download:`here <springfieldlib.py>`. - -The Springfield motor controller can be downloaded from -:download:`here <sf_motor_ctrl.py>`. - -The following code describes a minimal *Springfield* base motor controller -which is able to return both the state and position of a motor as well as move -a motor to the desired position: - -.. literalinclude:: sf_motor_ctrl.py - :pyobject: SpringfieldBaseMotorController - -This code is shown only to demonstrate the minimal controller :term:`API`. -The advanced motor controller chapters describe how to account for more complex -behaviour like reducing the number of hardware accesses or synchronize motion of -multiple motors. - -.. _sardana-motorcontroller-howto-axis-state: - -Get motor state -~~~~~~~~~~~~~~~ - -To get the state of a motor, sardana calls the -:meth:`~sardana.pool.controller.Controller.StateOne` method. This method -receives an axis as parameter and should return a sequence of three values: - -To get the state of a motor, sardana calls the -:meth:`~sardana.pool.controller.Controller.StateOne` method. This method -receives an axis as parameter and should return either: - - - state (:obj:`~sardana.sardanadefs.State`) or - - a sequence of two elements: - - state (:obj:`~sardana.sardanadefs.State`) - - status (:obj:`str`) *or* limit switches (:obj:`int`) - - a sequence of three elements: - - state (:obj:`~sardana.sardanadefs.State`) - - status (:obj:`str`) - - limit switches (:obj:`int`) - -The state should be a member of :obj:`~sardana.sardanadefs.State` (For backward -compatibility reasons, it is also supported to return one of -:class:`PyTango.DevState`). The status could be any string. The limit switches -is a integer with bits representing the three possible limits: home, upper -and lower. Sardana provides three constants which can be *or*\ed together to -provide the desired limit switch: - -.. hlist:: - :columns: 4 - - - :attr:`~MotorController.NoLimitSwitch` - - :attr:`~MotorController.HomeLimitSwitch` - - :attr:`~MotorController.UpperLimitSwitch` - - :attr:`~MotorController.LowerLimitSwitch` - -To say both home and lower limit switches are active (rare!) you can do:: - - limit_switches = MotorController.HomeLimitSwitch | MotorController.LowerLimitSwitch - -If you don't return a status, sardana will compose a status string with: - - <axis name> is in <state name> - -If you don't return limit switches, sardana will assume all limit switches are -off. - -Here is an example of the possible implementation of -:meth:`~sardana.pool.controller.Controller.StateOne`: - -.. code-block:: python - :emphasize-lines: 11 - - from sardana import State - - class SpringfieldMotorController(MotorController): - - StateMap = { - 1 : State.On, - 2 : State.Moving, - 3 : State.Fault, - } - - def StateOne(self, axis): - springfield = self.springfield - state = self.StateMap[ springfield.getState(axis) ] - status = springfield.getStatus(axis) - - limit_switches = MotorController.NoLimitSwitch - hw_limit_switches = springfield.getLimits(axis) - if hw_limit_switches[0]: - limit_switches |= MotorController.HomeLimitSwitch - if hw_limit_switches[1]: - limit_switches |= MotorController.UpperLimitSwitch - if hw_limit_switches[2]: - limit_switches |= MotorController.LowerLimitSwitch - return state, status, limit_switches - -.. _sardana-motorcontroller-howto-value: - -Get motor position -~~~~~~~~~~~~~~~~~~ - -To get the motor position, sardana calls the -:meth:`~sardana.pool.controller.Readable.ReadOne` method. This method -receives an axis as parameter and should return a valid position. Sardana -interprets the returned position as a :term:`dial position`. - -Here is an example of the possible implementation of -:meth:`~sardana.pool.controller.Readable.ReadOne`: - -.. code-block:: python - :emphasize-lines: 3 - - class SpringfieldMotorController(MotorController): - - def ReadOne(self, axis): - position = self.springfield.getPosition(axis) - return position - -.. _sardana-motorcontroller-howto-move: - -Move a motor -~~~~~~~~~~~~ - -When an order comes for sardana to move a motor, sardana will call the -:meth:`~sardana.pool.controller.Startable.StartOne` method. This method receives -an axis and a position. The controller code should trigger the hardware motion. -The given position is always the :term:`dial position`. - -Here is an example of the possible implementation of -:meth:`~sardana.pool.controller.Startable.StartOne`: - -.. code-block:: python - :emphasize-lines: 3 - - class SpringfieldMotorController(MotorController): - - def StartOne(self, axis, position): - self.springfield.move(axis, position) - -As soon as :meth:`~sardana.pool.controller.Startable.StartOne` is invoked, -sardana expects the motor to be moving. It enters a high frequency motion -loop which asks for the motor state through calls to -:meth:`~sardana.pool.controller.Controller.StateOne`. It will keep the loop -running as long as the controller responds with ``State.Moving``. -If :meth:`~sardana.pool.controller.Controller.StateOne` raises an exception -or returns something other than ``State.Moving``, sardana will assume the motor -is stopped and exit the motion loop. - -For a motion to work properly, it is therefore, **very important** that -:meth:`~sardana.pool.controller.Controller.StateOne` responds correctly. - -.. _sardana-motorcontroller-howto-stop: - -Stop a motor -~~~~~~~~~~~~ - -It is possible to stop a motor when it is moving. When sardana is ordered to -stop a motor motion, it invokes the :meth:`~sardana.pool.controller.Stopable.StopOne` -method. This method receives an axis parameter. The controller should make -sure the desired motor is *gracefully* stopped, if possible, respecting the -configured motion parameters (like deceleration and base_rate). - -Here is an example of the possible implementation of -:meth:`~sardana.pool.controller.Stopable.StopOne`: - -.. code-block:: python - :emphasize-lines: 3 - - class SpringfieldMotorController(MotorController): - - def StopOne(self, axis): - self.springfield.stop(axis) - -.. _sardana-motorcontroller-howto-abort: - -Abort a motor -~~~~~~~~~~~~~ - -In a danger situation (motor moving a table about to hit a wall), it is -desirable to abort a motion *as fast as possible*. When sardana is ordered to -abort a motor motion, it invokes the :meth:`~sardana.pool.controller.Stopable.AbortOne` -method. This method receives an axis parameter. The controller should make -sure the desired motor is stopped as fast as it can be done, possibly losing -track of position. - -Here is an example of the possible implementation of -:meth:`~sardana.pool.controller.Stopable.AbortOne`: - -.. code-block:: python - :emphasize-lines: 3 - - class SpringfieldMotorController(MotorController): - - def AbortOne(self, axis): - self.springfield.abort(axis) - -.. note:: - - The default implementation of :meth:`~sardana.pool.controller.Stopable.StopOne` - calls :meth:`~sardana.pool.controller.Stopable.AbortOne` so, if your - controller cannot distinguish stopping from aborting, it is sufficient - to implement :meth:`~sardana.pool.controller.Stopable.AbortOne`. - -.. _sardana-motorcontroller-howto-standard-axis-attributes: - -Standard axis attributes -~~~~~~~~~~~~~~~~~~~~~~~~ - -By default, sardana expects every axis to have a set of attributes: - -- acceleration -- deceleration -- velocity -- base rate -- steps per unit - -To set and retrieve the value of these attributes, sardana invokes pair of -methods: :meth:`~sardana.pool.controller.Controller.GetAxisPar` -/:meth:`~sardana.pool.controller.Controller.SetAxisPar` - -Here is an example of the possible implementation: - -.. code-block:: python - :emphasize-lines: 3, 18 - - class SpringfieldMotorController(MotorController): - - def GetAxisPar(self, axis, name): - springfield = self.springfield - name = name.lower() - if name == "acceleration": - v = springfield.getAccelerationTime(axis) - elif name == "deceleration": - v = springfield.getDecelerationTime(axis) - elif name == "base_rate": - v = springfield.getMinVelocity(axis) - elif name == "velocity": - v = springfield.getMaxVelocity(axis) - elif name == "step_per_unit": - v = springfield.getStepPerUnit(axis) - return v - - def SetAxisPar(self, axis, name, value): - springfield = self.springfield - name = name.lower() - if name == "acceleration": - springfield.setAccelerationTime(axis, value) - elif name == "deceleration": - springfield.setDecelerationTime(axis, value) - elif name == "base_rate": - springfield.setMinVelocity(axis, value) - elif name == "velocity": - springfield.setMaxVelocity(axis, value) - elif name == "step_per_unit": - springfield.setStepPerUnit(axis, value) - -.. seealso:: - - :ref:`sardana-motorcontroller-what-to-do` - What to do when your hardware motor controller doesn't support - steps per unit - -.. _sardana-motorcontroller-define-position: - -Define a position -~~~~~~~~~~~~~~~~~ - -Sometimes it is useful to reset the current position to a certain value. -Imagine you are writing a controller for a hardware controller which handles -stepper motors. When the hardware is asked for a motor position it will -probably answer some value from an internal register which is -incremented/decremented each time the motor goes up/down a step. Probably this -value as physical meaning so the usual procedure is to move the motor to a known -position (home switch, for example) and once there, set a meaningful position to -the current position. Some motor controllers support reseting the internal -register to the desired value. If your motor controller can do this the -implementation is as easy as writing the -:meth:`~sardana.pool.controller.MotorController.DefinePosition` and call the -proper code of your hardware library to do it: - -.. code-block:: python - - class SpringfieldMotorController(MotorController): - - def DefinePosition(self, axis, position): - self.springfield.setCurrentPosition(axis, position) - -.. seealso:: - - :ref:`sardana-motorcontroller-what-to-do` - - What to do when your hardware motor controller doesn't support - defining the position - -.. _sardana-motorcontroller-what-to-do: - -What to do when... -~~~~~~~~~~~~~~~~~~ - -This chapter describes common difficult situations you may face when writing -a motor controller in sardana, and possible solutions to solve them. - -*my controller doesn't support steps per unit* - Many (probably, most) hardware motor controllers don't support steps per - unit at the hardware level. This means that your sardana controller should - be able to emulate steps per unit at the software level. - This can be easily done, but it requires you to make some changes in your - code. - - We will assume now that the Springfield motor controller doesn't support - steps per unit feature. The first that needs to be done is to modify the - :meth:`~sardana.pool.controller.Controller.AddDevice` method so it is able to - to store the resulting conversion factor between the hardware read position - and the position the should be returned (the *step_per_unit*). - The :meth:`~sardana.pool.controller.Readable.ReadOne` also needs to be - rewritten to make the proper calculation. - Finally :meth:`~sardana.pool.controller.Controller.GetAxisPar` / - :meth:`~sardana.pool.controller.Controller.SetAxisPar` methods need to - be rewritten to properly get/set the step per unit value: - - .. code-block:: python - - class SpringfieldMotorController(MotorController): - - def AddDevice(self, axis): - self._motor[axis] = dict(step_per_unit=1.0) - - def ReadOne(self, axis): - step_per_unit = self._motor[axis]["step_per_unit"] - position = self.springfield.getPosition(axis) - return position / step_per_unit - - def GetAxisPar(self, axis, name): - springfield = self.springfield - name = name.lower() - if name == "acceleration": - v = springfield.getAccelerationTime(axis) - elif name == "deceleration": - v = springfield.getDecelerationTime(axis) - elif name == "base_rate": - v = springfield.getMinVelocity(axis) - elif name == "velocity": - v = springfield.getMaxVelocity(axis) - elif name == "step_per_unit": - v = self._motor[axis]["step_per_unit"] - return v - - def SetAxisPar(self, axis, name, value): - springfield = self.springfield - name = name.lower() - if name == "acceleration": - springfield.setAccelerationTime(axis, value) - elif name == "deceleration": - springfield.setDecelerationTime(axis, value) - elif name == "base_rate": - springfield.setMinVelocity(axis, value) - elif name == "velocity": - springfield.setMaxVelocity(axis, value) - elif name == "step_per_unit": - self._motor[axis]["step_per_unit"] = value - -*my controller doesn't support defining the position* - Some controllers may not be able to reset the position to a different value. - In these cases, your controller code should be able to emulate such a - feature. This can be easily done, but it requires you to make some changes - in your code. - - We will now assume that the Springfield motor controller doesn't support - steps per unit feature. The first thing that needs to be done is to modify the - :meth:`~sardana.pool.controller.Controller.AddDevice` method so it is able - to store the resulting offset between the hardware read position and the - position the should be returned (the *define_position_offset*). - The :meth:`~sardana.pool.controller.Readable.ReadOne` also needs to be - rewritten to take the *define_position_offset* into account. - Finally :meth:`~sardana.pool.controller.MotorController.DefinePosition` - needs to be written to update the *define_position_offset* to the desired - value: - - .. code-block:: python - - class SpringfieldMotorController(MotorController): - - def AddDevice(self, axis): - self._motor[axis] = dict(define_position_offset=0.0) - - def ReadOne(self, axis): - dp_offset = self._motor[axis]["define_position_offset"] - position = self.springfield.getPosition(axis) - return position + dp_offset - - def DefinePosition(self, axis, position): - current_position = self.springfield.getPosition(axis) - self._motor[axis]["define_position_offset"] = position - current_position - - -Advanced topics ---------------- - -.. _sardana-motorcontroller-howto-timestamp-position: - -Timestamp a motor position -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When you read the position of a motor from the hardware sometimes it is -necessary to associate a timestamp with that position so you can track the -position of a motor in time. - -If sardana is executed as a Tango device server, reading the position -attribute from the motor device triggers the execution of your controller's -:meth:`~sardana.pool.controller.Readable.ReadOne` method. Tango responds with -the value your controller returns from the call to -:meth:`~sardana.pool.controller.Readable.ReadOne` and automatically assigns -a timestamp. However this timestamp has a certain delay since the time the -value was actually read from hardware and the time Tango generates the timestamp. - -To avoid this, sardana supports returning in -:meth:`~sardana.pool.controller.Readable.ReadOne` an object that contains both -the value and the timestamp instead of the usual :class:`numbers.Number`. -The object must be an instance of :class:`~sardana.sardanavalue.SardanaValue`. - -Here is an example of associating a timestamp in -:meth:`~sardana.pool.controller.Readable.ReadOne`: - -.. code-block:: python - - import time - from sardana.pool.controller import SardanaValue - - class SpringfieldMotorController(MotorController): - - def ReadOne(self, axis): - return SardanaValue(value=self.springfield.getPosition(axis), - timestamp=time.time()) - -If your controller communicates with a Tango device, Sardana also supports -returning a :class:`~PyTango.DeviceAttribute` object. Sardana will use this -object's value and timestamp. Example: - -.. code-block:: python - - class TangoMotorController(MotorController): - - def ReadOne(self, axis): - return self.device.read_attribute("position") - -.. _sardana-motorcontroller-howto-mutiple-motion: - -Multiple motion synchronization -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This chapter describes an extended :term:`API` that allows you to better -synchronize motions involing more than one motor, as well as optimize -hardware communication (in case the hardware interface also supports this). - -Often it is the case that the experiment/procedure the user runs requires to -move more than one motor at the same time. -Imagine that the user requires motor at axis 1 to be moved to 100mm and motor -axis 2 to be moved to -20mm. -Your controller will receive two consecutive calls to -:meth:`~sardana.pool.controller.Startable.StartOne`: - -.. code-block:: python - - StartOne(1, 100) - StartOne(2, -20) - -and each StartOne will probably connect to the hardware (through serial line, -socket, Tango_ or EPICS_) and ask the motor to be moved. -This will do the job but, there will be a slight desynchronization between the -two motors because hardware call of motor 1 will be done before hardware call -to motor 2. - -Sardana provides an extended *start motion* which gives you the possibility -to improve the syncronization (and probably reduce communications) but your -hardware controller must somehow support this feature as well. - -The complete start motion :term:`API` consists of four methods: - - - :meth:`~sardana.pool.controller.Startable.PreStartAll` - - :meth:`~sardana.pool.controller.Startable.PreStartOne` - - :meth:`~sardana.pool.controller.Startable.StartOne` - - :meth:`~sardana.pool.controller.Startable.StartAll` - -Except for :meth:`~sardana.pool.controller.Startable.StartOne`, the -implemenation of all other start methods is optional and their default -implementation does nothing (:meth:`~sardana.pool.controller.Startable.PreStartOne` -actually returns ``True``). - -So, actually, the complete algorithm for motor motion in sardana is:: - - /FOR/ Each controller(s) implied in the motion - - Call PreStartAll() - /END FOR/ - - /FOR/ Each motor(s) implied in the motion - - ret = PreStartOne(motor to move, new position) - - /IF/ ret is not true - /RAISE/ Cannot start. Motor PreStartOne returns False - - /END IF/ - - Call StartOne(motor to move, new position) - /END FOR/ - - /FOR/ Each controller(s) implied in the motion - - Call StartAll() - /END FOR/ - -So, for the example above where we move two motors, the complete sequence of -calls to the controller is: - -.. code-block:: python - - PreStartAll() - - if not PreStartOne(1, 100): - raise Exception("Cannot start. Motor(1) PreStartOne returns False") - if not PreStartOne(2, -20): - raise Exception("Cannot start. Motor(2) PreStartOne returns False") - - StartOne(1, 100) - StartOne(2, -20) - - StartAll() - -Sardana assures that the above sequence is never interrupted by other calls, -like a call from a different user to get motor state. - -Suppose the springfield library tells us in the documentation that: - - ... to move multiple motors at the same time use:: - - moveMultiple(seq<pair<axis, position>>) - - Example:: - - moveMultiple([[1, 100], [2, -20]]) - -We can modify our motor controller to take profit of this hardware feature: - -.. code-block:: python - - class SpringfieldMotorController(MotorController): - - def PreStartAll(self): - # clear the local motion information dictionary - self._moveable_info = [] - - def StartOne(self, axis, position): - # store information about this axis motion - motion_info = axis, position - self._moveable_info.append(motion_info) - - def StartAll(self): - self.springfield.moveMultiple(self._moveable_info) - -A similar principle applies when sardana asks for the state and position of -multiple axis. The two sets of methods are, in these cases: - -.. hlist:: - :columns: 2 - - - :meth:`~sardana.pool.controller.Controller.PreStateAll` - - :meth:`~sardana.pool.controller.Controller.PreStateOne` - - :meth:`~sardana.pool.controller.Controller.StateAll` - - :meth:`~sardana.pool.controller.Controller.StateOne` - - :meth:`~sardana.pool.controller.Readable.PreReadAll` - - :meth:`~sardana.pool.controller.Readable.PreReadOne` - - :meth:`~sardana.pool.controller.Readable.ReadAll` - - :meth:`~sardana.pool.controller.Readable.ReadOne` - -The main differences between these sets of methods and the ones from start motion -is that :meth:`~sardana.pool.controller.Controller.StateOne` / -:meth:`~sardana.pool.controller.Readable.ReadOne` methods are called **AFTER** -the corresponding :meth:`~sardana.pool.controller.Controller.StateAll` / -:meth:`~sardana.pool.controller.Readable.ReadAll` counterparts and they are -expeced to return the state/position of the requested axis. - -The internal sardana algorithm to read position is:: - - /FOR/ Each controller(s) implied in the reading - - Call PreReadAll() - /END FOR/ - - /FOR/ Each motor(s) implied in the reading - - PreReadOne(motor to read) - /END FOR/ - - /FOR/ Each controller(s) implied in the reading - - Call ReadAll() - /END FOR/ - - /FOR/ Each motor(s) implied in the reading - - Call ReadOne(motor to read) - /END FOR/ - -Here is an example assuming the springfield library tells us in the -documentation that: - - ... to read the position of multiple motors at the same time use:: - - getMultiplePosition(seq<axis>) -> dict<axis, position> - - Example:: - - positions = getMultiplePosition([1, 2]) - -The new improved code could look like this:: - - class SpringfieldMotorController(MotorController): - - def PreRealAll(self): - # clear the local position information dictionary - self._position_info = [] - - def PreReadOne(self, axis): - self._position_info.append(axis) - - def ReadAll(self): - self._positions = self.springfield.getMultiplePosition(self._position_info) - - def ReadOne(self, axis): - return self._positions[axis] - - -.. _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/ -
\ No newline at end of file diff --git a/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst b/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst deleted file mode 100644 index 91ef8d7e..00000000 --- a/doc/source/devel/howto_controllers/howto_pseudocountercontroller.rst +++ /dev/null @@ -1,37 +0,0 @@ -.. currentmodule:: sardana.pool.controller - -.. _sardana-pseudocountercontroller-howto-basics: - -======================================== -How to write a pseudo counter controller -======================================== - -The basics ----------- - -.. todo:: document pseudo counter controller howto - -.. _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/ diff --git a/doc/source/devel/howto_controllers/howto_pseudomotorcontroller.rst b/doc/source/devel/howto_controllers/howto_pseudomotorcontroller.rst deleted file mode 100644 index b3a1a841..00000000 --- a/doc/source/devel/howto_controllers/howto_pseudomotorcontroller.rst +++ /dev/null @@ -1,37 +0,0 @@ -.. currentmodule:: sardana.pool.controller - -.. _sardana-pseudomotorcontroller-howto-basics: - -====================================== -How to write a pseudo motor controller -====================================== - -The basics ----------- - -.. todo:: document pseudo motor controller howto - -.. _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/ diff --git a/doc/source/devel/howto_controllers/index.rst b/doc/source/devel/howto_controllers/index.rst deleted file mode 100644 index 964f332d..00000000 --- a/doc/source/devel/howto_controllers/index.rst +++ /dev/null @@ -1,32 +0,0 @@ -.. currentmodule:: sardana.pool.controller - -.. _sardana-controller-howto: - -=================== -Writing controllers -=================== - -This chapter provides the necessary information to write controllers in sardana. - -An overview of the pool controller concept can be found -:ref:`here <sardana-controller-overview>`. - -The complete controller :term:`API` can be found -:ref:`here <sardana-controller-api>`. - -First, the common interface to all controller types is explained. After, a -detailed chapter will focus on each specific controller type: - -.. toctree:: - :maxdepth: 1 - - howto_controller - howto_motorcontroller - howto_countertimercontroller - howto_0dcontroller - howto_1dcontroller - howto_2dcontroller - howto_ioregistercontroller - howto_pseudomotorcontroller - howto_pseudocountercontroller - diff --git a/doc/source/devel/howto_controllers/sf_ct_ctrl.py b/doc/source/devel/howto_controllers/sf_ct_ctrl.py deleted file mode 100644 index 4da7c50a..00000000 --- a/doc/source/devel/howto_controllers/sf_ct_ctrl.py +++ /dev/null @@ -1,122 +0,0 @@ -############################################################################## -## -## This file is part of Sardana -## -## http://www.sardana-controls.org/ -## -## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain -## -## Sardana is free software: you can redistribute it and/or modify -## it under the terms of the GNU Lesser General Public License as published by -## the Free Software Foundation, either version 3 of the License, or -## (at your option) any later version. -## -## Sardana is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU Lesser General Public License for more details. -## -## You should have received a copy of the GNU Lesser General Public License -## along with Sardana. If not, see <http://www.gnu.org/licenses/>. -## -############################################################################## - -"""This file contains the code for an hypothetical Springfield counter/timer -controller used in documentation""" - -import time - -import springfieldlib - -from sardana import State -from sardana.pool.controller import CounterTimerController - -class SpringfieldBaseCounterTimerController(CounterTimerController): - """The most basic controller intended from demonstration purposes only. - This is the absolute minimum you have to implement to set a proper counter - controller able to get a counter value, get a counter state and do an - acquisition. - - This example is so basic that it is not even directly described in the - documentation""" - - def __init__(self, inst, props, *args, **kwargs): - """Constructor""" - super(SpringfieldBaseCounterTimerController, self).__init__(inst, props, *args, **kwargs) - self.springfield = springfieldlib.SpringfieldCounterHW() - - def ReadOne(self, axis): - """Get the specified counter value""" - return self.springfield.getValue(axis) - - def StateOne(self, axis): - """Get the specified counter state""" - springfield = self.springfield - state = springfield.getState(axis) - if state == 1: - return State.On, "Counter is stopped" - elif state == 2: - return State.Moving, "Counter is acquiring" - elif state == 3: - return State.Fault, "Counter has an error" - - def StartAll(self): - self.springfield.start_count() - - def StartOne(self, axis, value=None): - """acquire the specified counter""" - self.springfield.activate_channel(axis) - - def LoadOne(self, axis, value): - self.springfield.set_master(axis, value) - - def StopOne(self, axis): - """Stop the specified counter""" - self.springfield.stop(axis) - - -from sardana import DataAccess -from sardana.pool.controller import Type, Description, DefaultValue, Access, FGet, FSet - -class SpringfieldCounterTimerController(CounterTimerController): - - def __init__(self, inst, props, *args, **kwargs): - super(SpringfieldCounterTimerController, self).__init__(inst, props, *args, **kwargs) - - # initialize hardware communication - self.springfield = springfieldlib.SpringfieldCounterHW() - - # do some initialization - self._counters = {} - - def AddDevice(self, axis): - self._counters[axis] = True - - def DeleteDevice(self, axis): - del self._counters[axis] - - StateMap = { - 1 : State.On, - 2 : State.Moving, - 3 : State.Fault, - } - - def StateOne(self, axis): - springfield = self.springfield - state = self.StateMap[ springfield.getState(axis) ] - status = springfield.getStatus(axis) - return state, status - - def ReadOne(self, axis): - value = self.springfield.getValue(axis) - return value - - def StartOne(self, axis, position): - self.springfield.move(axis, position) - - def StopOne(self, axis): - self.springfield.stop(axis) - - def AbortOne(self, axis): - self.springfield.abort(axis) -
\ No newline at end of file diff --git a/doc/source/devel/howto_controllers/sf_motor_ctrl.py b/doc/source/devel/howto_controllers/sf_motor_ctrl.py deleted file mode 100644 index 80d938bb..00000000 --- a/doc/source/devel/howto_controllers/sf_motor_ctrl.py +++ /dev/null @@ -1,141 +0,0 @@ -############################################################################## -## -## This file is part of Sardana -## -## http://www.sardana-controls.org/ -## -## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain -## -## Sardana is free software: you can redistribute it and/or modify -## it under the terms of the GNU Lesser General Public License as published by -## the Free Software Foundation, either version 3 of the License, or -## (at your option) any later version. -## -## Sardana is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU Lesser General Public License for more details. -## -## You should have received a copy of the GNU Lesser General Public License -## along with Sardana. If not, see <http://www.gnu.org/licenses/>. -## -############################################################################## - -"""This file contains the code for an hypothetical Springfield motor controller -used in documentation""" - -import springfieldlib - -from sardana import State -from sardana.pool.controller import MotorController - -class SpringfieldBaseMotorController(MotorController): - """The most basic controller intended from demonstration purposes only. - This is the absolute minimum you have to implement to set a proper motor - controller able to get a motor position, get a motor state and move a - motor. - - This example is so basic that it is not even directly described in the - documentation""" - - MaxDevice = 128 - - def __init__(self, inst, props, *args, **kwargs): - """Constructor""" - super(SpringfieldBaseMotorController, self).__init__(inst, props, *args, **kwargs) - self.springfield = springfieldlib.SpringfieldMotorHW() - - def ReadOne(self, axis): - """Get the specified motor position""" - return self.springfield.getPosition(axis) - - def StateOne(self, axis): - """Get the specified motor state""" - springfield = self.springfield - state = springfield.getState(axis) - if state == 1: - return State.On, "Motor is stopped" - elif state == 2: - return State.Moving, "Motor is moving" - elif state == 3: - return State.Fault, "Motor has an error" - - def StartOne(self, axis, position): - """Move the specified motor to the specified position""" - self.springfield.move(axis, position) - - def StopOne(self, axis): - """Stop the specified motor""" - self.springfield.stop(axis) - - -from sardana import DataAccess -from sardana.pool.controller import Type, Description, DefaultValue, Access, FGet, FSet - -class SpringfieldMotorController(MotorController): - - axis_attributes = { - "CloseLoop" : { - Type : bool, - Description : "(de)activates the motor close loop algorithm", - DefaultValue : False, - }, - } - - def getCloseLoop(self, axis): - return self.springfield.isCloseLoopActive(axis) - - def setCloseLoop(self, axis, value): - self.springfield.setCloseLoop(axis, value) - - def __init__(self, inst, props, *args, **kwargs): - super(SpringfieldMotorController, self).__init__(inst, props, *args, **kwargs) - - # initialize hardware communication - self.springfield = springfieldlib.SpringfieldMotorHW() - - # do some initialization - self._motors = {} - - def AddDevice(self, axis): - self._motors[axis] = True - - def DeleteDevice(self, axis): - del self._motors[axis] - - StateMap = { - 1 : State.On, - 2 : State.Moving, - 3 : State.Fault, - } - - def StateOne(self, axis): - springfield = self.springfield - state = self.StateMap[ springfield.getState(axis) ] - status = springfield.getStatus(axis) - - limit_switches = MotorController.NoLimitSwitch - hw_limit_switches = springfield.getLimits(axis) - if hw_limit_switches[0]: - limit_switches |= MotorController.HomeLimitSwitch - if hw_limit_switches[1]: - limit_switches |= MotorController.UpperLimitSwitch - if hw_limit_switches[2]: - limit_switches |= MotorController.LowerLimitSwitch - return state, status, limit_switches - - def ReadOne(self, axis): - position = self.springfield.getPosition(axis) - return position - - def StartOne(self, axis, position): - self.springfield.move(axis, position) - - def StopOne(self, axis): - self.springfield.stop(axis) - - def AbortOne(self, axis): - self.springfield.abort(axis) - - def DefinePosition(self, axis, position): - self.springfield.setCurrentPosition(axis, position) diff --git a/doc/source/devel/howto_controllers/springfieldlib.py b/doc/source/devel/howto_controllers/springfieldlib.py deleted file mode 100644 index f141ffee..00000000 --- a/doc/source/devel/howto_controllers/springfieldlib.py +++ /dev/null @@ -1,653 +0,0 @@ -############################################################################## -## -## This file is part of Sardana -## -## http://www.sardana-controls.org/ -## -## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain -## -## Sardana is free software: you can redistribute it and/or modify -## it under the terms of the GNU Lesser General Public License as published by -## the Free Software Foundation, either version 3 of the License, or -## (at your option) any later version. -## -## Sardana is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU Lesser General Public License for more details. -## -## You should have received a copy of the GNU Lesser General Public License -## along with Sardana. If not, see <http://www.gnu.org/licenses/>. -## -############################################################################## - -"""This file contains the code for an hypothetical Springfield motor hardware -access library. It is intended to be used in the sardana documentation as -an aid to writing a sardana motor controller library. - -If you intend to use this code please put it in a directory accessible to -Python or in the same directory as sf_motor_ctrl.py""" - -__all__ = ["SpringfieldMotorHW", "SpringfieldCounterHW"] - -import time -from math import pow, sqrt - -class BaseMotion(object): - - def __init__(self): - self.min_vel = -1 - self.max_vel = -1 - self.accel_time = -1 - self.decel_time = -1 - self.accel = -1 - self.decel = -1 - - self.init_pos = -1 - self.final_pos = -1 - self.curr_pos = -1 - - -class Motion(BaseMotion): - - def __init__(self): - BaseMotion.__init__(self) - - self.close_loop = False - - self.dsplmnt_reach_max_vel = -1 - self.dsplmnt_reach_min_vel = -1 - self.dsplmnt = -1 - - self.curr_instant = -1 - self.start_instant = -1 - - self.positive_dsplmnt = True - self.small_motion = False - - # position where maximum velocity will be reached - self.curr_max_vel_pos = -1 - - # necessary displacement to reach maximum velocity - self.curr_dsplmnt_reach_max_vel = -1 - - # necessary diplacement to reach minimum velocity - self.curr_dsplmnt_reach_min_vel = -1 - - # maximum velocity possible - self.curr_max_vel = -1 - - # time at maximum velocity - self.curr_at_max_vel_dsplmnt = -1 - - # time to reach maximum velocity - self.curr_max_vel_time = -1 - - # time to reach minimum velocity - self.curr_min_vel_time = -1 - - # time at maximum velocity - self.curr_at_max_vel_time = -1 - - # instant when maximum velocity should be reached - self.curr_max_vel_instant = -1 - - # instant when should start decelerating - self.curr_min_vel_instant = -1 - - # time the motion will take - self.duration = -1 - - # instant the motion will end - self.final_instant = -1 - - # steps per unit - self.step_per_unit = 1 - - self.inMotion = False - - self.lower_ls = float('-inf') - self.upper_ls = float('+inf') - - self.power = True - self.enabled = True - - self.__recalculate_acc_constants() - - def isCloseLoopActive(self): - return self.close_loop - - def setCloseLoop(self, v): - self.close_loop = v - - def setMinVelocity(self,vi): - """ Sets the minimum velocity in ms^-1. A.k.a. base rate""" - vi = float(vi) - if vi < 0: - raise "Minimum velocity must be >= 0" - - self.min_vel = vi - - if self.max_vel < self.min_vel: - self.max_vel = self.min_vel - - # force recalculation of accelerations - if self.accel_time >= 0: - self.setAccelerationTime(self.accel_time) - if self.decel_time >= 0: - self.setDecelerationTime(self.decel_time) - - def getMinVelocity(self): - return self.min_vel - - def setMaxVelocity(self,vf): - """ Sets the maximum velocity in ms^-1.""" - vf = float(vf) - if vf <= 0: - raise "Maximum velocity must be > 0" - - self.max_vel = vf - - if self.min_vel > self.max_vel: - self.min_vel = self.max_vel - - # force recalculation of accelerations - if self.accel_time >= 0: - self.setAccelerationTime(self.accel_time) - if self.decel_time >= 0: - self.setDecelerationTime(self.decel_time) - - def getMaxVelocity(self): - return self.max_vel - - def setAccelerationTime(self,at): - """Sets the time to go from minimum velocity to maximum velocity in seconds""" - at = float(at) - if at <= 0: - raise "Acceleration time must be > 0" - - self.accel_time = at - self.accel = (self.max_vel - self.min_vel) / at - - self.__recalculate_acc_constants() - - def getAccelerationTime(self): - return self.accel_time - - def setDecelerationTime(self,dt): - """Sets the time to go from maximum velocity to minimum velocity in seconds""" - dt = float(dt) - if dt <= 0: - raise "Deceleration time must be > 0" - - self.decel_time = dt - self.decel = (self.min_vel - self.max_vel) / dt - - self.__recalculate_acc_constants() - - def getDecelerationTime(self): - return self.decel_time - - def setAcceleration(self,a): - """Sets the acceleration in ms^-2""" - a = float(a) - if a < 0: - raise "Acceleration must be >= 0" - - self.accel = float(a) - - if a > 0: - self.accel_time = (self.max_vel - self.min_vel) / a - else: - self.accel_time = float('INF') - - self.__recalculate_acc_constants() - - def setDeceleration(self,d): - """Sets the deceleration in ms^-2""" - d = float(d) - if d > 0: - raise "Deceleration must be <= 0" - - self.decel = d - - if d < 0: - self.decel_time = (self.min_vel - self.max_vel) / d - else: - self.decel_time = float('INF') - - self.__recalculate_acc_constants() - - def getStepPerUnit(self): - return self.step_per_unit - - def setStepPerUnit(self, spu): - self.step_per_unit = spu - - def __recalculate_acc_constants(self): - """precomputations assuming maximum speed can be reached in a motion""" - - self.dsplmnt_reach_max_vel = 0.5 * self.accel * pow(self.accel_time,2) - self.dsplmnt_reach_max_vel += self.min_vel * self.accel_time - - self.dsplmnt_reach_min_vel = 0.5 * self.decel * pow(self.decel_time,2) - self.dsplmnt_reach_min_vel += self.max_vel * self.decel_time - - def startMotion(self, initial_user_pos, final_user_pos, start_instant=None): - """starts a new motion""" - - if not self.power: - raise Exception("Motor is powered off") - - initial_pos = initial_user_pos * self.step_per_unit - final_pos = final_user_pos * self.step_per_unit - - if self.inMotion: - raise Exception("Already in motion") - - if initial_pos == final_pos: - return - - self.init_pos = initial_pos - self.final_pos = final_pos - self.curr_pos = initial_pos - self.dsplmnt = abs(final_pos - initial_pos) - - start_instant = start_instant or time.time() - self.curr_instant = start_instant - self.start_instant = start_instant - - self.positive_dsplmnt = final_pos >= initial_pos - - displmnt_not_cnst = self.dsplmnt_reach_max_vel + self.dsplmnt_reach_min_vel - self.small_motion = self.dsplmnt < displmnt_not_cnst - - if self.positive_dsplmnt: - self.curr_accel = self.accel - self.curr_decel = self.decel - else: - self.curr_accel = -self.accel - self.curr_decel = -self.decel - - - if not self.small_motion: - - # necessary displacement to reach maximum velocity - self.curr_dsplmnt_reach_max_vel = self.dsplmnt_reach_max_vel - # necessary diplacement to reach minimum velocity - self.curr_dsplmnt_reach_min_vel = self.dsplmnt_reach_min_vel - - if self.positive_dsplmnt: - self.curr_max_vel = self.max_vel - self.curr_min_vel = self.min_vel - # position where maximum velocity will be reached - self.curr_max_vel_pos = self.init_pos + self.curr_dsplmnt_reach_max_vel - else: - self.curr_max_vel = -self.max_vel - self.curr_min_vel = -self.min_vel - # position where maximum velocity will be reached - self.curr_max_vel_pos = self.init_pos - self.curr_dsplmnt_reach_max_vel - - # displacement at maximum velocity - self.curr_at_max_vel_dsplmnt = self.dsplmnt - (self.curr_dsplmnt_reach_max_vel + self.curr_dsplmnt_reach_min_vel) - - else: # Small movement - # position where maximum velocity will be reached - self.curr_max_vel_pos = self.init_pos * self.curr_accel - self.final_pos * self.curr_decel - self.curr_max_vel_pos /= self.curr_accel - self.curr_decel - - # necessary displacement to reach maximum velocity - self.curr_dsplmnt_reach_max_vel = abs(self.curr_max_vel_pos - self.init_pos) - - # necessary diplacement to reach minimum velocity - self.curr_dsplmnt_reach_min_vel = abs(self.final_pos - self.curr_max_vel_pos) - - # maximum velocity possible - cnst = 2 * self.curr_accel * self.curr_decel * self.dsplmnt / (self.curr_decel - self.curr_accel) - max_vel_2 = pow(self.min_vel, 2) + cnst - - self.curr_max_vel = sqrt(abs(max_vel_2)) - - if self.positive_dsplmnt: - self.curr_min_vel = self.min_vel - else: - self.curr_max_vel = -self.curr_max_vel - self.curr_min_vel = -self.min_vel - - # displacement at maximum velocity - self.curr_at_max_vel_dsplmnt = 0.0 - - # time to reach maximum velocity - self.curr_max_vel_time = abs((self.curr_max_vel - self.curr_min_vel) / self.curr_accel) - - # time to reach minimum velocity - self.curr_min_vel_time = abs((self.curr_min_vel - self.curr_max_vel) / self.curr_decel) - - # time at maximum velocity - self.curr_at_max_vel_time = abs(self.curr_at_max_vel_dsplmnt / self.curr_max_vel) - - # instant when maximum velocity should be reached - self.curr_max_vel_instant = self.start_instant + self.curr_max_vel_time - - # instant when should start decelerating - self.curr_min_vel_instant = self.curr_max_vel_instant + self.curr_at_max_vel_time - - # time the motion will take - self.duration = self.curr_max_vel_time + self.curr_at_max_vel_time + self.curr_min_vel_time - - # instant the motion will end - self.final_instant = self.start_instant + self.duration - - # uncomment following line if need output concerning the movement that - # has just started - # self.info() - - # ASSERTIONS - if self.positive_dsplmnt: - assert(self.curr_max_vel_pos >= self.init_pos) - assert(self.curr_max_vel_pos <= self.final_pos) - else: - assert(self.curr_max_vel_pos <= self.init_pos) - assert(self.curr_max_vel_pos >= self.final_pos) - - assert(self.curr_dsplmnt_reach_max_vel >= 0.0) - assert(self.curr_dsplmnt_reach_min_vel >= 0.0) - - assert(self.final_instant >= self.start_instant) - assert(self.curr_max_vel <= self.max_vel) - assert(self.start_instant <= self.curr_max_vel_instant) - assert(self.final_instant >= self.curr_min_vel_instant) - - assert(self.curr_max_vel_time > 0.0) - assert(self.curr_min_vel_time > 0.0) - assert(self.duration > 0.0) - - if self.small_motion: - assert(self.curr_max_vel_instant == self.curr_min_vel_instant) - assert(self.curr_at_max_vel_time == 0.0) - else: - assert(self.curr_max_vel_instant <= self.curr_min_vel_instant) - assert(self.curr_at_max_vel_time >= 0.0) - - self.inMotion = True - - def abortMotion(self, curr_instant=None): - curr_instant = curr_instant or time.time() - if not self.inMotion: - return self.curr_pos - - self.curr_pos = self.getCurrentPosition(curr_instant) - self.inMotion = False - return self.curr_pos - - def isInMotion(self,curr_instant=None): - curr_instant = curr_instant or time.time() - #we call getCurrentPosition because inside it updates the inMotion flag - self.getCurrentPosition(curr_instant) - return self.inMotion - - def setCurrentPosition(self, curr_pos): - self.curr_pos = curr_pos - self.init_pos = curr_pos - - def getCurrentPosition(self, curr_instant=None): - curr_instant = curr_instant or time.time() - self.curr_instant = curr_instant - pos = None - if self.inMotion: - # if motion should be ended... - if self.curr_instant >= self.final_instant: - self.inMotion = False - pos = self.final_pos - else: - pos = self.init_pos - if curr_instant > self.curr_min_vel_instant: - if self.positive_dsplmnt: - pos += self.curr_dsplmnt_reach_max_vel - pos += self.curr_at_max_vel_dsplmnt - else: - pos -= self.curr_dsplmnt_reach_max_vel - pos -= self.curr_at_max_vel_dsplmnt - dt = curr_instant - self.curr_min_vel_instant - pos += self.curr_max_vel * dt + 0.5 * self.curr_decel * pow(dt,2) - elif curr_instant > self.curr_max_vel_instant: - if self.positive_dsplmnt: - pos += self.curr_dsplmnt_reach_max_vel - else: - pos -= self.curr_dsplmnt_reach_max_vel - dt = curr_instant - self.curr_max_vel_instant - pos += self.curr_max_vel * dt - else: - dt = curr_instant - self.start_instant - pos += self.curr_min_vel * dt + 0.5 * self.curr_accel * pow(dt,2) - else: - pos = self.curr_pos - if pos <= self.lower_ls: - pos = self.lower_ls - self.inMotion = False - elif pos >= self.upper_ls: - pos = self.upper_ls - self.inMotion = False - self.curr_pos = pos - return pos - - def setCurrentUserPosition(self, user_pos): - self.setCurrentPosition(user_pos*self.step_per_unit) - - def getCurrentUserPosition(self, curr_instant=None): - return self.getCurrentPosition(curr_instant=curr_instant) / self.step_per_unit - - def hitLowerLimit(self): - user_pos = self.curr_pos / self.step_per_unit - return user_pos <= self.lower_ls - - def hitUpperLimit(self): - user_pos = self.curr_pos / self.step_per_unit - return user_pos >= self.upper_ls - - def getLowerLimitSwitch(self): - return self.lower_ls - - def setLowerLimitSwitch(self, user_lower_ls): - self.lower_ls = user_lower_ls - - def getUpperLimitSwitch(self): - return self.upper_ls - - def setUpperLimitSwitch(self, user_upper_ls): - self.upper_ls = user_upper_ls - - def turnOn(self): - self.power = True - - def turnOff(self): - self.power = False - - def isTurnedOn(self): - return self.power - - def hasPower(self): - return self.power - - def setPower(self, power): - self.power = power - - def info(self): - print "Small movement =",self.small_motion - print "length =",self.dsplmnt - print "position where maximum velocity will be reached =",self.curr_max_vel_pos - print "necessary displacement to reach maximum velocity =",self.curr_dsplmnt_reach_max_vel - print "necessary displacement to stop from maximum velocity =",self.curr_dsplmnt_reach_min_vel - print "maximum velocity possible =",self.curr_max_vel - print "time at top velocity =",self.curr_at_max_vel_time - print "displacement at top velocity =",self.curr_at_max_vel_dsplmnt - print "time to reach maximum velocity =",self.curr_max_vel_time - print "time to reach minimum velocity =",self.curr_min_vel_time - print "time the motion will take =",self.duration - print "instant when maximum velocity should be reached =",self.curr_max_vel_instant - print "instant when should start decelerating =",self.curr_min_vel_instant - print "instant the motion will end",self.final_instant - print "" - print "For long movements (where top vel is possible), necessary displacement to reach maximum velocity =",self.dsplmnt_reach_max_vel - print "For long movements (where top vel is possible), necessary displacement to stop from maximum velocity =",self.dsplmnt_reach_min_vel - - -class SpringfieldMotorHW(object): - - DefaultHost = "localhost" - DefaultPort = 10123 - - def __init__(self, host=DefaultHost, port=DefaultPort): - self.host = host - self.port = port - self._motions = {} - - def getMotion(self, axis): - motion = self._motions.get(axis) - if motion is None: - self._motions[axis] = motion = Motion() - return motion - - def getState(self, axis): - motion = self.getMotion(axis) - motion.getCurrentUserPosition() - if motion.isInMotion(): - return 2 - if motion.hitLowerLimit(): - return 3 - if motion.hitUpperLimit(): - return 3 - if not motion.hasPower(): - return 4 - return 1 - - def getStatus(self, axis): - motion = self.getMotion(axis) - motion.getCurrentUserPosition() - status = "Motor HW is ON" - if motion.isInMotion(): - status = "Motor HW is MOVING" - if motion.hitLowerLimit(): - status = "Motor HW is in ALARM. Hit hardware lower limit switch" - if motion.hitUpperLimit(): - status = "Motor HW is in ALARM. Hit hardware upper limit switch" - if not motion.hasPower(): - status = "Motor is powered off" - return status - - def getLimits(self, axis): - motion = self.getMotion(axis) - m.getCurrentUserPosition() - switchstate = 3*[False,] - if m.hitLowerLimit(): - switchstate[2] = True - if m.hitUpperLimit(): - switchstate[1] = True - return switchstate - - def getPosition(self, axis): - motion = self.getMotion(axis) - return motion.getCurrentUserPosition() - - def getAccelerationTime(self, axis): - return self.getMotion(axis).getAccelerationTime() - - def getDecelerationTime(self, axis): - return self.getMotion(axis).getDecelerationTime() - - def getMinVelocity(self, axis): - return self.getMotion(axis).getMinVelocity() - - def getMaxVelocity(self, axis): - return self.getMotion(axis).getMaxVelocity() - - def getStepPerUnit(self, axis): - return self.getMotion(axis).getStepPerUnit() - - def setAccelerationTime(self, axis, v): - self.getMotion(axis).setAccelerationTime(v) - - def setDecelerationTime(self, axis, v): - self.getMotion(axis).setDecelerationTime(v) - - def setMinVelocity(self, axis, v): - self.getMotion(axis).setMinVelocity(v) - - def setMaxVelocity(self, axis, v): - self.getMotion(axis).setMaxVelocity(v) - - def setStepPerUnit(self, axis, v): - self.getMotion(axis).setStepPerUnit(v) - - def isCloseLoopActive(self, axis): - return self.getMotion(axis).isCloseLoopActive() - - def setCloseLoop(self, axis, v): - self.getMotion(axis).setCloseLoop(v) - - def setCurrentPosition(self, axis, position): - motion = self.getMotion(axis) - motion.offset = position - motion.getCurrentUserPosition() - motion.setCurrentUserPosition(position) - - def move(self, axis, position): - t = time.time() - motion = self.getMotion(axis) - motion.startMotion(motion.getCurrentUserPosition(t), position, t) - - def stop(self, axis): - motion = self.getMotion(axis) - motion.abortMotion() - - def abort(self, axis): - motion = self.getMotion(axis) - motion.abortMotion() - - -class Channel: - - def __init__(self,idx): - self.idx = idx # 1 based index - self.value = 0.0 - self.is_counting = False - self.active = True - - -class SpringfieldCounterHW(object): - - DefaultHost = "localhost" - DefaultPort = 10124 - - def __init__(self, host=DefaultHost, port=DefaultPort): - self.host = host - self.port = port - self._channels = {} - - def getChannel(self, axis): - channel = self._channels.get(axis) - if channel is None: - self._channels[axis] = channel = Channel(axis) - return channel - - def getState(self, axis): - channel = self.getChannel(axis) - channel.getCurrentUserValue() - if channel.isAcquiring(): - return 2 - if not channel.hasPower(): - return 3 - return 1 - - def getStatus(self, axis): - channel = self.getChannel(axis) - channel.getCurrentUserValue() - status = "Counter HW is ON" - if channel.isAcquiring(): - status = "Counter HW is ACQUIRING" - if not channel.hasPower(): - status = "Counter is powered OFF" - return status - - def getValue(self, axis): - motion = self.getMotion(axis) - return motion.getCurrentUserPosition()
\ No newline at end of file |