What is This?¶
Pyepics is a well-established Python wrapper of libca. Caproto includes a client that is a drop-in replacement for pyepics. It is implemented as a shim on top of caproto’s main Threading Client. Caproto’s pyepics-compatible client is tested against a representative sample of the pyepics test suite.
Why would you ever want to use caproto’s pyepics instead of actual pyepics? It may be advantageous to run existing user code written for pyepics on top of caproto — for example, to leverage caproto’s verbose logging or portability.
Why are there two threading clients in caproto instead of just one pyepics-compatible one? Caproto’s main threading client makes different design choices, consistent with the rest of caproto:
- Caproto’s threading client provides a lower-level API, handing the user objects encapsulating the complete response from the server as opposed to just the value.
- Caproto pulls apart the subscription process into two steps—specifying a subscription and adding a user callback function to one—whereas pyepics elides them.
Caproto is speed-competitive with pyepics. Because it controls the entire network stack, rather than calling out to libca, it can batch requests into UDP datagrams and TCP packets more efficiently, leading to a ~250X speedup in connecting a large number of channels in bulk.
The authors of caproto are heavy pyepics users and occasional contributors. This module is intended as a friendly bridge to pyepics.
For full documentation on pyepics usage, see the pyepics doucmentation. This is a brief demonstration of caproto’s pyepics-compat client.
In a separate shell, start one of caproto’s demo IOCs.
$ python3 -m caproto.ioc_examples.random_walk PVs: ['random_walk:dt', 'random_walk:x']
Now, in Python we will talk to it using caproto’s pyepics-compatible client.
Get and put to
In : import caproto.threading.pyepics_compat as epics In : pv_name = 'random_walk:dt' In : epics.caget(pv_name) Out: 3.0 In : epics.caput(pv_name, 2) In : pv = epics.get_pv(pv_name) In : pv.get() Out: 2.0 In : pv.put(1) In : pv.get() Out: 1.0
Subscribe a user-defined callback function to
In : def f(value, **kwargs): ...: print('received value' , value) ...:
Note that pyepics recommends using
epics.get_pv(...) instead of
epics.PV(...) and so do we, but both usages are supported.
In : x_pv = epics.PV('random_walk:x') In : x_pv.add_callback(f) Out: 0 In : import time; time.sleep(5) # give some time for responses to come in received value 0.28571528901532783 received value 1.032235999841053 received value 0.7254466722500339 received value -0.09200421910693946 In : x_pv.clear_callbacks()
The underlying caproto PV and Context objects from caproto’s main Threading Client. are accessible:
In : pv._caproto_pv Out: <PV name='random_walk:dt' priority=0 address=('127.0.0.1', 49663), circuit_state=States.CONNECTED, channel_state=States.CONNECTED> In : pv._caproto_pv.context Out: <Context searches_pending=0 circuits=1 pvs=2 idle=0>
This brief demonstration has not exercised every aspect of the pyepics API, but caproto’s test suite is more comprehensive.