D D EPICS Access from Python Geoff Savage DØ Workshop Thursday June 22, 2000
DD
EPICS Access from Python
Geoff SavageDØ Workshop
Thursday June 22, 2000
DD
6/22/00 DZero Workshop 2
Game Plan
Discuss EPICS channel access (CA) C library High level discussion
Provide examples from CaChannel Python class that wraps CA library Wrap = provide python interface Delve further into CA functionality
DD
6/22/00 DZero Workshop 3
EPICS Channel Access
Role in EPICS architecture Architecture Services Library functionality
DD
6/22/00 DZero Workshop 4
Role in EPICS Architecture
IOC
Host
IOC
Network
Transfer information
between host and IOC
DD
6/22/00 DZero Workshop 5
CA Architecture
Client-server architecture IOC = server Host application = client
Client makes requests to server Channel = client-server
connection Connection between client and PV All channel creation requests made
by name Host and IOC must be on network
DD
6/22/00 DZero Workshop 6
CA Services
Dynamic channel creation Automatic reconnect Read/Write Monitoring
Events Access control Connection
Data type conversions Composite data structures
DD
6/22/00 DZero Workshop 7
Channel Creation
IOCIOCIOCHostWho has channel ‘XXXX’?
(broadcast)
IOCIOCIOCHost
I have channel ‘XXXX’.
IOCIOCIOCHostNegotiate connection.
(point-to-point)
DD
6/22/00 DZero Workshop 8
CA Library
Messages Connection Synchronous Asynchronous Group
DD
6/22/00 DZero Workshop 9
CA Messages
All CA requests are buffered in host Connect, get, put, … Increased efficiency
Buffer is sent when full or upon user request
PV get/put requests can’t be in same message as connection request
DD
6/22/00 DZero Workshop 10
Connection (CA Lib)
“Establish and maintain a virtual circuit between application and PV in a CA server”
Connection must be made before accessing PV
Network transparency (by name) Callback on connection status
change
DD
6/22/00 DZero Workshop 11
Synchronous (CA Lib)
Make requests Flush request buffer into
message Wait until all requests in
message have been completed or timeout occurs
Return control to application
DD
6/22/00 DZero Workshop 12
Asynchronous (CA Lib)
Make requests and specify callback
Flush request buffer into message
Return control to application You are notified of completion
through execution of callback at a later timeSynchronous and Asynchronousrequests can be in the same message
DD
6/22/00 DZero Workshop 13
Group (CA Lib)
“Guarantee that a set of CA requests have completed”
Synchronous (no callback) Create Get/put Block waiting for completion
(timeout) Test for completion Remove uncompleted requests
DD
6/22/00 DZero Workshop 14
EPICS CA Summary
High level discussion Role = transfer information Architecture = client-server Services - Covered in more detail
during CaChannel discussion Library functionality
Buffering for efficiency, Connections, Synchronous, Asynchronous, Group
DD
6/22/00 DZero Workshop 15
CaChannel
Architecture Simple examples Data types Synchronous I/O Asynchronous I/O Monitoring Use with Tkinter
DD
6/22/00 DZero Workshop 16
CaChannel Architecture
EPICS channel access C library
Collection of C functions
Distributed with EPICS
caPython
Python wrapper around the C library
Collection of python functions
CaChannel
Python class
Calls caPython functions
Move from C function to Python class interface.
DD
6/22/00 DZero Workshop 17
<d0olb> setup d0python
<d0olb> python
Python 1.5.2 (#6, May 10 1999, 21:44:32) [C] on osf1V4
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> from CaChannel import *
>>> ch = CaChannel()
>>> ch.searchw('catest')
>>> ch.putw(123.456)
>>> ch.getw()
123.456
Simple Interpreter Example
Each method performsone synchronous request.
connect
write
read
DD
6/22/00 DZero Workshop 18
Simple Script Example
#! /bin/env python
#
from CaChannel import *
def main():
try:
catest = CaChannel()
catest.searchw('catest')
catest.putw(123.456)
print catest.getw()
except CaChannelException, status:
print ca.message(status)
main()
DD
6/22/00 DZero Workshop 19
Data Types … Each PV has a
native type Different data
types can be requested
Requests using native type most efficient
>>> ch.getw()
123.456
>>> ch.getw(ca.DBR_INT)
123
>>> ch.getw(ca.DBR_STRING)
'123'
>>> ch.field_type()
6
>>> ca.dbr_text(ch.field_type())
'DBR_DOUBLE‘
>>>
Not all caPython functions
implemented as methods.
DD
6/22/00 DZero Workshop 20
… Data Types …>>> scan = CaChannel()
>>> scan.searchw('catest.SCAN')
>>> scan.getw()
6
>>> scan.getw(ca.DBR_STRING)
'1 second'
>>> scan.putw(5)
>>> scan.getw(ca.DBR_STRING)
'2 second‘
>>> ca.dbr_text(scan.field_type())
'DBR_ENUM'
>>>
DD
6/22/00 DZero Workshop 21
… Data Types>>> scan.putw('1 second')
Traceback (innermost last):
File "<stdin>", line 1, in ?
File "/d0usr/products/d0python/OSF1/v02_03/lib/CaChannel.py", line 383, in putw
count, pval = self.__setup_put(value, req_type)
File "/d0usr/products/d0python/OSF1/v02_03/lib/CaChannel.py", line 131, in __setup_put
CaChannel.dbr_d[req_type]['convert'](value),
ValueError: invalid literal for int(): 1 second
>>> scan.putw('1 second', ca.DBR_STRING)
>>> scan.getw(ca.DBR_STRING)
'1 second'
DD
6/22/00 DZero Workshop 22
Request Data Types
Request type
Python type Comments
DBR_STRING String
DBR_ENUM Integer Enumerated
DBR_CHAR Integer 8 bits
DBR_INT Integer 16 bits
DBR_LONG Integer 32 bits
DBR_FLOAT Float
DBR_DOUBLE
Float
Array of DBR List of type
DD
6/22/00 DZero Workshop 23
Synchronous I/O …
pend_io = flush request buffer and wait for CA requests to complete
pend_io affects requests from all CaChannel instances
Only one request buffer for all CaChannel instances
DD
6/22/00 DZero Workshop 24
… Synchronous I/O … Multiple requests
can be issued before each pend_io
All connections must be made before get or put
On a get a value is not valid until pend_io has returned with no errors
>>>
>>> ch.search('catest')
>>> ch.pend_io()
>>>
>>> ch.array_put(123.456)
>>> ch.pend_io()
>>>
>>> ch.array_get()
>>> ch.pend_io()
>>> ch.getValue()
123.456
DD
6/22/00 DZero Workshop 25
… Synchronous I/O catest.array_get()
cawave.array_get()
scan.array_get()
ca.pend_io(1.0)
print catest.getValue()
print cawave.getValue()
print scan.getValue()
except CaChannelException,status:
print ca.message(status)
try: catest = CaChannel() cawave = CaChannel() scan = CaChannel()
catest.search('catest') cawave.search('cawave') scan.search('catest.SCAN') catest.pend_io()
t=(0,1,2,3,4,5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19) catest.array_put(123.456) cawave.array_put(t) scan.array_put("1 second“, ca.DBR_STRING) cawave.pend_io()
DD
6/22/00 DZero Workshop 26
Asynchronous I/O … No waiting for CA requests to
complete A user specified callback function
is executed when the request has completed
ca_pend_event = flush the request buffer and wait for timeout seconds or until all CA background activity is processed
ca_flush_io = flush the request buffer
DD
6/22/00 DZero Workshop 27
… Asynchronous I/O …
Asynchronous callbacks do not preempt the main thread
Instead ca_pend_io, ca_pend_event, or ca_poll must be called at least every 15 seconds to allow background activity to process
ca_poll = ca_pend_event with short (0.001 second) timeout
DD
6/22/00 DZero Workshop 28
… Asynchronous I/O …try: chan = CaChannel() chan.search_and_connect('catest', connectCb) chan.flush_io() for i in range(20): chan.pend_event() chan.array_put_callback(3.3, None, None, putCb) chan.flush_io() for i in range(20): chan.pend_event() chan.array_get_callback(None, None, getCb1) chan.flush_io() for i in range(20): chan.pend_event() chan.array_get_callback(ca.dbf_type_to_DBR_STS( chan.field_type()), None,getCb2) chan.flush_io() for i in range(20): chan.pend_event()except CaChannelException, status: print ca.message(status)
DD
6/22/00 DZero Workshop 29
Compound Data Types
Only supported in asynchronous mode
Extra information with the value Status – alarm values Time – status + timestamp Graphics – status + alarm limits +
display limits Control – graphics + control limits
Routines are provided to convert DBR type to compound type
DD
6/22/00 DZero Workshop 30
Connection Callback
epics_args = 2 element tuple t[1] = channel identifier, used to get
the channels name t[2] = connection state
CA_OP_CONN_UP CA_OP_CONN_DOWN
user_args = tuple containing all python objects specified after the callback in the method
def connectCb(epics_args, user_args):
print epics_args
print user_args
DD
6/22/00 DZero Workshop 31
Put Callback
chid = channel identifier type = request type (DBR_XXXX) count = element count status = CA status code from server
def putCb(epics_args, user_args):
print ca.name(epics_args['chid'])
print ca.dbr_text(epics_args['type'])
print epics_args['count']
print ca.message(epics_args['status'])
print user_args
DD
6/22/00 DZero Workshop 32
Get Callback
value(s) = data returned by the server. Multiple data elements are returned in a tuple.
def getCb1(epics_args, user_args):
print "pvName = ", ca.name(epics_args['chid'])
print "type = ", ca.dbr_text(epics_args['type'])
print "count = ", epics_args['count']
print "status = ", ca.message(epics_args['status'])
print "user args = ", user_args
print "value(s) = ", epics_args['pv_value']
DD
6/22/00 DZero Workshop 33
Get Callback with Status
pv_severity = alarm severity pv_status = alarm status
def getCb2(epics_args, user_args):
print "pvName = ", ca.name(epics_args['chid'])
print "type = ", ca.dbr_text(epics_args['type'])
print "count = ", epics_args['count']
print "status = ", ca.message(epics_args['status'])
print "user args = ", user_args
print "value(s) = ", epics_args['pv_value']
print ca.alarmSeverityString(epics_args['pv_severity'])
print ca.alarmStatusString(epics_args['pv_status'])
DD
6/22/00 DZero Workshop 34
Asynchronous I/O Usage Tip
To obtain the CaChannel instance to which the callback corresponds use the name
Create a dictionary whose keys are names and contents are the CaChannel instances
DD
6/22/00 DZero Workshop 35
… Asynchronous I/O
Callbacks implemented for Time Graphics Control
DD
6/22/00 DZero Workshop 36
Monitoring …
Access rights Connection For technical reasons neither of
these are in the CaChannel interface
They can be implemented as a C function
DD
6/22/00 DZero Workshop 37
… Monitoring …
Asynchronous I/O originated at the server
Client requests notification from the server when the channel’s value changes by
more than the value dead band or alarm dead band
the channel’s alarm state changes Callbacks match those described
under asynchronous I/O
DD
6/22/00 DZero Workshop 38
… Monitoring …def eventCb(epics_args, user_args): print ca.message(epics_args['status']) print "new value = ", epics_args['pv_value'] print ca.alarmSeverityString(epics_args['pv_severity']) print ca.alarmStatusString(epics_args['pv_status'])
try: chan = CaChannel() chan.searchw('catest') chan.add_masked_array_event( ca.dbf_type_to_DBR_STS(chan.field_type()), None, ca.DBE_VALUE | ca.DBE_ALARM, eventCb)except CaChannelException, status: print ca.message(status)
DD
6/22/00 DZero Workshop 39
… Monitoring
Monitor mask Notification condition
ca.DBE_VALUEwhen the channel’s value changes by more than MDEL
ca.DBE_LOGwhen the channel’s value changes by more than MDEL
ca.DBE_ALARMwhen the channel’s alarm state changes
DD
6/22/00 DZero Workshop 40
Tkinter and CaChannel
EPICS CA is not thread safe All CaChannel activity must be
performed in the main thread Use the Tkinter after method to
interrupt the mainloop at regular intervals to allow CA backgroud activity to execute
Execute CaChannel calls from the update function called by after
DD
6/22/00 DZero Workshop 41
CaChannel Summary
Architecture = Python wrapper around C library
Data types = native types most efficient, extended types
Synchronous I/O = interpreter or scripts, easy to use
Asynchronous I/O and monitoring Callbacks Background activity
DD
6/22/00 DZero Workshop 42
Documentation
In the doc directory of the d0python package
CaChannel Guide caPython Guide Working on getting up to date
Time, graphic, and control return types
Timeouts (pend_io, pend_event)
DD
6/22/00 DZero Workshop 43
CaChannel Internals
Constructor searchw() – connect putw() - write getw() - read
DD
6/22/00 DZero Workshop 44
CaChannel Constructor def __init__(self):
# Un-initialized channel id structure
self.__chid = ca.new_chid()
# Monitor event id
self.__evid = None
self.__timeout = None # override class timeout
DD
6/22/00 DZero Workshop 45
searchw() def searchw(self, pvName):
status = ca.search(pvName, self.__chid)
if (ca.ECA_NORMAL != status):
raise CaChannelException, status
if self.__timeout is None:
timeout = CaChannel.ca_timeout
else:
timeout = self.__timeout
status = ca.pend_io(timeout)
if (ca.ECA_NORMAL != status):
raise CaChannelException, status
DD
6/22/00 DZero Workshop 46
putw() def putw(self, value, req_type=None): if(None == req_type): req_type = self.field_type() count, pval = self.__setup_put(value, req_type) status = ca.array_put(req_type, count, self.__chid, pval) if (ca.ECA_NORMAL != status): ca.ptrfree(pval) raise CaChannelException, status if self.__timeout is None: timeout = CaChannel.ca_timeout else: timeout = self.__timeout status = ca.pend_io(timeout) if (ca.ECA_NORMAL != status): ca.ptrfree(pval) raise CaChannelException, status ca.ptrfree(pval)
DD
6/22/00 DZero Workshop 47
getw() …def getw(self, req_type=None): if(None == req_type): req_type = ca.field_type(self.__chid) count, pval = self.__setup_get(req_type) status = ca.array_get(req_type, count, self.__chid, pval) if (ca.ECA_NORMAL != status): ca.ptrfree(pval) raise CaChannelException, status if self.__timeout is None: timeout = CaChannel.ca_timeout else: timeout = self.__timeout status = ca.pend_io(timeout)
DD
6/22/00 DZero Workshop 48
… getw() if (ca.ECA_NORMAL != status):
ca.ptrfree(pval)
raise CaChannelException, status
if(1 == count):
value = ca.ptrvalue(pval)
else:
value = self.__build_list(pval, count)
ca.ptrfree(pval)
return value
DD
6/22/00 DZero Workshop 49
Simple C Example …/*caExample.c*/#include <stddef.h>#include <stdlib.h>#include <stdio.h>#include <string.h>
#include <cadef.h>
main(int argc,char **argv){ struct dbr_ctrl_enum data; int ndx; int status; chid mychid;
DD
6/22/00 DZero Workshop 50
… Simple C Example SEVCHK(ca_task_initialize(),"ca_task_initialize");
SEVCHK(ca_search(argv[1],&mychid),"ca_search failure");
SEVCHK(ca_pend_io(5.0),"ca_pend_io failure");
SEVCHK(ca_get(DBR_CTRL_ENUM,mychid,(void *)&data),"ca_get failure");
SEVCHK(ca_get(DBR_CTRL_ENUM,mychid,(void *)&data),"ca_get failure");
SEVCHK(ca_pend_io(5.0),"ca_pend_io failure");
printf("status: %d, severity: %d, count: %d\n",
data.status, data.severity, data.no_str);
for (ndx = 0; ndx < data.no_str; ndx += 1)
printf(" %2d %s\n", ndx, data.strs[ndx]);
return(0);
}