Answers to exercises, assignments, and questions¶
1. Installing e3¶
Exercises¶
None
Assignments¶
Understanding makefiles and git submodules.
git submodules are used to link from an e3 wrapper to the module source code repository. The
make init
step performs the followinggit submodule
commands:git submodule init fug/ git submodule update --init --recursive fug/
You should end up with the following directories in your e3 installation location. Your host name will be different and the installation location may be different from the example below.
[test-vm-wl02] 19:03 $ ls -1 /epics base-7.0.5 base-7.0.6.1
2. An e3 IOC¶
Exercises¶
None
Assignments¶
A brief description of each expression meaning, function, and/or action follows, along with references for more details.
require
is the keyword used by the require module to indicate that a module needs to be loaded into this IOC. When the IOC starts, the require module will load all modules identified in the startup script, along with any dependencies.E3_CMD_TOP
is the path to the directory holding the startup script used to launch the IOC.system
is an IOC function used to execute an operating system command. EPICS system utility commandiocshLoad
loads a startup script snippet. This allows each module to define the required IOC startup script commands for the module in a file which is loaded by the IOC. EPICS iocshLoad descriptioniocInit
tells the IOC to perform all initialisation tasks, including driver and database initialisation. All database files must be loaded prior to callingiocInit
. iocInitiocInit
must be executed to start the IOC.iocsh
addsiocInit
to the generated startup script if it is not present in the user-provided startup script.>
is the output redirect command. It will send anystdout
output to the location following the>
. If it is writing to a file, it will overwrite any existing contents of the file. Use>>
to append to any existing contents in a file. Redirection commands<
is the input redirect command. In an IOC, this is used to read in the contents of another file and execute those commands in the IOC shell. We recommend usingiocshLoad
instead of<
. Redirection commands
The short answer to this question is “It depends.”.
In some cases, placing commands in a different order in the startup script makes no difference. This is true for commands that do not have any dependence on previous commands, including loading of database files. It also applies to the
iocshLoad
commands for startup script snippets, assuming the snippets are all independent of each other (e.g. referencing different devices).Any commands that call driver functions directly and depend on previous calls having been made (e.g. to create a named asyn port) need to be called in a specific order so that the required code has been executed and objects created and/or memory allocated in the correct order.
iocInit
must not be called until all databases and database definitions have been loaded, as no new records can be added afteriocInit
has run.
3. Installing other versions of modules¶
Exercises¶
Assignments¶
The command is:
$ make existent LEVEL=4
The output should look something like the following, with the
LEVEL=4
selection additionally (compared withLEVEL=3
) showing the installed files for each architecture in thelib
directory.[iocuser@host:e3-stream]$ make existent LEVEL=4 /epics/base-7.0.6.1/require/4.0.0/siteMods/stream └── 2.8.18+0 ├── dbd │ └── stream.dbd ├── include │ ├── devStream.h │ ├── MacroMagic.h │ ├── StreamBuffer.h │ ├── StreamBusInterface.h │ ├── StreamCore.h │ ├── StreamError.h │ ├── StreamFormatConverter.h │ ├── StreamFormat.h │ └── StreamProtocol.h ├── lib │ ├── linux-corei7-poky │ │ ├── libstream.so │ │ └── stream.dep │ ├── linux-ppc64e6500 │ │ ├── libstream.so │ │ └── stream.dep │ └── linux-x86_64 │ ├── libstream.so │ └── stream.dep ├── SetSerialPort.iocsh └── stream_meta.yaml 7 directories, 18 files [iocuser@host:e3-stream]$
make init
performs the following functions to set up the e3 wrapper and submodule for the subsequent steps:de-initialise and remove any stored data for the submodule
re-initialise the submodule
clone the submodule
update the submodule to the required reference
enter the submodule directory and clones the reference provided in the wrapper configuration file (
CONFIG_MODULE
orCONFIG_MODULE_DEV
)
e3 installed module names must consist only of:
lower-case characters in the a-z range (no accents or Unicode characters)
numbers
the underscore
_
character
The module name must begin with a character between ‘a’ and ‘z’.
Note
There are no restrictions on the repository or wrapper names. However, e3 best practice is to make the wrapper name, and the repository names/URLs, consistent with the installed module name where possible. This is not always possible for community-sourced modules, where upper-case characters are often used.
make uninstall
The commands can be combined as:
$ make build install
Alternatively, the
make rebuild
command will call the following targets in order:clean
build
install
require will default to loading the latest version that matches the
major.minor.patch
format, as these are considered the production releases of the module. To load a version that does not match this format, specify the version name explicitly when starting the IOC:[iocuser@host:e3-stream]$ iocsh -r stream,e3training
4. Startup scripts in e3¶
Exercises¶
First IOC¶
You should see lines like the following in the output of your IOC that show the variables being defined. These are created by
iocsh
, not by the startup script.[iocuser@host:cmds]$ iocsh 1.cmd <snip> # Set E3_IOCSH_TOP for the absolute path where iocsh is executed. epicsEnvSet E3_IOCSH_TOP "/home/iocuser/git/e3-training-material/4_startup_scripts_in_e3/cmds" <snip> # Set E3_CMD_TOP for the absolute path where 1.cmd exists epicsEnvSet E3_CMD_TOP "/home/iocuser/git/e3-training-material/4_startup_scripts_in_e3/cmds" <snip>
The definition of
E3_IOCSH_TOP
will change to the directory from which you calliocsh
.The definition of
E3_CMD_TOP
will not change, as it is the path to the startup script file.The following additional modules will be loaded as direct dependencies of stream:
asyn
calc
pcre
Additional modules that are indirect dependencies (via the direct dependencies) are also loaded:
sscan
sequencer
You may see a warning like
Warning: environment variable IOCNAME is not set.
IOCNAME
is an important variable that is used to uniquely identify the IOC. In particular, it is used to define a number of status PVs for the IOC that allow you to read what modules are loaded, as well as what versions of those modules are loaded. For example, if you start an IOC in one terminal with[iocuser@host:~]$ IOCNAME=my_ioc iocsh -r stream
and then in a separate terminal run
[iocuser@host:~]$ pvget my_ioc:MODULES my_ioc:MODULES 2022-05-06 16:21:51.233 [require, asyn, sequencer, sscan, calc, pcre, stream]
The second warning is:
drvStreamInit: Warning! STREAM_PROTOCOL_PATH not set. Defaults to "."
This is referring to a specific streamdevice requirement for the
STREAM_PROTOCOL_PATH
environment variable so that it knows where to search for protocol files. This will cause run-time issues with the IOC if there are protocol files that the IOC cannot find.It is good practice to check the IOC startup messages for any warnings, and make sure that you understand the implications of any warnings. Some, like the one above, can be safely ignored, but others may have an impact on the operation of the IOC.
Second IOC¶
The official description of the IOC initialisation process is here.
If you delete the
iocInit()
line from the startup script,iocsh
will add it in to the generated startup script used to start the IOC.If you comment out the
iocInit()
line:#iocInit()
then
iocsh
will not add the iocInit command, and the IOC will be only partially started, asiocInit()
will not be executed. You can runiocInit()
yourself from the IOC shell prompt to complete the IOC startup.There are two warnings in this IOC. They are described above.
The second script establishes a communications link to the simulator, so you should expect to see some information printed to the simulator console indicating that the connection has occurred.
IOC the three¶
E3_CMD_TOP
is used to define the value of theTOP
environment variable.epicsEnvSet("TOP","$(E3_CMD_TOP)/..")
TOP
is then used as the location for a number of other files in other commands in the startup script.random.bash
generates a random number and stores it inrandom.cmd
.random.cmd
is an IOC startup script snippet that sets an environment variable to the generated random number.The stream protocol file is
db/example_motor.proto
. This is listed in the INP or OUT field each of the records indb/example_motor.db
:field( INP, "@example_motor.proto read_position $(PORT)")
There should be some diagnostic printouts in the simulator reflecting the communications from the IOC.
For the fourth¶
You can locate the PV using
localhost-1593 > dbgrep *HEART*
This PV value increments once per second while the IOC is running. It can be used by other applications to confirm that the IOC is executing by monitoring for continuous changes in the value.
iocAdminSoft.db
is located in thedb
directory in theiocstats
module in your e3 environment. On the shared file system, the location is:[iocuser@host:~]$ ls /epics/base-7.0.6.1/require/4.0.0/siteMods/iocstats/3.1.16+0/db/iocAdminSoft.db /epics/base-7.0.6.1/require/4.0.0/siteMods/iocstats/3.1.16+0/db/iocAdminSoft.db
require generates a database search path variable,
EPICS_DB_INCLUDE_PATH
, which is used by thedbLoadRecords
command as the list of paths to search for database files.localhost-1593 > epicsEnvShow("EPICS_DB_INCLUDE_PATH") EPICS_DB_INCLUDE_PATH=.:/epics/base-7.0.6.1/require/4.0.0/siteMods/iocstats/3.1.16+0/db:/epics/base-7.0.6.1/require/4.0.0/siteMods/calc/3.7.4+1/db:/epics/base-7.0.6.1/require/4.0.0/siteMods/sscan/2.11.4+0/db:/epics/base-7.0.6.1/require/4.0.0/siteMods/asyn/4.42.0+0/db localhost-1593 >
Assignments¶
The list of parameters that can be passed to each module is usually defined in the startup script snippet associated with each module. Refer to:
iocStats.iocsh
autosave.iocsh
recsync.iocsh
from each module.
Most module startup script snippets expect something like an IOCNAME variable, as this is used to create the fully expanded PV names.
Some variables can have defaults defined in the startup script snippet, which can be overridden. See the
recsync.iocsh
snippet for examples of this, such as:var(reccastTimeout, "$(TIMEOUT=5.0)")
The
autosave.iocsh
file has a long list of variables that can be defined. Most are optional. Refer to the documentation at the top of theautosave.iocsh
file for the list of variables and their functions.Potential improvements include:
commenting.
a production IOC would not use random numbers for PV names, as clients would need to be updated each time the IOC is restarted.
using the
essioc
module to load the standard set of IOC modules (for ESS IOCs), which includes:iocstats
autosave
recsync
auth
caputlog
Add the following lines before the
iocInit
line:epicsEnvSet("IOCNAME2", "$(P)-2") epicsEnvSet("PORT2", "MOTOR2") drvAsynIPPortConfigure("$(PORT2)", "127.0.0.1:9998", 0, 0, 0) dbLoadRecords("$(TOP)/db/example_motor.db", "P=$(IOCNAME2),S=:,PORT=$(PORT2)")
Note how the same commands are used, the only changes are in the values of the variables so that the port name and PV names are unique.
You can run another instance of lewis on a separate port with:
lewis -k lewis.examples example_motor -p "stream: {bind_address: localhost, port: 9000}"
5. Anatomy of an e3 module¶
Exercises¶
None
Assignments¶
e3 make targets are defined a number of locations, including:
/epics/base-7.0.6.1/require/4.0.0/configure/RULES_*
/epics/base-7.0.6.1/require/4.0.0/tools/driver.makefile
Running
make help
gives a list of the commonly used targets.Create a
CONFIG_MODULE.local
file in thee3-<module>/configure
directory, and set the newEPICS_MODULE_TAG
value in this file. This will override the value inCONFIG_MODULE
.It is possible for the
git status
to not show a completely clean response, as it may identify two potential changes, one to the submodule contents, and one identifying the newCONFIG_MODULE.local
file as being untracked. This is OK.[iocuser@localhost:e3-iocStats]$ git status HEAD detached at 7.0.6.1-4.0.0/3.1.16+4-70c2311-20220316T161418 Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: iocStats (new commits) Untracked files: (use "git add <file>..." to include in what will be committed) configure/CONFIG_MODULE.local no changes added to commit (use "git add" and/or "git commit -a") [iocuser@localhost:e3-iocStats]$ cat configure/CONFIG_MODULE.local EPICS_MODULE_TAG:=tags/3.1.15 [iocuser@localhost:e3-iocStats]$
Another method of changing the reference is to define it on the command line when issuing the
make init
command:[iocuser@localhost:e3-iocStats]$ make init EPICS_MODULE_TAG:=tags/3.1.15
A
p0
patch uses the paths exactly as defined in the patch file, while thep1
patch strips any leading slash from the paths in the patch file, making it a relative path. Run theman patch
command and look for the documentation for the-p
option.For details of what happens behind the scenes, see the
RULES_PATCH
andDEFINES_FT
in/epics/base-7.0.6.1/require/4.0.0/configure
for the actual commands being executed.
6. Variables within e3¶
Exercises¶
Variables created by require¶
Add the following to the top of the IOC startup script:
require stream
There will be a few new <module>_DIR environment variables:
<snip> localhost-1593 > epicsEnvShow <snip> asyn_DIR=/epics/base-7.0.6.1/require/4.0.0/siteMods/asyn/4.42.0+0/ pcre_DIR=/epics/base-7.0.6.1/require/4.0.0/siteMods/pcre/8.44.0+0/ stream_DIR=/epics/base-7.0.6.1/require/4.0.0/siteMods/stream/2.8.22+0/ sequencer_DIR=/epics/base-7.0.6.1/require/4.0.0/siteMods/sequencer/2.2.9+0/ sscan_DIR=/epics/base-7.0.6.1/require/4.0.0/siteMods/sscan/2.11.4+0/ calc_DIR=/epics/base-7.0.6.1/require/4.0.0/siteMods/calc/3.7.4+1/ <snip>
asyn, pcre, and calc are all dependencies of stream, while sscan and sequencer are dependencies of calc. All of these modules must then be loaded, and require will then specify the respective paths.
EPICS variables, parameters, and environment variables¶
Single variables can be printed by passing the variable name to
epicsEnvShow
:localhost-1593 > epicsEnvShow("stream_DIR") stream_DIR=/epics/base-7.0.6.1/require/4.0.0/siteMods/stream/2.8.22+0/ localhost-1593 >
From the IOC’s perspective, the parentheses
()
and braces{}
are considered to be equivalent. See here for more information.Unix shells do not require the parentheses or braces when referencing an environment variable. The name is prefixed with the
$
symbol only. Braces can also be used around the variable name in a Unix shell. See the ‘Brace Expansion’ section on the bash man page.The
$(command)
form in Bash creates a subshell and executes thecommand
part, so has a completely different effect to the EPICS usage. See the ‘Command Substitution’ section in the bash man page.Apart from the fact that the startup script includes some
epicsEnvShow
commands, the main difference is that the version using thech6.cmd
file sets theE3_CMD_TOP
variable to the path for the startup script file.The
stream
module has thestreamDebug
variable.
Assignments¶
The
asyn_DB
EPICS environment variable defines the location for asyn databases.The
system
command can be used to access shell commands.localhost-1593 > system "ls $(asyn_DB)" asynFloat64TimeSeries.db asynInt32TimeSeries.db asynRecord.db localhost-1593 >
Use the
--debug
flag tomake
:[iocuser@localhost:e3-asyn]$ make --debug vars # --- snip snip --- Reading makefiles... Updating goal targets.... File `vars' does not exist. File `header' does not exist. Must remake target `header'. Invoking recipe from /epics/base-7.0.6.1/require/4.0.0/configure/RULES_VARS:19 to update target `header'.<snip>
The
Invoking recipe
line provides a clue to the location of thevars
make rule.In the
/epics/base-7.0.6.1/require/4.0.0/configure/RULES_VARS
file, thevars
rule is as follows:## Print relevant environment variables .PHONY: vars vars: header $(foreach v, $(filter-out %_DEP_VERSION,$(E3_MODULES_VARIABLES)), $(info $(v) = $($(v)))) @#noop $(foreach v, $(filter %_DEP_VERSION,$(E3_MODULES_VARIABLES)), $(info $(v) = $($(v)))) @#noop
7. Understanding module dependence¶
Exercises¶
Dependent environment variables¶
There are several possible reasons to use a CONFIG_MODULE.local
file instead
of directly modifying CONFIG_MODULE
. One is that it keeps the git status
clean, which can make one feel better.
Another reason is to allow for temporary site-specific changes while preserving the community wrapper.
Finally, another possible reason is to synchronise between multiple modules: if you store your wrappers in a common location:
[iocuser@host:e3]$ tree -L 1 modules/
modules/
|-- area
|-- bi
|-- CONFIG_MODULE.local
|-- communication
|-- core
|-- devices
|-- ecat
|-- ifc
|-- mps
|-- ps
|-- psi
|-- rf
|-- ts
`-- vac
then you can modify a single file in order to update the dependency versions of every module.
Updating a dependency¶
The expression
require stream
in your startup script will load the highest numerical version available. So if you have the following versions installed[iocuser@host:e3-stream]$ make existent LEVEL=1 /epics/base-7.0.6.1/require/4.0.0/siteMods/stream |-- 2.8.22+0 `-- 2.8.23+0
then
require stream
will load2.8.23+0
.In the first and third cases, where you load stream
2.8.22
, then the version of asyn that is loaded is4.42.0
. This can be seen in the output of the IOC upon startup, or by looking at the following PVs that are generated by require:localhost-25251 > dbgrep *_VER REQMOD:localhost-25251:MOD_VER REQMOD:localhost-25251:require_VER REQMOD:localhost-25251:asyn_VER REQMOD:localhost-25251:sequencer_VER REQMOD:localhost-25251:sscan_VER REQMOD:localhost-25251:calc_VER REQMOD:localhost-25251:pcre_VER REQMOD:localhost-25251:stream_VER
In the middle case, it is asyn 4.41.0 that is loaded, as specified in
CONFIG_MODULE.local
during the build and install of that version.If you run
iocsh -r stream -r asyn
, then it should load fine. However, if you runiocsh -r asyn -r stream
(with the versions installed as in this chapter), then the IOC will fail to start up:[iocuser@host:~]$ iocsh -r asyn -r stream # --- snip snip --- require asyn Module asyn version 4.42.0+0 found in /epics/base-7.0.6.1/require/4.0.0/siteMods/asyn/4.42.0+0/ Loading library /epics/base-7.0.6.1/require/4.0.0/siteMods/asyn/4.42.0+0/lib/linux-x86_64/libasyn.so Loaded asyn version 4.42.0+0 Loading dbd file /epics/base-7.0.6.1/require/4.0.0/siteMods/asyn/4.42.0+0/dbd/asyn.dbd Calling function asyn_registerRecordDeviceDriver Loading module info records for asyn require stream Module stream version 2.8.23+0 found in /epics/base-7.0.6.1/require/4.0.0/siteMods/stream/2.8.23+0/ Module stream depends on asyn 4.41.0+0 Conflict between requested asyn version 4.41.0+0 and already loaded version 4.42.0+0. Aborting startup script
This is due to the order in which the modules are loaded. First, we load the latest numeric version of asyn (4.42.0), followed by the latest numeric version of stream (2.8.18). However, that version of stream depends on a different version of asyn than is loaded, and due to this incompatibility, the IOC shuts down.
Note that this is one example of what is meant by the fact that require can only perform rudimentary dependency resolution.
The dependency information is stored in the file
$module.dep
:[iocuser@host:~]$ cat /epics/base-7.0.6.1/require/4.0.0/siteMods/stream/2.8.22+0/lib/linux-x86_64/stream.dep # Generated file. Do not edit. asyn 4.42.0+0 calc 3.7.4+1 pcre 8.44.0+0 [iocuser@host:~]$ cat /epics/base-7.0.6.1/require/4.0.0/siteMods/stream/2.8.23+0/lib/linux-x86_64/stream.dep # Generated file. Do not edit. asyn 4.41.0+0 calc 3.7.4+1 pcre 8.44.0+0
Dependency resolution limitations¶
If you were to release a new version of asyn after the IOC has been developed then as in the example above, you will have an inconsistency in loaded and dependent versions of asyn, causing the IOC to fail to start up.
This is a challenge for a maintainer of a shared environment because this means that a functional IOC can fail due to changes in the environment that seem unrelated to the IOC.
In this case, the fix is simple. An IOC should only load top-level modules (i.e. stream, but not asyn). However, this does not address all cases
Whence cometh the dependencies¶
calc is referenced in the file
devscalcoutStream.c
, and pcre is referenced inRegexpConverter.cc
.
Assignments¶
e3-fug
is contained in the groupps
(loaded with-s
):[iocuser@host:e3]$ ./e3.bash -so vars >> Vertical display for the selected modules : Modules List 0 : ps/e3-caenelsmagnetps 1 : ps/e3-fug # Here it is! 2 : ps/e3-sairem 3 : ps/e3-sorensen 4 : ps/e3-tdklambdagenesys 5 : ps/e3-magnetps 6 : ps/e3-caenelfastps 7 : ps/e3-caensyproxy
If you install it as a part of a group, you are certain to get all of the necessary dependencies installed as well - both from the dependent groups, but also from any modules within that group that may be necessary.
The only place that it is referenced in the temporary build files is in
fug.dep
:[iocuser@host:e3-fug]$ grep -nr stream fug/O.7.0.6.1_linux-x86_64/ fug/O.7.0.6.1_linux-x86_64/fug.dep:2:stream 2.8.18+0
e3-fug
knows that stream is a dependency due to the variableREQUIRED
used infug.Makefile
:[iocuser@host:e3-fug]$ grep stream fug.Makefile REQUIRED += stream stream_VERSION=$(STREAM_DEP_VERSION)
This allows require to keep track of run-time dependencies.
8. Building an e3 module¶
Exercises¶
IOCs¶
As seen in An e3 IOC, you can use the variable
$(E3_CMD_TOP)
to refer to the directory which holdsst.cmd
.
External modules¶
make init patch
should always be run before building a new module. This will do two things:Make sure that the submodule is initialised correctly
Make sure that all of the correct patches have been applied Without these two steps, it is possible that the module you are trying to build might not build as expected, or could even fail to build at all.
Assignments¶
There is an
st.cmd
included with the repository that we can use as a basis for our e3 startup script. One possibility isrequire stream require fimscb epicsEnvSet("STREAM_PROTOCOL_PATH", "$(fimscb_DB)") epicsEnvSet(P, FIMSCB) epicsEnvSet(R, KAM) epicsEnvSet("PORT", "FIMSCB") drvAsynIPPortConfigure($(PORT), "127.0.0.1:9999", 0, 0, 0) asynOctetSetInputEos($(PORT), 0, "\r\n") asynOctetSetOutputEos($(PORT), 0, "\r") dbLoadRecords("db/fimscb.db", "P=$(P)-$(R):FimSCB:,PORT=FIMSCB") iocInit dbl > "$(TOP)/PVs.list"
Note that the fimscb module defined in this chapter does not add stream as a dependency, and so for the IOC to run correctly we need to include
require stream
in the startup script. A better solution, of course, is to add stream as a run-time dependency.One should use cookiecutter for this, same as for
e3-fimscb
. A minimal module makefile could look something like the following.where_am_I := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) include $(E3_REQUIRE_TOOLS)/driver.makefile include $(E3_REQUIRE_CONFIG)/DECOUPLE_FLAGS REQUIRED += sequencer # This, of course, should be defined in CONFIG_MODULE sequencer_VERSION:=$(SEQUENCER_DEP_VERSION) APP:=ch8App APPDB:=$(APP)/Db APPSRC:=$(APP)/src TEMPLATES += $(wildcard $(APPDB)/*.db) SOURCES += $(APPSRC)/dbSubExample.c SOURCES += $(APPSRC)/myexampleHello.c SOURCES += $(APPSRC)/sncExample.stt DBDS += $(APPSRC)/dbSubExample.dbd DBDS += $(APPSRC)/myexampleHello.dbd DBDS += $(APPSRC)/sncExample.dbd db:
One possible startup script could be the following.
require ch8 epicsEnvSet("IOCNAME", "test_ioc") dbLoadRecords("$(ch8_DB)/dbExample1.db", "user=$(IOCNAME)") dbLoadRecords("$(ch8_DB)/dbExample2.db", "user=$(IOCNAME),no=1,scan=1 Second") dbLoadRecords("$(ch8_DB)/dbSubExample.db", "user=$(IOCNAME)") iocInit seq sncExample "user=$(IOCNAME)"
If you run this IOC, you should see output like this after a few seconds.
[iocuser@host:e3-ch8]$ make cellinstall [iocuser@host:e3-ch8]$ iocsh -l cellMods st.cmd # --- snip snip --- iocRun: All initialization complete seq sncExample "user=test_ioc" sevr=info Sequencer release 2.2.9+0, compiled Fri May 7 14:04:03 2021 sevr=info Spawning sequencer program "sncExample", thread 0x189bc90: "sncExample" # Set the IOC Prompt String One epicsEnvSet IOCSH_PS1 "localhost-5721 > " # sevr=info sncExample[0]: all channels connected & received 1st monitor localhost-5721 > sncExample: Startup delay over sncExample: Changing to high sncExample: Changing to low
In order to include all of the requisite functionality, your
myexample.Makefile
should have the following lines (with$(APPSRC)
defined appropriately).TEMPLATES += $(wildcard $(APPDB)/*.db) TEMPLATES += $(wildcard $(APPDB)/*.substitutions) SOURCES += $(APPSRC)/dbSubExample.c SOURCES += $(APPSRC)/devXxxSoft.c SOURCES += $(APPSRC)/initTrace.c SOURCES += $(APPSRC)/myexampleHello.c SOURCES += $(APPSRC)/sncExample.stt SOURCES += $(APPSRC)/xxxRecord.c DBDS += $(APPSRC)/dbSubExample.dbd DBDS += $(APPSRC)/myexampleHello.dbd DBDS += $(APPSRC)/sncExample.dbd DBDS += $(APPSRC)/xxxRecord.dbd DBDS += $(APPSRC)/xxxSupport.dbd
Note that you should not include both
sncExample.stt
andsncExample.st
(why not? What error do you get if you do?).If you run
make build
now, you should see something a little bit surprising:make[4]: Entering directory `/home/iocuser/data/git/e3.pages.esss.lu.se/e3-myexample/myexample/O.7.0.6.1_linux-x86_64' /usr/bin/gcc -D_GNU_SOURCE -D_DEFAULT_SOURCE -DUSE_TYPED_RSET -D_X86_64_ -DUNIX -Dlinux -MD -O3 -g -Wall -Werror-implicit-function-declaration -mtune=generic -m64 -fPIC -I. -I../myexampleApp/src/ -I../O.7.0.6.1_Common/ -I/epics/base-7.0.6.1/require/4.0.0/siteMods/sequencer/2.2.9+0/include -I/epics/base-7.0.6.1/require/4.0.0/siteMods/sequencer/2.2.9+0/include -I/epics/base-7.0.6.1/include -I/epics/base-7.0.6.1/include/compiler/gcc -I/epics/base-7.0.6.1/include/os/Linux -c ../myexampleApp/src/xxxRecord.c ../myexampleApp/src/xxxRecord.c:21:23: fatal error: xxxRecord.h: No such file or directory #include "xxxRecord.h" ^ compilation terminated.
The issue here is that the source files
xxxRecord.c
anddevXxxSoft.c
both requirexxxRecord.h
which does not exist: it is generated at build-time fromxxxRecord.dbd
by the EPICS utilitydbdToRecordtypeH.pl
.However, the build actually works correctly! If you check the full output, you can see that the following happens a few lines later:
perl -CSD /epics/base-7.0.6.1/bin/linux-x86_64/dbdToRecordtypeH.pl -I ../myexampleApp/src/ -I ./ -I /epics/base-7.0.6.1/dbd -I. -I.. -I../O.7.0.6.1_Common -I/epics/base-7.0.6.1/require/4.0.0/siteMods/myexample/master/dbd -o xxxRecord.h ../myexampleApp/src/xxxRecord.dbd /usr/bin/gcc -D_GNU_SOURCE -D_DEFAULT_SOURCE -DUSE_TYPED_RSET -D_X86_64_ -DUNIX -Dlinux -MD -O3 -g -Wall -Werror-implicit-function-declaration -mtune=generic -m64 -fPIC -I. -I../myexampleApp/src/ -I../O.7.0.6.1_Common/ -I/epics/base-7.0.6.1/require/4.0.0/siteMods/sequencer/2.2.9+0/include -I/epics/base-7.0.6.1/require/4.0.0/siteMods/sequencer/2.2.9+0/include -I/epics/base-7.0.6.1/include -I/epics/base-7.0.6.1/include/compiler/gcc -I/epics/base-7.0.6.1/include/os/Linux -c ../myexampleApp/src/devXxxSoft.c /usr/bin/gcc -D_GNU_SOURCE -D_DEFAULT_SOURCE -DUSE_TYPED_RSET -D_X86_64_ -DUNIX -Dlinux -MD -O3 -g -Wall -Werror-implicit-function-declaration -mtune=generic -m64 -fPIC -I. -I../myexampleApp/src/ -I../O.7.0.6.1_Common/ -I/epics/base-7.0.6.1/require/4.0.0/siteMods/sequencer/2.2.9+0/include -I/epics/base-7.0.6.1/require/4.0.0/siteMods/sequencer/2.2.9+0/include -I/epics/base-7.0.6.1/include -I/epics/base-7.0.6.1/include/compiler/gcc -I/epics/base-7.0.6.1/include/os/Linux -c ../myexampleApp/src/xxxRecord.c
So the module builds correctly after all.
As for the startup script, you can look inside the directory
iocBoot/iocmyexample
at the existingst.cmd
file for inspiration, which could yield for example the following startup script for an IOC:require myexample dbLoadTemplate("$(myexample_DB)/user.substitutions") dbLoadRecords("$(myexample_DB)/dbSubExample.db", "user=jhlee") iocInit seq sncExample, "user=jhlee"
9. Other dependencies¶
Exercises¶
Fixing the dependency¶
We do not need to run
make init
ormake patch
since there is no embedded git submodule;make init
does nothing in this case, and it would be extremely weird to apply patches from your own repository to the same repository.
Assignments¶
If you look at the output from an IOC trying to load
pid.db
, you should see the following.dbLoadRecords("/home/iocuser/data/git/e3.pages.esss.lu.se/e3-mypid/cellMods/base-7.0.6.1/require-4.0.0/mypid/master/db/pid.db") Record "mypid:PID1_limits" is of unknown type "transform" Error at or before ")" in file "/home/iocuser/data/git/e3.pages.esss.lu.se/e3-mypid/cellMods/base-7.0.6.1/require-4.0.0/mypid/master/db/pid.db" line 22 Error: syntax error dbLoadRecords: failed to load '/home/iocuser/data/git/e3.pages.esss.lu.se/e3-mypid/cellMods/base-7.0.6.1/require-4.0.0/mypid/master/db/pid.db'
The database file uses the
transform
record type, which is not a part of EPICS base. How can we determine which module contains this? Consider[iocuser@host:e3-mypid]$ grep -nr "\btransform\b" /epics/base-7.0.6.1/require/4.0.0/siteMods/ --include=*.dbd /epics/base-7.0.6.1/require/4.0.0/siteMods/calc/3.7.4+1/dbd/calc.dbd:15:recordtype(transform) {
Note that this pinpoints that the record type
transform
is defined incalc.dbd
. This means that we need to also include the calc module.FETCH_BUILD_NUMBER
is a macro defined indriver.makefile
, which is the main build engine in requireEPICS base also include the function
dbLoadTemplate
which can be used to load.substitution
files instead of just.db
files (and which does so at run-time). Hence the linedbLoadTemplate("$(mypid_DB)/pid.substitutions")
will produce the same output.
If you have the
ps
modules cloned in a common directory (as would be done by runninge3.bash -s mod
), then the following will display all of the run-time dependencies.[iocuser@host:e3]$ grep -nr "^REQUIRED\b" ps --include=*.Makefile ps/e3-magnetps/magnetps.Makefile:33:REQUIRED += iocshutils ps/e3-sorensen/sorensen.Makefile:39:REQUIRED += stream ps/e3-caenelfastps/caenelfastps.Makefile:39:REQUIRED += stream ps/e3-fug/fug/fug.Makefile:39:REQUIRED += stream ps/e3-fug/fug.Makefile:39:REQUIRED += stream ps/e3-caenelsmagnetps/caenelsmagnetps.Makefile:39:REQUIRED += stream ps/e3-sairem/sairem.Makefile:39:REQUIRED += modbus ps/e3-tdklambdagenesys/tdklambdagenesys.Makefile:39:REQUIRED += stream
We can see that the only dependent modules are stream, modbus, and iocshutils. stream and modbus are pretty common dependencies, but what is iocshutils? It turns out that it includes a utility to update database definitions after the IOC has started (but before it has run
iocInit
), which is what is used in that case. See the file magnetps.iocsh.
10. Additional working modes¶
Exercises¶
Development mode¶
make existent
andmake devexistent
both run the commandtree
in the directory${EPICS_BASE}/require/${E3_REQUIRE_VERSION}/siteMods/${E3_MODULE_NAME}
. So these will only differ if any of those variables differ between the regular and_DEV
configure files.
Assignments¶
In order to change the install path used in cell mode, you need to redefine
E3_CELL_PATH
. This is best done in aCONFIG_CELL.local
file either in the configure directory, or in the parent directory of the wrapper.Alternatively, you can also just export the variable into the environment via, for example,
[iocuser@host:e3-module]$ export E3_CELL_PATH=/absolute/path/to/cellMods [iocuser@host:e3-module]$ make cellinstall
Assuming the modules are in different locations, then it is simply a matter of running
[iocuser@host:~]$ iocsh -l <path/to/cellMods_1> -l <path/to/cellMods_2> st.cmd
Note that the module search will prioritise the last specified path.
As seen before, one should create a
CONFIG_MODULE_DEV.local
file with an updatedE3_MODULE_DEV_GITURL
, which git will ignore.Technically,
make devdistclean
does two things: it runsmake devclean
and then deletes the dev source directory. However, that first step is not relevant as deleting the source tree also gets rid of the temporary files.This is not really a necessary make target, but there is some value in having a common interface for building, cleaning, installing, etc. a module.
Like all development mode tasks, there is a target for this: if you have patch files in
e3-module/patch/Site
, then runningmake devpatch
will apply them to the development source files.
11. Supplementary tools¶
Exercises¶
Starting an IOC in a procServ container¶
The syntax for
procServ
isprocServ [options] <endpoint> <command args ...>
In this case,
[options]
is-n iocsh
, which is the child process’ name.<endpoint>
is 2000, which is the port to connect to, and the output ofwhich iocsh
(i.e. the absolute path ofiocsh
) is the command to run in the child container.
Assignments¶
In order to have procServ listen to a TCP socket instead of a UDS, you simply need to change the argument to
--port
to be the port you wish to listen on. If you only intend to have a single IOC on each host, then it is possible to statically assign this in theExecStart
directive via, for example,ExecStart=/usr/bin/procServ \ --foreground \ --name=%i \ --logfile=/var/log/procServ/%i/out.log \ --info-file=/var/run/procServ/%i/info \ --ignore=^C^D \ --logoutcmd=^Q \ --chdir=/var/run/procServ/%i \ --port=2000 \ /epics/base-7.0.6.1/require/4.0.0/bin/iocsh \ /opt/iocs/e3-ioc-%i/st.cmd
If your e3 installation is on an NFS server, you should make sure that the NFS share is mounted before the IOC starts up. You can do this with an explicit call to
mount
.[iocuser@host:~]$ mount -r "nfs-host.xxx.xxx.xxx:$MOUNT_POINT" "/path/to/local/mount"
One can also add the mount points to
/etc/fstab
, which ensures that the mount will persist upon rebooting the machine. This should have an entry that looks something likenfs-host.xxx.xxx.xxx:/e3-mount-point /epics nfs ro 0 0
for a read-only mount point. See fstab for more information about the remaining syntax.
Note that it is possible to mount an NFS share via systemd, but it is suggested to use fstab unless you have a particularly complicated setup.
One issue with the above solution is that we have hard-coded the versions of EPICS base and require, as well as (in the solution to assignment 1) the procServ port.
To get around this, one can use the
EnvironmentFile
directive to load an environment file that could be deployed with the IOC to set these variables. For example, your IOC could also come with anenv.sh
file that looks likeEPICS_BASE=/epics/base-7.0.6.1 E3_REQUIRE_VERSION=4.0.0 PROCSERV_PORT=2000
and then you could modify the
Service
section to beUser=iocuser Group=iocgroup PermissionsStartOnly=true ExecStartPre=/bin/mkdir -p /var/log/procServ/%i ExecStartPre=/bin/chown -R iocuser:iocgroup /var/log/procServ/%i ExecStartPre=/bin/mkdir -p /var/run/procServ/%i ExecStartPre=/bin/chown -R iocuser:iocgroup /var/run/procServ/%i EnvironmentFile=/opt/iocs/e3-ioc-%i/env.sh ExecStart=/usr/bin/procServ \ --foreground \ --name=%i \ --logfile=/var/log/procServ/%i/out.log \ --info-file=/var/run/procServ/%i/info \ --ignore=^C^D \ --logoutcmd=^Q \ --chdir=/var/run/procServ/%i \ --port=${PROCSERV_PORT} \ ${EPICS_BASE}/require/${E3_REQUIRE_VERSION}/bin/iocsh \ /opt/iocs/e3-ioc-%i/st.cmd