Modbus TCP / UDP ドライバ
概要
Modbus 産業標準 (Modbus-IDA / IEC 61158-6-12) ベースの PLC 通信。
| 項目 | 値 |
|---|---|
opc_type | MODBUS (TCP) / MODBUSUDP (UDP) |
| 実装クラス | plantpulse.driver.protocol.modbus.ModbusTCPDriver / ModbusUDPDriver |
| ライブラリ | 独自ネイティブ (plantpulse-plc-protocol の ModbusTcp / ModbusUdp) |
| read | ✅ |
| write | ✅ (coil / holding-register) |
| セキュリティ | なし (Modbus 標準仕様上) |
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_ip | Modbus スレーブ IP | 192.168.0.50 |
opc_agent_port | Modbus ポート | 502 (TCP 既定) |
timecycle | ポーリング周期 (ms) | 1000 |
options.request-timeout | リクエストタイムアウト (ms) | 5000 |
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:1 | Holding Register 1 (16-bit) |
holding-register:1:UINT | 16-bit unsigned |
holding-register:1:DINT | 32-bit signed (2 register) |
holding-register:1:REAL | 32-bit float (2 register) |
holding-register:100:STRING[10] | 100~109 の ASCII 文字列 |
coil:1 | コイル 1 ビット |
discrete-input:1 | discrete input 1 ビット |
input-register:1 | input register 16-bit |
詳細な表記ルールは PLC4j Modbus ドキュメント を参照してください。
data_type / format マッピング
Edge の format が PLC4j の type suffix にそのままマッピングされます。
edge data_type | edge format | PLC4j addr マッピング | 備考 |
|---|---|---|---|
| Boolean | (empty) / BOOL | coil:N または :BOOL | 1 bit |
| Integer / Short | (empty) | :INT | 16-bit signed |
| UInteger | UI / UWORD | :UINT | 16-bit unsigned |
| Integer (32) | DW | :DINT | 32-bit signed (2 reg) |
| UInteger (32) | UDW | :UDINT | 32-bit unsigned |
| Float | REAL | :REAL | 32-bit IEEE 754 |
| Double | LREAL | :LREAL | 64-bit IEEE 754 |
| String | STR[N] | :STRING[N] | N 文字 |
format が STR[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_type | format | plc_address 例 | 備考 |
|---|---|---|---|
Boolean | (empty) | coil:1 | FC 01 (Read Coils) |
Boolean | (empty) | discrete-input:1 | FC 02 (Read Discrete Inputs) |
Boolean | BIN[3] | holding-register:1 | ワード read 後に bit 3 を抽出 |
16-bit 整数
data_type | format | plc_address 例 | 備考 |
|---|---|---|---|
Integer | (empty) | holding-register:1 | signed INT 16 |
Integer | (empty) | holding-register:1:INT | 同一 (PLC4j 表記) |
Integer | UI | holding-register:1:UINT | unsigned 16 |
Integer | (empty) | input-register:1 | input register (FC 04) |
32-bit 整数
data_type | format | plc_address 例 | 備考 |
|---|---|---|---|
Integer | DW | holding-register:1:DINT | signed 32 (2 register) |
Integer | UDW | holding-register:1:UDINT | unsigned 32 |
Integer | UDINT | holding-register:1 | format 明示による 32-bit |
実数
data_type | format | plc_address 例 | 備考 |
|---|---|---|---|
Float | REAL | holding-register:1 | 32-bit float (2 register) |
Float | REAL | holding-register:1:REAL | 同一表記 |
Double | LREAL | holding-register:1:LREAL | 64-bit double (4 register) |
文字列
data_type | format | plc_address 例 | 備考 |
|---|---|---|---|
String | STR[5] | holding-register:10 | 5 word = 10 byte ASCII |
String | STR[16] | holding-register:100:STRING[16] | PLC4j 表記 |
Formula の活用
| 用途 | data_type | fomula | 備考 |
|---|---|---|---|
| 整数 raw → 小数 | Float | ${VALUE}*0.1 | 圧力/温度の raw スケール |
| Word swap 補正 | Float | ${VALUE}*1.0 | 32-bit float の byte order が異なるスレーブ用。代替: :REAL_LSWORD_FIRST |
| RH/Temp combo (1 ワードに 2 値) | 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"}
]
}'