Device with 'externally' updated attributes

Hi,

Let's say you have a hardware device that sends you updates at more or less random intervals and that a client wants to receive events every time this happens. I tried to write a PyTango Device that simulates this using a thread that pushes new attribute values. Actually, I also want to have a simulator, so having the simulator work is useful too :)

I'm completely new to Tango, so I'm not sure what is the right way to go about it. Some questions:

1) What Device() methods are thread safe? I'm pretty sure I'm doing something wrong since if I uncomment the time.sleep() between set_value() and fire_change_event() I get segfaults. What is the correct way to do this? Is there a lock somewhere I must grab, or is there some thread-safe way to push the attribute value?

2) It seems set_value() stores the new value somewhere in the Attribute instance, but how to get hold of it? I would like the attribute read function to return that same value rather than having to cache it in a private variable.

3) Is it possible to control the attribute date when using the attribute read method? If a client does reading = dev.read_attribute('randomnumber') (where dev is a DeviceProxy) I would like reading.time to refer to the time when the value was received from the device (i.e. the same time as used for self.randomnumber.set_date()), not the time that the attribute read method was called.

Below is my device server

Thanks
Neilen


import time
import threading
import random
import logging

import PyTango

from PyTango.server import server_run
from PyTango.server import Device, DeviceMeta
from PyTango.server import attribute, command

from PyTango import AttrQuality, AttrWriteType, DispLevel

logger = logging.getLogger('ThingumDS')

class Thingum(Device):
    __metaclass__ = DeviceMeta

    def __init__(self, *args, **kwargs):
        logging.info('In __init__')
        self._randomnumber = 0
        super(Thingum, self).__init__(*args, **kwargs)


    @attribute(label="Random Number", dtype=float,
               access=AttrWriteType.READ)
    def randomnumber(self):
        print 'Reading randomnumber at {}'.format(time.time())
        return self._randomnumber

    @attribute
    def time(self):
        return time.time()

    def init_device(self):
        super(Thingum, self).init_device()
        self.t = threading.Thread(target=self.update_loop)
        self.t.setDaemon(True)
        self.t.start()

    def update_loop(self):
        while True:
            try:
                new_number = random.randint(0, 10000)
                ts = time.time()
                sleeptime = random.random()*10
                logger.info(
                    'Timestamp: {}    New count: {}    sleeptime: {}'
                    .format(ts, new_number, sleeptime))
                self.randomnumber.set_value(new_number)
                self.randomnumber.set_date(
                    PyTango.TimeVal.fromtimestamp(ts))
                self._randomnumber = new_number
                # Uncomment this sleep to get segfaults if a 
                #client reads an attribute
                # time.sleep(10)
                try:
                    self.randomnumber.fire_change_event()
                except Exception:
                    logger.exception(
                        'Exception while firing change event')
                time.sleep(sleeptime)
            except Exception:
                logger.exception('Exception in update loop')
                time.sleep(1)



if __name__ == "__main__":
    logging.basicConfig(
        format='%(asctime)s - %(name)s - %(levelname)s - %(module)s - '
        '%(pathname)s : %(lineno)d - %(message)s',
        level=logging.INFO)

    server_run([Thingum])
Edited 8 years ago
Hi Neilen,

I experienced segfaults in PyTango when the threads are too intensive, started before init_device() or are not properly joined at exit. You may try to use a threading.Event with a short wait to control it:


def updateLoop(self):
  self.ev = threading.Event():
  while not self.ev.isSet():
    self.ev.wait(1e-3)
    try: 
      …

Regarding your question about read_attribute(): may be the new API works different that the old one, but in the classical PyTango API (using a read_AttrName(self,attr) method) you just pass the date to the client using the attr.set_value_date_quality(…) method.

Sergi
Keep on dancing,

http://www.tango-controls.org/resources/howto/how-fandango/
Edited 8 years ago
It turns out the threading stuff works OK if you use the device's push_change_event().

Regarding the attribute timestamps, it seems the "new python" way to return a tuple of (new_value, timestamp, AttrQuality.ATTR…) in the read method, nicely described here:

http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/pytango/latest/tep/tep-0001.html

I personally find stackoverflow a great forum for answers to specific questions, so I asked and answered my own question with an example here: http://stackoverflow.com/questions/35460896/pytango-device-with-externally-updated-attributes/

I tried to add a 'tango-controls' tag (there is already a "tango" tag referring to another project), but I don't have enough reputations on SO. If anyone has > 1500 reputation please make a tango-controls tag and add it to my question :)

Thanks
Neilen

 
Register or login to create to post a reply.