274 lines
10 KiB
Python
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
|
|
|