The catools library provides the following functions for access to EPICS
“process variables” over channel access:
-
caget(pvs, …)
-
Returns a single snapshot of the current value of each PV.
-
caput(pvs, values, …)
-
Writes values to one or more PVs.
-
camonitor(pvs, callback, …)
-
Receive notification each time any of the listed PVs changes.
-
connect(pvs, …)
-
Can be used to establish a connection to a list of PVs before calling the
other routines. This routine is optional.
To use these functions a certain amount of setup work is required. The
following code illustrates a simple application which reads a value from one
PV, writes to another PV, and monitors a third until terminated with
control-C.
# Library version specification required for dls libraries
from pkg_resources import require
require('numpy==1.1.0')
require('cothread')
import cothread
from cothread.catools import *
# Using caput: write 1234 into PV1. Raises exception on failure
caput('PV1', 1234)
# Print out the value reported by PV2.
print caget('PV2')
# Monitor PV3, printing out each update as it is received.
def callback(value):
print 'callback', value
camonitor('PV3', callback)
# Now run the camonitor process until interrupted by Ctrl-C.
cothread.WaitForQuit()
The following details are general to all cothread applications.
-
The routine pkg_resources.require() must be used to specify a particular
version of the library to use. The catools submodule also depends on
numpy and this should also be specified, thus the following lines are
required at the start of any catools application:
from pkg_resources import require
require('numpy==1.1.0')
require('cothread==1.15')
or if the most recent version is ok then the version number can be omitted as
in the example.
-
Any EPICS_CA_ environment variables should be set at this point, before
importing catools (see below).
-
Of course, the libraries must be imported. The catools library is a
sub-module of the cothread library, and can be imported separately.
-
If camonitor is being used then the program should suspend in an event
loop of some sort. The routine cothread.WaitForQuit() can be used, as
otherwise the camonitor activity has no opportunity to run before the
program exits!
The following notes may be helpful when using this library.
Environment Variables
A number of environment variables affect the operation of channel access.
These can be set using the os.environ dictionary — but note that these
need to be set before loading the catools module. The following are
documented in the
EPICS channel access developers manual.
-
EPICS_CA_MAX_ARRAY_BYTES
-
Configures the maximum number of bytes that can be
transferred in a single channel access message.
-
EPICS_CA_ADDR_LIST
-
A space separated list of channel access server
addresses.
-
EPICS_CA_AUTO_ADDR_LIST
-
If set to “NO” the automatic scanning of
networks is disabled.
-
EPICS_CA_CONN_TMO
-
Connection timeout, 30 seconds by default.
-
EPICS_CA_BEACON_PERIOD
-
Beacon polling period, 15 seconds by default.
-
EPICS_CA_SERVER_PORT, EPICS_CA_REPEATER_PORT
-
Set these to configure the
ports used to connect to channel access. By default ports 5064 and 5065 are
used (respectively); for testing we use 6064 and 6065.
import os
os.environ['EPICS_CA_MAX_ARRAY_BYTES'] = '1000000'
# Note: the first import of catools must come after the environ is set up.
from cothread.catools import *
The catools API consists of the three functions caput, caget and
camonitor together with an auxilliary connect function. The functions
caget and camonitor return or deliver “augmented” values which are
documented in more detail in the section Working with Values
below.
Common Notes
All four functions take an argument pvs which can specify the name of a
single PV or can be a list of PVs. In all cases the returned result has the
same “shape” as the pvs argument, in other words, if pvs is a single
string then a single value (error code, value, or subscription) is returned,
and if pvs is a list then a list of exactly the same length is returned.
In general there are advantages to calling caput, caget or connect on a
list of PVs, as in this case the channel access delays will occur in parallel.
Several arguments are common amongst several: throw determines how errors
are handled, timeout determines timeouts, and finally datatype, format
and count determine data formats and are documented in Augmented Values.
-
timeout
-
The timeout argument specified how long caput or caget will wait for the
entire operation to complete. This timeout is in seconds, and can be one of
several formats: a timeout interval in seconds, an absolute deadline (in
time() format) as a single element tuple, or None to specify that no timeout
will occur. Note that a timeout of 0 will timeout immediately if any waiting
is required.
If a timeout occurs then a Timedout exception will be raised unless
throw=False has been set.
-
throw
-
This parameter determines the behaviour of caget and caput when an error
occurs. If throw=True (the default) is set then an exception is raised,
otherwise if False is specified an error code value is returned for each
failing PV.
Functions
-
caput(pvs, values, repeat_value=False, datatype=None, wait=False, timeout=5, throw=True)
-
Writes values to one or more PVs. If pvs is a single string then values
is treated as a single value to be written to the named process variable,
otherwise pvs must be iterable, and unless repeat_value=True is set,
values must also be an iterable of the same length in which case
values[i] is written to pvs[i]. Otherwise, if a single value is given or
if repeat_value=True is specified, values is written to all PVs.
The arguments control the behavour of caput as follows:
-
repeat_value
-
When writing a value to a list of PVs ensures that values is treated as a
single value to be written to each PV.
-
datatype
-
See documentation for Augmented Values below. Used to force
transmitted data to the requested format, or select special alarm
acknowledgement handling. Note that only standard Python type conversion will
be done, in particular conversion to and from strings is not automatic!
-
wait
-
If wait=True is specified then channel access put with callback is
invoked, and the caput operation will wait until the server
acknowledges successful completion before returning.
-
timeout, throw
-
Documented in Common Notes above.
The return value from caput is either a list or a single value, depending on
the shape of pvs. For each PV a ca_nothing success code is
returned on success, otherwise either an exception is raised or an appropriate
error code is returned for each failing PV if throw=True is set. The return
code can be tested for boolean success, so for example it
is possible to write:
if not caput(pv, value, throw=False):
# process caput error
If all the PVs listed in pvs have already been connected, through a
successful call to any catools method, then the library guarantees that the
puts for each PV will occur strictly in sequence. For any PVs which need a
connection to be established the order of execution of puts is completely
undefined.
-
caget(pvs, timeout=5, datatype=None, format=FORMAT_RAW, count=0, throw=True)
-
Retrieves a value from one or more PVs. If pvs is a single string then
a single value is returned, otherwise a list of values is returned. Each
value returned is an Augmented Value, see
below for details.
If .ok is False then the .errorcode field is set to the appropriate
ECA_ error code and str(value) will return an error message.
The various arguments control the behaviour of caget as follows:
-
datatype, format, count
-
See documentation for Augmented Values below.
-
timeout, throw
-
Documented in Common Notes above. If a value cannot be retrieved
and throw=False is set then for each failing PV an empty value with
.ok==False is returned.
The format of values returned depends on the number of values requested
for each PV. If only one value is requested then the value is returned
as a scalar, otherwise as a numpy array.
-
camonitor(pvs, callback, events=DBE_VALUE, datatype=None, format=FORMAT_RAW, count=0, all_updates=False, notify_disconnect=False)
-
Creates a subscription to one or more PVs, returning a subscription
object for each PV. If a single PV is given then a single subscription
object is returned, otherwise a list of subscriptions is returned.
Subscriptions will remain active until the .close() method is called on
the returned subscription object.
The precise way in which the callback routine is called on updates
depends on whether pvs is a single name or a list of names. If it is
single name then it is called as
for each update. If pvs is a list of names then each update is
reported as
where index is the position in the original array of PVs of the PV
generating this update. The values passed to callback are Augmented Values.
The parameters modify the behaviour as follows:
-
events
-
This identifies the type of update which will be notified. A
bit-wise or of any the following are possible:
DBE_VALUE |
Notify normal value changes |
DBE_LOG |
Notify archive value changes |
DBE_ALARM |
Notify alarm state changes |
DBE_PROPERTY |
Notify property changes (on 3.14.11 and later servers) |
-
datatype, format, count
-
See documentation for Augmented Values below.
-
all_updates
-
If this is True then every update received from channel access will
be delivered to the callback, otherwise multiple updates received
between callback queue dispatches will be merged into the most recent
value.
If updates are being merged then the value returned will be
augmented with a field .update_count recording how many updates
occurred on this value.
-
notify_disconnect
-
If this is True then IOC disconnect events and channel access
error reports will be reported by calling the callback with a ca_nothing
error with .ok False. By default False these notifications are
suppressed so that only valid values will be passed to the callback routine.
-
connect(pvs, cainfo=False, wait=True, timeout=5, throw=True)
-
Establishes a connection to one or more PVs, optionally returning detailed
information about the connection. A single PV or a list of PVs
can be given. This does not normally need to be called, as the ca…()
routines will establish their own connections as required, but after a
successful connection we can guarantee that caput(…, wait=False) will
complete immediately without suspension and that caput(pvs, values) will
execute in order if all PVs in pvs have been successfully connected.
It is possible to test whether a channel has successfully connected by calling
connect(pv, wait=True, timeout=0, throw=False) and testing the result. More
detailed information about a PV can be obtained by calling connect(pv,
cainfo=True).
The various arguments control the behaviour of connect as follows:
-
wait
-
Normally the connect routine will not return until the requested
connection is established. If wait=False is set then a connection
request will be queued and connect will unconditionally succeed.
-
cainfo
-
By default a simple ca_nothing value is returned, but if cainfo=True is
set then a ca_info structure is returned recording the following information
about the connection:
.ok |
True iff the channel was successfully connected |
.name |
Name of PV |
.state |
State of channel as an integer. Look up
.state_strings[.state] for textual description. |
.host |
Host name and port of server providing this PV |
.read |
True iff read access to this PV |
.write |
True iff write access to this PV |
.count |
Data count of this channel |
.datatype |
Underlying channel datatype as DBR_ value. Look up
.datatype_strings[.datatype] for description. |
The str() representation of this structure can be printed to produce output
similar to that produced by the cainfo command line tool.
-
timeout, throw
-
Documented in Common Notes above. If a value cannot be retrieved
and throw=False is set then for each failing PV an empty value with
.ok==False is returned.
There are two types of values returned by catools functions: “augmented
values” and “error codes”. The caput function only returns an error code
value (which may indicate success), while caget and camonitor will
normally return (or deliver) augmented values, but will return (or deliver) an
error code on failure.
The following fields are common to both types of value. This means that is is
always safe to test value.ok for a value returned by caget or caput or
delivered by camonitor.
-
.ok
-
Set to True if the data is good, False if there was an error. For
augmented values .ok is always set to True.
-
.name
-
Name of the pv.
Augmented Values
Augmented values are normally Python or numpy values with extra fields: the
.ok and .name fields are already mentioned above, and further extra fields
will be present depending on format requested for the data. As pointed
out above, .ok is always True.
Four different types of augmented value are returned: strings, integers,
floating point numbers or arrays, depending on the length of the data
requested — an array is only used when the data length is >1.
In almost all circumstances an augmented value will behave exactly like a
normal value, but there are a few cases where differences in behaviour are
observed (these are mostly bugs). If this occurs the augumentation can be
stripped from an augmented value value by writing +value — this returns
the underlying value.
The type of augmented values is determined both by parameters passed to
caget and camonitor and by the underlying datatype. Both of these
functions share parameters datatype, format and count which can be used
to control the type of the data returned:
-
datatype
-
For caget and camonitor this controls the format of the data that will be
requested, while for caput the data will be coerced into the requested
format. datatype can be any of the following:
-
None (the default). In this case the “native” datatype provided
by the channel will be returned.
-
A DBR_ value, one of the following:
-
DBR_STRING
-
Strings are up to 39 characters long — this is a constraint
set by EPICS. For longer strings the data type DBR_CHAR_STR can be used if
the IOC can deliver strings as arrays of char.
-
DBR_CHAR, DBR_SHORT, DBR_LONG
-
These are all signed integer types,
with 8, 16 and 32 bit values respectively. The parameter as_string can be
set to convert arrays of DBR_CHAR to strings.
-
DBR_FLOAT, DBR_DOUBLE
-
Floating point values with 32 and 64 bit values,
respectively.
-
DBR_ENUM
-
A 16 bit unsigned integer value representing an index into an
array of strings. The associated strings can be retrieved by using
format=FORMAT_CTRL and inspecting the .enums field.
-
A python type compatible with any of the above values, such as
int, float or str. These correspond to DBR_LONG, DBR_DOUBLE and
DBR_STRING respectively.
-
Any numpy dtype compatible with any of the above values.
-
The special value DBR_CHAR_STR. This is used to request a char
array which is then converted to a Python string on receipt. It
is not sensible to specify count with this option.
-
For caget and camonitor two further special values are supported. In
both of these cases format is ignored:
-
DBR_STSACK_STRING
-
Returns the current value as a string together with
extra fields .status, .severity, .ackt, .acks.
-
DBR_CLASS_NAME
-
Returns the name of the “enclosing interface”, typically
the record type, and typically the same as the .RTYP field.
For caput also two further values are supported:
-
DBR_PUT_ACKT, DBR_PUT_ACKS
-
These are used for global alarm
acknowledgement, where _ACKT configures whether alarms need to be
acknowleged and _ACKS acknowledges alarms of a particular severity.
-
format
-
This controls how much auxilliary information will be returned with
the retrieved data, and can be one of the following:
-
FORMAT_RAW (default)
-
The data is returned unaugmented except for the .name and .ok fields.
-
FORMAT_TIME
-
The data is augmented by timestamp fields .timestamp and .raw_stamp
together with .alarm, .status and .severity fields. The value in
.timestamp is in time() format (seconds in Unix UTC epoch).
-
FORMAT_CTRL
-
The data is augmented by channel access “control” fields. The set of fields
returned depends on the underlying datatype as follows:
-
DBR_SHORT, DBR_CHAR, DBR_LONG
-
The alarm .status and .severity fields together with .units
and limit fields:
.upper_disp_limit, .lower_disp_limit,
.upper_alarm_limit, .lower_alarm_limit,
.upper_warning_limit, .lower_warning_limit,
.upper_ctrl_limit, .lower_ctrl_limit.
The meaning of these fields is determined by channel access.
-
DBR_FLOAT, DBR_DOUBLE
-
As above together with a .precision field.
-
DBR_ENUM
-
Alarm .status and .severity fields together with .enums, a
list of possible enumeration strings. The underlying value for an enumeration
will be an index into .enums.
-
DBR_STRING
-
_CTRL format is not supported for this field type, and
FORMAT_TIME data is returned instead.
-
count
-
If specified this can be used to limit the number of waveform points
retrieved from the server, otherwise the entire waveform is always returned.
Fields
Summary of all available fields in augmented values.
The following fields are present in all augmented values.
-
.name
-
Name of record, always present.
-
.ok
-
Set to True, always present.
The following fields are present in all values if FORMAT_TIME is specified.
-
.raw_stamp
-
Record timestamp in raw format as provided by EPICS (but in the
local Unix epoch, not the EPICS epoch). Is a tuple of the form (secs, nsec)
with integer seconds and nanosecond values, provided in case full ns timestamp
precision is required.
-
.timestamp
-
Timestamp in seconds in format compatible with time.time()
rounded to the nearest microsecond: for nanosecond precision use .raw_stamp
instead.
To compute the timestamp in datetime format, which can be more suitable for
display applications, it can be added to the value by computing
import datetime
value.datetime = datetime.datetime.fromtimestamp(value.timestamp)
The following fields are present in all values if FORMAT_TIME or
FORMAT_CTRL is specified.
-
.severity
-
EPICS alarm severity, normally one of the values listed below.
0 |
⇒ No alarm |
1 |
⇒ Alarm condition, minor severity |
2 |
⇒ Alarm condition, major severity. |
3 |
⇒ Invalid value. |
-
.status
-
Reason code associated with alarm severity, always present with
.severity code.
The following fields are present in numeric values if FORMAT_CTRL is
specified. Values of type DBR_ENUM or DBR_STRING are not numeric.
-
.units
-
Units for display.
-
.upper_disp_limit, .lower_disp_limit
-
Suggested display limits for
numerical values.
-
.upper_alarm_limit, .lower_alarm_limit, .upper_warning_limit, .lower_warning_limit, .upper_ctrl_limit, .lower_ctrl_limit
-
Various EPICS numeric limits.
-
.precision
-
For floating point values only, the specified display precision
(or 0 if not specified). Present if value is a floating point type.
The following field is only present in DBR_ENUM values.
-
.enums
-
For enumeration values only, an array of enumeration strings
indexable by enumeration value.
Error Code Values
Error code values are used to indicate a success return from caput (in
which case .ok is True), to indicated disconnection using camonitor, and
to indicate any other failure, either as a return value or raised as an
exception.
All error code values have type ca_nothing and provide the following fields:
-
.ok
-
Set to True if the data is good, False if there was an error. Testing an
error code value for boolean will return the value of .ok, so for example it
is possible to write:
if not caput(pv, value, throw=False):
process caput error
-
.name
-
Name of the PV which generated this error..
-
.errorcode
-
Channel access error code. The following values are worth noting:
-
ECA_SUCCESS
-
Success error code. In this case .ok is True. Returned
by successful caput and connect calls.
-
ECA_DISCONN
-
Channel disconnected. This is used by canotify to report
channel disconnect events.
-
ECA_TIMEOUT
-
Channel timed out. Reported if user specified timeout
ocurred before completion and if throw=False specified.