PyTango dynamic attributes

Hi,

I am trying to create a device server that can create new attributes during run-time. I am following the guide which gives the following example:


from PyTango import Util, Attr
from PyTango.server import DeviceMeta, Device, command

class MyDevice(Device):
    __metaclass__ = DeviceMeta

    @command(dtype_in=str)
    def CreateFloatAttribute(self, attr_name):
        attr = Attr(attr_name, PyTango.DevDouble)
        self.add_attribute(attr, self.read_General, self.write_General)

    def read_General(self, attr):
        self.info_stream("Reading attribute %s", attr.get_name())
        attr.set_value(99.99)

    def write_General(self, attr):
        self.info_stream("Writting attribute %s", attr.get_name())

Since I am not using the high-level Python API, I have created a command via POGO that creates a scalar attribute like so:


    def createScalarAttribute(self, argin):
        """ A method that creates a new scalar attribute.
        
        :param argin: New attribute name.
        :type: PyTango.DevString
        :return: 
        :rtype: PyTango.DevVoid """
        self.debug_stream("In createScalarAttribute()")
        #----- PROTECTED REGION ID(TPM_DS.createScalarAttribute) ENABLED START -----#
        attr = Attr(argin, PyTango.DevULong)
        self.add_attribute(attr, self.read_GeneralScalar, self.write_GeneralScalar)
        #----- PROTECTED REGION END -----#	//	TPM_DS.createScalarAttribute

Following this, I need to create new methods that are used for reading and writing this attribute. I can create these new methods in my python code, but since they are not in a protected region, every time I re-run POGO to add some other commands etc. these read/write methods are removed. Where would be the best place to put my methods?

Thanks. 0_o

Some additional information:

Given a method like the following, which creates a scalar attribute for a particular name, I require two methods for reading and writing this attribute. The method pointers are self.read_GeneralScalar (reading) and self.write_GeneralScalar.


    def createScalarAttribute(self, argin):
        """ A method that creates a new scalar attribute.
        
        :param argin: New attribute name.
        :type: PyTango.DevString
        :return: 
        :rtype: PyTango.DevVoid """
        self.debug_stream("In createScalarAttribute()")
        #----- PROTECTED REGION ID(TPM_DS.createScalarAttribute) ENABLED START -----#
        attr = Attr(argin, PyTango.DevULong)
        self.add_attribute(attr, self.read_GeneralScalar, self.write_GeneralScalar)
        #----- PROTECTED REGION END -----#	//	TPM_DS.createScalarAttribute

My problem is how to write those methods. In my scenario, when reading, I require the code to access an external API (which accesses the board given some parameters, and reads off a register value. So in my case I have written something like:


def read_GeneralScalar(self, attr):
        """ A method that reads from a scalar attribute.

        :param attr: The attribute to read from.
        :type: PyTango.DevAttr
        :return: The read data.
        :rtype: PyTango.DevULong """
        self.info_stream("Reading attribute %s", attr.get_name())
        arguments = {}
        dev = self.getDevice(attr.get_name())
        arguments['device'] = dev.value
        arguments['register'] = attr.get_name()
        arguments['words'] = 1
        arguments['offset'] = 0
        args = str(arguments)
        values_array = self.readRegister(args)  #get actual value by reading from register
        return values_array[0]  # readRegister returns an array, so a scalar requires a read from 0'th location

Am I right in assuming this function has to return the value? I ask because in this
tutorial, there seems to be a “set_value” method for Attr, which is not in the PyTango API (v.8.1.1)


    def read_General(self, attr):
        self.info_stream("Reading attribute %s", attr.get_name())
        attr.set_value(99.99)

Furthermore, when writing values to the attribute, I need to pass some actual data to the write function. So I would like to have this kind of function, where I am calling another method “writeRegister” with particular arguments as a string.


    def write_GeneralScalar(self, attr, data):
        """ A method that writes to a scalar attribute.

        :param attr: The attribute to write to.
        :type: PyTango.DevAttr
        :param data: The data to be written
        :type: PyTango.DevULong
        :return: Success or failure.
        :rtype: PyTango.DevBoolean """
        self.info_stream("Writing attribute %s", attr.get_name())
        arguments = {}
        dev = self.getDevice(attr.get_name())
        arguments['device'] = dev.value
        arguments['register'] = attr.get_name()
        arguments['offset'] = 0
        arguments['values'] = data
        args = str(arguments)
        return self.writeRegister(args)

But I’m not sure how the Tango device server would be able to pass the “data” argument. The tutorial mentioned above gives this example:


def write_General(self, attr):
        self.info_stream("Writting attribute %s", attr.get_name())

But I can’t get how this writes to the attribute. Past API’s seem to have had a set_value method, but this is not available anymore. Any help/suggestions are much appreciated.

Hi drea,

I don’t use pogo generator very often but unless I am mistaken, in the beginning of the device declaration there is an empty protected region (defined as global variables) you can use for that. Something like:


class MyDevice(PyTango.Device_4Impl):

    #--------- Add you global variables here --------------------------
    #----- PROTECTED REGION ID(MyDevice.global_variables) ENABLED START -----#

    def read_GeneralScalar(self, attr):
        pass

    def write_GeneralScalar(self, attr):
        pass
    
    #----- PROTECTED REGION END -----#	//	MyDevice.global_variables

[quote=“drea”]Am I right in assuming this function has to return the value? I ask because in this
tutorial, there seems to be a “set_value” method for Attr, which is not in the PyTango API (v.8.1.1)[/quote]

When you declare a new attribute you do it with object Attr. When read_[…](self, attr) or write_(self, attr) is called, attr is an instance of Attribute (not Attr). You can find the documentation here

[quote=“drea”]Furthermore, when writing values to the attribute, I need to pass some actual data to the write function. So I would like to have this kind of function, where I am calling another method “writeRegister” with particular arguments as a string.
[/quote]
I am not sure I understand what you mean. I will assume that data is actually the value the client is trying to write into a certain attribute. If that is the case, then you should do:


class MyDevice(Device_4_impl):

    def write_GeneralAttribute(self, attr):
        name = attr.get_name()
        data = attr.get_write_value()
        self.info_stream("Writing into attribute %s the value %s", name, data)
        # [...]

Hope it helps

Thanks a lot, that explains it perfectly! :slight_smile:

Hi all,

I have another question related to creation of dynamic attributes. For the dynamic attributes created with the add_attribute() API call, I would also like to add optional parameters, such as MIN/MAX alarm values. The attr_list dictionary does not appear to be modifiable in PyTango as far as I can tell. On the other hand, using the Jive tool, I can set these values dynamically.

So assuming I have created an attribute in one of my commands with this code:


attr = Attr(argin, PyTango.DevULong)
self.add_attribute(attr, self.read_general_scalar, self.write_general_scalar)

How can I also add min/max alarm values (or any other optional parameters) to the attribute (perhaps even update old values)?

Thanks!

Hi,

To setup attribute properties at attribute creation time do:


attr = Attr(argin, PyTango.DevULong)
prop = PyTango.UserDefaultAttrProp()
prop.set_label("attr label")
prop.set_min_alarm("-5.0")
attr.set_default_properties(prop)
self.add_attribute(attr, self.read_general_scalar, self.write_general_scalar)

In the server, to update properties of an existing attribute do:


attr_name = "voltage"
multi_prop = PyTango.MultiAttrProp()
multi_attr = self.get_device_attr()
attribute = multi_attr.get_attr_by_name(attr_name)
multi_prop = attribute.get_properties()
multi_prop.label = "attr label"
multi_prop.min_alarm = "-5.0"
attribute.set_properties(multi_prop)

Hope it helps

That works wonderfully, thanks. I am still getting used to the API - I didn’t know about MultiAttrProp() - problem solved now!