跳到主要内容

Modbus TCP / UDP 驱动

概述

基于 Modbus 工业标准 (Modbus-IDA / IEC 61158-6-12) 的 PLC 通信。

项目
opc_typeMODBUS (TCP) / MODBUSUDP (UDP)
实现类plantpulse.driver.protocol.modbus.ModbusTCPDriver / ModbusUDPDriver
自研原生 (plantpulse-plc-protocolModbusTcp / ModbusUdp)
read
write✅ (coil / holding-register)
安全无 (Modbus 标准所限)
2026-07 原生化改造 (TCP + UDP)

Modbus TCP/UDP 均已移除旧的 PLC4j 委托,改为使用 plantpulse-plc-protocol 的原生 ModbusTcp / ModbusUdp(公共基类 AbstractNativeModbusDriver)重写 —— 与 FINS / MELSEC / AB-ETH 采用相同模式。UDP 为 MBAP-over-UDP(单个数据报 = 单个 ADU),其地址、格式与 write 层与 TCP 完全一致。地址语法(holding-register:N[:TYPE])原样保留 PLC4X modbus4x 表示法。


OPC 注册表单

字段含义示例
opc_agent_ipModbus 从站 IP192.168.0.50
opc_agent_portModbus 端口502 (TCP 默认)
timecycle轮询周期 (ms)1000
options.request-timeout请求超时 (ms)5000
unit-identifier

Modbus TCP 通常以 IP+端口区分从站,因此 unit-identifier(别名 unit-id) 默认为 1。 当串口-TCP 网关后面挂接多个从站时,指定为 1~247。


标签 plc_address 格式

地址表示 (<area>:<address> + :<datatype> + [<count>]) —— 原样保留 PLC4X modbus4x 语法。 地址为 1-based(在 wire 上转换为 0-based,holding-register:5 → wire 寄存器 4)。

表示含义
holding-register:1Holding Register 1 (16-bit)
holding-register:1:UINT16-bit unsigned
holding-register:1:DINT32-bit signed (2 个寄存器)
holding-register:1:REAL32-bit float (2 个寄存器)
holding-register:100:STRING[10]100~109 的 ASCII 字符串
coil:1线圈 1 位
discrete-input:1discrete input 1 位
input-register:1input register 16-bit

详细表示规则参见 PLC4j Modbus 文档


data_type / format 映射

Edge 的 format 会直接映射为 PLC4j 的 type suffix。

edge data_typeedge formatPLC4j addr 映射备注
Boolean(empty) / BOOLcoil:N:BOOL1 bit
Integer / Short(empty):INT16-bit signed
UIntegerUI / UWORD:UINT16-bit unsigned
Integer (32)DW:DINT32-bit signed (2 reg)
UInteger (32)UDW:UDINT32-bit unsigned
FloatREAL:REAL32-bit IEEE 754
DoubleLREAL:LREAL64-bit IEEE 754
StringSTR[N]:STRING[N]N 个字符

formatSTR[N]REAL[N]UDINT[N] 开头,则按数组处理 (readWordValues)。


Write (原生 —— TCP/UDP 通用)

ModbusTCPDriver / ModbusUDPDriver 支持 write(isWriteSupported()=true):

  • coil:N → single coil write (Boolean)。
  • holding-register:N[:TYPE] → 按 datatype 执行 register write。REAL / DINT 为 MSWord-first 的 2 个寄存器,其余为单个 16-bit。值由调用方以 ProtocolAddress.value 传入。
  • discrete-input / input-register 在协议上为 read-only —— 无法 write。

常见错误与解决

消息 / 现象原因解决
PLC_READ_TIMEOUT_EXCEPTION从站响应延迟或断开检查线缆/防火墙/端口(502)。增大 request-timeout
PLC_READ_RUNTIME_EXCEPTION: Invalid PLC4j address地址表示拼写错误校验 holding-register:N 格式,注意勿遗漏 :
仅返回 0 值寄存器种类不匹配 (input vs holding)在从站手册中确认属于 fc 03/04/01/02 中的哪一种
32-bit 值 swap 异常byte/word order若从站为 little-endian,则将 format 改为 :UDINT_LSWORD_FIRST 等 (PLC4j 选项)

curl 注册示例 (Modbus TCP)

curl -X POST http://<edge-host>/api/v1/opc \
-H "Content-Type: application/json" \
-d '{
"opc_id": "OPC_MB_HVAC",
"opc_type": "MODBUS",
"opc_name": "HVAC Slave",
"opc_agent_ip": "192.168.0.50",
"opc_agent_port": "502",
"site_id": "SITE_00001",
"auto_collect": true,
"timecycle": 1000,
"options": { "request-timeout": "5000" },
"tag_list": [
{
"tag_id": "OPC_MB_HVAC_TAG_00001",
"tag_name": "Temperature",
"plc_address": "holding-register:1:REAL",
"data_type": "Float",
"format": "REAL"
},
{
"tag_id": "OPC_MB_HVAC_TAG_00002",
"tag_name": "Status",
"plc_address": "coil:1",
"data_type": "Boolean"
}
]
}'

UDP 时改为 opc_type: "MODBUSUDP",端口通常为 502 或依从站配置而定。

读取值:

curl -s http://<edge-host>/api/v1/tag/OPC_MB_HVAC_TAG_00001/value | jq

示例集 (按数据类型)

线圈 / 离散量 (Boolean)

data_typeformatplc_address 示例备注
Boolean(empty)coil:1FC 01 (Read Coils)
Boolean(empty)discrete-input:1FC 02 (Read Discrete Inputs)
BooleanBIN[3]holding-register:1读取字后提取 bit 3

16-bit 整数

data_typeformatplc_address 示例备注
Integer(empty)holding-register:1signed INT 16
Integer(empty)holding-register:1:INT相同 (PLC4j 表示)
IntegerUIholding-register:1:UINTunsigned 16
Integer(empty)input-register:1input register (FC 04)

32-bit 整数

data_typeformatplc_address 示例备注
IntegerDWholding-register:1:DINTsigned 32 (2 个寄存器)
IntegerUDWholding-register:1:UDINTunsigned 32
IntegerUDINTholding-register:1通过显式 format 指定为 32-bit

浮点数

data_typeformatplc_address 示例备注
FloatREALholding-register:132-bit float (2 个寄存器)
FloatREALholding-register:1:REAL相同表示
DoubleLREALholding-register:1:LREAL64-bit double (4 个寄存器)

字符串

data_typeformatplc_address 示例备注
StringSTR[5]holding-register:105 word = 10 byte ASCII
StringSTR[16]holding-register:100:STRING[16]PLC4j 表示

Formula 应用

用途data_typefomula备注
整数 raw → 小数Float${VALUE}*0.1压力/温度 raw 缩放
Word swap 校正Float${VALUE}*1.0适用于 32-bit float byte order 不同的从站。替代方案::REAL_LSWORD_FIRST
RH/Temp combo (一个字两个值)Float${VALUE}/256仅高 8 bit

curl 综合示例

curl -X POST http://<edge-host>/api/v1/opc \
-H "Content-Type: application/json" \
-d '{
"opc_id": "OPC_MB_FULL",
"opc_type": "MODBUS",
"opc_name": "Modbus Full",
"opc_agent_ip": "192.168.0.50",
"opc_agent_port": "502",
"site_id": "SITE_00001",
"auto_collect": true,
"timecycle": 1000,
"options": { "request-timeout": "5000" },
"tag_list": [
{"tag_id":"OPC_MB_FULL_T01", "tag_name":"Coil1", "plc_address":"coil:1", "data_type":"Boolean"},
{"tag_id":"OPC_MB_FULL_T02", "tag_name":"DI1", "plc_address":"discrete-input:1", "data_type":"Boolean"},
{"tag_id":"OPC_MB_FULL_T03", "tag_name":"HRSigned", "plc_address":"holding-register:1", "data_type":"Integer"},
{"tag_id":"OPC_MB_FULL_T04", "tag_name":"HRUnsigned", "plc_address":"holding-register:2:UINT", "data_type":"Integer", "format":"UI"},
{"tag_id":"OPC_MB_FULL_T05", "tag_name":"DInt", "plc_address":"holding-register:3:DINT", "data_type":"Integer", "format":"DW"},
{"tag_id":"OPC_MB_FULL_T06", "tag_name":"Temp", "plc_address":"holding-register:5:REAL", "data_type":"Float", "format":"REAL"},
{"tag_id":"OPC_MB_FULL_T07", "tag_name":"BatchName", "plc_address":"holding-register:10:STRING[10]", "data_type":"String", "format":"STR[10]"},
{"tag_id":"OPC_MB_FULL_T08", "tag_name":"PressScale", "plc_address":"holding-register:7:REAL", "data_type":"Float", "format":"REAL", "fomula":"${VALUE}*0.1"}
]
}'