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 initstep performs the followinggit submodulecommands: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.
requireis 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_TOPis the path to the directory holding the startup script used to launch the IOC.systemis an IOC function used to execute an operating system command. EPICS system utility commandiocshLoadloads 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 descriptioniocInittells the IOC to perform all initialisation tasks, including driver and database initialisation. All database files must be loaded prior to callingiocInit. iocInitiocInitmust be executed to start the IOC.iocshaddsiocInitto the generated startup script if it is not present in the user-provided startup script.>is the output redirect command. It will send anystdoutoutput 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 usingiocshLoadinstead 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
iocshLoadcommands 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.
iocInitmust not be called until all databases and database definitions have been loaded, as no new records can be added afteriocInithas 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=4selection additionally (compared withLEVEL=3) showing the installed files for each architecture in thelibdirectory.[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 initperforms 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_MODULEorCONFIG_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 uninstallThe commands can be combined as:
$ make build install
Alternatively, the
make rebuildcommand will call the following targets in order:cleanbuildinstall
require will default to loading the latest version that matches the
major.minor.patchformat, 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_TOPwill change to the directory from which you calliocsh.The definition of
E3_CMD_TOPwill 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.IOCNAMEis 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_PATHenvironment 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,iocshwill add it in to the generated startup script used to start the IOC.If you comment out the
iocInit()line:#iocInit()
then
iocshwill 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_TOPis used to define the value of theTOPenvironment variable.epicsEnvSet("TOP","$(E3_CMD_TOP)/..")TOPis then used as the location for a number of other files in other commands in the startup script.random.bashgenerates a random number and stores it inrandom.cmd.random.cmdis 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.dbis located in thedbdirectory in theiocstatsmodule 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 thedbLoadRecordscommand 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.iocshautosave.iocshrecsync.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.iocshsnippet for examples of this, such as:var(reccastTimeout, "$(TIMEOUT=5.0)")The
autosave.iocshfile has a long list of variables that can be defined. Most are optional. Refer to the documentation at the top of theautosave.iocshfile 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
essiocmodule to load the standard set of IOC modules (for ESS IOCs), which includes:iocstatsautosaverecsyncauthcaputlog
Add the following lines before the
iocInitline: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 helpgives a list of the commonly used targets.Create a
CONFIG_MODULE.localfile in thee3-<module>/configuredirectory, and set the newEPICS_MODULE_TAGvalue in this file. This will override the value inCONFIG_MODULE.It is possible for the
git statusto not show a completely clean response, as it may identify two potential changes, one to the submodule contents, and one identifying the newCONFIG_MODULE.localfile 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 initcommand:[iocuser@localhost:e3-iocStats]$ make init EPICS_MODULE_TAG:=tags/3.1.15
A
p0patch uses the paths exactly as defined in the patch file, while thep1patch strips any leading slash from the paths in the patch file, making it a relative path. Run theman patchcommand and look for the documentation for the-poption.For details of what happens behind the scenes, see the
RULES_PATCHandDEFINES_FTin/epics/base-7.0.6.1/require/4.0.0/configurefor 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 streamThere 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 thecommandpart, 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
epicsEnvShowcommands, the main difference is that the version using thech6.cmdfile sets theE3_CMD_TOPvariable to the path for the startup script file.The
streammodule has thestreamDebugvariable.
Assignments¶
The
asyn_DBEPICS environment variable defines the location for asyn databases.The
systemcommand can be used to access shell commands.localhost-1593 > system "ls $(asyn_DB)" asynFloat64TimeSeries.db asynInt32TimeSeries.db asynRecord.db localhost-1593 >
Use the
--debugflag 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 recipeline provides a clue to the location of thevarsmake rule.In the
/epics/base-7.0.6.1/require/4.0.0/configure/RULES_VARSfile, thevarsrule 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 streamin 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 streamwill 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.localduring 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-fugis 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-fugknows that stream is a dependency due to the variableREQUIREDused 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 patchshould 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.cmdincluded 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 streamin 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.Makefileshould 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.sttandsncExample.st(why not? What error do you get if you do?).If you run
make buildnow, 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.canddevXxxSoft.cboth requirexxxRecord.hwhich does not exist: it is generated at build-time fromxxxRecord.dbdby 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/iocmyexampleat the existingst.cmdfile 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 initormake patchsince there is no embedded git submodule;make initdoes 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
transformrecord 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
transformis defined incalc.dbd. This means that we need to also include the calc module.FETCH_BUILD_NUMBERis a macro defined indriver.makefile, which is the main build engine in requireEPICS base also include the function
dbLoadTemplatewhich can be used to load.substitutionfiles instead of just.dbfiles (and which does so at run-time). Hence the linedbLoadTemplate("$(mypid_DB)/pid.substitutions")
will produce the same output.
If you have the
psmodules 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 existentandmake devexistentboth run the commandtreein 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_DEVconfigure 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.localfile 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.localfile with an updatedE3_MODULE_DEV_GITURL, which git will ignore.Technically,
make devdistcleandoes two things: it runsmake devcleanand 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 devpatchwill apply them to the development source files.
11. Supplementary tools¶
Exercises¶
Starting an IOC in a procServ container¶
The syntax for
procServisprocServ [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
--portto 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 theExecStartdirective 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 0for 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
EnvironmentFiledirective 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.shfile 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
Servicesection 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