PyEpics: Epics Channel Access for Python

# 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 the `ca` 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 Boolean `PREEMPTIVE_CALLBACK` (default value of `True`) 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 with `atexit.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 with `pend_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:
    libca object

      ca library object, used for all subsequent ca calls

  Notes

  1. This function must be called prior to any real CA calls.

  2. 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 calls `flush_io()` and `poll()` a few times.

  Parameters:

    maxtime (float) – maximimum time (in seconds) to wait for `flush_io()` and `poll()` 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:

    ctx (int) – 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

  1. writer=None will suppress all CA messages.

  2. writer should have a signature of writer(*args), as sys.stderr.write and sys.stdout.write do.

  3. 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

  1. writer=None will suppress all CA messages.

  2. 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.

  3. 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:

    - pvname (string) – the name of the PV for which a channel should be created.

    - connect (bool) – whether to (try to) connect to PV as soon as possible.

    - auto_cb (bool) – whether to automatically use an internal connection callback.

    - callback (callable or `None`) – user-defined Python function to be called when the connection state change s.

  Returns:

    chid – channel ID.

  Return type:

    ctypes.c_long

  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` or `False`.

  This is usually not needed, as implicit connection will be done when needed in most cases.

  Parameters:

    - chid (ctypes.c_long) – Channel ID

    - timeout (float) – maximum time to wait for connection.

    - verbose (bool) – whether to print out debugging information

  Returns:

    connection_state – that is, whether the Channel is connected

  Return type:

    bool

  Notes

  1. If timeout is `None`, the value of `DEFAULT_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 for `False`.

epics.ca.write_access(chid)

  return write access for a channel: 1 for `True`, 0 for `False`.

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:

    - chid (ctypes.c_long) – Channel ID

    - ftype (int) – field type to use (native type is default)

    - count (int) – maximum element count to return (full data returned by default)

    - as_string (bool) – whether to return the string representation of the value. See notes below.

    - as_numpy (bool) – whether to return the Numerical Python representation for array / waveform data.

    - wait (bool) – whether to wait for the data to be received, or return immediately.

    - timeout (float) – maximum time to wait for data before returning `None`.

  Returns:

    data – 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.

  Return type:

    object

  Notes

  1. 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 with `get_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:

    - chid (ctypes.c_long) – Channel ID

    - ftype (int) – field type to use (native type is default)

    - count (int) – maximum element count to return (full data returned by default)

    - as_string (bool) – whether to return the string representation of the value. See notes.

    - as_numpy (bool) – whether to return the Numerical Python representation for array / waveform data.

    - wait (bool) – whether to wait for the data to be received, or return immediately.

    - timeout (float) – maximum time to wait for data before returning `None`.

  Returns:

    - data (dict 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 returned `None`, either because wait=False was used or because the data transfer did not complete before the timeout passed.

  Parameters:

    - chid (ctypes.c_long) – Channel ID

    - ftype (int) – field type to use (native type is default)

    - count (int) – maximum element count to return (full data returned by default)

    - as_string (bool) – whether to return the string representation of the value.

    - as_numpy (bool) – whether to return the Numerical Python representation for array / waveform data.

    - timeout (float) – maximum time to wait for data before returning `None`.

  Returns:

    data – This function will return `None` if the previous `get()` actually completed, or if this data transfer also times out.

  Return type:

    object

  Notes

  1. The default timeout is dependent on the element count:: default_timout = 1.0 + log10(count) (in seconds)

  2. 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 returned `None`, either because wait=False was used or because the data transfer did not complete before the timeout passed.

  Parameters:

    - chid (ctypes.c_long) – Channel ID

    - ftype (int) – field type to use (native type is default)

    - count (int) – maximum element count to return (full data returned by default)

    - as_string (bool) – whether to return the string representation of the value.

    - as_numpy (bool) – whether to return the Numerical Python representation for array / waveform data.

    - timeout (float) – maximum time to wait for data before returning `None`.

  Returns:

    - data (dict or None) – This function will return `None` if the previous `get()` 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:

    - chid (ctypes.c_long) – Channel ID

    - wait (bool) – whether to wait for processing to complete (or time-out) before returning.

    - timeout (float) – 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_data (object) – 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:

    status – 1 for success, -1 on time-out

  Return type:

    int

  Notes

  1. Specifying a callback will override setting wait=True.

  2. 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:

    - chid (ctypes.c_long) – channel ID

    - use_time (bool) – whether to use the TIME variant for the PV type

    - use_ctrl (bool) – whether to use the CTRL variant for the PV type

    - ftype (integer 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.

    - mask (integer or None) – bitmask combination of `dbr.DBE_ALARM`, `dbr.DBE_LOG`, and `dbr.DBE_VALUE`, to control which changes result in a callback. If `None`, defaults to `DEFAULT_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:

    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).

  Return type:

    (callback_ref, user_arg_ref, event_id)

  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 a `PV` object with auto_monitor is set to `True`.

  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 nornmally `dbr.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 be `dbr.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 a `chid` 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:

    - chid (ctypes.c_long or `None`) – channel ID (if not None, used for determining count and ftype)

    - data (object) – raw data as returned by internal libca functions.

    - count (integer) – number of elements to fetch (defaults to element count of chid or 1)

    - ftype (integer) – data type of channel (defaults to native type of chid)

    - as_numpy (bool) – whether to convert to numpy array.

## 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:

> 1. how your function will be called.

> 2. 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 that `use_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