New Python EPICS Interface
Motivation
History
Geoff Savage created
caPython(CaChannel) and Mark Rivers extended it to
epicsPV and upon that created device support modules, epicsMotor, epicsLogger, epicsScaler, epicsMed, epicsMca. In today's point of view, it has the following limitations:
- Based on EPICS 3.13. Using APIs obsoleted in EPICS 3.14.
- For the same reason, no thread support.
- It is not actively maintained.
- The code is wrapped by SWIG-1.1 and requires extra work to be used by SWIG-1.3.
On the other hand, Noboru Yamamoto created and still maintains
PythonCA. It has been brought up to date with EPICS 3.14. It has the following advantages:
- With thread support.
- Preemptive callback is enabled by default, which does not require periodic calls to poll() to process background CA activities.
- Wrapped with Python/C API, no SWIG dependency.
Indeed there is a third development from Matthew Newville, formerly known as Ezca and has evolved into
EpicsCA.
- EPICS 3.14 compatible.
- Lacks thread support and disables preemptive callback.
- Caches all connected PV channels in the C extension level.
- Wrapped with SWIG 1.3.
Update: From the same author,
PyEpics3 is a complete rewrite:
- EPICS 3.14 compatible using preemptive callback.
- ctypes is used to wrap ca library.
- Requires Python 2.5+.
- Multiple thread support by function decorators.
- wx integration.
What is in SLS
Our Approach
PythonCA is chosen as the low-level implementation of the Python to EPICS interface. After a thorough
analysis of the API of both CaPython and PythonCA, we rebuilt the CaChannel implementation. The results are the following files:
- _ca_fnal.py: holds all namespace from
_ca and add necessary functions(mostly data type related) not directly implemented in _ca but existing in CaPython.
- CaChannel.py: CaChannel class interface
Our modification of PythonCA has been kindly accepted into the main release, since
1.20.1.beta2.
Regression Test
ca_cb_gr.py and
ca_wf.py are two unmodified test programs from CaPython. The new interface passes all test.
However there are subtle differences, see blow.
How to Convert Old Codes
If you only use searchw(), getw(), putw() function calls either using CaChannel or epicsPV classes, then you can safely skip the following discussions. Continue reading if you worry about the internals.
- In CaPython one needs to call poll() periodically for CA library to process callback functions. This is not needed in PythonCA because preemptive callback is enabled by default.
- In CaPython, CaChannelException has a member variable status to hold the integer CA error code, ECA_NORMAL etc. In PythonCA, it has been used to hold the string message. But in both cases,
print will show the error message.
- In PythonCA version 1.7 and below, add_masked_array_event ignores the req_type parameter, which means only native field type is supported.
- In PythonCA, ca.get is always asynchronous. So CaChannel?.array_get is simulated by calling getw. If one calls array_get on a lot of channels and then call pend_io, one would expect all values are got at one shot. But this is not supported. Instead the get request are sent out immediately following array_get call.
Test Suite
Here we demonstrate two typical usage, one is for command line based program and one is for graphical based program with multithread capability.
- Before Start
- Load CVS:/G/EPICS/extensions/src/PythonCA/test/test.db to an IOC:
iocsh> dbLoadRecords("test.db") It provides the following records,
- catest - ao
- castr - stringout
- cabo - bo
- cawave - waveform of double
- cawavec - waveform of char
- cawaves - waveform of string
- Point your Python package search path to the new module,
export PYTHONPATH=/somewhere/to/be/decided:$PYTHONPATH
- CVS:G/EPICS/extensions/src/PythonCA/test/test.py is a command line program doing synchronous CA get/put calls.
- CVS:G/EPICS/extensions/src/PythonCA/test/thread.py is a multithread GUI program using asynchronous CA calls. It spawns a thread to calculate the Fibonacci numbers and put it to PV catest. In the main thread PV catest is monitored and displayed in a Tkinter Label widget.
- CaChannel has doctest built in. See the test results with command,
python CaChannel.py -v
Documentation
Basically the
CaChannel? API is the same as CaPython, so the documentation of
CaChannel is still valid. In the meantime, docstring is fully implemented in
CaChannel?.
While for the 'ca' API, the compatibilty has been achieved for those functions/definitions without requiring a channel access id, i.e. pend_io, test_io, pend_event, pend_io, poll, alarmStatusString, alarmSeverityString, dbf_type_to_DBR_XXX, dbr_type_is_XXX, dbr_text, dbf_text.
Enhancement
With version 1.8, we have added the following features while keeping backwards compatible,
- Before that getw accepts only plain request type i.e.
DBR_XXX and return a single value or a list. In the new version, it accepts also DBR_CTRL_XXX etc. It returns a dictionary having the following keys, | Key | Request Type | Description |
| pv_value | All | a single value or a list |
| pv_severity | All | alarm severity |
| pv_status | All | alarm status |
| pv_seconds | DBR_TIME_XXX | time stamp in seconds.nanoseconds format. Note that EPICS epoch is January 1, 1990 |
| pv_units | DBR_GR_XXX and DBR_CTRL_XXX | units string |
| pv_updislim | DBR_GR_XXX and DBR_CTRL_XXX | upper display limit |
| pv_lodislim | DBR_GR_XXX and DBR_CTRL_XXX | lower display limit |
| pv_upalarmlim | DBR_GR_XXX and DBR_CTRL_XXX | upper alarm limit |
| pv_loalarmlim | DBR_GR_XXX and DBR_CTRL_XXX | lower alarm limit |
| pv_upwarnlim | DBR_GR_XXX and DBR_CTRL_XXX | upper warning limit |
| pv_lowarnlim | DBR_GR_XXX and DBR_CTRL_XXX | lower warning limit |
| pv_precision | DBR_GR_XXX and DBR_CTRL_XXX | precision |
| pv_nostrings | DBR_GR_XXX and DBR_CTRL_XXX | number of enum states |
| pv_statestrings | DBR_GR_XXX and DBR_CTRL_XXX | list of strings, representation of enum states |
| pv_upctrllim | DBR_CTRL_XXX | upper control limit |
| pv_loxtrllim | DBR_CTRL_XXX | lower ontrol limit |
- numpy support will be enabled at compile time if
$NUMPY_MODULE/core/include/numpy/arrayobject.h is found. At runtime numpy can be enabled for numeric waveform PVs,
Known Problems
- With EPICS base < 3.14.11 on Linux, message "epicsThreadOnceOsd epicsMutexLock failed." randomly appears after scripts finish running and while CA context is destroyed. This sometimes result in exit code 255.
- In version 1.5 and below, long type field is not converted properly, which cause problems under 64 bit system, see http://www.aps.anl.gov/epics/tech-talk/2010/msg02077.php.
- Not compatible with Python 3.0, yet.
Install
The releases can be downloaded from
https://sourceforge.net/downloads/igor2epics/CaChannel/.
Binary:
| OS | Python | EPICS | GCC |
| Windows | 2.6 | 3.14.11 | MinGW 3.4.5 |
| Linux | 2.4 | 3.14.8.2 | GCC 4.1.2 |
Sources:
Since release 1.5, it also includes the source from PythonCA, so just run
make to build. On the other hand, one can always use the following
- Download PythonCA from http://www-acc.kek.jp/EPICS_Gr/products.html and untar it, resulting a directory like
PythonCA-1.20.2.
- Download the source package and extract the python scripts to
PythonCA-1.20.2.
- Edit
EPICS_config_XXX.py to setup your EPICS environment variables.
- Edit
ca.py, at the bottom of the file, replace from _ca_kek import * with from _ca_fnal import *
- Run
python setup.py build.
Notes:
On Microsoft Windows, if statically compiled, then the compiler version must be the same as used to compile Python.
FAQ
Simple use in a one-shot script
Use
ca_util,
import ca_util
ca_util.caput('myPV', 12)
print ca_util.caget('myPV')
Get the string representation of a enum PV (menu choice)
import CaChannel
chan=CaChannel.CaChannel('myPV')
chan.searhw()
print chan.getw(CaChannel.ca.DBR_STRING)
Get a PV's control information, high/low limits, units?
- Use
CaChannel, version 1.8+,
import CaChannel
chan = CaChannel.CaChannel('myPV')
chan.searhw()
print chan.getw(CaChannel.ca.dbf_type_to_CTRL(chan.field_type()))
- Use
epicsPV,
import epicsPV
pv = epicsPV.epicsPV('myPV')
pv.getControl()
for field in dir(pv.callBack):
print field, ':', getattr(pv.callBack, field)
How to move a motor and wait until it finishes moving?
How to connect to thousands of channels quickly?
- Use
CaChannel,
import CaChannel
pvs = {}
for pvName in pvNames:
pv = CaChannel.CaChannel(pvName)
pv.search()
pvs[pvName] = pv
pvs['FOO'].pend_io()
- User
epicsPV,
import epicsPV
pvs = {}
for pvName in pvNames:
pv = epicsPV.epicsPV(pvName, wait=0)
pvs[pvName] = pv
pvs['FOO'].pend_io()
--
XiaoqiangWang - 22 Aug 2008
keywords: Python
CaChannel? PythonCA?