Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 readelf from binutils is 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:

TerminalRoleWorking directoryEnvironment
Terminal 1IOC (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 2CA and PVA clientAny directorysource "$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

  1. In both terminals, activate the tree matching your operating system and direct clients to loopback. If you installed the distribution outside ~/epics, set TREE to 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
    
  2. In terminal 1, create a working directory outside the distribution:

    IOC_WORK="$(mktemp -d)"
    cd "$IOC_WORK"
    pwd
    

    Keep terminal 1 in this directory. Relative database paths resolve from the current working directory even when you pass an absolute startup filename.

  3. In $IOC_WORK, save this database as busy.db:

    record(busy, "$(P)Run") {
        field(DESC, "Training loader demonstration")
        field(ZNAM, "Idle")
        field(ONAM, "Busy")
    }
    
  4. Save this startup file as st.cmd beside busy.db:

    on error break
    module busy
    dbLoadRecords("busy.db", "P=TRAINING:")
    iocInit()
    

    module busy selects the installed default version of busy. Your startup loads record instances and calls iocInit() once.

  5. In terminal 1, print the generated startup without starting the IOC:

    iocsh.bash -n st.cmd
    

    The 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 comments
    

    Each part of this generated startup serves a distinct role:

    • # elf: lines verify every shared library edge with readelf across module, base, and system classes before the IOC starts.
    • dlload and dbLoadDatabase load busy and its transitive dependencies (seq, sscan, calc, asyn, and autosave) once in dependency order.
    • epicsEnvSet exports each module’s install directory macro, such as BUSY, from its installed cfg/iocsh.conf.
    • registerAllRecordDeviceDrivers(pdbbase) registers all loaded support once before your database records load.
    • /dev/fd/3 holds the generated wrapper, while /dev/fd/4 holds a line-preserving copy of st.cmd with module lines turned into comments. Keep st.cmd as your saved startup file; the /dev/fd/ paths exist only during one loader invocation.

Start the IOC and read its PV

  1. In terminal 1, start the IOC from $IOC_WORK:

    iocsh.bash st.cmd
    

    Wait for iocRun: All initialization complete and leave the IOC running.

  2. In terminal 2, read the initial state through CA:

    caget -t TRAINING:Run
    

    The initial state is Idle:

    Idle
    
  3. In terminal 2, set the busy state and confirm it through CA:

    caput TRAINING:Run 1
    caget -t TRAINING:Run
    

    The CA client prints the state transition and current value:

    Old : TRAINING:Run                   Idle
    New : TRAINING:Run                   Busy
    Busy
    
  4. In terminal 2, read the same PV through PVA:

    pvxget TRAINING:Run
    

    QSRV2 inside softIocPVX returns an NTEnum structure with index 1 and 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"
    
  5. In terminal 2, return the record to Idle:

    caput TRAINING:Run 0
    caget -t TRAINING:Run
    

    CA reports Idle:

    Old : TRAINING:Run                   Busy
    New : TRAINING:Run                   Idle
    Idle
    
  6. In terminal 1, stop the IOC at the iocsh prompt:

    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.