본문으로 건너뛰기

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 register)
holding-register:1:REAL32-bit float (2 register)
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 만 반환등록 종류 mismatch (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 또는 슬레이브 설정에 따름.

값 read:

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워드 read 후 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 register)
IntegerUDWholding-register:1:UDINTunsigned 32
IntegerUDINTholding-register:1format 명시로 32-bit

실수

data_typeformatplc_address비고
FloatREALholding-register:132-bit float (2 register)
FloatREALholding-register:1:REAL동일 표기
DoubleLREALholding-register:1:LREAL64-bit double (4 register)

문자열

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.032-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"}
]
}'