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-iocwhich 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
pythonSoftIocversion), 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
requireis 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.requireis standard across all use of thedls-pythonPython interpreter at Diamond, and in this example we are using bothcothreadandepicsdbbuilder. 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
softioclibrary is part ofpythonIocand is automatically added to the path. The two submodulessoftioc.softiocandsoftioc.builderprovide 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 othercothreadblocking 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:
MakefileThis file is necessary in order to run
dls-release.py, and needs to have bothinstallandcleantargets, but doesn’t need to actually do anything. Thus the following content for this file is enough:install: clean:
start-iocAn 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.pyI recommend that the top level Python script used to launch the IOC contain only
pkg_resources.requirestatements, 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.pywith 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.