How to write a trigger/gate controller¶
The basics¶
An example of a hypothetical Springfield trigger/gate 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 TriggerGateController with:
- a proper constructor
- add and delete axis methods
- get axis state
import springfieldlib
from sardana.pool.controller import TriggerGateController
class SpringfieldTriggerGateController(TriggerGateController):
def __init__(self, inst, props, *args, **kwargs):
super(SpringfieldTriggerGateController, self).__init__(inst, props, *args, **kwargs)
# initialize hardware communication
self.springfield = springfieldlib.SpringfieldTriggerHW()
# do some initialization
self._triggers = {}
def AddDevice(self, axis):
self._triggers[axis] = True
def DeleteDevice(self, axis):
del self._triggers[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 springfieldlib
module which emulates a trigger/gate
hardware access library.
The springfieldlib
can be downloaded from
here
.
The Springfield trigger/gate controller can be downloaded from
here
.
The following code describes a minimal Springfield base trigger/gate controller which is able to return the state of an individual trigger as well as to start a synchronization:
class SpringfieldBaseTriggerGateController(TriggerGateController):
"""The most basic controller intended from demonstration purposes only.
This is the absolute minimum you have to implement to set a proper trigger
controller able to get a trigger value, get a trigger 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(SpringfieldBaseTriggerGateController, self).__init__(
inst, props, *args, **kwargs)
self.springfield = springfieldlib.SpringfieldTriggerHW()
def StateOne(self, axis):
"""Get the specified trigger state"""
springfield = self.springfield
state = springfield.getState(axis)
if state == 1:
return State.On, "Trigger is stopped"
elif state == 2:
return State.Moving, "Trigger is running"
elif state == 3:
return State.Fault, "Trigger has an error"
def StartOne(self, axis, value=None):
"""acquire the specified trigger"""
self.springfield.StartChannel(axis)
def SynchOne(self, axis, synchronization):
self.springfield.SynchChannel(axis, synchronization)
def StopOne(self, axis):
"""Stop the specified trigger"""
self.springfield.stop(axis)
Get trigger state¶
To get the state of a trigger, sardana calls the
StateOne()
method. This method
receives an axis as parameter and should return either:
The state should be a member of State
(For backward
compatibility reasons, it is also supported to return one of
PyTango.DevState
). The status could be any string.
Load synchronization description¶
To load a trigger with the synchronization description
sardana calls the SynchOne()
method.
This method receives axis and synchronization parameters.
Here is an example of the possible implementation of
SynchOne()
:
class SpringfieldTriggerGateController(TriggerGateController):
def SynchOne(self, axis, synchronization):
self.springfield.SynchChannel(axis, synchronization)
Synchronization description¶
Synchronization is a data structure following a special convention. It is composed from the groups of equidistant intervals described by: the initial point and delay, total and active intervals and the number of repetitions. These information can be expressed in different synchronization domains if necessary: time and/or position.
Sardana defines two enumeration classes to help in manipulations of the
synchronization description. The SynchParam
defines the
parameters used to describe a group. The SynchDomain
defines the possible domains in which a parameter may be expressed.
The following code demonstrates creation of a synchronization description expressed in time and position domains (moveable’s velocity = 10 units/second and acceleration time = 0.1 second). It will generate 10 synchronization pulses of length 0.1 second equally spaced on a distance of 100 units.
from sardana.pool import SynchParam, SynchDomain
synchronization = [
{
SynchParam.Delay: {SynchDomain.Time: 0.1, SynchDomain.Position: 0.5},
SynchParam.Initial: {SynchDomain.Time: None, SynchDomain.Position: 0},
SynchParam.Active: {SynchDomain.Time: 0.1, SynchDomain.Position: 1},
SynchParam.Total: {SynchDomain.Time: 1, SynchDomain.Position: 10},
SynchParam.Repeats: 10,
}
]
Start a trigger¶
When an order comes for sardana to start a trigger, sardana will call the
StartOne()
method. This method receives
an axis as parameter. The controller code should trigger the hardware acquisition.
Here is an example of the possible implementation of
StartOne()
:
class SpringfieldTriggerGateController(TriggerGateController):
def StartOne(self, axis):
self.springfield.StartChannel(axis)
As soon as StartOne()
is invoked,
sardana expects the trigger to be running. It enters a high frequency
synchronization loop which asks for the trigger state through calls to
StateOne()
. It will keep the loop
running as long as the controller responds with State.Moving
.
If StateOne()
raises an exception
or returns something other than State.Moving
, sardana will assume the trigger
is stopped and exit the synchronization loop.
For an synchronization to work properly, it is therefore, very important that
StateOne()
responds correctly.
Stop a trigger¶
It is possible to stop a trigger when it is running. When sardana is ordered to
stop a trigger synchronization, it invokes the
StopOne()
method. This method receives
an axis parameter. The controller should make sure the desired trigger is
gracefully stopped.
Here is an example of the possible implementation of
StopOne()
:
class SpringfieldTriggerGateController(TriggerGateController):
def StopOne(self, axis):
self.springfield.StopChannel(axis)
Abort a trigger¶
In an emergency situation, it is desirable to abort a synchronization
as fast as possible. When sardana is ordered to abort a trigger synchronization,
it invokes the AbortOne()
method. This method receives an axis parameter. The controller should make
sure the desired trigger is stopped as fast as it can be done.
Here is an example of the possible implementation of
AbortOne()
:
class SpringfieldTriggerGateController(TriggerGateController):
def AbortOne(self, axis):
self.springfield.AbortChannel(axis)