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
- Complete Configure an IOC for TCP communication, Run a TCP echo simulator, Reuse a device startup fragment, and Run a command-aware TCP simulator.
- Stop any IOC or simulator listening on ports
9399,9400, or9401. - Activate the matching EPICS-env 1.5.0 tree in the IOC and client terminals.
Open three Bash terminals on the same host for the single-device setup, and add terminals 4 and 5 for the three-device section:
| Terminal | Role | Working directory | Environment |
|---|---|---|---|
| Terminal 1 | TCP simulator (9399) | jeonglee_Demo/simulator/ | No EPICS environment required |
| Terminal 2 | IOC (iocsh.bash) | jeonglee_Demo/ then jeonglee_Demo/loader_tcp/ | source "$TREE/setEpicsEnv.bash" |
| Terminal 3 | 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 |
| Terminals 4-5 | Extra 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
-
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 -
In terminal 2 inside
loader_tcp/, save this startup file asst.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 StreamDeviceselectsStreamDeviceand its recorded dependencies, includingasynandcalc.iocsh.bashhandles library loading and support registration, whilest.cmdconfigures the TCP port, loads the records, and callsiocInit(). -
In terminal 2 inside
loader_tcp/, preview the generated startup:iocsh.bash -n st.cmd
Run the single-device connection
-
In terminal 1 inside
jeonglee_Demo/simulator/, start the echo server on port9399:./tcpserver.bash 9399 -
In terminal 2 inside
loader_tcp/, start the IOC:iocsh.bash st.cmd -
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 -
In terminal 3, send a query with
caput -cand read the reply PV (Cmd-RB) through CA:caput -c LOADER:SIM:Cmd "DEVICE_ID?" caget LOADER:SIM:Cmd-RBCA confirms the write and returns
DEVICE_ID?:Old : LOADER:SIM:Cmd New : LOADER:SIM:Cmd DEVICE_ID? LOADER:SIM:Cmd-RB DEVICE_ID? -
Read the same reply PV through PVA:
pvxget LOADER:SIM:Cmd-RBQSRV2exposesCmd-RBas anNTScalarstring 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" -
Send a second query to confirm repeated message exchange:
caput -c LOADER:SIM:Cmd "STATUS?" caget LOADER:SIM:Cmd-RBThe readback updates to
STATUS?:Old : LOADER:SIM:Cmd DEVICE_ID? New : LOADER:SIM:Cmd STATUS? LOADER:SIM:Cmd-RB STATUS? -
Enter
exitat theiocshprompt 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
-
In terminal 2 inside
loader_tcp/, copytraining_device.iocshfrom Chapter 4 without changing a line:cp ../iocsh/training_device.iocsh . -
In terminal 2 inside
loader_tcp/, save this three-device startup file asst_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 StreamDeviceappears once inst_multi.cmd, never insidetraining_device.iocsh.iocsh.bashscans only the top-level startup file before launchingsoftIocPVXand loads each module once for the process. Keepingtraining_device.iocshfree of loader directives lets compiled IOCs (Chapter 4) andiocsh.bashshare the exact same fragment. -
Start one command-aware simulator per port from
jeonglee_Demo/simulator/. In terminal 1, listen on port9399:./tcpserver.bash 9399 advanced_connection_handler.shIn terminal 4, listen on port
9400:./tcpserver.bash 9400 advanced_connection_handler.shIn terminal 5, listen on port
9401:./tcpserver.bash 9401 advanced_connection_handler.sh -
In terminal 2 inside
loader_tcp/, start the three-device IOC:iocsh.bash st_multi.cmd -
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-RBEach connection reports a distinct handler PID:
LOADER:SIM1:Cmd-RB 242338 LOADER:SIM2:Cmd-RB 242342 LOADER:SIM3:Cmd-RB 242344 -
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-RBEach readback returns an integer between
0and100, whileTCP3printsasyntrace 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.