python - 具有 'externally' 更新属性的 PyTango 设备

标签 python events tango-controls

假设您有一个硬件设备,它以或多或少的随机间隔向您发送更新,并且客户端希望在每次发生这种情况时接收事件。您将如何编写一个 PyTango(Tango controls library PyTango.server.Device 类的 Python 包装器,使用推送新属性值的线程来模拟这一点?

最佳答案

答案似乎是

  1. 调用 PyTango.server.Device 方法 set_change_event() 告诉 Tango 设备正在处理自己的更改事件推送,而无需使用轮询循环。
  2. 在更新线程中使用 Pytango.sever.Device 方法 push_change_event()。到目前为止似乎是线程安全的,即使更新率非常高,也没有看到任何胡言乱语。如果有人能证实这一点那就太好了。
  3. 还缓存更新,以便轮询属性的客户端获得正确的值和时间戳。这似乎有点多余,有更好的方法吗?
  4. 不要在属性上设置轮询,因为它可能会导致客户端轮询获取过时的值(Tango 似乎以一种我不太明白的方式缓存轮询中的属性值)。

这里是一个使用外部更新的 randomnumber 属性的示例 (python 2.7) 服务器

import time
import threading
import random

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

from PyTango import AttrQuality


class ExternallyUpdated(Device):
    __metaclass__ = DeviceMeta

    def __init__(self, *args, **kwargs):
        super(ExternallyUpdated, self).__init__(*args, **kwargs)
        self._randomnumber = (0, 0, AttrQuality.ATTR_VALID)
        # Tell Tango that we don't need a polling loop, we'll 
        # push change events explicitly
        self.set_change_event('randomnumber', True)

    @attribute(label="Random Number", dtype=int,
               # Enables update events for absolute changes >= 1
               abs_change=1)
    def randomnumber(self):
        return self._randomnumber

    def init_device(self):
        super(ExternallyUpdated, 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
                print ('Timestamp: {:.5f}    New value: {}    sleeptime: {}'
                        .format(ts, new_number, sleeptime))
                # Need to cache the value so that clients can poll the attribute
                self._randomnumber = (new_number, ts, AttrQuality.ATTR_VALID)
                self.push_change_event(
                    'randomnumber', new_number, ts, AttrQuality.ATTR_VALID)
                time.sleep(sleeptime)
            except Exception:
                logger.exception('Exception in update loop')
                time.sleep(1)

if __name__ == "__main__":
    server_run([ExternallyUpdated])

下面是一个示例客户端,假设您将设备导出为 so_example/external/1。每次更新随机数时,它都应该打印一条消息。

import time
import logging

import PyTango


logger = logging.getLogger()
logging.basicConfig(
        format='%(asctime)s - %(name)s - %(levelname)s - %(module)s - '
        '%(pathname)s : %(lineno)d - %(message)s',
        level=logging.INFO)

device_name = 'so_example/external/1'

td = PyTango.DeviceProxy(device_name)
attr_name = 'randomnumber'

# Set up a listener
def printer(event_data):
    try:
        print event_data # A PyTango.EventData instance
    except Exception:
        # Not handling exceptions seems to break the event update loop. Or I was
        # doing something else stupid, must still investigate :)
        logger.exception('Exception while handling event, event_data: {}'
                         .format(event_data))
poll_event_id = td.subscribe_event(
    attr_name, PyTango.EventType.CHANGE_EVENT, printer)

# Do something that blocks here, or just run in an interactive session
# time.sleep(60)

# This is how you would unsubscribe from the events.
#td.unsubscribe_event(poll_event_id)

关于python - 具有 'externally' 更新属性的 PyTango 设备,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35460896/

相关文章:

python - Scrapy 无法正确收集电子邮件

python - 从具有 DenseVector 行的 pyspark 数据框中获取行的最大值

python - 多处理 imap 抑制子进程打印

python - 如何将元组传递给 python 函数

java - 一个类中的事件可以触发另一个类中的操作吗?

c# - 为什么将伪对象传递给C#中的事件处理程序?

javascript - 如何在 JavaScript(或 jQuery)中实现自定义广播事件?