python epics包提供了若干函数,模块和类用于与EPICS通道访问交互。最简单的方法是使用在顶层epics模块内的caget(), caput()和cainfo()来读写EPICS过程变量的值。这些函数类似于标准的命令行工具和EZCA库函数接口,并且在下面更详细地被描述。
使用epics包,导入它:
import epics
这个模块地主要组件包括:
- caget(), caput()和cainfo()函数和其它,在下面更加详细的被描述
- 一个ca模块,以一个函数集合提供底层库,意味着非常接近用于通道访问的C库
- 一个PV对象,代表一个过程变量(PV)并且给出了一个用于EPICS通道访问的更高级接口
- 一个Device对象:相关联PVs的集合,类似于一个EPICS记录
- 一个Motor对象:一个代表一个EPICS电机的Device
- 一个Alarm对象:其可被用于在一个PV的值超出可接受边界时设置通知
- 一个epics.wx模块,提供了被设计为用于EPICS PVs的wxPython类
如果想要编写快速脚本或简单介绍来使用通道访问,caget()和caput()函数可能是你想要开始的地方。
如果你正在构建更大的脚本和程序,推荐使用PV对象。PV类提供了一个过程变量(PV)对象,其有读取和更改PV的方法(包括get()和put()),以及保持与远程通道自动同步的属性。对于你发现自己使用相关PVs的集合的更大程序,你可能觉得Device类有用。
在ca模块以及相伴的dbr模块中展现了最底层CA功能。尽管不推荐给大多数使用情况,这个模块确实提供了基本EPICS CA库的一个相当完整的包装。对于从C或其它语言已经使用CA的人,如果比起PV对象更详细一点和类C,这个模块应该熟悉以及非常有用。
另外,epics包包含了包含了用于警报,epics电机和若干其它设备(PVs集合)以及用wxPython使用EPICS PVs的wxPython小部件类的更加专用模块。
在Linux,MacOS X和Windows上用Python 2.7和3.5以上,支持epics包并且很好测试了。
快速使用
无论你是否熟悉EPICS通道访问,从这里开始。你将接着能够使用Python的自检工具和内建帮助系统,并且这个文档剩下部分作为参考并且用于详细的讨论。
过程方法:caget() caput()
要从PVs获取值,你可以使用caget()函数:
>>> from epics import caget, caput, cainfo
>>> m1 = caget('XXX:m1.VAL')
>>> print(m1)
1.2001
要设置PV值,你可以使用caput()函数:
>>> caput('XXX:m1.VAL', 1.90)
>>> print(caget('XXX:m1.VAL'))
1.9000
要看有关一个PV的更加详细信息,使用cainfo()函数:
>>> cainfo('XXX:m1.VAL')
== XXX:m1.VAL (time_double) ==
value = 1.9
char_value = '1.9000'
count = 1
nelm = 1
type = time_double
units = mm
precision = 4
host = somehost.aps.anl.gov:5064
access = read/write
status = 0
severity = 0
timestamp = 1513352940.872 (2017-12-15 09:49:00.87179)
posixseconds = 1513352940.0
nanoseconds= 871788105
upper_ctrl_limit = 50.0
lower_ctrl_limit = -48.0
upper_disp_limit = 50.0
lower_disp_limit = -48.0
upper_alarm_limit = 0.0
lower_alarm_limit = 0.0
upper_warning_limit = 0.0
lower_warning_limit = 0.0
PV is internally monitored, with 0 user-defined callbacks:
=============================
这些函数的简单和明确使得它们适合很多用途。
创建和使用PV对象
如果你重复引用相同的PV,你可以发现创建一个PV对象并且以更面向对象方式使用它更加的方便。
>>> from epics import PV
>>> pv1 = PV('XXX:m1.VAL')
PV对象有若干方法和属性。最重要的方法是接收和发送PV的值的get()和put(),以及存储当前值的value属性。类似于以上caget()和caput()示例,可以用以下方式之一获取一个PV的值:
>>> print(pv1.get()) # 方式1
1.90
>>> print(pv1.value) # 方式2
1.90
要设置一个PV的值,你可以使用以下方式之一:
>>> pv1.put(1.9) # 方式1
>>> pv1.value = 1.9 # 方式2,给value属性赋值
你可以通过简单地打印一个PV,见到它地一些最重要地属性:
>>> print(pv1)
<PV 'XXX:m1.VAL', count=1, type=time_double, access=read/write>
通过打印PVs info属性,可以见到更加完整地信息:
>>> print(pv1.info)
== XXX:m1.VAL (time_double) ==
value = 1.9
char_value = '1.9000'
count = 1
nelm = 1
type = time_double
units = mm
precision = 4
host = somehost.aps.anl.gov:5064
access = read/write
status = 0
severity = 0
timestamp = 1513352940.872 (2017-12-15 09:49:00.87179)
posixseconds = 1513352940.0
nanoseconds= 871788105
upper_ctrl_limit = 50.0
lower_ctrl_limit = -48.0
upper_disp_limit = 50.0
lower_disp_limit = -48.0
upper_alarm_limit = 0.0
lower_alarm_limit = 0.0
upper_warning_limit = 0.0
lower_warning_limit = 0.0
PV is internally monitored, with 0 user-defined callbacks:
=============================
PV对象有监视PV值或者包括在值变化时要被运行的用户定义函数连接状态变化相关联的若干其它方法。也有与一个PVs控制属性相关联的属性,像在以上info属性中显示的那些属性。进一步细节在PV:Epics Process。
简单的epics函数:caget() caput()等
如以上展示,用函数caget(), caput()和cainfo()找到了EPICS通道访问的最简单接口。也有函数camonitor和camonitor_clear()设置和清除一个PV的一个简单变化监视。这些函数都接收一个EPICS过程变量(PV)作为第一个参数并且类似于相同名称的EPICS命令行工具。
这些函数在内部维护一个已连接PV的缓存(在这种情况中,使用PV对象),使得一个PV名称的重复使用将不会实际产生对这个PV的一次新的连接--更加详细见get_pv()函数和PVs的_PVcache_缓存。因而,虽然功能简单和直接,使用这些简单函数的性能非常好。此外,也有用于一次设置和获取多个PVs值的函数caget_many()和caput_many()。
caget()
epics.caget(pvname[,as_string=False[,count=None[,as_numpy=True[,timeout=None,use_monitor=True]]]])
获取和返回指定名称PV的值。
参数:
- pvname:EPICS过程变量的名称
- as_string(True/False):是否返回PV值的字符串表示。
- count(integer/None):返回对应数组数据的元素数目
- as_numpy(float/None):是否返回对应数组数据的数值Python表示
- timeout:在返回None前,等待值的最长时间(秒为单位)
- use_monitor(True/False):是否依靠monitor回调或者现在显式获取值
count和as_numpy只用于数组或wave数据。默认行为是返回完整数据数组并且转成numpy数组。count选项可以用于显式地限制返回的数组元素数目,并且as_numpy可以开启或关闭到一个numpy数组的转换。
timeout选项设置等待通过网络获取一个值的最长时间。如果超出了超时时间,caget()将返回None。这可能意味着这个PV实际不可用,但它也可能意味着数据非常大或者网络太慢只是使得数据没有被接收到。这使得caget()作用更像命令行工具,并且比创建一个PV并用其获取值效率稍低。如果性能是关注的,推荐使用monitors。要使得caget()更高效详细见Automatic Monitoring of a PV和The wait and timeout options for get(), ca.get_complete()。
as_string参数告诉这个函数返回这个值的字符串表示。字符串表示的细节取决于这个PV的变量类型。对于整数(short或long)以及字符串PVs,字符串表示非常简单:例如0将变为'0'。对于float和doubles,使用PV的内部精度来格式化这个字符串值。对于枚举类型,返回enum状态的名称。
>>> from epics import caget, caput, cainfo
>>> print(caget('XXX:m1.VAL')) # A double PV
0.10000000000000001
>>> print(caget('XXX:m1.DESC')) # A string PV
'Motor 1'
>>> print(caget('XXX:m1.FOFF')) # An Enum PV
1
添加as_string=True参数总是导致字符串被返回,用取决于数据类型的转换方法,例如,使用一个double PV的精度字段来决定如何格式化这个字符串,或者使用对应一个枚举PV的枚举状态的名称:
>>> print(caget('XXX:m1.VAL', as_string=True))
'0.10000'
>>> print(caget('XXX:m1.FOFF', as_string=True))
'Frozen'
对于来自EPICS waveform记录的整数或者double数组数据,常规值将是一个numpy数组(或者如果没有安装numpy,一个python list)。取决于waveform的尺寸和类型,字符串表示将是像'<array size=128,type=int>'的东西。一个doubles数组可能是:
>>> print(caget('XXX:scan1.P1PA')) # A Double Waveform
array([-0.08 , -0.078 , -0.076 , ...,
1.99599814, 1.99799919, 2. ])
>>> print(caget('XXX:scan1.P1PA', as_string=True))
'<array size=2000, type=time_double>'
作为一个重要特殊情况,当as_string为True时,CHAR waveform将被转换乘Python字符串。这对于解决EPICS strings最大长度(40个字符)的下限有用,这样促进了char waveform表示更长字符串的普通使用。
>>> epics.caget('MyAD:TIFF1:FilePath')
array([ 47, 104, 111, 109, 101, 47, 101, 112, 105, 99, 115, 47, 115,
99, 114, 97, 116, 99, 104, 47, 0], dtype=uint8)
>>> epics.caget('MyAD:TIFF1:FilePath', as_string=True)
'/home/epics/scratch/'
当然,字符waveforms不但用于长字符串,而且也可以用于保存字节输数数据,诸如可能来自一些探测器和设备。
caput()
epics.caput(pvname, value[,wait=False[,timeout=60]])
设置指定名称PV的值。
参数:
- pvname:EPICS过程变量的名称
- value:要发送的值
- wait(True/False):是否等待到运行的process完成
- timeout(double):在放弃前等待输入完成多长时间(秒为单位)
返回类型:整数
可选的wait参数告诉这个函数在运行中的process完成前等待。这对于花费显著时间来完成的PVs是有用的(或者因为它使得一个物理设备(电机,阀等)移动或者因为它触发了一个复杂的计算或者数据处理序列)。timeout参数给出了要等待的最长时间(以秒为单位)。即使caput()没有完成,这个函数将在这个(大约)时间后返回。
这个函数在成功时返回1。如果超出了超时时间,它将返回None。
>>> from epics import caget, caput, cainfo
>>> caput('XXX:m1.VAL',2.30)
1
>>> caput('XXX:m1.VAL',-2.30, wait=True)
... waits a few seconds ...
cainfo()
epics.cainfo(pvname[, print_out=True])
打印(或者以一个字符串返回)有关这个PV的一个信息段落,包括控制设置。
参数:
- pvname:EPICS过程变量的名称。
- print_out:是否写输出到标准输出(否则返回这个字符串)
camonitor()
epics.camonitor(pvname[, writer=None[,callback=None]])
这在指定名称的PV上设置一个监视器,这将使得在这个值每次变化时将引起某件事情被做。默认,当值变化时,PV名称,时间和值将被输出(到标准输出),但实际发生的动作可以被自定义。
参数:
- pvname:EPICS过程变量的名称。
- writer(None或者一个接收一个字符串参数的可回调函数)--
- callback(None或者可调用函数)--接收结果的用户提供的函数
可以指定任何能够接收一个的函数作为writer,诸如一个已经被打开用于写入的打开文件的write()方法。如果留为None,更改的消息将被发送到sys.stdout.write()。为了更完整的控制,可以指定一个callback函数在每次变化事件时被调用。这个回调应该对应pvname,value和char_value的关键字参数。有关为camonitor编写回调函数的信息,见User-supplied Callback functions。
>>> from epics import camonitor
>>> camonitor('XXX.m1.VAL')
XXX.m1.VAL 2010-08-01 10:34:15.822452 1.3
XXX.m1.VAL 2010-08-01 10:34:16.823233 1.2
XXX.m1.VAL 2010-08-01 10:34:17.823233 1.1
XXX.m1.VAL 2010-08-01 10:34:18.823233 1.0
camonitor_clear()
epics.camonitor_clear(pvname)
清除通过camonitor()指定名称的PV上的一个监视器设置。
参数:
pvname:EPICS过程变量的名称
这个简单示例用camonitor()监视一个PV一段时间,变化被保存到一个日志文件。在一段时间后,这个监视器被清除并且检查日志文件。
>>> fh = open('PV1.log','w')
>>> epics.camonitor('XXX:DMM1Ch2_calc.VAL',writer=fh.write)
>>> .... wait for changes ...
>>> epics.camonitor_clear('XXX:DMM1Ch2_calc.VAL')
>>> fh.close()
>>> fh = open('PV1.log','r')
>>> for i in fh.readlines(): print(i[:-1])
XXX:DMM1Ch2_calc.VAL 2010-03-24 11:56:40.536946 -183.5035
XXX:DMM1Ch2_calc.VAL 2010-03-24 11:56:41.536757 -183.6716
XXX:DMM1Ch2_calc.VAL 2010-03-24 11:56:42.535568 -183.5112
XXX:DMM1Ch2_calc.VAL 2010-03-24 11:56:43.535379 -183.5466
XXX:DMM1Ch2_calc.VAL 2010-03-24 11:56:44.535191 -183.4890
XXX:DMM1Ch2_calc.VAL 2010-03-24 11:56:45.535001 -183.5066
XXX:DMM1Ch2_calc.VAL 2010-03-24 11:56:46.535813 -183.5085
XXX:DMM1Ch2_calc.VAL 2010-03-24 11:56:47.536623 -183.5223
XXX:DMM1Ch2_calc.VAL 2010-03-24 11:56:48.536434 -183.6832
caget_many()
epics.caget_many(pvlist[,as_string=False[,count=None[,as_numpy=True[,timeout=None]]]])
尽可能快的获取一个PVs列表。返回一个对应在这个列表中每个PV的值的列表。不同于caget(),这个方法不能自动监视(见Automatic Monitoring of a PV)。
参数:
- pvlist(list或str的tuple):一个过程变量名称的列表
- as_string(True/False):是否返回PV值的字符串表示
- count(整数或None):对应数组数据要返回的元素数目
- as_numpy(True/False):是否返回对应数组数据的数值Python表示
- timeout(float或None):在返回None前,等待值的最长时间(秒为单位)
有关参数的详细信息,见有关caget()的文档。更多讨论也见Stagegies for connecting to a large number of PVs。
caput_many()
epics.caput_many(pvlist,values[,wait=False[,connection_timeout=None[,put_timeout=60]]])
尽可能快得把值写入到一个PVs列表。返回一个对应在这个列表中每个PV得ints列表:如果成功为1,如果超时,为-1。不同于caput(),这个方法不使用自动监视(见Automatic Monitoring of a PV)。
参数:
- pvlist(list或str的tuple):一个过程变量名称的列表
- values(list或tuple)
- wait:如果'each',caput_many()在启动下一个PV前将等待每个PV运行。如果'all',caput_many()将立即为所有PVs发布输入,接着等待它们所有结束。如果任何其它值,caput_many()将不等待输入运行结束。
- connection_timeout(float或None):等待一个建立每个PV连接的最长时间。
- put_timeout(float或None):等待用于每个PV的运行结束(如果wait是'each'),或者等待所有PVs运行结束(如果wait是'all')的最长时间。
因为对通道的连接通常连接非常快(少于1秒),但运行一个写入可能会花费一段显著的时间(由于物理设备移动,或者由于复杂的计算或者数据处理序列),可以为连接和处理写入指定一个单独的超时时段。
动记和设计概念
也有用于EPICS通道访问的其它Python包装,因此为PyEpics列出设计目标是有用的。PyEpics3的动记包括:
- 提供对EPICS通道访问的底层(类C)和更高级访问(Python对象)
- 支持尽可能多的EPICS 3.14以及以上的特性,包括抢占式回调和线程支持
- 对Windows和类Unix系统的简单支持和发布
- 支持Python2(现在不再被支持)和Python3
- 使用Python的ctypes库
想法是提供一个非常类似对CA的C接口的对EPICS通道访问(CA)的底层接口以及在此基础上构建更高级功能和复杂对象。Python ctype库方便地允许一个共享库的这样直接包装,并且不需要用于在Python和CA库之间桥梁的编译代码。这使得从Python代码包装实际上所有CA变得容易,并且支持多种平台。由于ctypes在运行时装载一个共享对象库,底层CA库可以被升级而不需要重建这个Python装饰器。ctypes接口提供可用的最可靠线程安全,由于对底层CA库的每次调用是自动变得线程感知,而无需显式代码。最终,通过完全避免C API,支持Python2和Python3被大大简化。
状态
PyEpics包被积极地维护,但核心库是相当稳定地并且准备用于生产代码。特性被缓慢地添加,并且测试被集成到了开发,因而引入漏洞到已有代码地机会非常小。把这个包在所有可支持的Python上运行作为目标并且被测试,当前Python 3.6以及以上。