Skip to main content

PyTango-DeviceProxy

·446 words·3 mins
WFUing
Author
WFUing
A graduate who loves coding.
Table of Contents

在PyTango中,DeviceProxy是一个核心类,用于表示和操作Tango设备的客户端代理。这个类封装了与Tango设备通信的所有细节,包括网络通信、命令执行、属性读写、事件订阅等。DeviceProxy的实现隐藏了底层的CORBA细节,提供了一种简洁的方式来与Tango设备进行交互。

设计和实现
#

DeviceProxy 类的设计主要目标是为用户提供一个简单、直观的接口来与Tango设备进行交互,而不需要用户关心通信的底层细节。其主要功能包括:

  • 设备命令执行:允许用户向设备发送命令并接收响应。
  • 属性读写:提供接口读取和写入设备的属性。
  • 事件订阅:允许用户订阅设备事件(如属性变化、警告、错误等)并定义回调函数处理这些事件。
  • 状态和信息查询:可以查询设备的状态、信息和其他元数据。

实现细节

DeviceProxy 的实现依赖于Tango库(C++实现的Tango核心库)和CORBA通信机制。当创建一个 DeviceProxy 实例时,会进行以下步骤:

  1. 初始化ORB:如果还没有初始化,PyTango会初始化CORBA的对象请求代理(ORB),这是通信的基础设施。
  2. 查询设备IOR:使用设备的全名(如domain/family/member)向Tango数据库查询设备的IOR(Interoperable Object Reference)。IOR是一个字符串,包含了定位和访问CORBA对象所需的所有信息。
  3. 创建设备引用:使用IOR,通过ORB创建对设备的引用。这个引用是与设备通信的代理。
  4. 封装通信细节: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)
  1. 解析设备名称,确定要查询的设备。
  2. 使用设备名称作为关键字,调用底层API向Tango中心数据库请求设备的详细信息。
  3. 从返回的信息中提取设备的IOR。
  4. 使用IOR与设备建立CORBA连接(或其他通信机制),以便后续的通信。

Resources
#


💬评论