假设您有一个硬件设备,它会以或多或少的随机间隔向您发送更新,并且客户端希望每次发生这种情况时都会收到事件。你会如何写一个PyTango(Tango controls library PyTango.server.Device
类的Python包装器,它使用一个推送新属性值的线程来模拟它?
答案 0 :(得分:0)
答案似乎是
PyTango.server.Device
方法set_change_event()
告诉Tango设备正在处理自己的更改事件,而无需使用轮询循环。Pytango.sever.Device
方法push_change_event()
。到目前为止似乎线程安全,即使具有非常高的更新速率也没有看到任何malarkey。如果有人能证实这一点会很好。此处使用外部更新的randomnumber
属性
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
。每次更新randomnumber时都应该打印一条消息。
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)