Skip to content

Regulation Tango Device Server

Launching the server process

(base) user@beamline:~/bliss$ conda activate bliss
(bliss) user@beamline:~/bliss$ export TANGO_HOST=localhost:20000

(bliss) user@beamline:~/bliss$ Regulation -?
usage :  Regulation instance_name [-v[trace level]]
                                  [-nodb [-dlist <device name list>]]
Instance name defined in database for server Regulation :
        test

(bliss) user@beamline:~/bliss$ Regulation test
Serialization mode set to  BY_PROCESS
Ready to accept request

The Tango serialization model can be modified using the --serial_model option with one of these values {BY_PROCESS, BY_DEVICE, NO_SYNC}. The default uses BY_PROCESS.

Tango Server Configuration

Tango devices must be declared in database. Jive wizard can be used for that.

Warning

For now, the 2nd field of tango names must contain "regulation". example: idXX/regulation/output_XXX.

Note

While exporting a Loop object as a server, the associated Input and Output objects must be exported too. Whereas inputs and outputs can be exported alone.

 Regulation
   └── test
        ├── Input
        |    └── id00/regulation/input
        |         └── properties
        |              └── beacon_name: thermo_sample_new
        ├── Output
        |    └── id00/regulation/output
        |         └── properties
        |              └── beacon_name: heater_new
        └── Loop
             └── id00/regulation/loop
                  └── properties
                       └── beacon_name: sample_regulation_new
  • beacon_name: the name of the target object that will be served (object must already existing in the Beacon configuration)

Client Configuration (with Beacon)

In a yml file declare the regulation client objects.

- class: TangoInput # The class for an Input as a Tango client
  module: tango_ds
  name: inputds
  tango_name: id00/regulation/input    # must correspond to the beacon_name
                                       # of the server object declared above

- class: TangoOutput # The class for an Output as a Tango client
  module: tango_ds
  name: outputds
  tango_name: id00/regulation/output   # must correspond to the beacon_name
                                       # of the server object declared above

- class: TangoLoop # The class for a Loop as a Tango client
  module: tango_ds
  name: loopds
  tango_name: id00/regulation/loop     # must correspond to the beacon_name
                                       # of the server object declared above

Note

Here if you just need the Loop object (loopds), it is not necessary to declare the Input and Output objects (inputds, outputds). The input and output associated to the loop are directly available via the loop itself loppds.input and loopds.output.

Usage

The client object can now be imported from a Bliss session as usual:

BLISS [1]: loopds=config.get("loopds")
BLISS [2]: loopds
  Out [2]:

           === Loop: sample_regulation_new ===
           controller: RegulMockup_b8830a0c94c22281512932c6386c072b
           Input: thermo_sample_new @ 0.000 deg
           output: heater_new @ 0.000 Volt

           === Setpoint ===
           setpoint: 0.0 deg
           ramprate: 1.0 deg/s
           ramping: False

           === PID ===
           kp: 0.5
           ki: 0.2
           kd: 0.0

The client objects share the same API than the genuine objects of the regulation framework for all standard methods.

BLISS [3]: loopds.setpoint
  Out [3]: 0.0

BLISS [4]: loopds.setpoint = 10

BLISS [5]: loopds.ramprate
  Out [5]: 1.0

BLISS [6]:  loopds.axis
  Out [6]:  AXIS:
                 name (R): sample_regulation_new_axis
                 unit (R): deg
                 offset (R): 0.00000
                 backlash (R): 0.00000
                 sign (R): 1
                 steps_per_unit (R): 1.00
                 tolerance (R) (to check pos. before a move): 0.05
                 limits (RW):    Low: -inf High: inf
                 dial (RW): 10.00841
                 position (RW): 10.00841
                 state (R): READY (Axis is READY)
                 acceleration: None
                 velocity: None
            Controller: SoftController_140570134546000 (SoftController)

            ERROR: Unable to get axis info from controller
            ENCODER:
                 None

BLISS [7]: loopds.input
  Out [7]:

            === Input: thermo_sample_new ===
            controller: RegulMockup_b8830a0c94c22281
            channel: A
            current value: 10.002 deg

BLISS [8]: loopds.output
  Out [8]:

            === Output: heater_new ===
            controller: RegulMockup_b8830a0c94c22281
            channel: A
            current value: 9.014 Volt

            === Output.set_value ramping options ===
            ramprate: 0.0
            ramping: False
            limits: (0.0, 100.0)

BLISS [9]: loopds.output.read()
  Out [9]: 11.74790420829029

BLISS [10]: loopds.input.read()
  Out [10]: 10.01260308409754

For custom methods that are specific to one kind of regulation device (e.g. Lakshore, Eurotherm), the client objects provide an access to the DeviceProxy via the device attribute:

BLISS [11]: loopds.device
  Out [11]: DeviceProxy(id00/regulation/loop,140570137683760)

The DeviceProxy of each client regulation objects has 4 different methods to access non-standard-API methods:

@command(dtype_in=str, dtype_out=str)
def target_getattr(self, attr):
    """ get a property of the served object.
        'attr' is the attribute as a string.
        return the attribute value as a string.
    """

@command(dtype_in=str, dtype_out=str)
def target_setattr(self, attr_and_value):
    """ set a property of the served object.
        'attr_and_value' is a string of the form 'attibute value'.
        'value' is converted to a float if possible.
    """

@command(dtype_in=str, dtype_out=str)
def target_call(self, cmd):
    """ Call a method of the served object.
        'cmd' is a string of the form 'method arg' (arg is optional).
        It acts as an equivalent of 'obj.method(arg)'.
        'arg' is converted to a float if possible.
    """

@command(dtype_in=str, dtype_out=str)
def controller_cmd(self, cmd):
    """ Call a method of the controller of the served object.
        'cmd' is a string of the form 'method arg' (arg is optional).
        It acts as an equivalent of 'obj.controller.method(arg)'.
        'arg' is converted to a float if possible.
    """

BLISS [12]: loopds.device.target_getattr('unit')  # Lakeshore Loop
  Out [12]: 'K'                                   #  specific property

BLISS [13]: loopds.device.target_setattr('unit C')  # Lakeshore Loop
  Out [13]: 'None'                                  #  specific property

BLISS [14]: loopds.device.target_call('hold')  # Linkam Loop
  Out [14]: 'None'                             #  specific method

BLISS [15]: loopds.device.controller_cmd('purge')   # Oxford controller
  Out [15]: 'RegulMockup_b883cc072b.purge '   #  specific method

BLISS [16]: loopds.device.controller_cmd('cool 10')    # Oxford controller
  Out [16]: 'RegulMockup_b883cc072b.cool @ 10.0' #  specific method with arg

It is also possible to send raw commands to the controller hardware via the standard WRraw method that exist on each regulation controller:

BLISS [17]: loopds.device.controller_cmd('WRraw MOUT 10.2') # Lakeshore hardware
  Out [17]: 'RegulMockup_b883cc072b.WRraw MOUT 10 1'  #  controller specific cmd