ca: Low-level Channel Access module¶
The ca
module provides a low-level wrapping of the EPICS Channel Access
(CA) library, using ctypes. Most users of the epics module will not need to
be concerned with most of the details here, and will instead use the simple
procedural interface (epics.caget()
, epics.caput()
and so on), or
use the epics.PV
class to create and use epics PV objects.
General description, difference with C library¶
The ca
module provides a fairly complete mapping of the C interface to
the CA library while also providing a pleasant Python experience. It is
expected that anyone using this module is somewhat familiar with Channel
Access and knows where to consult the Channel Access Reference Documentation. Here, we focus on the
differences with the C interface, and assume a general understanding of what
the functions are meant to do.
Name Mangling¶
As a general rule, a CA function named ca_XXX in the C library will have the equivalent function called XXX in the ca module. This is because the intention is that one will import the ca module with
>>> from epics import ca
so that the Python function ca.XXX()
will corresponds to the C
function ca_XXX. That is, the CA library called its functions ca_XXX
because C does not have namespaces. Python does have namespaces, and so
they are used.
Similar name un-mangling also happens with the DBR prefixes for constants, held here in the dbr module. Thus, the C constant DBR_STRING becomes dbr.STRING in Python.
Supressing Messages from libCA¶
Added in version 3.5.7.
When using pyepics or any CA client, messages from libCA are sometimes seen to be written to a Terminal screen. These might be messages about like:
failed to start executable - "caRepeater"
or Virtual Circuit Disconnect messages if an Epics IOC goes down or offline. These messages are mostly informational and harmless. But they are also fairly annoying.
Starting with version 3.5.7, these messages are disabled by default. You can re-enable these by setting:
from epics import ca
ca.WITH_CA_MESSAGES = True
before initializing CA (say, creating any PVs or Channels).
You can also run the functions disable_ca_messages()
or
enable_ca_messages()
at runtime. However, due to limitaions in Python
ctypes modules, re-enabling messages once disabled does not fully work: The
messages from CA will often including multiple values that should be formatted
as with C’s printf function. But, when re-enabled, only the first formatting
string will be received through the ctypes interface.
Setting ca.WITH_CA_MESSAGES before CA is initialized will correctly receive the full messages.
Other Changes and Omissions¶
Several function in the C version of the CA library are not implemented in the Python module. Most of these unimplemented functions are currently seen as unnecessary for Python, though some of these could be added without much trouble if needed. See Omissions for further details.
In addition, while the CA library supports several DBR types in C, not all of these are supported in Python. Only native types and their DBR_TIME and DBR_CTRL variants are supported here. The DBR_STS and DBR_GR variants are not, as they are subsets of the DBR_CTRL type, and space optimization is not something you’ll be striving for with Python. Several dbr_XXX functions are also not supported, as they appear to be needed only to be able to dynamically allocate memory, which is not necessary in Python.
Initialization, Finalization, and Life-cycle¶
The Channel Access library must be initialized before it can be used. There are 3 main reasons for this need:
1. CA requires a context model (preemptive callbacks or non-preemptive callbacks) to be specified before any actual calls can be made.
2. the ctypes interface requires that the shared library be loaded before it is used.
3. ctypes also requires that references to the library and callback functions be kept for the life-cycle of CA-using part of a program (or else they will be garbage collected).
As far as is possible, the ca
module hides the details of the CA
lifecyle from the user, so that it is not necessary to to worry about
explicitly initializing a Channel Access session. Instead, the library is
initialized as soon as it is needed, and intervention is really only
required to change default settings. The ca
module also handles
finalizing the CA session, so that core-dumps and warning messages do not
happen due to CA still being ‘alive’ as a program ends.
Because some users may wish to customize the initialization and finalization process, the detailed steps will be described here. These initialization and finalization tasks are handled in the following way:
The
libca
variable in theca
module holds a permanent, global reference to the CA shared object library (DLL).the function
initialize_libca()
is called to initialize libca. This function takes no arguments, but does use the global BooleanPREEMPTIVE_CALLBACK
(default value ofTrue
) to control whether preemptive callbacks are used.the function
finalize_libca()
is used to finalize libca. Normally, this is function is registered to be called when a program ends withatexit.register()
. Note that this only gets called on a graceful shutdown. If the program crashes (for a non-CA related reason, for example), this finalization may not be done, and connections to Epics Variables may not be closed completely on the Channel Access server.
- epics.ca.PREEMPTIVE_CALLBACK¶
sets whether preemptive callbacks will be used. The default value is
True
. If you wish to run without preemptive callbacks this variable MUST be set before any other use of the CA library. With preemptive callbacks enabled, EPICS communication will not require client code to continually poll for changes. With preemptive callback disables, you will need to frequently poll epics withpend_io()
and func:pend_event.
- epics.ca.DEFAULT_CONNECTION_TIMEOUT¶
sets the default timeout value (in seconds) for
connect_channel()
. The default value is 2.0
- epics.ca.AUTOMONITOR_MAXLENGTH¶
sets the default array length (ie, how many elements an array has) above which automatic conversion to numpy arrays and automatic monitoring for PV variables is suppressed. The default value is 65536. To be clear: waveforms with fewer elements than this value will be automatically monitored changes, and will be converted to numpy arrays (if numpy is installed). Larger waveforms will not be automatically monitored.
Working with waveform / array data and Strategies for working with large arrays for more details.
- epics.ca.WITH_CA_MESSAGES¶
whether to enable message from CA (default is False, set to True to enable messages.
Supressing Messages from libCA for more details.
Using the CA module¶
Many general-purpose CA functions that deal with general communication and threading contexts are very close to the C library:
- epics.ca.initialize_libca()¶
Initialize the Channel Access library.
This loads the shared object library (DLL) to establish Channel Access Connection. The value of
PREEMPTIVE_CALLBACK
sets the pre-emptive callback model.- This must be called prior to any actual use of the CA library, but
will be called automatically by the the
withCA()
decorator, so you should not need to call this directly from most real programs.
- Returns:
- libcaobject
ca library object, used for all subsequent ca calls
Notes
This function must be called prior to any real CA calls.
This function will disable messages from CA.
See the withCA decorator to ensure CA is initialized
- epics.ca.finalize_libca()¶
shutdown channel access:
run
clear_channel()
for all chids in_cache
, then callsflush_io()
andpoll()
a few times.- Parameters:
- maxtimefloat
maximimum time (in seconds) to wait for
flush_io()
andpoll()
to complete.
- epics.ca.pend_io(timeout=1.0)¶
polls CA for i/o.
- epics.ca.pend_event(timeout=1.e-5)¶
polls CA for events
- epics.ca.poll(evt=1.e-5[, iot=1.0])¶
a convenience function which is equivalent to:: pend_event(evt) pend_io_(iot)
- epics.ca.create_context()¶
Create a new context, using the value of
PREEMPTIVE_CALLBACK
to set the context type. Note that both context_create and create_context (which is more consistent with the Verb_Object of the rest of the CA library) are supported.- Parameters:
- ctxint
0 – No preemptive callbacks, 1 – use use preemptive callbacks, None – use value of
PREEMPTIVE_CALLBACK
- epics.ca.destroy_context()¶
destroy current context
- epics.ca.current_context()¶
return the current context
- epics.ca.attach_context(context)¶
attach to the supplied context
- epics.ca.detach_context()¶
detach context
- epics.ca.use_initial_context()¶
Attaches to the context created when libca is initialized. Using this function is recommended when writing threaded programs that using CA.
See the advanced section in doc for further discussion.
- epics.ca.client_status(context, level)¶
print (to stderr) information about Channel Access status, including status for each channel, and search and connection statistics.
- epics.ca.version()¶
Print Channel Access version string. Currently, this should report ‘4.13’
- epics.ca.message(status)¶
Print a message corresponding to a Channel Access status return value.
- epics.ca.flush_io()¶
flush i/o
- epics.ca.replace_printf_handler(writer=None)¶
replace the normal printf() output handler with the supplied writer function
- Parameters:
- writer: callable or None [default]
function to use for handling messages
Notes
writer=None will suppress all CA messages.
writer should have a signature of writer(*args), as sys.stderr.write and sys.stdout.write do.
Due to limitations in ctypes, this will not work as well as expected. Once disabled, re-enabling ca_messages will receive only the first string argument, not the list of strings to be formatted.
- epics.ca.disable_ca_messages()¶
disable messages rom CA: replace_printf_handler(None)
- epics.ca.enable_ca_messages(writer='stderr')¶
enable messages from CA using the supplier writer
- Parameters:
- writer: callable, `stderr`, `stdout`, or `None`
function to use for handling messages
Notes
writer=None will suppress all CA messages.
writer should have a signature of writer(*args), as sys.stderr.write and sys.stdout.write do, though only the first value will actually be use.
Due to limitations in ctypes, this will not work as well as expected. Once disabled, re-enabling ca_messages will receive only the first string argument, not the list of strings to be formatted.
Creating and Connecting to Channels¶
The basic channel object is the Channel ID or chid
. With the CA
library (and ca
module), one creates and acts on the chid
values.
These are simply ctypes.c_long
(C long integers) that hold the
memory address of the C representation of the channel, but it is probably
a good idea to treat these as object instances.
- epics.ca.create_channel(pvname, connect=False, callback=None, auto_cb=True)¶
create a Channel for a given pvname
creates a channel, returning the Channel ID
chid
used by other functions to identify this channel.- Parameters:
- pvnamestring
the name of the PV for which a channel should be created.
- connectbool
whether to (try to) connect to PV as soon as possible.
- auto_cbbool
whether to automatically use an internal connection callback.
- callbackcallable or
None
user-defined Python function to be called when the connection state change s.
- Returns:
- chidctypes.c_long
channel ID.
Notes
1. The user-defined connection callback function should be prepared to accept keyword arguments of
keyword
meaning
pvname
name of PV
chid
Channel ID
conn
whether channel is connected
2. If auto_cb is
True
, an internal connection callback is used so that you should not need to explicitly connect to a channel, unless you are having difficulty with dropped connections.3. If the channel is already connected for the PV name, the callback will be called immediately.
- epics.ca.connect_channel(chid, timeout=None, verbose=False)¶
connect to a channel, waiting up to timeout for a channel to connect. It returns the connection state,
True
orFalse
.This is usually not needed, as implicit connection will be done when needed in most cases.
- Parameters:
- chidctypes.c_long
Channel ID
- timeoutfloat
maximum time to wait for connection.
- verbosebool
whether to print out debugging information
- Returns:
- connection_statebool
that is, whether the Channel is connected
Notes
If timeout is
None
, the value ofDEFAULT_CONNECTION_TIMEOUT
is used (defaults to 2.0 seconds).
2. Normally, channels will connect in milliseconds, and the connection callback will succeed on the first attempt.
3. For un-connected Channels (that are nevertheless queried), the ‘ts’ (timestamp of last connection attempt) and ‘failures’ (number of failed connection attempts) from the
_cache
will be used to prevent spending too much time waiting for a connection that may never happen.
Many other functions require a valid Channel ID, but not necessarily a connected Channel. These functions are essentially identical to the CA library versions, and include:
- epics.ca.name(chid)¶
return PV name for channel name
- epics.ca.host_name(chid)¶
return host name and port serving Channel
- epics.ca.element_count(chid)¶
return number of elements in Channel’s data. 1 for most Channels, > 1 for waveform Channels
- epics.ca.replace_access_rights_event(chid, callback=None)¶
- epics.ca.read_access(chid)¶
return read access for a Channel: 1 for
True
, 0 forFalse
.
- epics.ca.write_access(chid)¶
return write access for a channel: 1 for
True
, 0 forFalse
.
- epics.ca.field_type(chid)¶
return the integer DBR field type.
See the ftype column from Table of DBR Types.
- epics.ca.clear_channel(chid)¶
clear the channel
- epics.ca.state(chid)¶
return state (that is, attachment state) for channel
A few additional pythonic functions have been added:
- epics.ca.isConnected(chid)¶
return whether channel is connected: dbr.CS_CONN==state(chid)
This is
True
for a connected channel,False
for an unconnected channel.
- epics.ca.access(chid)¶
returns a string describing read/write access: one of no access, read-only, write-only, or read/write
- epics.ca.promote_type(chid[, use_time=False[, use_ctrl=False]])¶
promotes the native field type of a
chid
to its TIME or CTRL variant. Returns the integer corresponding to the promoted field value.
See Table of DBR Types.
- epics.ca._cache¶
The ca module keeps a global cache of Channels that holds connection status and a bit of internal information for all known PVs. This cache is not intended for general use.
- epics.ca.show_cache(print_out=True)¶
print out a listing of PVs in the current session to standard output. Use the print_out=False option to be returned the listing instead of having it printed out.
- epics.ca.clear_cache()¶
Clears global caches of Epics CA connections, and fully detaches from the CA context. This is important when doing multiprocessing (and is done internally by CAProcess), but can be useful to fully reset a Channel Access session.
Any class PV created prior this call without using get_pv() have to be disconnected (use PV.disconnect()) explicitly because disconnection clears subscriptions to events of Epics CA connection which are to be cleared here. No instance of the class PV created prior this call should be used after because underlaying Epics CA connections are cleared here. Failing to follow these rules may cause your application to experience a random SIGSEGV from inside Epics binaries.
Use register_clear_cache() to register a function which will be called before Epics CA connections are cleared.
This function is not thread safe.
Interacting with Connected Channels¶
Once a chid
is created and connected there are several ways to
communicating with it. These are primarily encapsulated in the functions
get()
, put()
, and create_subscription()
, with a few
additional functions for retrieving specific information.
These functions are where this python module differs the most from the
underlying CA library, and this is mostly due to the underlying CA function
requiring the user to supply DBR TYPE and count as well as chid
and
allocated space for the data. In python none of these is needed, and
keyword arguments can be used to specify such options.
- epics.ca.get(chid, ftype=None, count=None, as_string=False, as_numpy=True, wait=True, timeout=None)¶
return the current value for a Channel. Note that there is not a separate form for array data.
- Parameters:
- chidctypes.c_long
Channel ID
- ftypeint
field type to use (native type is default)
- countint
maximum element count to return (full data returned by default)
- as_stringbool
whether to return the string representation of the value. See notes below.
- as_numpybool
whether to return the Numerical Python representation for array / waveform data.
- waitbool
whether to wait for the data to be received, or return immediately.
- timeoutfloat
maximum time to wait for data before returning
None
.
- Returns:
- dataobject
Normally, the value of the data. Will return
None
if the channel is not connected, wait=False was used, or the data transfer timed out.
Notes
Returning
None
indicates an incomplete get
2. The as_string option is not as complete as the as_string argument for
PV.get()
. For Enum types, the name of the Enum state will be returned. For waveforms of type CHAR, the string representation will be returned. For other waveforms (with count > 1), a string like <array count=3, type=1> will be returned.3. The as_numpy option will convert waveform data to be returned as a numpy array. This is only applied if numpy can be imported.
4. The wait option controls whether to wait for the data to be received over the network and actually return the value, or to return immediately after asking for it to be sent. If wait=False (that is, immediate return), the get operation is said to be incomplete. The data will be still be received (unless the channel is disconnected) eventually but stored internally, and can be read later with
get_complete()
. Using wait=False can be useful in some circumstances.5. The timeout option sets the maximum time to wait for the data to be received over the network before returning
None
. Such a timeout could imply that the channel is disconnected or that the data size is larger or network slower than normal. In that case, the get operation is said to be incomplete, and the data may become available later withget_complete()
.
See Table of DBR Types for a listing of values of ftype,
See Strategies for working with large arrays for a discussion of strategies for how to best deal with very large arrays.
See Strategies for connecting to a large number of PVs for a discussion of when using wait=False can give a large performance boost.
See The wait and timeout options for get(), ca.get_complete() for further discussion of the wait and timeout options and the associated get_complete()
function.
- epics.ca.get_with_metadata(chid, ftype=None, count=None, as_string=False, as_numpy=True, wait=True, timeout=None)¶
Return the current value along with metadata for a Channel
- Parameters:
- chidctypes.c_long
Channel ID
- ftypeint
field type to use (native type is default)
- countint
maximum element count to return (full data returned by default)
- as_stringbool
whether to return the string representation of the value. See notes.
- as_numpybool
whether to return the Numerical Python representation for array / waveform data.
- waitbool
whether to wait for the data to be received, or return immediately.
- timeoutfloat
maximum time to wait for data before returning
None
.
- Returns:
- datadict or None
The dictionary of data, guaranteed to at least have the ‘value’ key. Depending on ftype, other keys may also be present:
{'precision', 'units', 'status', 'severity', 'enum_strs', 'status', 'severity', 'timestamp', 'posixseconds', 'nanoseconds', 'upper_disp_limit', 'lower_disp_limit', 'upper_alarm_limit', 'upper_warning_limit', 'lower_warning_limit','lower_alarm_limit', 'upper_ctrl_limit', 'lower_ctrl_limit'}
Returns
None
if the channel is not connected, wait=False was used, or the data transfer timed out.- See get() for additional usage notes.
- epics.ca.get_complete(chid, ftype=None, count=None, as_string=False, as_numpy=True, timeout=None)¶
returns the current value for a Channel, completing an earlier incomplete
get()
that returnedNone
, either because wait=False was used or because the data transfer did not complete before the timeout passed.- Parameters:
- chidctypes.c_long
Channel ID
- ftypeint
field type to use (native type is default)
- countint
maximum element count to return (full data returned by default)
- as_stringbool
whether to return the string representation of the value.
- as_numpybool
whether to return the Numerical Python representation for array / waveform data.
- timeoutfloat
maximum time to wait for data before returning
None
.
- Returns:
- dataobject
This function will return
None
if the previousget()
actually completed, or if this data transfer also times out.
Notes
1. The default timeout is dependent on the element count:: default_timout = 1.0 + log10(count) (in seconds)
Consult the doc for
get()
for more information.
See The wait and timeout options for get(), ca.get_complete() for further discussion.
- epics.ca.get_complete_with_metadata(chid, ftype=None, count=None, as_string=False, as_numpy=True, timeout=None)¶
Returns the current value and associated metadata for a Channel
This completes an earlier incomplete
get()
that returnedNone
, either because wait=False was used or because the data transfer did not complete before the timeout passed.- Parameters:
- chidctypes.c_long
Channel ID
- ftypeint
field type to use (native type is default)
- countint
maximum element count to return (full data returned by default)
- as_stringbool
whether to return the string representation of the value.
- as_numpybool
whether to return the Numerical Python representation for array / waveform data.
- timeoutfloat
maximum time to wait for data before returning
None
.
- Returns:
- datadict or None
This function will return
None
if the previousget()
actually completed, or if this data transfer also times out.- See get_complete() for additional usage notes.
- epics.ca.put(chid, value, wait=False, timeout=30, callback=None, callback_data=None, ftype=None)¶
sets the Channel to a value, with options to either wait (block) for the processing to complete, or to execute a supplied callback function when the process has completed.
- Parameters:
- chidctypes.c_long
Channel ID
- waitbool
whether to wait for processing to complete (or time-out) before returning.
- timeoutfloat
maximum time to wait for processing to complete before returning anyway.
- callback
None
or callable user-supplied function to run when processing has completed.
- callback_dataobject
extra data to pass on to a user-supplied callback function.
- ftype
None
or int (valid dbr type) force field type to be a non-native form (None will use native form)
- Returns:
- statusint
1 for success, -1 on time-out
Notes
Specifying a callback will override setting wait=True.
- A put-callback function will be called with keyword arguments
pvname=pvname, data=callback_data
See User-supplied Callback functions for more on this put callback,
- epics.ca.create_subscription(chid, use_time=False, use_ctrl=False, mask=None, callback=None)¶
create a subscription to changes. Sets up a user-supplied callback function to be called on any changes to the channel.
- Parameters:
- chidctypes.c_long
channel ID
- use_timebool
whether to use the TIME variant for the PV type
- use_ctrlbool
whether to use the CTRL variant for the PV type
- ftypeinteger or None
ftype to use, overriding native type, use_time or use_ctrl if
None
, the native type is looked up, which requires a connected channel.- maskinteger or None
bitmask combination of
dbr.DBE_ALARM
,dbr.DBE_LOG
, anddbr.DBE_VALUE
, to control which changes result in a callback. IfNone
, defaults toDEFAULT_SUBSCRIPTION_MASK
.- callback
None
or callable user-supplied callback function to be called on changes
- timeout
None
or int connection timeout used for unconnected channels.
- Returns:
- (callback_ref, user_arg_ref, event_id)
The returned tuple contains callback_ref an user_arg_ref which are references that should be kept for as long as the subscription lives (otherwise they may be garbage collected, causing no end of trouble). event_id is the id for the event (useful for clearing a subscription).
Notes
Keep the returned tuple in named variable!! if the return argument gets garbage collected, a coredump will occur.
If the channel is not connected, the ftype must be specified for a successful subscription.
See User-supplied Callback functions for more on writing the user-supplied callback,
Warning
event_id is the id for the event (useful for clearing a subscription). You must keep the returned tuple in active variables, either as a global variable or as data in an encompassing class. If you do not keep this data, the return value will be garbage collected, the C-level reference to the callback will disappear, and you will see coredumps.
On Linux, a message like:
python: Objects/funcobject.c:451: func_dealloc: Assertion 'g->gc.gc_refs != (-2)' failed.
Abort (core dumped)
is a hint that you have not kept this data.
- epics.ca.DEFAULT_SUBSCRIPTION_MASK¶
This value is the default subscription type used when calling
create_subscription()
with mask=None. It is also used by default when creating aPV
object with auto_monitor is set toTrue
.The initial default value is dbr.DBE_ALARM|dbr.DBE_VALUE (i.e. update on alarm changes or value changes which exceeds the monitor deadband.) The other possible flag in the bitmask is dbr.DBE_LOG for archive-deadband changes.
If this value is changed, it will change the default for all subsequent calls to
create_subscription()
, but it will not change any existing subscriptions.
- epics.ca.clear_subscription(event_id)¶
cancel subscription given its event_id
Several other functions are provided:
- epics.ca.get_timestamp(chid)¶
return the timestamp of a Channel – the time of last update.
- epics.ca.get_severity(chid)¶
return the severity of a Channel.
- epics.ca.get_precision(chid)¶
return the precision of a Channel. For Channels with native type other than FLOAT or DOUBLE, this will be 0
- epics.ca.get_enum_strings(chid)¶
return list of names for ENUM states of a Channel. Returns None for non-ENUM Channels
- epics.ca.get_ctrlvars(chid)¶
return the CTRL fields for a Channel.
- Depending on the native type, the keys may include
status, severity, precision, units, enum_strs*, upper_disp_limit, lower_disp_limit, upper_alarm_limit*, lower_alarm_limit, upper_warning_limit*, lower_warning_limit, upper_ctrl_limit, lower_ctrl_limit
Notes
enum_strs will be a list of strings for the names of ENUM states.
See Table of Control Attributes
Table of Control Attributes
attribute
data types
status
severity
precision
0 for all but double, float
units
enum_strs
enum only
upper_disp_limit
lower_disp_limit
upper_alarm_limit
lower_alarm_limit
upper_warning_limit
lower_warning_limit
upper_ctrl_limit
lower_ctrl_limit
Note that enum_strs will be a tuple of strings for the names of ENUM states.
- epics.ca.get_timevars(chid)¶
returns a dictionary of TIME fields for a Channel. This will contain keys of status, severity, and timestamp.
Synchronous Groups¶
Warning
Synchronous groups are simulated in pyepics, but are not recommended, and probably don’t really make sense for usage within pyepics and using asynchronous i/o anyway.
Synchronous Groups are can be used to ensure that a set of Channel Access calls all happen together, as if in a transaction. Synchronous Groups should be avoided in pyepics, and are not well tested. They probably make little sens in the context of asynchronous I/O. The documentation here is given for historical purposes.
The idea is to first create a synchronous group, then add a series of
sg_put()
and sg_get()
which do not happen immediately, and
finally block while all the channel access communication is done for the
group as a unit. It is important to not issue pend_io()
during the
building of a synchronous group, as this will cause pending sg_put()
and sg_get()
to execute.
- epics.ca.sg_create()¶
create synchronous group. Returns a group id, gid, which is used to identify this group and to be passed to all other synchronous group commands.
- epics.ca.sg_delete(gid)¶
delete a synchronous group
- epics.ca.sg_block(gid[, timeout=10.0])¶
block for a synchronous group to complete processing
- epics.ca.sg_get(gid, chid[, ftype=None[, as_string=False[, as_numpy=True]]])¶
synchronous-group get of the current value for a Channel. same options as get()
This function will not immediately return the value, of course, but the address of the underlying data.
After the
sg_block()
has completed, you must use_unpack()
to convert this data address to the actual value(s).Examples
>>> chid = epics.ca.create_channel(PV_Name) >>> epics.ca.connect_channel(chid1) >>> sg = epics.ca.sg_create() >>> data = epics.ca.sg_get(sg, chid) >>> epics.ca.sg_block(sg) >>> print(epics.ca._unpack(data, chid=chid))
- epics.ca.sg_put(gid, chid, value)¶
perform a put within a synchronous group.
This put cannot wait for completion or for a a callback to complete.
- epics.ca.sg_test(gid)¶
test whether a synchronous group has completed.
- epics.ca.sg_reset(gid)¶
resets a synchronous group
Implementation details¶
The details given here should mostly be of interest to those looking at the implementation of the ca module, those interested in the internals, or those looking to translate lower-level C or Python code to this module.
DBR data types¶
Table of DBR Types
CA type
integer ftype
Python ctypes type
string
0
string
int
1
integer
short
1
integer
float
2
double
enum
3
integer
char
4
byte
long
5
integer
double
6
double
time_string
14
time_int
15
time_short
15
time_float
16
time_enum
17
time_char
18
time_long
19
time_double
20
ctrl_string
28
ctrl_int
29
ctrl_short
29
ctrl_float
30
ctrl_enum
31
ctrl_char
32
ctrl_long
33
ctrl_double
34
PySEVCHK and ChannelAccessExcepction: checking CA return codes¶
- exception epics.ca.ChannelAccessException¶
This exception is raised when the
ca
module experiences unexpected behavior and must raise an exception
- epics.ca.PySEVCHK(func_name, status[, expected=dbr.ECA_NORMAL])¶
This checks the return status returned from a libca.ca_*** and raises a
ChannelAccessException
if the value does not match the expected value (which is nornmallydbr.ECA_NORMAL
.The message from the exception will include the func_name (name of the Python function) and the CA message from
message()
.
- epics.ca.withSEVCHK(fcn)¶
decorator to raise a ChannelAccessException if the wrapped ca function does not return status = dbr.ECA_NORMAL. This handles the common case of running
PySEVCHK()
for a function whose return value is from a corresponding libca function and whose return value should bedbr.ECA_NORMAL
.
Function Decorators¶
In addition to withSEVCHK()
, several other decorator functions are
used heavily inside of ca.py or are available for your convenience.
- epics.ca.withCA(fcn)¶
decorator to ensure that libca and a context are created prior to function calls to the channel access library. This is intended for functions that need CA started to work, such as
create_channel()
.Note that CA functions that take a Channel ID (chid) as an argument are NOT wrapped by this: to get a chid, the library must have been initialized already.
- epics.ca.withCHID(fcn)¶
decorator to ensure that first argument to a function is a Channel ID,
chid
. The test performed is very weak, as any ctypes long or python int will pass, but it is useful enough to catch most accidental errors before they would cause a crash of the CA library.
- epics.ca.withConnectedCHID(fcn)¶
decorator to ensure that the first argument of a function is a fully connected Channel ID,
chid
. This test is (intended to be) robust, and will try to make sure achid
is actually connected before calling the decorated function.
- epics.ca.withInitialContext(fcn)¶
decorator to ensure that the wrapped function uses the initial threading context created at initialization of CA
See Using Python Threads for further discussion.
Unpacking Data from Callbacks¶
Throughout the implementation, there are several places where data returned
by the underlying CA library needs to be be converted to Python data. This
is encapsulated in the _unpack()
function. In general, you will not
have to run this code, but there is one exception: when using
sg_get()
, the values returned will have to be unpacked with this
function.
- epics.ca._unpack(chid, data[, count=None[, ftype=None[, as_numpy=None]]])¶
unpacks raw data for a Channel ID chid returned by libca functions including ca_array_get_callback or subscription callback, and returns the corresponding Python data
Normally, users are not expected to need to access this function, but it will be necessary why using
sg_get()
.- Parameters:
- chidctypes.c_long or
None
channel ID (if not None, used for determining count and ftype)
- dataobject
raw data as returned by internal libca functions.
- countinteger
number of elements to fetch (defaults to element count of chid or 1)
- ftypeinteger
data type of channel (defaults to native type of chid)
- as_numpybool
whether to convert to numpy array.
- chidctypes.c_long or
User-supplied Callback functions¶
User-supplied callback functions can be provided for put()
,
replace_access_rights_event()
, and create_subscription()
.
Note that callbacks for PV objects are slightly different: see
User-supplied Callback functions in the pv
module for details.
When defining a callback function to be run either when a put()
completes
or on changes to the Channel, as set from create_subscription()
, or when
read/write permissions change from replace_access_rights_event()
, it is
important to know two things:
how your function will be called.
what is permissible to do inside your callback function.
Callbacks will be called with keyword arguments for put()
and for
create_subscription()
. You should be prepared to have them passed to
your function. Use **kw unless you are very sure of what will be sent.
For the case of replace_access_rights_event()
, only positional arguments
will be passed.
For callbacks sent when a put()
completes, your function will be passed these:
pvname : the name of the pv
data: the user-supplied callback_data (defaulting to
None
).
For subscription callbacks, your function will be called with keyword/value pairs that will include:
pvname: the name of the pv
value: the latest value
count: the number of data elements
ftype: the numerical CA type indicating the data type
status: the status of the PV (0 for OK)
chid: the integer address for the channel ID.
For access rights event callbacks, your function will be passed:
read_access: boolean indicating read access status
write_access: boolean indicating write access status
Depending on the data type, and whether the CTRL or TIME variant was used, the callback function may also include some of these as keyword arguments:
enum_strs: the list of enumeration strings
precision: number of decimal places of precision.
units: string for PV units
severity: PV severity
timestamp: timestamp from CA server.
posixseconds: integer seconds since POSIX epoch of timestamp from CA server.
nanoseconds: integer nanoseconds of timestamp from CA server.
Note that a the user-supplied callback will be run inside a CA function,
and cannot reliably make any other CA calls. It is helpful to think “this
all happens inside of a pend_event()
call”, and in an epics thread
that may or may not be the main thread of your program. It is advisable to
keep the callback functions short and not resource-intensive. Consider
strategies which use the callback only to record that a change has occurred
and then act on that change later – perhaps in a separate thread, perhaps
after pend_event()
has completed.
Omissions¶
Several parts of the CA library are not implemented in the Python module. These are currently seen as unneeded (with notes where appropriate for alternatives), though they could be added on request.
- epics.ca.ca_add_exception_event()¶
Not implemented: Python exceptions are raised where appropriate and can be used in user code.
- epics.ca.ca_add_fd_registration()¶
Not implemented
- epics.ca.ca_client_status()¶
Not implemented
- epics.ca.ca_set_puser()¶
Not implemented : it is easy to pass user-defined data to callbacks as needed.
- epics.ca.ca_puser()¶
Not implemented: it is easy to pass user-defined data to callbacks as needed.
- epics.ca.ca_SEVCHK()¶
Not implemented: the Python function
PySEVCHK()
is approximately the same.
- epics.ca.ca_signal()¶
Not implemented: the Python function
PySEVCHK()
is approximately the same.
- epics.ca.ca_test_event()¶
Not implemented: this appears to be a function for debugging events. These are easy enough to simulate by directly calling Python callback functions.
- epics.ca.ca_dump_dbr()¶
Not implemented
In addition, not all DBR types in the CA C library are supported.
Only native types and their DBR_TIME and DBR_CTRL variants are supported: DBR_STS and DBR_GR variants are not. Several dbr_XXX functions are also not supported, as they are needed only to dynamically allocate memory.
CAThread
class¶
- class epics.ca.CAThread(group=None[, target=None[, name=None[, args=()[, kwargs={}]]]])¶
create a CA-aware subclass of a standard Python
threading.Thread
. See the standard library documentation for further information on how to use Thread objects.A CAThread simply runs
use_initial_context()
prior to running each target function, so thatuse_initial_context()
does not have to be explicitly put inside the target function.The See Using Python Threads for further discussion.
Examples¶
Here are some example sessions using the ca
module.
Create, Connect, Get Value of Channel¶
Note here that several things have been simplified compare to using CA in C: initialization and creating a main-thread context are handled, and connection of channels is handled in the background:
from epics import ca
chid = ca.create_channel('XXX:m1.VAL')
count = ca.element_count(chid)
ftype = ca.field_type(chid)
print("Channel ", chid, count, ftype)
value = ca.get()
print(value)
Put, waiting for completion¶
Here we set a PVs value, waiting for it to complete:
from epics import ca
chid = ca.create_channel('XXX:m1.VAL')
ca.put(chid, 1.0, wait=True)
The put()
method will wait to return until the processing is
complete.
Define a callback to Subscribe to Changes¶
Here, we subscribe to changes for a PV, which is to say we define a callback function to be called whenever the PV value changes. In the case below, the function to be called will simply write the latest value out to standard output:
from epics import ca
import time
import sys
# define a callback function. Note that this should
# expect certain keyword arguments, including 'pvname' and 'value'
def onChanges(pvname=None, value=None, **kw):
fmt = 'New Value: %s value=%s, kw=%s\n'
sys.stdout.write(fmt % (pvname, str(value), repr(kw)))
sys.stdout.flush()
# create the channel
mypv = 'XXX.VAL'
chid = ca.create_channel(mypv)
# subscribe to events giving a callback function
eventID = ca.create_subscription(chid, callback=onChanges)
# now we simply wait for changes
t0 = time.time()
while time.time()-t0 < 10.0:
time.sleep(0.001)
It is vital that the return value from create_subscription()
is
kept in a variable so that it cannot be garbage collected. Failure to keep
this value will cause trouble, including almost immediate segmentation
faults (on Windows) or seemingly inexplicable crashes later (on linux).
Define a connection callback¶
Here, we define a connection callback – a function to be called when the connection status of the PV changes. Note that this will be called on initial connection:
import epics
import time
def onConnectionChange(pvname=None, conn=None, chid=None):
print(f'ca connection status changed: {pvname}, {conn}, {chid}')
# create channel, provide connection callback
motor1 = '13IDC:m1'
chid = epics.ca.create_channel(motor1, callback=onConnectionChange)
print('Now waiting, watching values and connection changes:')
t0 = time.time()
while time.time()-t0 < 30:
time.sleep(0.001)
This will run the supplied callback soon after the channel has been
created, when a successful connection has been made. Note that the
callback should be prepared to accept keyword arguments of pvname,
chid, and conn for the PV name, channel ID, and connection state
(True
or False
).
Define an access rights change event callback¶
The following example demonstrates the addition of a function to be called when the access rights of a channel changes. Note this will be called immediately after successful installation:
import epics
import time
def on_access_rights_change(read_access, write_access):
print(f'read access={read_access}, write access={write_access}')
# create a channel and attach the above function for access rights events
chid = epics.ca.create_channel('pv_name')
# a message should be printed immediately following this registration
epics.ca.replace_access_rights_event(chid, callback=on_access_rights_change)
# Affecting the channel's access rights, (for example, by enabling/disabling
# CA Security rules), should produce additional console messages
try:
while True:
time.sleep(0.25)
except KeyboardInterrupt:
pass