I'm working on developing basic device support for a Huber 8-fold attenuator over modbus. The device modbus interface has a single holding register with read/write access where each of the first 8 bits corresponds to the position of each attenuator, either
in (1) or out (0). I was able to use (read: copy) the Koyo1 example in the modbus documentation with a couple of tweaks and get a set of binary inputs (using the modbus bi_word.template) and binary outputs (using the modbus bo_word.template) that work as expected,
and it was quite quick (so thanks!).
The wrinkle is that this device can also be controlled through other means, both via a browser-based web interface and via a TCP/telnet style interface. If I use the web interface to change an attenuator position, the binary inputs update appropriately, however
the binary outputs do not. More concretely, suppose that attenuator 1 is out. The binary input and output for this attenuator both have the expected value: 0. If I use the web interface to put it in, the binary input position reads as expected, 1, but the
binary output still has a value of 0.
My question is whether that's a simple way to update the output position when the input position changes, to ensure that the outputs stay synced with the actual attenuator position if someone controls the device through these other interfaces. Note that I don't
need to write to the device in this case, I just want to update the state of the EPICS record to reflect the new device state.
The other possibility is that I just don't tell anyone else about the other ways to control the device and hope they only use the EPICS control.
In case it's useful, I've included my st.cmd and substitutions files as text at the end of this message.
All the best.
- Jesse
----
Jesse Hopkins, PhD (he/him)
Director, BioCAT
Sector 18, Advanced Photon Source
Research Associate Professor, Illinois Tech
st.cmd:
#!../../bin/linux-x86_64/huber_atten
#- SPDX-FileCopyrightText: 2003 Argonne National Laboratory
#-
#- SPDX-License-Identifier: EPICS
#- You may have to change huber_atten to something else
#- everywhere it appears in this file
< envPaths
## Register all support components
dbLoadDatabase "../../dbd/huber_atten.dbd"
huber_atten_registerRecordDeviceDriver pdbbase
# Use the following commands for TCP/IP
#drvAsynIPPortConfigure(const char *portName,
# const char *hostInfo,
# unsigned int priority,
# int noAutoConnect,
# int noProcessEos);
drvAsynIPPortConfigure("Huber1","164.54.204.27:502",0,0,0)
asynSetOption("Huber1",0, "disconnectOnReadTimeout", "Y")
#modbusInterposeConfig(const char *portName,
# modbusLinkType linkType,
# int timeoutMsec,
# int writeDelayMsec)
modbusInterposeConfig("Huber1",0,5000,0)
# Word access at Modbus address 0
# Access 1 words as inputs.
# Modbus standard functions 3 (Read Multiple), 4 (Read One), 6 (Write One), and 16 (Write Multiple).
# default data type unsigned integer.
# drvModbusAsynConfigure("portName",
# "tcpPortName",
# slaveAddress,
# modbusFunction,
# modbusStartAddress,
# modbusLength,
# dataType,
# pollMsec,
# "plcType")
drvModbusAsynConfigure("H1_Yn_In_Word", "Huber1", 0, 3, 0, 01, "UINT16", 100, "Huber")
drvModbusAsynConfigure("H1_Yn_Out_Word", "Huber1", 0, 6, 0, 01, "UINT16", 100, "Huber")
dbLoadTemplate("Huber1.substitutions")
iocInit
date
Huber1.substitutions:
# asyn record for the underlying asyn octet port
file "$(ASYN)/db/asynRecord.db" { pattern
{P, R, PORT, ADDR, IMAX, OMAX}
{18ID:HUBER1: OctetAsyn, Huber1, 0, 80, 80}
}
# These are the Yn inputs done with word access Y0-Y7
file "$(MODBUS)/db/bi_word.template" { pattern
{P, R, PORT, OFFSET, MASK, ZNAM, ONAM, ZSV, OSV, SCAN}
{18ID:HUBER1:, Y0InW, H1_Yn_In_Word, 0, 0x0001, Out, In, NO_ALARM, MAJOR, "I/O Intr"}
{18ID:HUBER1:, Y1InW, H1_Yn_In_Word, 0, 0x0002, Out, In, NO_ALARM, MAJOR, "I/O Intr"}
{18ID:HUBER1:, Y2InW, H1_Yn_In_Word, 0, 0x0004, Out, In, NO_ALARM, MAJOR, "I/O Intr"}
{18ID:HUBER1:, Y3InW, H1_Yn_In_Word, 0, 0x0008, Out, In, NO_ALARM, MAJOR, "I/O Intr"}
{18ID:HUBER1:, Y4InW, H1_Yn_In_Word, 0, 0x0010, Out, In, NO_ALARM, MAJOR, "I/O Intr"}
{18ID:HUBER1:, Y5InW, H1_Yn_In_Word, 0, 0x0020, Out, In, NO_ALARM, MAJOR, "I/O Intr"}
{18ID:HUBER1:, Y6InW, H1_Yn_In_Word, 0, 0x0040, Out, In, NO_ALARM, MAJOR, "I/O Intr"}
{18ID:HUBER1:, Y7InW, H1_Yn_In_Word, 0, 0x0080, Out, In, NO_ALARM, MAJOR, "I/O Intr"}
}
file "$(MODBUS)/db/mbbiDirect.template" { pattern
{P, R, PORT, OFFSET, MASK, SCAN}
{18ID:HUBER1:, YnInWL, H1_Yn_In_Word, 0, 0xFFFF, "I/O Intr"}
}
file "$(MODBUS)/db/intarray_in.template" { pattern
{P, R, PORT, NELM, SCAN}
{18ID:HUBER1:, YnInWArray, H1_Yn_In_Word, 8, "I/O Intr"}
}
file "$(MODBUS)/db/asynRecord.template" { pattern
{P, R, PORT, ADDR, TMOD, IFACE}
{18ID:HUBER1:, YnInWAsyn, H1_Yn_In_Word, 0, Read, asynInt32}
}
file "$(MODBUS)/db/statistics.template" { pattern
{P, R, PORT, SCAN}
{18ID:HUBER1:, YnInW, H1_Yn_In_Word, "10 second"}
}
file "$(MODBUS)/db/poll_delay.template" { pattern
{P, R, PORT}
{18ID:HUBER1:, YnInWPollDelay, H1_Yn_In_Word}
}
# These are the Yn outputs done with word access. Y0-Y7
file "$(MODBUS)/db/bo_word.template" { pattern
{P, R, PORT, OFFSET, MASK, ZNAM, ONAM}
{18ID:HUBER1:, Y0OutW, H1_Yn_Out_Word, 0, 0x0001, Out, In}
{18ID:HUBER1:, Y1OutW, H1_Yn_Out_Word, 0, 0x0002, Out, In}
{18ID:HUBER1:, Y2OutW, H1_Yn_Out_Word, 0, 0x0004, Out, In}
{18ID:HUBER1:, Y3OutW, H1_Yn_Out_Word, 0, 0x0008, Out, In}
{18ID:HUBER1:, Y4OutW, H1_Yn_Out_Word, 0, 0x0010, Out, In}
{18ID:HUBER1:, Y5OutW, H1_Yn_Out_Word, 0, 0x0020, Out, In}
{18ID:HUBER1:, Y6OutW, H1_Yn_Out_Word, 0, 0x0040, Out, In}
{18ID:HUBER1:, Y7OutW, H1_Yn_Out_Word, 0, 0x0080, Out, In}
}
file "$(MODBUS)/db/mbboDirect.template" { pattern
{P, R, PORT, OFFSET, MASK}
{18ID:HUBER1:, YnOutWL, H1_Yn_Out_Word, 0, 0xFFFF}
}
file "$(MODBUS)/db/asynRecord.template" { pattern
{P, R, PORT, ADDR, TMOD, IFACE}
{18ID:HUBER1:, YnOutWAsyn, H1_Yn_Out_Word, 0, Read, asynUInt32Digital}
}
file "$(MODBUS)/db/statistics.template" { pattern
{P, R, PORT, SCAN}
{18ID:HUBER1:, YnOutW, H1_Yn_Out_Word, "10 second"}
}