API
Python soft IOC module.
softioc
The top level softioc module contains a number of packages that can be used in the creation of IOCs:
softioc.softioc
This module wraps the basic interface to the EPICS IOC. A large number of interactive EPICS commands are wrapped and can be made available through the interpreter by invoking the interpreter through this module.
softioc.asyncio_dispatcher
A dispatcher for
asyncio
based applications instead of the defaultUsing the cothread Library
onesoftioc.alarm
This module simply contains definitions for severity and alarm values taken from the EPICS
alarm.h
header file.softioc.builder
This module provides facilities for creating PVs.
softioc.pvlog
The act of importing this module configures the IOC to log every external put to the database.
The following submodules implement internals and should not normally be looked at directly:
softioc.imports
Imports and wraps C functions from EPICS IOC support.
softioc.fields
Used internally as part of record support to implement access to EPICS record fields.
softioc.device_core
Implements the basics of
Python
EPICS device support.softioc.device
Implements
Python
device support for all the record types supported.softioc.pythonSoftIoc
Implements
epicsdbbuilder
interface for all of thePython
records.
- softioc.__version__: str
Version number as calculated by https://github.com/dls-controls/versiongit
Top Level IOC Interface: softioc.softioc
This module provides the following functions for general use (available by
importing *
):
- softioc.softioc.iocInit(dispatcher=None)[source]
This must be called exactly once after loading all EPICS database files. After this point the EPICS IOC is running and serving PVs.
- Parameters
dispatcher – A callable with signature
dispatcher(func, *args)
. Will be called in response to caput on a record. If not supplied useUsing the cothread Library
as a dispatcher.
See also
softioc.asyncio_dispatcher
is a dispatcher forasyncio
applications
- softioc.softioc.dbLoadDatabase(database, path=None, substitutions=None)[source]
Loads a database file and applies any given substitutions.
Note
This function is not normally called directly, instead
softioc.builder.LoadDatabase
is normally used to create and load the EPICS database on the fly.However, if required, an existing EPICS database can be loaded explicitly using this method. Note that
dbLoadDatabase
cannot be called afteriocInit
.
- softioc.softioc.devIocStats(ioc_name)[source]
This will load a template for the devIocStats library with the specified IOC name. This should be called before
iocInit
- softioc.softioc.interactive_ioc(context={}, call_exit=True)[source]
Fires up the interactive IOC prompt with the given context.
- Parameters
context – A dictionary of values that will be made available to the interactive Python shell together with a number of EPICS test functions
call_exit – If
True
, the IOC will be terminated by calling epicsExit which means thatinteractive_ioc
will not return
While the interactive shell is running a number of EPICS test functions are made
available for use together with the constant value exit
with special
behaviour: typing exit
at the interpreter prompt will immediately call
epicsExit
causing the Python interpreter and IOC to terminate.
This module provides Python wrappers for the following EPICS test functions and
makes them available to the interactive_ioc
interpreter shell. See the IOC
Test Facilities documentation for more details of each function.
- softioc.softioc.dba(field)
Prints value of each field in dbAddr structure associated with field.
- softioc.softioc.dbl(pattern='', fields='')
Prints the names of records in the database matching pattern. If a (space separated) list of fields is also given then the values of the fields are also printed.
- softioc.softioc.dbnr(all=0)
Print number of records of each record type.
- softioc.softioc.dbgrep(pattern)
Lists all record names that match the pattern. * matches any number of characters in a record name.
- softioc.softioc.dbgf(field)
Prints field type and value.
- softioc.softioc.dbpf(field, value)
Writes the given value into the field.
- softioc.softioc.dbpr(record, interest=0)
Prints all the fields in record up to the indicated interest level:
0
Application fields which change during record processing
1
Application fields which are fixed during processing
2
System developer fields of major interest
3
System developer fields of minor interest
4
All other fields
- softioc.softioc.dbtr(record)
Tests processing of the specified record.
- softioc.softioc.dbtgf(field_name)
This performs a dbNameToAddr and then calls dbGetField with all possible request types and options. It prints the results of each call. This routine is of most interest to system developers for testing database access.
- softioc.softioc.dbtpf(field_name, value)
This command performs a dbNameToAddr, then calls dbPutField, followed by dbgf for each possible request type. This routine is of interest to system developers for testing database access.
- softioc.softioc.dbior(driver='', interest=0)
Prints driver reports for the selected driver (or all drivers if driver is omitted) at the given interest level.
- softioc.softioc.dbhcr()
Prints hardware configuration report.
- softioc.softioc.gft(field)
Get Field Test for old database access
- softioc.softioc.pft(field, value)
Put Field Test for old database access
- softioc.softioc.tpn(field, value)
Test Process Notify for old database access
- softioc.softioc.dblsr(recordname, level)
This command generates a report showing the lock set to which each record belongs. If recordname is 0, “”, or “*” all records are shown, otherwise only records in the same lock set as recordname are shown.
level can have the following values:
0
Show lock set information only
1
Show each record in the lock set
2
Show each record and all database links in the lock set
- softioc.softioc.dbLockShowLocked(level)
This command generates a report showing all locked locksets, the records they contain, the lockset state and the thread that currently owns the lockset. The level argument is passed to epicsMutexShow to adjust the information reported about each locked epicsMutex.
- softioc.softioc.scanppl(rate=0.0)
Prints all records with the selected scan rate (or all if rate=0).
- softioc.softioc.scanpel(event=0)
Prints all records with selected event number (or all if event=0).
- softioc.softioc.scanpiol()
Prints all records in the I/O event scan lists.
- softioc.softioc.generalTimeReport(int level)
This routine displays the time providers and their priority levels that have registered with the General Time subsystem for both current and event times. At level 1 it also shows the current time as obtained from each provider.
- softioc.softioc.eltc(noYes)
This determines if error messages are displayed on the IOC console. 0 means no and any other value means yes.
- softioc.softioc.non_interactive_ioc()[source]
Function to run the IOC in non-interactive mode. This mode is useful for running the IOC as a background process without user interaction. This function expects a stop signal. When it receives one, the IOC stops.
When used with a service manager, use python’s -u option or the environment variable PYTHONUNBUFFERED=TRUE. This ensures that python output, i.e. stdout and stderr streams, is sent directly to the terminal.
- softioc.softioc.exit
Displaying this value will invoke
epicsExit()
causing the IOC to terminate immediately.
Asyncio Dispatcher: softioc.asyncio_dispatcher
If your application uses asyncio
then this module gives an alternative
dispatcher for caput requests.
- class softioc.asyncio_dispatcher.AsyncioDispatcher(loop=None, debug=False)[source]
A dispatcher for
asyncio
based IOCs, suitable to be passed tosoftioc.iocInit
. Means that on_update callback functions can be async.If a
loop
is provided it must already be running. Otherwise a new Event Loop will be created and run in a dedicated thread.debug
is passed through toasyncio.run()
.For a clean exit, call
softioc.interactive_ioc(..., call_exit=False)
Creating Records: softioc.builder
This module publishes functions for creating records. All of the other methods
in this module must be called before calling LoadDatabase()
, after which
no function in this module is usable.
See softioc.device
for a detailed explanation of record support and creation,
but note that only the following records types have direct support from this
module:
ai, ao, bi, bo, int64in, int64out, longin, longout, mbbi, mbbo, stringin, stringout, waveform
The following methods create records of the corresponding type. For all records the initial_value parameter can be used to specify an initial value for the record.
The following optional keyword arguments are available for all of these functions:
initial_value
This is used to specify an initial value for each record.
status, severity
Only available on IN records. These can be used with the alarm value enums
from softioc.alarm
to set the initial alarm state and severity of a record.
These are not useful unless PINI
is set to NO
, as otherwise the record
will be processed on initialization and the alarm status will be reset.
on_update
This is only available on OUT records (including those created by
WaveformOut()
). This specifies a function that will be called after
record processing has completed.
If used this should be set to a callable taking exactly one argument. After successful record processing this function will be called with the new value just written to the record.
Note that this callback occurs at an unpredictable time after record processing and if repeated high speed channel access puts are in progress it is possible that callbacks may be delayed. Each callback will be passed the value at the time the record was processed.
Note also that on_update callbacks occur as part of cothread processing and normal cothread operations can occur during the callback. However only one callback is dispatched at a time, so if a callback blocks it will delay on_update callbacks for other records.
on_update_name
This is an alternative form of on_update with the same behaviour: note that at most one of on_update and on_update_name may be passed. The difference is that on_update_name is called with the record name as its second argument after the value as the first argument.
validate
Also only available on OUT records, specifies a function called during
record processing. If used this should be set to a callable taking two
arguments. The first argument will be the record object, and the second
will be the new value being written. The validate function can reject
the update by returning False
or accept it by returning True
.
Note
This function is called asynchronously on a thread determined by EPICS and it is not safe to perform any cothread actions within this callback.
always_update
Again only on OUT records, determines whether record writes which don’t
change the existing value are passed through. If this field is not set then
writing to .PROC
will have no visible effect.
This flag defaults to False
, in which case updates to the record
which don’t change its value will be discarded. In particular this means
that such updates don’t call validate or on_update.
blocking
Only available on OUT records. When set to True
the record will set the
PACT
field when processing is ongoing. This means that caput
and
similar tools can correctly wait for processing to complete.
This flag defaults to False
, to retain compatibility with previous
versions.
See also
SetBlocking
for configuring a global default blocking value
For all of these functions any EPICS database field can be assigned a value by
passing it as a keyword argument for the corresponding field name (in upper
case) or by assigning to the corresponding field of the returned record object.
Thus the **fields
argument in all of the definitions below refers to both
the optional keyword arguments listed above and record field names.
All functions return a wrapped ProcessDeviceSupportIn
or
ProcessDeviceSupportOut
instance.
- softioc.builder.aIn(name, LOPR=None, HOPR=None, EGU=None, PREC=None, **fields)[source]
- softioc.builder.aOut(name, DRVL=None, DRVH=None, EGU=None, PREC=None, **fields)[source]
Create
ai
andao
records. The lower and upper limits for the record can be specified. Forao
records theLOPR
andHOPR
fields will be set by default to the values of theDRVL
andDRVH
fields respectively.
- softioc.builder.boolIn(name, ZNAM=None, ONAM=None, **fields)[source]
- softioc.builder.boolOut(name, ZNAM=None, ONAM=None, **fields)[source]
Create
bi
andbo
records with the specified names for false (zero) and true (one).
- softioc.builder.int64In(name, LOPR=None, HOPR=None, EGU=None, **fields)[source]
- softioc.builder.int64Out(name, DRVL=None, DRVH=None, EGU=None, **fields)[source]
Create
int64In
andint64Out
records with specified limits and units. The lower and upper display limits for the record can be specified. Forint64Out
records theLOPR
andHOPR
fields will be set by default to the values of theDRVL
andDRVH
fields respectively.
- softioc.builder.longIn(name, LOPR=None, HOPR=None, EGU=None, **fields)[source]
- softioc.builder.longOut(name, DRVL=None, DRVH=None, EGU=None, **fields)[source]
Create
longin
andlongout
records with specified limits and units. The lower and upper display limits for the record can be specified. Forlongout
records theLOPR
andHOPR
fields will be set by default to the values of theDRVL
andDRVH
fields respectively.
- softioc.builder.stringIn(name, **fields)[source]
- softioc.builder.stringOut(name, **fields)[source]
Create
stringin
andstringout
records.
- softioc.builder.mbbIn(name, *options, **fields)[source]
- softioc.builder.mbbOut(name, *options, **fields)[source]
Create
mbbi
andmbbo
records. Up to 16 options can be specified as either an option name or a tuple of two fields. The name or first field of the tuple names the option, and the second optional field is the option severity. For example:status = mbbIn('STATUS', 'OK', ('FAILING', 'MINOR'), ('FAILED', 'MAJOR'), ('NOT CONNECTED', 'INVALID'))
Severities can also be assigned using the
softioc.alarm
numerical severities if preferred.Numerical values are assigned to options sequentially from 0 to 15 and cannot be overridden.
Warning
This is a strictly incompatible change from version 2, but is now compatible with version 2 of epics_device.
- softioc.builder.Waveform(name, [value, ]**fields)
- softioc.builder.WaveformIn(name, [value, ]**fields)[source]
- softioc.builder.WaveformOut(name, [value, ]**fields)[source]
Create
waveform
records. Depending on whetherWaveformIn
orWaveformOut
is called the record is configured to behave as an IN or an OUT record, in particular on_update can only be specified when callingWaveformOut
. The nameWaveform
is an alias forWaveformIn
and exists for largely historical reasons.If
value
is specified or if an initial_value is specified (only one of these can be used) the value is used to initialise the waveform and to determine its field type and length. If no initial value is specified then the keyword argumentlength
must be used to specify the length of the waveform.The field type can be explicitly specified either by setting the
datatype
keyword to a Python type name, or by settingFTVL
to the appropriate EPICS field type name. Otherwise the field type is taken from the initial value if given, or defaults to'FLOAT'
.Note
Storing arrays of strings differs from other values. String arrays will always be assumed to be encoded as Unicode strings, and will be returned to the user as a Python list rather than a Numpy array.
The following functions generates specialised records.
- softioc.builder.Action(name, **fields)[source]
Creates a record (using
boolOut
) which will always call the on_update method when processed. Used for action records. Either the on_update or on_update_name keyword should always be passed.
- softioc.builder.longStringIn(name, **fields)[source]
- softioc.builder.longStringOut(name, **fields)[source]
Creates a Waveform record specialised to contain Python (Unicode) strings. Only accepts string values, and only returns string values. initial_value and
length
keywords accepted as per Waveform records.If no
length
or initial_value is provided a default length of 256 is used.Note
If you wish to store byte strings, use a
WaveformIn/Out
record
The following functions manage record names. The record device name must be
specified before creating records, then each record will be created with a
standard two part name of the form device:name
where the device
part is
specified by the functions below and the name
part is specified in the
record creation function.
- softioc.builder.SetDeviceName(device_name)[source]
Sets up the prefix part of the record name, referred to here as the “device” part. This function must be called before creating any records. Note that only this function need be used, function below is entirely optional.
- softioc.builder.UnsetDevice()[source]
This can optionally be called after completing the creation of records to prevent the accidential creation of records with the currently set device name.
- softioc.builder.SetBlocking(blocking)[source]
This can be used to globally set the default of the blocking flag, which will apply to all records created after this point. This allows blocking to be easily set/unset when creating groups of records.
Returns the previous value of the blocking flag, which enables code like this:
old_blocking = SetBlocking(new_blocking) create_records() SetBlocking(old_blocking)
This does not change the blocking value for any already created records.
See also
blocking for description of the flag
The following helper functions are useful when constructing links between records.
- softioc.builder.PP(record)[source]
- softioc.builder.CP(record)[source]
- softioc.builder.NP(record)[source]
- softioc.builder.MS(record)[source]
When assigned to a link field in a record these functions add the appropriate processing attributes to the link. These are not normally used.
The following attributes allow more direct access to record creation.
- softioc.builder.records
This is the
epicsdbbuilder.records
object, and is populated with functions named after each available record type. Records created with these calls are created with soft device support and Python is not involved in their processing.See Use soft records in an IOC for a full example of its usage.
- softioc.builder.ClearRecords()[source]
- This can be used to remove all created records. This means the same record
- names can be re-used for different record types. This cannot be used once the
- record database has been loaded, and is only designed to be used to help with
- testing.
Finally, the following function is used to load record definitions before starting the IOC.
- softioc.builder.LoadDatabase()[source]
This must be called exactly once after creating all the records required by the IOC and before calling
iocInit()
. After this function has been called none of the functions provided bysoftioc.builder
are usable.
Alarm Value Definitions: softioc.alarm
The following values can be passed to IN record set()
and
set_alarm()
methods, and to OUT record
set()
and
set_alarm()
.
- NO_ALARM = 0
- MINOR_ALARM = 1
- MAJOR_ALARM = 2
- INVALID_ALARM = 3
These are severity values. The default severity is
NO_ALARM
.
- softioc.alarm.READ_ALARM
- softioc.alarm.WRITE_ALARM
- softioc.alarm.HIHI_ALARM
- softioc.alarm.HIGH_ALARM
- softioc.alarm.LOLO_ALARM
- softioc.alarm.LOW_ALARM
- softioc.alarm.STATE_ALARM
- softioc.alarm.COS_ALARM
- softioc.alarm.COMM_ALARM
- softioc.alarm.TIMEOUT_ALARM
- softioc.alarm.HW_LIMIT_ALARM
- softioc.alarm.CALC_ALARM
- softioc.alarm.SCAN_ALARM
- softioc.alarm.LINK_ALARM
- softioc.alarm.SOFT_ALARM
- softioc.alarm.BAD_SUB_ALARM
- softioc.alarm.UDF_ALARM
- softioc.alarm.DISABLE_ALARM
- softioc.alarm.SIMM_ALARM
- softioc.alarm.READ_ACCESS_ALARM
- softioc.alarm.WRITE_ACCESS_ALARM
Alarm code definitions. Frankly these values aren’t terribly useful, only the severity is used for most notifications, but an alarm code needs to be specified when specifying a non zero severity.
Automatic PV logging: softioc.pvlog
Once this module has been imported all channel access writes to any PV published by this IOC will be logged by writing a suitable message to stdout. There is currently no control or customisation of this feature.
Record Support in the Python Soft IOC: softioc.device
The Python soft IOC implements EPICS device support (almost) entirely in Python. This is used to invoke Python processing in response to record processing, making it easy to integrate Python into the EPICS IOC layer.
Records are created dynamically during IOC startup before calling
iocInit()
and with the help of the softioc.builder
module can be loaded with LoadDatabase()
.
All records are created internally using methods of the PythonDevice
class, one method for each of the supported record types, however the
corresponding wrapping functions published by softioc.builder
should be
used as they configure sensible defaults and are generally easier to use.
Create IN records (used for publishing data from the IOC, the naming of the
direction is confusing) using the following softioc.builder
methods:
Create OUT records for receiving control information into the IOC using the following methods:
For all records the initial_value keyword argument can be used to specify the records value on startup.
Working with IN records
EPICS IN records are implemented as subclasses of the ProcessDeviceSupportIn
class which provides the methods documented below.
- class softioc.device.ProcessDeviceSupportIn[source]
This class is used to implement Python device support for the record types
ai
,bi
,int64in
,longin
,mbbi
and INwaveform
records.- set(value, severity=NO_ALARM, alarm=NO_ALARM, timestamp=None)[source]
Updates the stored value and severity status and triggers an update. If
SCAN
has been set to'I/O Intr'
(which is the default if thebuilder
methods have been used) then the record will be processed by EPICS and the given value will be published to all users.Optionally an explicit timestamp can be set. This is a value in seconds in the Unix epoch, as returned by
time.time()
. This argument only has any effect ifTSE = -2
was set when the record was created.Note that when calling
set()
for a waveform record the value is always copied immediately – this avoids accidents with mutable values.
- set_alarm(severity, alarm, timestamp=None)[source]
This is exactly equivalent to calling:
rec.set(rec.get(), severity, alarm, timestamp)
and triggers an alarm status change without changing the value.
- get()[source]
This returns the value last written to this record with
set()
.Note that channel access puts to a Python soft IOC input record are completely ineffective, and this includes waveform records.
- get_field(field)
This returns the named field from the record. An exception will be raised if the field cannot be found.
Note that this function can only be used after the IOC has been initialized. If you need to retrieve a field’s value before that, access it directly via an attribute e.g.
my_record.EGU
. (This will not work after the IOC is initialized)
- set_field(field, value)
This sets the given field to the given value. The value will always be converted to a Python String, which is then interpreted by EPICS as a DBF_STRING type. Note that values can be no longer than 39 bytes.
Note that this function can only be used after the IOC has been initialized. If you need to set a field’s value before that, set it directly as an attribute on the record e.g.
my_record.EGU
. (This will not work after the IOC is initialized)
Working with OUT records
- class softioc.device.ProcessDeviceSupportOut[source]
This class is used to implement Python device support for the record types
ao
,bo
,int64out
,longout
,mbbo
and OUTwaveform
records. All OUT records support the following methods.- set(value, process=True, severity=NO_ALARM, alarm=NO_ALARM)[source]
Updates the stored value and severity status. By default this will trigger record processing, and so will cause any associated on_update and validate methods to be called. If
process
isFalse
then neither of these methods will be called, but the value will still be updated.
- set_alarm(severity, alarm)[source]
This is exactly equivalent to calling:
rec.set(rec.get(), severity=severity, alarm=alarm)
and triggers an alarm status change without changing the value.
- get_field(field)
This returns the named field from the record. An exception will be raised if the field cannot be found.
Note that this function can only be used after the IOC has been initialized. If you need to retrieve a field’s value before that, access it directly via an attribute e.g.
my_record.EGU
. (This will not work after the IOC is initialized)
- set_field(field, value)
This sets the given field to the given value. The value will always be converted to a Python String, which is then interpreted by EPICS as a DBF_STRING type. Note that values can be no longer than 39 bytes.
Note that this function can only be used after the IOC has been initialized. If you need to set a field’s value before that, set it directly as an attribute on the record e.g.
my_record.EGU
. (This will not work after the IOC is initialized)