Skip to content

Commit 3708bea

Browse files
First draft of rewritten creating-an-ioc
Includes extracting out the creation of publishable IOC to a separate how-to page (which is currently just a copy-paste of the text from the tutorial page)
1 parent f198e73 commit 3708bea

File tree

3 files changed

+84
-91
lines changed

3 files changed

+84
-91
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
Create a Publishable IOC
2+
--------------------------
3+
4+
As the example script above shows, a single Python script can be an IOC.
5+
However, to fit into the DLS framework for publishing IOCs in ``/dls_sw/prod`` a
6+
bit more structure is needed. I recommend at least four files as shown:
7+
8+
``Makefile``
9+
This file is necessary in order to run ``dls-release.py``, and needs to have
10+
both ``install`` and ``clean`` targets, but doesn't need to actually do
11+
anything. Thus the following content for this file is enough::
12+
13+
install:
14+
clean:
15+
16+
``start-ioc``
17+
An executable file for starting the IOC needs to be created. I recommend
18+
that this consist of the following boilerplate::
19+
20+
#!/bin/sh
21+
22+
PYIOC_VER=2-6
23+
EPICS_VER=3.14.12.3
24+
25+
PYIOC=/dls_sw/prod/R$EPICS_VER/support/pythonSoftIoc/$PYIOC_VER/pythonIoc
26+
27+
exec $PYIOC ioc_entry.py "$@"
28+
29+
Here I have given the startup script for the IOC the name ``ioc_entry.py``.
30+
This name should be replaced by any appropriate name.
31+
32+
``ioc_entry.py``
33+
I recommend that the top level Python script used to launch the IOC contain
34+
only ``pkg_resources.require`` statements, simple code to start the body
35+
of the IOC, and it should end with standard code to start the IOC. The
36+
following structure can be followed (here I've assumed that the rest of the
37+
IOC is in a single file called ``ioc_body.py``::
38+
39+
from pkg_resources import require
40+
41+
require('cothread==2.12')
42+
require('epicsdbbuilder==1.0')
43+
# Any other requires needed by this IOC
44+
45+
from softioc import softioc
46+
47+
# Do whatever makes sense to create all the PVs and get ready to go
48+
import ioc_body
49+
ioc_body.initialise()
50+
51+
# Start the IOC -- this is boilerplate
52+
builder.LoadDatabase()
53+
softioc.iocInit()
54+
55+
# If activities need to be started after iocInit, now's the time
56+
ioc_body.start()
57+
58+
softioc.interactive_ioc(globals())
59+
60+
Note that *all* requires *must* occur in this initial startup file.
61+
62+
The rest of the IOC
63+
Of course, a Python script can be structured into any number of Python
64+
modules. In the example above I have illustrated just one such module
65+
called ``ioc_body.py`` with two entry points.

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ About the documentation
5353
:maxdepth: 1
5454

5555
how-to/use-asyncio-in-an-ioc
56+
how-to/make-publishable-ioc
5657

5758
.. toctree::
5859
:caption: Explanations

docs/tutorials/creating-an-ioc.rst

Lines changed: 18 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ with two Process Variables (PVs):
1313

1414
.. literalinclude:: ../examples/example_cothread_ioc.py
1515

16-
This example script illustrates the following points.
16+
Each section is explained in detail below:
1717

1818
.. literalinclude:: ../examples/example_cothread_ioc.py
1919
:start-after: # Import
@@ -23,21 +23,30 @@ The :mod:`softioc` library is part of ``pythonIoc``. The two submodules
2323
:mod:`softioc.softioc` and :mod:`softioc.builder` provide the basic
2424
functionality for Python soft IOCs and are the ones that are normally used.
2525

26+
:mod:`cothread` is one of the two possible libraries the IOC can use for
27+
asynchronous operations.
28+
(see :doc:`../how-to/use-asyncio-in-an-ioc` for the other option)
29+
30+
2631

2732
.. literalinclude:: ../examples/example_cothread_ioc.py
2833
:start-after: # Create
2934
:end-before: # Boilerplate
3035

3136
PVs are normally created dynamically using :mod:`softioc.builder`. All PV
32-
creation must be done before initialising the IOC. We define `on_update` for
33-
``ao`` such that whenever we set ``ao``, ``ai`` will be set to the same value.
37+
creation must be done before initialising the IOC. We define a lambda function for
38+
`on_update` on ``ao`` such that whenever we set ``ao``, ``ai`` will be set to the
39+
same value.
3440

3541
.. literalinclude:: ../examples/example_cothread_ioc.py
3642
:start-after: # Boilerplate
3743
:end-before: # Start
3844

39-
Once PVs have been created then the associated EPICS database can be created
40-
and loaded into the IOC and then the IOC can be started.
45+
46+
Initializing the IOC is simply a matter of calling two functions:
47+
:func:`~softioc.builder.LoadDatabase` and :func:`~softioc.softioc.iocInit`,
48+
which must be called in this order. After calling
49+
:func:`~softioc.builder.LoadDatabase` it is no longer possible to create PVs.
4150

4251
.. literalinclude:: ../examples/example_cothread_ioc.py
4352
:start-after: # Start
@@ -68,79 +77,14 @@ can be run to observe the increasing value of ``ai``::
6877

6978
And the :func:`~softioc.softioc.dbpf` method allows data to be set and to observe
7079
the functionality of the lambda passed to `on_update` . We set the value on ``ao``
71-
and read the value on ``ai`` (exact values may vary based on time between commands)::
80+
and read the value on ``ai`` (exact values will vary based on time taken)::
7281

82+
>>> dbgf("MY-DEVICE-PREFIX:AI")
83+
DBF_DOUBLE: 15
7384
>>> dbpf("MY-DEVICE-PREFIX:AO","999")
7485
DBF_DOUBLE: 999
7586
>>> dbgf("MY-DEVICE-PREFIX:AI")
76-
DBF_DOUBLE: 1024
77-
78-
79-
Creating a Publishable IOC
80-
--------------------------
81-
82-
As the example script above shows, a single Python script can be an IOC.
83-
However, to fit into the DLS framework for publishing IOCs in ``/dls_sw/prod`` a
84-
bit more structure is needed. I recommend at least four files as shown:
85-
86-
``Makefile``
87-
This file is necessary in order to run ``dls-release.py``, and needs to have
88-
both ``install`` and ``clean`` targets, but doesn't need to actually do
89-
anything. Thus the following content for this file is enough::
90-
91-
install:
92-
clean:
93-
94-
``start-ioc``
95-
An executable file for starting the IOC needs to be created. I recommend
96-
that this consist of the following boilerplate::
97-
98-
#!/bin/sh
99-
100-
PYIOC_VER=2-6
101-
EPICS_VER=3.14.12.3
102-
103-
PYIOC=/dls_sw/prod/R$EPICS_VER/support/pythonSoftIoc/$PYIOC_VER/pythonIoc
104-
105-
exec $PYIOC ioc_entry.py "$@"
106-
107-
Here I have given the startup script for the IOC the name ``ioc_entry.py``.
108-
This name should be replaced by any appropriate name.
109-
110-
``ioc_entry.py``
111-
I recommend that the top level Python script used to launch the IOC contain
112-
only ``pkg_resources.require`` statements, simple code to start the body
113-
of the IOC, and it should end with standard code to start the IOC. The
114-
following structure can be followed (here I've assumed that the rest of the
115-
IOC is in a single file called ``ioc_body.py``::
116-
117-
from pkg_resources import require
118-
119-
require('cothread==2.12')
120-
require('epicsdbbuilder==1.0')
121-
# Any other requires needed by this IOC
122-
123-
from softioc import softioc
124-
125-
# Do whatever makes sense to create all the PVs and get ready to go
126-
import ioc_body
127-
ioc_body.initialise()
128-
129-
# Start the IOC -- this is boilerplate
130-
builder.LoadDatabase()
131-
softioc.iocInit()
132-
133-
# If activities need to be started after iocInit, now's the time
134-
ioc_body.start()
135-
136-
softioc.interactive_ioc(globals())
137-
138-
Note that *all* requires *must* occur in this initial startup file.
139-
140-
The rest of the IOC
141-
Of course, a Python script can be structured into any number of Python
142-
modules. In the example above I have illustrated just one such module
143-
called ``ioc_body.py`` with two entry points.
87+
DBF_DOUBLE: 1010
14488

14589

14690
Creating PVs
@@ -186,22 +130,5 @@ reading and writing the current value of the record. For IN records calling
186130
(all IN records are by default created with ``SCAN='I/O Intr'``).
187131

188132

189-
Initialising the IOC
190-
--------------------
191-
192-
This is simply a matter of calling two functions:
193-
:func:`~softioc.builder.LoadDatabase` and :func:`~softioc.softioc.iocInit`,
194-
which must be called in this order. After calling
195-
:func:`~softioc.builder.LoadDatabase` it is no longer possible to create PVs.
196-
197-
It is sensible to start any server background activity after the IOC has been
198-
initialised by calling :func:`~softioc.softioc.iocInit`. After this has been
199-
done (:class:`cothread.Spawn` is recommended for initiating persistent background
200-
activity) the top level script must pause, as as soon as it exits the IOC will
201-
exit. Calling :func:`~softioc.softioc.interactive_ioc` is recommended for this
202-
as the last statement in the top level script.
203-
204-
205-
.. _numpy: http://www.numpy.org/
206133
.. _cothread: https://github.com/dls-controls/cothread
207134
.. _epicsdbbuilder: https://github.com/Araneidae/epicsdbbuilder

0 commit comments

Comments
 (0)