EPICS -- pyDevice

pyDevice是一个用于Python解释器的EPICS设备支持。它允许把EPICS数据库记录连接Python代码。

这个项目的目的是为Python开发者提供非常容易地接口把Python代码集成到EPICS控制系统。通过允许从EPICS记录调用任意Python代码实现这个,包括但不限于内建函数,计算表达式,自定义函数等。此外,Python代码可以被从IOC shell执行,这对设置资源或者python代码排错有用。由于PyDevice仅调用Python代码,这允许在独立非EPICS环境中开发和测试Python模块,并且最终被连接到EPICS PVs。

Hello World示例

record(longout, "PyDev:Test:HelloWorld") {

    field(DTYP, "pydev")

    field(OUT,  "@print('Hello world!')")

}

当这个记录运行时,Hello world!文本将被打印到IOC console。

快速入门

这个教程通过示例介绍PyDevice的特性。

简单的Python代码

让我们从在一个名为mydevice.py文件中定义一个单独的Python类开始,这个类提供了一个到我们设备的接口:

import socket

class MyDevice(object):

def __init__(self, ip, port=4011):

    self.ip = ip

    self.port = port

    self.socket = None

    self.sent = 0

def connect(self, timeout=1.0):

    """ Connects to device, throws exception if can't connect """

    if self.socket:

        self.socket.close()

    self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    self.socket.settimeout(timeout)

    self.socket.connect((self.ip, self.port))

def disconnect(self):

      if self.socket:

          self.socket.close()

          self.socket = None

def send(self, msg):

    if not self.socket:

        raise RuntimeError("not connected")



    # TODO: send() doesn't guarantee sending entire message

    sent = self.socket.send(msg)

    if sent == 0:

        self.disconnect()

        raise RuntimeError("socket connection broken")



self.sent += sent

这个类能够在任何Python环境中被实例化和测试:

import mydevice

if __name__ == "__main__":

    device = MyDevice()

    device.connect("127.0.0.1")

device.send("test")

在IOC环境中实例化Python对象:

假设mydevice.py是在Python的搜索路径中,我们可以从IOC st.cmd文件实例化一个MyDevice对象。这将在全局的Python环境中创建一个device1变量:

pydev("from mydevice import MyDevice")

pydev("device1 = MyDevice('127.0.0.1')")

提示:pydev()函数允许从IOC shell执行任意单行Python代码

从数据库触发connect()

在这个简单的数据库记录示例中,在记录运行时我们将触发与这个设备建立连接:

record(longout, "Device1:Connect")

{

    field(DTYP, "pydev")

    field(OUT,  "@device1.connect(timeout=2.0)

}

从以上Python代码注意,connect()函数可能抛出一个异常。在这种情况下,PyDevice将截获异常并且通过SEVR和STAT字段反映出错。

向设备发送一条消息

当记录运行时,这个示例将使用stingout记录发送简单的ASCII文本给设备:

record(stringout, "Device1:Send")

{

    field(DTYP, "pydev")

    field(OUT,  "@device1.send('VAL')

    field(VAL,  "127.0.0.1")

}

PyDevice将识别常见记录字段并且在Python代码执行前用实际字段值替换它们。字段名称必须时单独的大写词语,并且需要是一个有效的记录字段。当字段是这个字符串的一部分时,它应该被%符号包围,例如:RecordNames%NAME%。

提示:可以从任何支持记录执行任意的单行Python代码。

把值从Python压入记录(就是说中断扫描)

为了支持I/O Intr扫描,PyDevice提供名为pydev.iointr(param, value=None)的内建函数。记录的INP或OUT格式应该只用指定的参数名调用pydev.iointr(param)函数,并且记录的SCAN字段应该应该被指定为I/O Intr。当Python代码想要把新值压入记录时,它必须用参数名称和值调用相同的函数。这了这件事将立即触发使用相同参数名称的所有记录运行。参数名称可以时任何有效的字符串并且不于任何记录名称或Python变量耦合。

注意:只有在IOC装置数据库时,而不是运行时,SCAN才可以选择I/O Intr。

PyDevice内部定义了pydev.iointr()函数并且注册它为内建函数。不需要从Python代码导入任何模块。但当在PyDevice环境外测试Python代码,pydev.iointr()将不可用。通过定义哑pydev类简单地修复这个问题,例如,作为允许相同脚本作为一个独立脚本被执行的脚本既定机制的部分或者作为PyDevice的部分:

if __name__ == "__main__":

  class pydev(object):

    @staticmethod

    def iointr(param, value=None):

      pass

  # rest of your codeif __name__ == "__main__":

  class pydev(object):

    @staticmethod

    def iointr(param, value=None):

      pass

  # rest of your code

字段宏:当记录运行时,PyDevice将对INP或OUT字段搜索字段宏,并且用来自记录的实际字段值替换它们。字段宏只是所有大写字母的字段名,例如VAL。它们需要独立词语并且不被其它字母数字字符包围,在那种情况中,它们应该被包围在%符号中。取决于这个记录,只支持一个字段子集。waveform记录的VAL字段宏被转成了一个Python list。

注意:取决于使用的字符串字符,你可能需要用引号包围它们。考虑两种有效示例:

record(stringout, "PyDev:Log") {

  field(DTYP, "pydev")

  field(OUT,  "@print('VAL')")

}

record(stringout, "PyDev:Exec") {

  field(DTYP, "pydev")

  field(OUT,  "@VAL")

  # caput PyDev:Exec "print('hello')"

}

每种记录可支持的字段宏:

  1. longin&longout:VAL NAME EGU HOPR LOPR HIGH HIHI LOW LOLO
  2. bi&bo:VAL NAME ZNAM ONAM
  3. mbbi&mbbo:VAL RVAL NAME ZRVL ONVL TWVL THVL FRVL FVVL SXVL EIVL NIVL TEVL ELVL TVVL TTVL FTVL FFVL ZRST ONST TWST THST FRST FVST SXST SVST EIST NIST TEST ELST TVST TTST FTST FFST
  4. ai&ao:VAL RVAL ORAW NAME EGU HOPR LOPR PREC
  5. stringin&stringout:VAL NAME
  6. lsi&lso:VAL NAME SIZV LEN
  7. waveform:VAL

串行化记录运行:

PyDevice使用单个线程执行来自记录的任何代码。它认为Python的全局解释器锁(GIL)无论如何都会串行化请求,尽管在未来可能会添加对多个运行线程的支持。当记录运行时,要被执行的请求的Python代码被放入FIFO队列,记录的PACT字段被设置成1并且句柄被返回给调用者。这使得没有回调的请求立即返回。当轮到它时,请求出队并且执行Python代码。一旦Python代码结束时,记录的值被更新并且回调被执行。这使得带完成的请求在Python代码结束时得到恰当地获得通知。

单线程策略不应用于从Python代码生成地任何线程。

构建和添加到IOC

依赖:

  1. EPICS 3.14+或者7.0+
  2. python 2.x或者3.5+头和库
    1. RHEL:yum install python-devel
    2. Debian&Ubuntu:
      1. 对于python2: apt install python-dev
      2. 对于python3: apt install python3-dev

编译PyDevice:

为了PyDevice,必须安装所有它的依赖。在configure/RELEASE文件中,更改EPICS_BASE。在configure/CONFIG_SITE中,如果你使用python3设置PYTHON_CONFIG=python3-config。设置之后,在顶层发出make命令。编译将提供提供动态库,要被包含在IOC中以及一个可以从iocBoot/iocpydev文件夹被执行的测试IOC。

假设满足了所有依赖,项目应该构建了可链接的库和测试的IOC二进制文件。从测试iocBoot/iocpydev文件夹运行st.cmd将启动一个示例IOC。到此,数据库和Python代码可以被更改,而不用重新构建PyDevice源代码。

添加PyDevice支持到IOC

为了已有的IOC得到PyDevice支持,需要添加一些东西。

  1. 编辑configure/RELEASE和添加PYDEVICE变量指向PyDevice源位置
  2. 编辑再IOC的App/src文件夹中的Makefile并且向向顶端添加:

include $(PYDEVICE)/configure/CONFIG.PyDevice

3) 在相同Makefile中,在应用程序的位置添加以下两行:

       <yourioc>_DBD += pydev.dbd

       <yourioc>_LIB += pydev

在重新构建后,IOC应该有对pydev的支持了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值