Creating an IOC¶
THIS NEEDS UPDATING
Using pythonSoftIoc
¶
Probably the best way to use pythonSoftIoc
is to start by copying fragments
of a simple example such as CS-DI-IOC-02
. This consists of the following
elements:
A startup shell script
start-ioc
which launches the soft IOC using a production build ofpythonSoftIoc
. This script typically looks like this:#!/bin/sh PYIOC=/path/to/pythonSoftIoc/pythonIoc cd "$(dirname "$0")" exec $PYIOC start_ioc.py "$@"
The startup Python script. This establishes the essential component versions (apart from the
pythonSoftIoc
version), performs the appropriate initialisation and starts the IOC running. The following template is a useful starting point:from pkg_resources import require require('cothread==2.12') require('epicsdbbuilder==1.0') # Import the basic framework components. from softioc import softioc, builder import cothread # Import any modules required to run the IOC import ... # Boilerplate get the IOC started builder.LoadDatabase() softioc.iocInit() # Start processes required to be run after iocInit ... # Finally leave the IOC running with an interactive shell. softioc.interactive_ioc(globals())
Note that the use of
require
is specific to DLS, and you may have a different way of managing your installations.
Introduction¶
The Python Soft IOC consists of two components: a command pythonIoc
and an
associated library softioc
. The pythonIoc
command consists of a bare
EPICS IOC linked together with the DLS Python interpreter and configured so that
startup arguments are interpreted by the Python interpreter – this means that
when pythonIoc
is run it behaves the same as running dls-python
.
Scripts run from within pythonIoc
differ from standard Python scripts in one
detail: they have the ability to create and publish PVs through the
softioc
library. Typically both cothread
and
epicsdbbuilder
will be recruited to help: cothread
is used for
dispatching OUT record processing callback methods, epicsdbbuilder
is
used for constructing records during IOC initialisation.
An EPICS IOC created with the help of pythonIoc
and softioc
is
referred to as a “Python soft IOC”. The code below illustrates a simple IOC
with one PV:
# DLS requires
from pkg_resources import require
require('cothread==2.12')
require('epicsdbbuilder==1.0')
# Import basic softioc framework
from softioc import softioc, builder
# Create PVs
builder.SetDeviceName('TS-TEST-TEST-01')
builder.stringIn('TEST', initial_value = 'This is a test')
# Run the IOC. This is boilerplate, and must always be done in this order,
# and must always be done after creating all PVs.
builder.LoadDatabase()
softioc.iocInit()
softioc.interactive_ioc(globals())
This example script illustrates the following points.
The use of
pkg_resources.require
is standard across all use of thedls-python
Python interpreter at Diamond, and in this example we are using bothcothread
andepicsdbbuilder
. Of course, in an officially published IOC specific versions must be specified, in this example I’m using the most recent versions at the time of writing.The
softioc
library is part ofpythonIoc
and is automatically added to the path. The two submodulessoftioc.softioc
andsoftioc.builder
provide the basic functionality for Python soft IOCs and are the ones that are normally used.PVs are normally created dynamically using
softioc.builder
. All PV creation must be done before initialising the IOC.Once PVs have been created then the associated EPICS database can be created and loaded into the IOC and then the IOC can be started.
Finally the application must refrain from exiting until the IOC is no longer needed. The
interactive_ioc()
runs a Python interpreter shell with a number of useful EPICS functions in scope, and passingglobals()
through can allow interactive interaction with the internals of the IOC while it’s running. The alternative is to call something likecothread.WaitForQuit()
or some othercothread
blocking action.
Creating a Publishable IOC¶
As the example script above shows, a single Python script can be an IOC.
However, to fit into the DLS framework for publishing IOCs in /dls_sw/prod
a
bit more structure is needed. I recommend at least four files as shown:
Makefile
This file is necessary in order to run
dls-release.py
, and needs to have bothinstall
andclean
targets, but doesn’t need to actually do anything. Thus the following content for this file is enough:install: clean:
start-ioc
An executable file for starting the IOC needs to be created. I recommend that this consist of the following boilerplate:
#!/bin/sh PYIOC_VER=2-6 EPICS_VER=3.14.12.3 PYIOC=/dls_sw/prod/R$EPICS_VER/support/pythonSoftIoc/$PYIOC_VER/pythonIoc exec $PYIOC ioc_entry.py "$@"
Here I have given the startup script for the IOC the name
ioc_entry.py
. This name should be replaced by any appropriate name.ioc_entry.py
I recommend that the top level Python script used to launch the IOC contain only
pkg_resources.require
statements, simple code to start the body of the IOC, and it should end with standard code to start the IOC. The following structure can be followed (here I’ve assumed that the rest of the IOC is in a single file calledioc_body.py
:from pkg_resources import require require('cothread==2.12') require('epicsdbbuilder==1.0') # Any other requires needed by this IOC from softioc import softioc # Do whatever makes sense to create all the PVs and get ready to go import ioc_body ioc_body.initialise() # Start the IOC -- this is boilerplate builder.LoadDatabase() softioc.iocInit() # If activities need to be started after iocInit, now's the time ioc_body.start() softioc.interactive_ioc(globals())
Note that all requires must occur in this initial startup file.
- The rest of the IOC
Of course, a Python script can be structured into any number of Python modules. In the example above I have illustrated just one such module called
ioc_body.py
with two entry points.
Creating PVs¶
See the documentation of softioc.builder
for details, but an overview is
provided here.
PVs are created internally and dynamically using functionality provided by
epicsdbbuilder
, which in this context simply provides mechanisms for
creating .db
files, but softioc.builder
also binds each created PV to
a special Python
device – this allows PV processing to be hooked into
Python support.
PV creation must be done in two stages: first the device name must be set by
calling SetDeviceName()
. After this PVs can be created
by calling any of the following PV creation functions:
These functions create, respectively, Python
device bound records of the
following types:
ai
,ao
,bi
,bo
,longin
,longout
,mbbi
,mbbo
,stringin
,stringout
,waveform
Occasionally it may be desirable to create a soft record without Python
device support, particularly if any other record type is required. This can be done using the corresponding record creation
functions provided as methods of softioc.builder.records
. For example, if a calc
record is required then this can be created by calling
softioc.builder.records.calc
.
For all records created by these methods both
get()
and
set()
methods are available for
reading and writing the current value of the record. For IN records calling
set()
will trigger a record update
(all IN records are by default created with SCAN='I/O Intr'
).
Initialising the IOC¶
This is simply a matter of calling two functions:
LoadDatabase()
and iocInit()
,
which must be called in this order. After calling
LoadDatabase()
it is no longer possible to create PVs.
It is sensible to start any server background activity after the IOC has been
initialised by calling iocInit()
. After this has been
done (cothread.Spawn
is recommended for initiating persistent background
activity) the top level script must pause, as as soon as it exits the IOC will
exit. Calling interactive_ioc()
is recommended for this
as the last statement in the top level script.