6.1 Run an IOC with installed modules
Create a process variable (PV) using the installed busy module, start an
Input/Output Controller (IOC) with iocsh.bash, and write and read the PV
through Channel Access (CA) and pvAccess (PVA).
The iocsh.bash loader starts the installed softIocPVX binary, resolves the
modules named in your startup file, loads their recorded dependencies, and
registers their database definition (DBD) support. This exercise needs no IOC
compilation or physical hardware.
Prerequisites
- Complete Install the EPICS environment.
- Ensure
readelffrombinutilsis installed on the host. - Confirm that no other IOC on this host publishes
TRAINING:Run.
Open two Bash terminals on the same host with these roles and settings:
| Terminal | Role | Working directory | Environment |
|---|---|---|---|
| Terminal 1 | IOC (iocsh.bash) | $IOC_WORK (mktemp -d) | source "$TREE/setEpicsEnv.bash", export EPICS_CA_AUTO_ADDR_LIST=NO EPICS_CA_ADDR_LIST=127.0.0.1, export EPICS_PVA_AUTO_ADDR_LIST=NO EPICS_PVA_ADDR_LIST=127.0.0.1 |
| Terminal 2 | CA and PVA client | Any directory | source "$TREE/setEpicsEnv.bash", export EPICS_CA_AUTO_ADDR_LIST=NO EPICS_CA_ADDR_LIST=127.0.0.1, export EPICS_PVA_AUTO_ADDR_LIST=NO EPICS_PVA_ADDR_LIST=127.0.0.1 |
Prepare the database and startup
-
In both terminals, activate the tree matching your operating system and direct clients to loopback. If you installed the distribution outside
~/epics, setTREEto that installation’s directory instead of the second line:HOST_ID="$(. /etc/os-release && printf '%s-%s' "$ID" "$VERSION_ID")" TREE="$HOME/epics/1.5.0/$HOST_ID/7.0.10" source "$TREE/setEpicsEnv.bash" export EPICS_CA_AUTO_ADDR_LIST=NO EPICS_CA_ADDR_LIST=127.0.0.1 export EPICS_PVA_AUTO_ADDR_LIST=NO EPICS_PVA_ADDR_LIST=127.0.0.1 -
In terminal 1, create a working directory outside the distribution:
IOC_WORK="$(mktemp -d)" cd "$IOC_WORK" pwdKeep terminal 1 in this directory. Relative database paths resolve from the current working directory even when you pass an absolute startup filename.
-
In
$IOC_WORK, save this database asbusy.db:record(busy, "$(P)Run") { field(DESC, "Training loader demonstration") field(ZNAM, "Idle") field(ONAM, "Busy") } -
Save this startup file as
st.cmdbesidebusy.db:on error break module busy dbLoadRecords("busy.db", "P=TRAINING:") iocInit()module busyselects the installed default version ofbusy. Your startup loads record instances and callsiocInit()once. -
In terminal 1, print the generated startup without starting the IOC:
iocsh.bash -n st.cmdThe preview prints the resolved dependency graph and startup sequence:
# iocsh.bash: generated startup for /tmp/tmp.ysekybke1K/st.cmd (runs as /dev/fd/3) # iocsh.bash: static ELF inspection of 156 dependency edges; resolved files as class, owner, file # elf: module pvxs-1.5.3 /home/jeonglee/epics/1.5.0/debian-13/7.0.10/modules/pvxs-1.5.3/lib/linux-x86_64/libpvxsIoc.so.1.5 # elf: base - /home/jeonglee/epics/1.5.0/debian-13/7.0.10/base/lib/linux-x86_64/libdbCore.so.3.25.0 # elf: system - /usr/lib/x86_64-linux-gnu/libevent_core-2.1.so.7.0.2 # Additional # elf: lines and dependency loads (seq, sscan, calc, asyn, autosave) omitted on error break # busy a4a272d dlload("/home/jeonglee/epics/1.5.0/debian-13/7.0.10/modules/busy-a4a272d/lib/linux-x86_64/libbusy.so") dbLoadDatabase("/home/jeonglee/epics/1.5.0/debian-13/7.0.10/modules/busy-a4a272d/dbd/busySupport.dbd", "/home/jeonglee/epics/1.5.0/debian-13/7.0.10/modules/busy-a4a272d/dbd:/home/jeonglee/epics/1.5.0/debian-13/7.0.10/modules/sscan-ce9660c/dbd:/home/jeonglee/epics/1.5.0/debian-13/7.0.10/modules/calc-3.8.0/dbd:/home/jeonglee/epics/1.5.0/debian-13/7.0.10/modules/asyn-4.46.0/dbd:/home/jeonglee/epics/1.5.0/debian-13/7.0.10/modules/autosave-6.0.0/dbd:/home/jeonglee/epics/1.5.0/debian-13/7.0.10/base/dbd:/home/jeonglee/epics/1.5.0/debian-13/7.0.10/modules/pvxs-1.5.3/dbd", "") epicsEnvSet("SNCSEQ", "/home/jeonglee/epics/1.5.0/debian-13/7.0.10/modules/seq-2.2.9") epicsEnvSet("SSCAN", "/home/jeonglee/epics/1.5.0/debian-13/7.0.10/modules/sscan-ce9660c") epicsEnvSet("CALC", "/home/jeonglee/epics/1.5.0/debian-13/7.0.10/modules/calc-3.8.0") epicsEnvSet("ASYN", "/home/jeonglee/epics/1.5.0/debian-13/7.0.10/modules/asyn-4.46.0") epicsEnvSet("AUTOSAVE", "/home/jeonglee/epics/1.5.0/debian-13/7.0.10/modules/autosave-6.0.0") epicsEnvSet("BUSY", "/home/jeonglee/epics/1.5.0/debian-13/7.0.10/modules/busy-a4a272d") registerAllRecordDeviceDrivers(pdbbase) iocshLoad("/dev/fd/4") # iocsh.bash: /tmp/tmp.ysekybke1K/st.cmd runs as /dev/fd/4 with 4 lines, directives replaced by commentsEach part of this generated startup serves a distinct role:
# elf:lines verify every shared library edge withreadelfacrossmodule,base, andsystemclasses before the IOC starts.dlloadanddbLoadDatabaseloadbusyand its transitive dependencies (seq,sscan,calc,asyn, andautosave) once in dependency order.epicsEnvSetexports each module’s install directory macro, such asBUSY, from its installedcfg/iocsh.conf.registerAllRecordDeviceDrivers(pdbbase)registers all loaded support once before your database records load./dev/fd/3holds the generated wrapper, while/dev/fd/4holds a line-preserving copy ofst.cmdwithmodulelines turned into comments. Keepst.cmdas your saved startup file; the/dev/fd/paths exist only during one loader invocation.
Start the IOC and read its PV
-
In terminal 1, start the IOC from
$IOC_WORK:iocsh.bash st.cmdWait for
iocRun: All initialization completeand leave the IOC running. -
In terminal 2, read the initial state through CA:
caget -t TRAINING:RunThe initial state is
Idle:Idle -
In terminal 2, set the busy state and confirm it through CA:
caput TRAINING:Run 1 caget -t TRAINING:RunThe CA client prints the state transition and current value:
Old : TRAINING:Run Idle New : TRAINING:Run Busy Busy -
In terminal 2, read the same PV through PVA:
pvxget TRAINING:RunQSRV2insidesoftIocPVXreturns anNTEnumstructure with index1and both state choices:TRAINING:Run value.index int32_t = 1 value.choices string[] = {2}["Idle", "Busy"] alarm.severity int32_t = 0 alarm.status int32_t = 0 alarm.message string = "" timeStamp.secondsPastEpoch int64_t = 1794421214 timeStamp.nanoseconds int32_t = 338775272 timeStamp.userTag int32_t = 0 display.description string = "Training loader demonstration" -
In terminal 2, return the record to
Idle:caput TRAINING:Run 0 caget -t TRAINING:RunCA reports
Idle:Old : TRAINING:Run Busy New : TRAINING:Run Idle Idle -
In terminal 1, stop the IOC at the
iocshprompt:exit
Pin module versions and select trees
To check which directory the default busy symlink targets, run:
readlink -f "$EPICS_MODULES/busy"
In EPICS-env 1.5.0, the resolved directory ends in busy-a4a272d. To pin that
exact version in st.cmd, specify the version token:
module busy a4a272d
Re-run iocsh.bash -n st.cmd after changing module directives. Put module
directives only in the top-level startup file passed to iocsh.bash; entering
them at the iocsh prompt or inside an iocshLoad fragment fails because
module is handled by iocsh.bash, not by softIocPVX.
To preview a startup from a shell where setEpicsEnv.bash has not been
sourced, pass -e with the tree root:
"$TREE/base/bin/linux-x86_64/iocsh.bash" -e "$TREE" -n st.cmd
Set TREE to the absolute installation path first. The -e option activates
the environment only inside iocsh.bash; client shells require
setEpicsEnv.bash for caget and pvxget.
Chapters 2 through 5 build compiled IOC executables that link their own DBD
and registration function. Keep those startup files for compiled IOCs, and use
module directives when launching through iocsh.bash.
Verification
Confirm that iocsh.bash -n st.cmd exits with status 0 before starting the
IOC:
iocsh.bash -n st.cmd
echo "$?"
The status code is 0:
0
During the live run, TRAINING:Run transitions from Idle to Busy and back
to Idle, pvxget reports value.index int32_t = 1 while busy, and exit
returns terminal 1 to the Bash prompt.