Support Modules

An EPICS Record definition for most record types will include setting the DTYP and INP or OUT fields. An example with the longin would be:

record(longin, "instance:name") {
  field(DTYP, "Python Device")
  field(INP , "@pymodule some other string")
}

Or equivalently:

record(longin, "instance:name") {
  field(DTYP, "Python Device")
  field(INP , "@some other string")
  info("pySupportMod", "pymodule")
}

This minimal example will attempt to import a Python module named ‘pymodule’. This module is expected to provide a build() function. Which will be called with a Record instance and the string “some other string”.

build() Function

build(record, args)

Called when the IOC requests a device support instance for the given record. This function should return an object providing the methods of a DeviceSupport.

Parameters:
  • record – The Record instance to which this support will be attached. May be used to introspect and query initial field values.
  • args – The remainder of the INP or OUT field string.
def build(record, args):
  print 'Need device support for', record.name()
  print 'Provided:', args
  raise RuntimeError("Not support found!")

DeviceSupport Interface

class DeviceSupport

DeviceSupport is not a class. Rather it is a description of the methods which all Python device support instances must provide. These methods will be called during the course of IOC processing.

Exceptions raised by these methods are printed to the IOC console, but will otherwise be ignored.

The module devsup.interfaces provides a Zope Interface definition by this name which may be referenced.

raw

A boolean value indicating whether this device support uses “raw” access. A Raw support module will update the VAL field even if the recordtype has an RVAL field (eg. ai/ao, mbbi/mbbo).

Omitting this attribute is the same as False.

process(record, reason)
Parameters:
  • recordRecord from which the request originated.
  • reasonNone or an object provided when processing was requested.

This method is called whenever the associated record needs to be updated in response to a request. The source of this request is typically determined by the record’s SCAN field.

The actions taken by this method will depend heavily on the application. Typically this will include reading or writing values from fields. Record fields can be access through the provided Record instance.

detach(record)
Parameters:recordRecord from which the request originated.

Called when a device support instance is being dis-associated from its Record. This will occur when the IOC is shutdown or the INP or OUT field of a record is modified.

No further calls to this object will be made in relation to record.

allowScan(record)
Parameters:recordRecord from which the request originated.
Return type:bool or Callable

Called when an attempt is made to set the record’s SCAN field to “I/O Intr” either at startup, or during runtime. To permit this the method must return an object which evaluates to True. If not then the attempt will fail and SCAN will revert to “Passive”.

If a callable object is returned, then it will be invoked when SCAN is changed again, or just before detach() is called.

This method will typically be implemented using the add method of an I/O scan list object. (IOScanListBlock or IOScanListThread)

class MySup(object):
  def __init__(self):
    self.a_scan = devsup.db.IOScanListThread()
  def allowScan(self, record):
    return self.a_scan.add(record)

Which in most cases can be abbriviated to

class MySup(object):
  def __init__(self):
    self.a_scan = devsup.db.IOScanListThread()
    self.allowScan = self.a_scan.add

Example

A simple counter. The processing action is to increment the value of the VAL field. The following code should be placed in a file named counter.py which should be placed in the Python module import path.

class MySupport(object):
  def detach(self, record):
    pass # no cleanup needed

  def allowScan(self, record):
    return False # I/O Intr not supported

  def process(self, record, reason):
    record.VAL = record.VAL + 1
    try:
      record.UDF = 0
    except AttributeError:
      pass # not all record types implement this

def build(record, args):
  if not args.startswith('hello'):
    raise RuntimeError('%s is not friendly.'%record)
  return MySupport()

This support code can then be referenced from records.

record(longin, "my:int:counter") {
  field(DTYP, "Python Device")
  field(INP , "@counter hello world")
}

The following will fail to associate.

record(longin, "my:int:counter") {
  field(DTYP, "Python Device")
  field(INP , "@counter do what I say")
}

If a shorter INP link string is necessary, or to prevent runtime switching of modules, the module name may also be given in the pySupportMod info() tag.

record(longin, "my:int:counter") {
  field(DTYP, "Python Device")
  field(INP , "@hello world")
  info("pySupportMod", "counter")
}