
.. module:: nicos.devices.generic.sequence

Sequencer classes
=================

The :class:`SequencerMixin` is used to perform a dynamically generated
sequence of actions. This is useful for devices which needs to
make several intermediate steps to reach their goal, like a
Monochromator changer, or if some interlocked device need to be
moved in a certain lock-step sequence.

The sequence is basically a list of actions or tuples of actions,
where all actions inside a tuple are tried to be performed in a
parallel fashion, if possible.

Each possible action is implemented as a class derived from :class:`SequenceItem`.

The Sequence to be performed is generated by :meth:`_generateSequence`
which is called by the start method. This method needs to be implemented in
derived classes.

Real devices needs to be implemented in classes derived from BaseSequencer and
define their own ``attached_devices``, ``parameters``,
the :meth:`_generateSequence` and ``doRead`` methods.

A proper status synchronisation between devices being polled by a poller process
and instances being used inside the NICOS daemon is provided by the internal
method :meth:`SequencerMixin._set_seq_status`.

This mechanism can also be used by devices wanting to define their own movement thread,
without having to use the Sequencer.
In this case, derive from BaseSequencer and redefine :meth:`_sequence` and upon known status
changes call ``_set_seq_status(statusCode, statusString)`` whereas statuscode and statusString
are the quantities a call to :meth:`status` should return.


.. _SequenceItem:

SequenceItems
-------------

Each possible action is derived from `SequenceItem`. You can choose between:

* :class:`SeqDev` for moving a device to a new position (and waiting for its arrival)

* :class:`SeqParam` for changing a parameter of a device (and checking that value)

* :class:`SeqCall` for calling an arbitrary function or lambda with given arguments.

* :class:`SeqMethod` for calling an method of an object with given arguments.
  This may also be used to call methods of devices (just use ``dev.method`` as func).
  Often used to for ``fix``/``release`` a device.

* :class:`SeqSleep` for waiting a certain time (used for well-known delays,
  e.g. ramping um high voltages)

* :class:`SeqWait` to wait until the given device has reached the idle state

* :class:`SeqNOP` for the ``No operation`` task.

Of course you can define your own actions by defining more classes derived
from :class:`SequenceItem`.

Each :class:`SequenceItem` needs to define the following methods:

* :meth:`SequenceItem.check` This checks all parameters of a particular instance of
  :class:`SequenceItem` and raises an error, if the action can not be performed.
  This is usually called before the sequence starts.

* :meth:`SequenceItem.run` This is intended to initiate the action (start a device, call a
  function, ....)

* :meth:`SequenceItem.isCompleted` This checks if the action is completed and
  returns a bool.

* :meth:`SequenceItem.stop` This is intended to stop the action. Normally only called, if the
  :class:`Device` derived from the Sequencer got a ``stop``.

* :meth:`SequenceItem.retry` This is intended to retry the action for a given number of retries.
  Only needs to be defined for actions which can be retried.

It is encouraged to also define a :meth:`__repr__` method returning a NICOS
equivalent of the action of the device, where possible, or an *short*,
but descriptive string describing the action.
The result is used to create the status string.

-------


.. autoclass:: SequenceItem()
.. autoclass:: SeqDev
.. autoclass:: SeqParam
.. autoclass:: SeqCall
.. autoclass:: SeqMethod
.. autoclass:: SeqSleep
.. autoclass:: SeqWait
.. autoclass:: SeqNOP

.. autoclass:: SequencerMixin()

  .. automethod:: _set_seq_status
  .. automethod:: _startSequence
  .. automethod:: _asyncSequence
  .. automethod:: _run
  .. automethod:: _sequence
  .. automethod:: _stopAction
  .. automethod:: _checkFailed
  .. automethod:: _runFailed
  .. automethod:: _retryFailed
  .. automethod:: _waitFailed
  .. automethod:: _stopFailed


.. autoclass:: BaseSequencer()

  .. automethod:: doStart

.. autoclass:: MeasureSequencer()
