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.3 Read 32 temperature channels

Run the Chapter 4 32-channel temperature database through iocsh.bash without the compiled jeonglee_Demo executable, and group representative channels into one structured pvAccess (PVA) object with dbLoadGroup. You inspect both individual Channel Access (CA) and PVA records and a composite QSRV2 group process variable (PV) served by the Input/Output Controller (IOC).

Prerequisites

Open three Bash terminals on the same host with these roles and settings:

TerminalRoleWorking directoryEnvironment
Terminal 1TC-32 emulator (9399)jeonglee_Demo/simulator/No EPICS environment required
Terminal 2IOC (iocsh.bash)jeonglee_Demo/ then jeonglee_Demo/loader_tc32/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

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 database, group JSON, and startup

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

    mkdir -p loader_tc32
    cp db/TC-32.db db/tc32.proto loader_tc32/
    cd loader_tc32
    
  2. In terminal 2 inside loader_tc32/, save this QSRV2 group definition as tc32_group.json:

    {
        "$(P)$(R)Summary": {
            "+id": "training:nt/TC32Summary:1.0",
    
            "device.model": {"+type": "const",  "+const": "TC-32"},
    
            "ch01":         {"+type": "scalar", "+channel": "$(P)$(R)CH01.VAL"},
            "ch16":         {"+type": "scalar", "+channel": "$(P)$(R)CH16.VAL"},
            "ch32":         {"+type": "scalar", "+channel": "$(P)$(R)CH32.VAL", "+trigger": "*"}
        }
    }
    

    Each mapping key configures how QSRV2 assembles LOADER:TC32:Summary:

    • "+id" assigns the top-level PVA structure identifier training:nt/TC32Summary:1.0.
    • "+type": "const" with "+const": "TC-32" publishes a constant device.model string field without an underlying EPICS record.
    • "+type": "scalar" embeds each selected channel’s NTScalar value, alarm, timestamp, and display metadata from $(P)$(R)CH*.VAL.
    • "+trigger": "*" on ch32 publishes an atomic subscription update across all group fields when channel 32 finishes a streaming sweep. Omitting +putorder keeps the group read-only.
  3. In terminal 2 inside loader_tc32/, save this startup file as st.cmd:

    on error break
    module StreamDevice
    
    epicsEnvSet("STREAM_PROTOCOL_PATH", ".")
    drvAsynIPPortConfigure("TC32", "127.0.0.1:9399", 0, 0, 0)
    asynOctetSetInputEos("TC32", 0, "\n")
    asynOctetSetOutputEos("TC32", 0, "\n")
    dbLoadRecords("TC-32.db", "P=LOADER:,R=TC32:,PORT=TC32")
    dbLoadGroup("tc32_group.json", "P=LOADER:,R=TC32:")
    iocInit()
    

    softIocPVX links pvxs with QSRV2 enabled by default, so dbLoadGroup needs no extra module directive in st.cmd.

  4. In terminal 2 inside loader_tc32/, preview the generated startup before launching the IOC:

    iocsh.bash -n st.cmd
    

Read scalar channels and the structured group

  1. In terminal 1 inside jeonglee_Demo/simulator/, start the TC-32 emulator on port 9399:

    ./tc32_emulator.bash --port 9399
    
  2. In terminal 2 inside loader_tc32/, start the IOC:

    iocsh.bash st.cmd
    
  3. At the iocsh prompt in terminal 2, list the registered QSRV2 group definition:

    pvxgl(1, "*")
    

    pvxgl reports the group name, atomic delivery mode, and four mapped members (device.model, ch01, ch16, and ch32):

    LOADER:TC32:Summary
      Atomic Get/Put:yes Atomic Members:4
    
  4. 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
    
  5. In terminal 3, read three scalar temperature channels and channel 01 metadata through CA:

    caget LOADER:TC32:CH01 LOADER:TC32:CH16 LOADER:TC32:CH32
    caget LOADER:TC32:CH01.EGU LOADER:TC32:CH01.DESC LOADER:TC32:CH01.SEVR
    

    Live temperatures fall between 10 and 90 degrees Celsius with NO_ALARM severity:

    LOADER:TC32:CH01               77.1052
    LOADER:TC32:CH16               86.3493
    LOADER:TC32:CH32               76.6859
    LOADER:TC32:CH01.EGU           Celsius
    LOADER:TC32:CH01.DESC          TC temperature at Channel 01
    LOADER:TC32:CH01.SEVR          NO_ALARM
    
  6. In terminal 3, read channel 01 as an individual PVA NTScalar record:

    pvxget LOADER:TC32:CH01
    

    pvxget prints the value, alarm, timestamp, and engineering units from the same ai record:

    LOADER:TC32:CH01
        value double = 76.4265
        alarm.severity int32_t = 0
        alarm.status int32_t = 0
        alarm.message string = ""
        timeStamp.secondsPastEpoch int64_t = 1794422277
        timeStamp.nanoseconds int32_t = 691465958
        timeStamp.userTag int32_t = 0
        display.limitLow double = 0
        display.limitHigh double = 0
        display.description string = "TC temperature at Channel 01"
        display.units string = "Celsius"
        display.precision int32_t = 0
        display.form.index int32_t = 0
        display.form.choices string[] = {7}["Default", "String", "Binary", "Decimal", "Hex", "Exponential", "Engineering"]
        control.limitLow double = 0
        control.limitHigh double = 0
        valueAlarm.lowAlarmLimit double = nan
        valueAlarm.lowWarningLimit double = nan
        valueAlarm.highWarningLimit double = nan
        valueAlarm.highAlarmLimit double = nan
    
  7. In terminal 3, read the composite LOADER:TC32:Summary group PV in a single PVA request:

    pvxget LOADER:TC32:Summary
    

    One atomic PVA read returns ch01, ch16, ch32, and device.model together from the server:

    LOADER:TC32:Summary
        record._options.atomic bool = true
        ch01.value double = 76.4265
        ch01.alarm.severity int32_t = 0
        ch01.alarm.status int32_t = 0
        ch01.alarm.message string = ""
        ch01.timeStamp.secondsPastEpoch int64_t = 1794422277
        ch01.timeStamp.nanoseconds int32_t = 691465958
        ch01.timeStamp.userTag int32_t = 0
        ch01.display.limitLow double = 0
        ch01.display.limitHigh double = 0
        ch01.display.description string = "TC temperature at Channel 01"
        ch01.display.units string = "Celsius"
        ch01.display.precision int32_t = 0
        ch01.display.form.index int32_t = 0
        ch01.display.form.choices string[] = {7}["Default", "String", "Binary", "Decimal", "Hex", "Exponential", "Engineering"]
        ch01.control.limitLow double = 0
        ch01.control.limitHigh double = 0
        ch01.valueAlarm.lowAlarmLimit double = nan
        ch01.valueAlarm.lowWarningLimit double = nan
        ch01.valueAlarm.highWarningLimit double = nan
        ch01.valueAlarm.highAlarmLimit double = nan
        ch16.value double = 86.5753
        ch16.alarm.severity int32_t = 0
        ch16.alarm.status int32_t = 0
        ch16.alarm.message string = ""
        ch16.timeStamp.secondsPastEpoch int64_t = 1794422277
        ch16.timeStamp.nanoseconds int32_t = 729144531
        ch16.timeStamp.userTag int32_t = 0
        # Repeated display, control, and valueAlarm fields omitted for ch16 and ch32
        ch32.value double = 77.3631
        ch32.alarm.severity int32_t = 0
        ch32.alarm.status int32_t = 0
        ch32.alarm.message string = ""
        ch32.timeStamp.secondsPastEpoch int64_t = 1794422277
        ch32.timeStamp.nanoseconds int32_t = 688521675
        ch32.timeStamp.userTag int32_t = 0
        device.model string = "TC-32"
    

Verification

  1. In terminal 3, read all 32 scalar channels once to confirm every record receives live stream updates between 10 and 90:

    for ch in {01..32}; do caget -t "LOADER:TC32:CH$ch"; done
    
  2. In terminal 3, inspect the published PVA type identifier on LOADER:TC32:Summary:

    pvxinfo LOADER:TC32:Summary | head -n 3
    

    The structure header shows the custom +id contract:

    LOADER:TC32:Summary from 127.0.0.1:43259
    struct "training:nt/TC32Summary:1.0" {
        struct {
    
  3. In terminal 3, subscribe to three scalar channels with camonitor to verify continuous updates:

    camonitor LOADER:TC32:CH01 LOADER:TC32:CH16 LOADER:TC32:CH32
    

    Stop camonitor in terminal 3 with Ctrl+C, stop tc32_emulator.bash in terminal 1 with Ctrl+C, and then enter exit in terminal 2.