lewis.adapters.epics

Members

BoundPV

Class to represent PVs that are bound to an adapter

EpicsAdapter

This adapter provides ChannelAccess server functionality through the pcaspy module.

EpicsInterface

Inheriting from this class provides an EPICS-interface to a device for use with EpicsAdapter.

PV

The PV-class is used to declare the EPICS-interface exposed by a sub-class of EpicsAdapter.

PropertyExposingDriver

class lewis.adapters.epics.BoundPV(pv, target, meta_target=None)[source]

Bases: object

Class to represent PVs that are bound to an adapter

This class is very similar to Func, in that it is the result of a binding operation between a user-specified PV-object and a Device and/or Adapter object. Also, it should rarely be used directly. objects are generated automatically by EpicsAdapter.

The binding happens by supplying a target-object which has an attribute or a property named according to the property-name stored in the PV-object, and a meta_target-object which has an attribute named according to the meta_data_property in PV.

The properties read_only, config, and poll_interval simply forward the data of PV, while doc uses the target object to potentially obtain the property’s docstring.

To get and set the value of the property on the target, the value-property of this class can be used, to get the meta data dict, there’s a meta-property.

Parameters:
  • pv – PV object to bind to target and meta_target.

  • target – Object that has an attribute named pv.property.

  • meta_target – Object that has an attribute named pv.meta_data_property.

property config

Config dict passed on to pcaspy-machinery.

property doc

Docstring of property on target or override specified on PV-object.

property meta

Value of the bound meta-property on the target.

property poll_interval

Interval at which to update PV in pcaspy.

property read_only

True if the PV is read-only.

property value

Value of the bound property on the target.

class lewis.adapters.epics.EpicsAdapter(options=None)[source]

Bases: Adapter

This adapter provides ChannelAccess server functionality through the pcaspy module.

It’s possible to configure the prefix for the PVs provided by this adapter. The corresponding key in the options dictionary is called prefix:

options = {"prefix": "PVPREFIX:"}
Parameters:

options – Dictionary with options.

property documentation

This property can be overridden in a sub-class to provide protocol documentation to users at runtime. By default it returns the indentation cleaned-up docstring of the class.

handle(cycle_delay=0.1) None[source]

Call this method to spend about cycle_delay seconds processing requests in the pcaspy server. Under load, for example when running caget at a high frequency, the actual time spent in the method may be much shorter. This effect is not corrected for.

Parameters:

cycle_delay – Approximate time to be spent processing requests in pcaspy server.

property is_running

This property indicates whether the Adapter’s server is running and listening. The result of calls to start_server() and stop_server() should be reflected as expected.

start_server() None[source]

Creates a pcaspy-server.

Note

The server does not process requests unless handle() is called regularly.

stop_server() None[source]

This method must be re-implemented to stop and tear down anything that has been setup in start_server(). This method should close all connections to clients that have been established since the adapter has been started.

Note

This method may be called multiple times over the lifetime of the Adapter, so it is important to make sure that this does not cause problems.

class lewis.adapters.epics.EpicsInterface[source]

Bases: InterfaceBase

Inheriting from this class provides an EPICS-interface to a device for use with EpicsAdapter. In the simplest case all that is required is to inherit from this class and override the pvs-member. It should be a dictionary that contains PV-names (without prefix) as keys and instances of PV as values. The prefix is handled by EpicsAdapter.

For a simple device with two properties, speed and position, the first of which should be read-only, it’s enough to define the following:

class SimpleDeviceEpicsInterface(EpicsInterface):
    pvs = {"VELO": PV("speed", read_only=True), "POS": PV("position", lolo=0, hihi=100)}

For more complex behavior, the interface could contain properties that do not exist in the device itself. If the device should also have a PV called STOP that “stops the device”, the interface could look like this:

class SimpleDeviceEpicsInterface(EpicsInterface):
    pvs = {
        "VELO": PV("speed", read_only=True),
        "POS": PV("position", lolo=0, hihi=100),
        "STOP": PV("stop", type="int"),
    }

    @property
    def stop(self):
        return 0

    @stop.setter
    def stop(self, value):
        if value == 1:
            self.device.halt()

Even though the device does not have a property called stop (but a method called halt), issuing the command

$ caput STOP 1

will achieve the desired behavior, because EpicsInterface merges the properties of the device into SimpleDeviceEpicsInterface itself, so that it is does not matter whether the specified property in PV exists in the device or the adapter.

The intention of this design is to keep device classes small and free of protocol specific stuff, such as in the case above where stopping a device via EPICS might involve writing a value to a PV, whereas other protocols may offer an RPC-way of achieving the same thing.

property adapter

Adapter type that is required to process and expose interfaces of this type. Must be implemented in subclasses.

class lewis.adapters.epics.PV(target_property, poll_interval=1.0, read_only=False, meta_data_property=None, doc=None, **kwargs)[source]

Bases: object

The PV-class is used to declare the EPICS-interface exposed by a sub-class of EpicsAdapter. The target_property argument specifies which property of the adapter the PV maps to. To make development easier it can also be a part of the exposed device. If the property exists on both the Adapter-subclass and the device, the former has precedence. This is useful for overriding behavior for protocol specific “quirks”.

If the PV should be read only, this needs to be specified via the corresponding parameter. The information about the poll interval is used py EpicsAdapter to update the PV in regular intervals. All other named arguments are forwarded to the pcaspy server’s pvdb, so it’s possible to pass on limits, types, enum-values and so on.

In case those arguments change at runtime, it’s possible to provide meta_data_property, which should contain the name of a property that returns a dict containing these values. For example if limits change:

class Interface(EpicsInterface):
    pvs = {"example": PV("example", meta_data_property="example_meta")}

    @property
    def example_meta(self):
        return {
            "lolim": self.device._example_low_limit,
            "hilim": self.device._example_high_limit,
        }

The PV infos are then updated together with the value, determined by poll_interval.

In cases where the device is accessed via properties alone, this class provides the possibility to expose methods as PVs. A common use case would be to model a getter:

class SomeDevice(Device):
    def get_example(self):
        return 42


class Interface(EpicsInterface):
    pvs = {"example": PV("get_example")}

It is also possible to model a getter/setter pair, in this case a tuple has to be provided:

class SomeDevice(Device):
    _ex = 40

    def get_example(self):
        return self._ex + 2

    def set_example(self, new_example):
        self._ex = new_example - 2


class Interface(EpicsInterface):
    pvs = {"example": PV(("get_example", "set_example"))}

Any of the two members in the tuple can be substituted with None in case it does not apply. Besides method names it is also allowed to provide callables. Valid callables are for example bound methods and free functions, but also lambda expressions and partials.

There are however restrictions for the supplied functions (be it as method names or directly as callables) with respect to their signature. Getter functions must be callable without any arguments, setter functions must be callable with exactly one argument. The self of methods does not count towards this.

Parameters:
  • target_property – Property or method name, getter function, tuple of getter/setter.

  • poll_interval – Update interval of the PV.

  • read_only – Should be True if the PV is read only. If not specified, the PV is read_only if only a getter is supplied.

  • meta_data_property – Property or method name, getter function, tuple of getter/setter.

  • doc – Description of the PV. If not supplied, docstring of mapped property is used.

  • kwargs – Arguments forwarded into pcaspy pvdb-dict.

bind(*targets)[source]

Tries to bind the PV to one of the supplied targets. Targets are inspected according to the order in which they are supplied.

Parameters:

targets – Objects to inspect from.

Returns:

BoundPV instance with the PV bound to the target property.