Project

General

Profile

How to write a Control Object

What are Control Object?

Control objects are an abstraction over the different control systems used at ESRF (tango, taco, spec, sardana).

Writing a Control Object

  • Control objects must inherit from the class Framework4.Control.Core.COBject.CObjectBase
  • The /signals/ class variable is an array of Signal objects the control object can emit
  • The /slots/ class variable is an array of Slot objects the control object has

Signal and Slot classes are found in Framework4.Control.Core.CObject

These signals and slots are used by the framework to determine whether a connection with a given brick is possible. They must be compatible with which signals and slots the brick expects.

The control object has a connectNotify method, called whenever one of its signal gets connected to a brick. You may want to over-ride this method. By default it just sends the values of all channels to the client that just connected. See the commented code sample below for an alternative.

Control objects also need a template which describes which parameters the control objects accepts. The template is used by Beacon, so the user can create the XML config file for the control object graphically.

You may want to learn more about the configuration template format.

Commented Example

# Import the classes we need, note the complete package name
from Framework4.Control.Core.CObject import CObjectBase, Signal, Slot

# Control Objects inherit from CObjectBase
class Shutter(CObjectBase):
    # 'signals' and 'slots' class variables, our shutter 
    # has 2 signals and 2 slots
    signals = [ Signal("statusChanged"), Signal("stateChanged") ]
    slots = [ Slot("open"), Slot("close") ]

    shutterState = {
        0: 'unknown',
        3: 'closed',
        4: 'opened',
        9: 'moving',
        17: 'automatic',
        23: 'fault',
        46: 'disabled',
        -1: 'error'
    }

    # Python constructor which usually doesn't do much
    # Don't forget to call the superclass constructor
    def __init__(self, *args, **kwargs):
        CObjectBase.__init__(self, *args, **kwargs)

    # The Framework calls 'init' shortly after the object has 
    # been instantiated. Most setup code will be placed in here.
    def init(self):
        self.shutter_state = "unknown" 

        # We get the 2 channels we have (they are configured 
        # in a xml config file) and connect them to 2 methods
        # of our object.
        # This is not a connection used to communicate with 
        # a remote object or brick. Channels can be connected
        # to methods, so we can be notified when the corresponding
        # hardware does something. Here we wantt to be notified
        # when the channel value changes
        # Channels defined in the XML config file are automatically
        # added to the self.channels dictionnary. Same with
        # commands in self.commands dictionnary.
        status_chan = self.channels.get("status")
        if status_chan is not None:
            status_chan.connect("update", self.statusChanged)

        state_chan = self.channels.get("state")
        if state_chan is not None:
            state_chan.connect("update", self.stateChanged)

    # This method is called whenever a signal is connected. Here when
    # a signal is connected, we emit it to the client so it can get the
    # right value as soon as it connects.
    def connectNotify(self, signal_name):
        if signal_name == 'statusChanged':
          status_chan = self.channels.get("status")
          if status_chan is not None: self.statusChanged(status_chan.value())
        elif signal_name == 'stateChanged':
          state_chan = self.channels.get("state")
          if state_chan is not None: self.stateChanged(state_chan.value())

    # Our 2 slots
    def open(self):
        print '********** OPEN****************'
        self.commands['open']()

    def close(self):
        print '***********CLOSE *******************'
        self.commands['close']()

    # these 2 methods are connected to a lower-level signal. We then
    # emit the new value to the framework when something changes
    # so all clients get it
    def statusChanged(self, status):
        self.emit("statusChanged", status)

    def stateChanged(self, state):
        self.shutter_state = Shutter.shutterState.get(state, "unknown")
        self.emit("stateChanged", self.shutter_state)