在PyTango中,DeviceProxy
是一个核心类,用于表示和操作Tango设备的客户端代理。这个类封装了与Tango设备通信的所有细节,包括网络通信、命令执行、属性读写、事件订阅等。DeviceProxy
的实现隐藏了底层的CORBA细节,提供了一种简洁的方式来与Tango设备进行交互。
设计和实现 #
DeviceProxy
类的设计主要目标是为用户提供一个简单、直观的接口来与Tango设备进行交互,而不需要用户关心通信的底层细节。其主要功能包括:
- 设备命令执行:允许用户向设备发送命令并接收响应。
- 属性读写:提供接口读取和写入设备的属性。
- 事件订阅:允许用户订阅设备事件(如属性变化、警告、错误等)并定义回调函数处理这些事件。
- 状态和信息查询:可以查询设备的状态、信息和其他元数据。
实现细节
DeviceProxy
的实现依赖于Tango库(C++实现的Tango核心库)和CORBA通信机制。当创建一个 DeviceProxy
实例时,会进行以下步骤:
- 初始化ORB:如果还没有初始化,PyTango会初始化CORBA的对象请求代理(ORB),这是通信的基础设施。
- 查询设备IOR:使用设备的全名(如domain/family/member)向Tango数据库查询设备的IOR(Interoperable Object Reference)。IOR是一个字符串,包含了定位和访问CORBA对象所需的所有信息。
- 创建设备引用:使用IOR,通过ORB创建对设备的引用。这个引用是与设备通信的代理。
- 封装通信细节:DeviceProxy方法调用将通过设备引用,使用CORBA协议与设备进行通信。所有的网络通信、数据编码/解码、异常处理等底层细节都被封装在这一步中。
示例代码
import tango
# 创建设备代理
device_proxy = tango.DeviceProxy("domain/family/member")
# 读取属性
attribute_value = device_proxy.read_attribute("AttributeName").value
# 写入属性
device_proxy.write_attribute("AttributeName", value)
# 执行命令
result = device_proxy.command_inout("CommandName", arg)
在这个过程中,用户不需要直接处理任何关于CORBA或网络通信的细节,所有这些都由 DeviceProxy
内部处理。
底层代码 #
device_proxy.cpp #
利用Boost.Python库将C++中的Tango::DeviceProxy类暴露给Python环境。以下是对主要部分的解读:
- DeviceProxy是Tango控制系统中的核心类,它为客户端应用程序提供了与Tango设备交云的接口。
- 这段代码主要工作是将Tango::DeviceProxy的方法和功能以Python友好的方式暴露出来,包括设备状态查询、属性读写、命令执行、事件订阅等。
主要功能和方法
- 属性和命令操作:提供了一系列方法来读写设备属性(read_attribute, write_attribute等),执行设备命令(command_inout)。
- 事件订阅:实现了对设备事件的订阅功能,允许Python端注册回调以响应设备发送的事件(subscribe_event, unsubscribe_event等)。
- 设备信息查询:可以查询设备的基本信息和状态(info, _status, _state等)。
- 异步操作:支持异步读写属性和执行命令的能力,提高了与设备交互的效率(read_attributes_asynch, write_attributes_asynch等)。
- 锁定:提供了设备锁定和解锁的接口,用于控制对设备的访问(lock, unlock等)。
技术实现
- 利用Boost.Python库将C++方法映射为Python方法。例如,通过.def来定义Python可调用的方法,并将其关联到C++的方法实现。
- 使用了多线程控制技术(AutoPythonAllowThreads)来处理可能阻塞的操作,保证Python的GIL(全局解释器锁)在等待期间被释放,从而提高多线程应用的效率。
- 采用了模板和泛型编程技术来处理不同类型的属性和命令参数。
device_proxy.py #
@green(consume_green_mode=False)
def get_device_proxy(*args, **kwargs):
"""get_device_proxy(self, dev_name, green_mode=None, wait=True, timeout=True) -> DeviceProxy
get_device_proxy(self, dev_name, need_check_acc, green_mode=None, wait=True, timeout=None) -> DeviceProxy
Returns a new :class:`~tango.DeviceProxy`.
There is no difference between using this function and the direct
:class:`~tango.DeviceProxy` constructor if you use the default kwargs.
The added value of this function becomes evident when you choose a green_mode
to be *Futures* or *Gevent* or *Asyncio*. The DeviceProxy constructor internally
makes some network calls which makes it *slow*. By using one of the *green modes* as
green_mode you are allowing other python code to be executed in a cooperative way.
.. note::
The timeout parameter has no relation with the tango device client side
timeout (gettable by :meth:`~tango.DeviceProxy.get_timeout_millis` and
settable through :meth:`~tango.DeviceProxy.set_timeout_millis`)
:param dev_name: the device name or alias
:type dev_name: str
:param need_check_acc: in first version of the function it defaults to True.
Determines if at creation time of DeviceProxy it should check
for channel access (rarely used)
:type need_check_acc: bool
:param green_mode: determines the mode of execution of the device (including
the way it is created). Defaults to the current global
green_mode (check :func:`~tango.get_green_mode` and
:func:`~tango.set_green_mode`)
:type green_mode: :obj:`~tango.GreenMode`
:param wait: whether or not to wait for result. If green_mode
Ignored when green_mode is Synchronous (always waits).
:type wait: bool
:param timeout: The number of seconds to wait for the result.
If None, then there is no limit on the wait time.
Ignored when green_mode is Synchronous or wait is False.
:type timeout: float
:returns:
if green_mode is Synchronous or wait is True:
:class:`~tango.DeviceProxy`
else if green_mode is Futures:
:class:`concurrent.futures.Future`
else if green_mode is Gevent:
:class:`gevent.event.AsynchResult`
else if green_mode is Asyncio:
:class:`asyncio.Future`
:throws:
* a *DevFailed* if green_mode is Synchronous or wait is True
and there is an error creating the device.
* a *concurrent.futures.TimeoutError* if green_mode is Futures,
wait is False, timeout is not None and the time to create the device
has expired.
* a *gevent.timeout.Timeout* if green_mode is Gevent, wait is False,
timeout is not None and the time to create the device has expired.
* a *asyncio.TimeoutError* if green_mode is Asyncio,
wait is False, timeout is not None and the time to create the device
has expired.
New in PyTango 8.1.0
"""
return DeviceProxy(*args, **kwargs)
- 解析设备名称,确定要查询的设备。
- 使用设备名称作为关键字,调用底层API向Tango中心数据库请求设备的详细信息。
- 从返回的信息中提取设备的IOR。
- 使用IOR与设备建立CORBA连接(或其他通信机制),以便后续的通信。