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.2 Connect the TCP simulator

Run the Chapter 3 TCP database and protocol, and then the Chapter 4 three-device fragment, through iocsh.bash without compiling an Input/Output Controller (IOC) executable. You send commands through StreamDevice and read replies over Channel Access (CA) and pvAccess (PVA).

Prerequisites

Open three Bash terminals on the same host for the single-device setup, and add terminals 4 and 5 for the three-device section:

TerminalRoleWorking directoryEnvironment
Terminal 1TCP simulator (9399)jeonglee_Demo/simulator/No EPICS environment required
Terminal 2IOC (iocsh.bash)jeonglee_Demo/ then jeonglee_Demo/loader_tcp/source "$TREE/setEpicsEnv.bash"
Terminal 3CA 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
Terminals 4-5Extra TCP simulators (9400, 9401, three-device section)jeonglee_Demo/simulator/No EPICS environment required

In each EPICS terminal, set TREE to your installation path from lesson 1.1 (such as "$HOME/epics/1.5.0/debian-13/7.0.10") before running source "$TREE/setEpicsEnv.bash".

Prepare the single-device startup

  1. In terminal 2 from your jeonglee_Demo/ application directory, copy the database and protocol files into a loader directory:

    mkdir -p loader_tcp
    cp jeonglee_DemoApp/Db/training.db loader_tcp/
    cp jeonglee_DemoApp/Db/training.proto loader_tcp/
    cd loader_tcp
    
  2. In terminal 2 inside loader_tcp/, save this startup file as st.cmd:

    on error break
    module StreamDevice
    
    epicsEnvSet("STREAM_PROTOCOL_PATH", ".")
    drvAsynIPPortConfigure("TCP", "127.0.0.1:9399", 0, 0, 0)
    asynOctetSetInputEos("TCP", 0, "\n")
    asynOctetSetOutputEos("TCP", 0, "\n")
    dbLoadRecords("training.db", "P=LOADER:,R=SIM:,PORT=TCP")
    iocInit()
    

    module StreamDevice selects StreamDevice and its recorded dependencies, including asyn and calc. iocsh.bash handles library loading and support registration, while st.cmd configures the TCP port, loads the records, and calls iocInit().

  3. In terminal 2 inside loader_tcp/, preview the generated startup:

    iocsh.bash -n st.cmd
    

Run the single-device connection

  1. In terminal 1 inside jeonglee_Demo/simulator/, start the echo server on port 9399:

    ./tcpserver.bash 9399
    
  2. In terminal 2 inside loader_tcp/, start the IOC:

    iocsh.bash st.cmd
    
  3. In terminal 3, direct CA and PVA searches to loopback:

    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
    
  4. In terminal 3, send a query with caput -c and read the reply PV (Cmd-RB) through CA:

    caput -c LOADER:SIM:Cmd "DEVICE_ID?"
    caget LOADER:SIM:Cmd-RB
    

    CA confirms the write and returns DEVICE_ID?:

    Old : LOADER:SIM:Cmd
    New : LOADER:SIM:Cmd                 DEVICE_ID?
    LOADER:SIM:Cmd-RB              DEVICE_ID?
    
  5. Read the same reply PV through PVA:

    pvxget LOADER:SIM:Cmd-RB
    

    QSRV2 exposes Cmd-RB as an NTScalar string structure:

    LOADER:SIM:Cmd-RB
        value string = "DEVICE_ID?"
        alarm.severity int32_t = 0
        alarm.status int32_t = 0
        alarm.message string = ""
        timeStamp.secondsPastEpoch int64_t = 1794422274
        timeStamp.nanoseconds int32_t = 859089854
        timeStamp.userTag int32_t = 0
        display.description string = "Raw query readback"
    
  6. Send a second query to confirm repeated message exchange:

    caput -c LOADER:SIM:Cmd "STATUS?"
    caget LOADER:SIM:Cmd-RB
    

    The readback updates to STATUS?:

    Old : LOADER:SIM:Cmd                DEVICE_ID?
    New : LOADER:SIM:Cmd                STATUS?
    LOADER:SIM:Cmd-RB              STATUS?
    
  7. Enter exit at the iocsh prompt in terminal 2 and stop the echo server in terminal 1 with Ctrl+C before starting the three-device setup.

Reuse the Chapter 4 fragment across three devices

  1. In terminal 2 inside loader_tcp/, copy training_device.iocsh from Chapter 4 without changing a line:

    cp ../iocsh/training_device.iocsh .
    
  2. In terminal 2 inside loader_tcp/, save this three-device startup file as st_multi.cmd:

    on error break
    module StreamDevice
    
    epicsEnvSet("DATABASE_TOP", ".")
    epicsEnvSet("STREAM_PROTOCOL_PATH", ".")
    epicsEnvSet("PREFIX", "LOADER:")
    
    iocshLoad("training_device.iocsh", "DEVICE=SIM1:,PORT_NAME=TCP1,PORT=9399")
    iocshLoad("training_device.iocsh", "DEVICE=SIM2:,PORT_NAME=TCP2,PORT=9400")
    iocshLoad("training_device.iocsh", "DEVICE=SIM3:,PORT_NAME=TCP3,PORT=9401,ASYNTRACE=")
    iocInit()
    

    module StreamDevice appears once in st_multi.cmd, never inside training_device.iocsh. iocsh.bash scans only the top-level startup file before launching softIocPVX and loads each module once for the process. Keeping training_device.iocsh free of loader directives lets compiled IOCs (Chapter 4) and iocsh.bash share the exact same fragment.

  3. Start one command-aware simulator per port from jeonglee_Demo/simulator/. In terminal 1, listen on port 9399:

    ./tcpserver.bash 9399 advanced_connection_handler.sh
    

    In terminal 4, listen on port 9400:

    ./tcpserver.bash 9400 advanced_connection_handler.sh
    

    In terminal 5, listen on port 9401:

    ./tcpserver.bash 9401 advanced_connection_handler.sh
    
  4. In terminal 2 inside loader_tcp/, start the three-device IOC:

    iocsh.bash st_multi.cmd
    
  5. In terminal 3, request each handler’s process ID (PID) and read all three replies:

    caput -c LOADER:SIM1:Cmd "GetID?"
    caput -c LOADER:SIM2:Cmd "GetID?"
    caput -c LOADER:SIM3:Cmd "GetID?"
    caget LOADER:SIM1:Cmd-RB LOADER:SIM2:Cmd-RB LOADER:SIM3:Cmd-RB
    

    Each connection reports a distinct handler PID:

    LOADER:SIM1:Cmd-RB             242338
    LOADER:SIM2:Cmd-RB             242342
    LOADER:SIM3:Cmd-RB             242344
    
  6. Query a simulated temperature from each device:

    caput -c LOADER:SIM1:Cmd "GetTemp?"
    caput -c LOADER:SIM2:Cmd "GetTemp?"
    caput -c LOADER:SIM3:Cmd "GetTemp?"
    caget LOADER:SIM1:Cmd-RB LOADER:SIM2:Cmd-RB LOADER:SIM3:Cmd-RB
    

    Each readback returns an integer between 0 and 100, while TCP3 prints asyn trace lines in terminal 2:

    LOADER:SIM1:Cmd-RB             53
    LOADER:SIM2:Cmd-RB             65
    LOADER:SIM3:Cmd-RB             11
    

Verification

In terminal 3, check alarm status and severity across all three command records while the simulators run:

caget LOADER:SIM1:Cmd.SEVR LOADER:SIM2:Cmd.SEVR LOADER:SIM3:Cmd.SEVR

Every record reports NO_ALARM:

LOADER:SIM1:Cmd.SEVR           NO_ALARM
LOADER:SIM2:Cmd.SEVR           NO_ALARM
LOADER:SIM3:Cmd.SEVR           NO_ALARM

Enter exit in terminal 2 and stop the simulators in terminals 1, 4, and 5 with Ctrl+C when finished.