Files
dev/script/_Lib/epics/epicsPV.py
2018-04-17 12:05:48 +02:00

274 lines
10 KiB
Python

"""
This module defines the epicsPV class, which adds additional features to
Geoff Savage's CaChannel class.
Author: Mark Rivers
Created: Sept. 16, 2002.
Modifications:
"""
import CaChannel
class epicsPV(CaChannel.CaChannel):
"""
This class subclasses Geoff Savage's CaChannel class to add the following
features:
- If a PV name is given then the class constructor will do a searchw()
by default.
- setMonitor() sets a generic callback routine for value change events.
Subsequent getw(), getValue() or array_get() calls will return the
value from the most recent callback, and hence do not result in any
network activity or latency. This can greatly improve performance.
- checkMonitor() returns a flag to indicate if a callback has occured
since the last call to checkMonitor(), getw(), getValue() or
array_get(). It can be used to increase efficiency in polling
applications.
- getControl() reads the "control" and other information from an
EPICS PV without having to use callbacks.
In addition to the PV value, this will return the graphic, control and
alarm limits, etc.
- putWait() calls array_put_callback() and waits for the callback to
occur before it returns. This allows programs to use array_put_callback()
synchronously and without user-written callbacks.
Created: Mark Rivers, Sept. 16, 2002.
Modifications:
"""
def __init__(self, pvName=None, wait=1):
"""
Keywords:
pvName:
An optional name of an EPICS Process Variable.
wait:
If wait==1 and pvName != None then this constructor will do a
CaChannel.searchw() on the PV. If wait==0 and pvName != None then
this constructor will do a CaChannel.search() on the PV, and the user
must subsequently do a pend_io() on this or another epicsPV or CaChannel
object.
Procedure:
Invokes CaChannel.__init__() and then searchw() or search() as explained
above
"""
# Invoke the base class initialization
self.callBack = callBack()
CaChannel.CaChannel.__init__(self)
if (pvName != None):
if (wait):
self.searchw(pvName)
else:
self.search(pvName)
def setMonitor(self):
"""
Sets a generic callback routine for value change events.
Subsequent getw(), getValue() or array_get() calls will return the
value from the most recent callback, do not result in any network
latency. This can greatly improve efficiency.
"""
self.add_masked_array_event(None, None, CaChannel.ca.DBE_VALUE,
getCallback, self.callBack)
self.callBack.monitorState = 1
def clearMonitor(self):
"""
Cancels the effect of a previous call to setMonitor().
Calls CaChannel.clear_event().
Subsequent getw(), getValue() or array_get() calls will no longer
return the value from the most recent callback, but will actually result
in channel access calls.
"""
self.clear_event()
self.callBack.monitorState = 0
def checkMonitor(self):
"""
Returns 1 to indicate if a value callback has occured
since the last call to checkMonitor(), getw(), getValue() or
array_get(), indicating that a new value is available. Returns 0 if
no such callback has occurred.
It can be used to increase efficiency in polling applications.
"""
# This should be self.poll(), but that is generating errors
self.pend_event(.0001)
m = self.callBack.newMonitor
self.callBack.newMonitor = 0
return m
def getControl(self, req_type=None, count=None, wait=1, poll=.01):
"""
Provides a method to read the "control" and other information from an
EPICS PV without having to use callbacks.
It calls CaChannel.array_get_callback() with a database request type of
CaChannel.ca.dbf_type_to_DBR_CTRL(req_type).
In addition to the PV value, this will return the graphic, control and
alarm limits, etc.
Example:
>>> pv = epicsPV('13IDC:m1')
>>> pv.getControl()
>>> for field in dir(pv.callBack):
>>> print field, ':', getattr(pv.callBack, field)
chid : _bfffec34_chid_p
count : 1
monitorState : 0
newMonitor : 1
putComplete : 0
pv_loalarmlim : 0.0
pv_loctrllim : -22.0
pv_lodislim : -22.0
pv_lowarnlim : 0.0
pv_precision : 4
pv_riscpad0 : 256
pv_severity : 0
pv_status : 0
pv_units : mm
pv_upalarmlim : 0.0
pv_upctrllim : 28.0
pv_updislim : 28.0
pv_upwarnlim : 0.0
pv_value : -15.0
status : 1
type : 34
Note the fields such as pv_plocrtllim, the lower control limit, and
pv_precision, the display precision.
Keywords:
wait:
If this keyword is 1 (the default) then this routine waits for
the callback before returning. If this keyword is 0 then it is
the user's responsibility to wait or check for the callback
by calling checkMonitor().
poll:
The timeout for pend_event() calls, waiting for the callback
to occur. Shorter times reduce the latency at the price of CPU
cycles.
"""
if (req_type == None): req_type = self.field_type()
if (wait != 0): self.callBack.newMonitor = 0
self.array_get_callback(CaChannel.ca.dbf_type_to_DBR_CTRL(req_type),
count, getCallback, self.callBack)
if (wait != 0):
while(self.callBack.newMonitor == 0):
self.pend_event(poll)
def array_get(self, req_type=None, count=None):
"""
If setMonitor() has not been called then this function simply calls
CaChannel.array_get(). If setMonitor has been called then it calls
CaChannel.pend_event() with a very short timeout, and then returns the
PV value from the last callback.
"""
if (self.callBack.monitorState != 0):
# This should be self.poll(), but that is generating errors
self.pend_event(.0001)
if (self.callBack.monitorState == 2):
self.callBack.newMonitor = 0
return self.callBack.pv_value
else:
return CaChannel.CaChannel.array_get(self, req_type, count)
def getw(self, req_type=None, count=None):
"""
If setMonitor() has not been called then this function simply calls
CaChannel.getw(). If setMonitor has been called then it calls
CaChannel.pend_event() with a very short timeout, and then returns the
PV value from the last callback.
"""
if (self.callBack.monitorState != 0):
# This should be self.poll(), but that is generating errors
self.pend_event(.0001)
if (self.callBack.monitorState == 2):
self.callBack.newMonitor = 0
if (count == None):
return self.callBack.pv_value
else:
return self.callBack.pv_value[0:count]
else:
return CaChannel.CaChannel.getw(self, req_type, count)
def getValue(self):
"""
If setMonitor() has not been called then this function simply calls
CaChannel.getValue(). If setMonitor has been called then it calls
CaChannel.pend_event() with a very short timeout, and then returns the
PV value from the last callback.
"""
if (self.callBack.monitorState != 0):
# This should be self.poll(), but that is generating errors
self.pend_event(.0001)
if (self.callBack.monitorState == 2):
self.callBack.newMonitor = 0
return self.callBack.pv_value
else:
return CaChannel.CaChannel.getValue(self)
def putWait(self, value, req_type=None, count=None, poll=.01):
"""
Calls CaChannel.array_put_callback() and waits for the callback to
occur before it returns. This allows programs to use array_put_callback()
without having to handle asynchronous callbacks.
Keywords:
req_type:
See CaChannel.array_put_callback()
count:
See CaChannel.array_put_callback()
poll:
The timeout for pend_event() calls, waiting for the callback
to occur. Shorter times reduce the latency at the price of CPU
cycles.
"""
self.callBack.putComplete=0
self.array_put_callback(value, req_type, count, putCallBack, self.callBack)
while(self.callBack.putComplete == 0):
self.pend_event(poll)
class callBack:
"""
This class is used by the epicsPV class to handle callbacks. It is required
to avoid circular references to the epicsPV object itself when dealing with
callbacks, in order to allow the CaChannel destructor to be called.
Users will only be interested in the fields that are copied to this class in
the callback resulting from a call to epicsPV.getControl().
"""
def __init__(self):
self.newMonitor = 0
self.putComplete = 0
self.monitorState = 0
# monitorState:
# 0=not monitored
# 1=monitor requested, but no callback yet
# 2=monitor requested, callback has arrived
def putCallBack(epicsArgs, userArgs):
"""
This is the generic callback function used by the epicsPV.putWait() method.
It simply sets the callBack.putComplete flag to 1.
"""
userArgs[0].putComplete=1
def getCallback(epicsArgs, userArgs):
"""
This is the generic callback function enabled by the epicsPV.setMonitor() method.
It sets the callBack.monitorState flag to 2, indicating that a monitor has
been received. It copies all of the attributes in the epicsArgs dictionary
to the callBack attribute of the epicsPV object.
"""
for key in epicsArgs.keys():
setattr(userArgs[0], key, epicsArgs[key])
if (userArgs[0].monitorState == 1): userArgs[0].monitorState = 2
userArgs[0].newMonitor = 1