Modbus/TCP スレーブ
FC03 / FC04 / FC06 に応答する Modbus スレーブを再現。Modbus マスタ(クライアント)アプリの開発・試験を実機なしで行えます。スクリプト応答で動的長応答を実装した比較用のスクリプト版(ポート 503)も同梱しています。
TCP 502スクリプト版 503MBAP 長さ前置ビッグエンディアン
概要
Modbus/TCP のファンクションコード FC03・FC04・FC06 の通信相手として振る舞うサンプルです。
MBAP ヘッダの長さ前置フレーミング、複数の構造体定義をファンクションコード(判別フィールド)で振り分ける仕組み、アドレス(フィールド条件)に応じた応答値の切り替えを試せます。スクリプト版(ポート 503)は、要求の Quantity に応じた動的長応答をインライン C# で実装した発展例です。
ファイル構成
| ファイル | 内容 |
|---|---|
modbus-tcp/modbus-tcp.commsim | プロジェクト本体(構造体定義・構造体ルールを含む) |
modbus-tcp/modbus-tcp.commsim-struct | 構造体定義のみを切り出した可搬ファイル(別プロジェクトへのインポート用) |
modbus-tcp/modbus-tcp-script.commsim | スクリプト応答版(動的長 FC03 応答・ポート 503) |
機器構成
単一の TCP サーバセッション。Modbus マスタ(CommSim.Sample のシナリオ「Modbus」等)が接続します。
通信フロー
FC03 はアドレスで値を出し分け、FC06 は要求をエコーします。
- Master →FC03 読出 @addr0 qty2
- ← CommSimレジスタ 100, 200
- Master →FC03 読出 @addr10 qty2
- ← CommSim
0x1234, 0x5678 - Master →FC04 入力レジスタ読出
- ← CommSim250
- Master →FC06 単一レジスタ書込
- ← CommSim要求をエコー(同一内容)
想定プロトコル(MBAP)
Modbus/TCP の共通ヘッダ(MBAP)。Length 以降のバイト数が Length フィールドに入ります。
| 受信例(FC03) | 分解 |
|---|---|
00 01 00 00 00 06 01 03 00 00 00 02 | Txn=1 / FC=3 / StartAddr=0 / Quantity=2 |
応答 00 01 00 00 00 07 01 03 04 00 64 00 C8 | 2 レジスタ(100, 200) |
使用する機能
設定内容
プロジェクト設定
| 既定データ形式 | RawHex(バイナリ HEX) |
| エンディアン | Big |
| フレーミング方式 | 長さ前置(LengthPrefixed) |
| 共通ヘッダ長 / 長さ ofs / size | 7B / 4 / 2 |
| 長さ基準 / 補正 | 長さフィールド以降 / 0 |
構造体定義(FC で判別)
| 定義 | 判別値 | 主なフィールド |
|---|---|---|
| FC03 ReadHoldingRegisters | FunctionCode = 3 | StartAddress / Quantity |
| FC04 ReadInputRegisters | FunctionCode = 4 | StartAddress / Quantity |
| FC06 WriteSingleRegister | FunctionCode = 6 | Address / Value |
構造体ルール
優先度の昇順で評価され、最初にマッチした応答テンプレートを返します。
| ルール名 | 対象構造体 | フィールド条件 | 優先度 | 応答テンプレート(HEX) |
|---|---|---|---|---|
| FC03 @addr0 → 100,200 | FC03 ReadHoldingRegisters | StartAddress = 0 | 10 | {field:TransactionId} 00 00 00 07 {field:UnitId} 03 04 00 64 00 C8 |
| FC03 @addr10 → 0x1234,0x5678 | FC03 ReadHoldingRegisters | StartAddress = 10 | 11 | {field:TransactionId} 00 00 00 07 {field:UnitId} 03 04 12 34 56 78 |
| FC03 その他アドレス → 0,0 | FC03 ReadHoldingRegisters | (条件なし) | 20 | {field:TransactionId} 00 00 00 07 {field:UnitId} 03 04 00 00 00 00 |
| FC04 入力レジスタ → 250 | FC04 ReadInputRegisters | (条件なし) | 30 | {field:TransactionId} 00 00 00 05 {field:UnitId} 04 02 00 FA |
| FC06 単一書込 → エコー応答 | FC06 WriteSingleRegister | (条件なし) | 40 | {field:TransactionId} 00 00 00 06 {field:UnitId} 06 {field:RegisterAddress} {field:RegisterValue} |
設定の考え方
| 設定 | なぜそうするか |
|---|---|
| フレーミングを長さ前置(共通ヘッダ 7・長さオフセット 4・サイズ 2・基準「長さフィールド以降」) | Modbus/TCP は MBAP ヘッダ(7 バイト)の中の Length フィールドに「UnitId+PDU のバイト数」が入る仕様だから。TCP ストリームから 1 要求を正しく切り出すには、この Length の意味に合わせて基準を選ぶ必要がある。 |
| ファンクションコードを判別フィールドにして定義を 3 つに分ける | 同じヘッダでも FC ごとに後続フィールドの構成が違うから。FC で分岐させた方が分解表示も応答も正確になる。 |
| FC03 をアドレス+優先度で 3 ルールに分ける | 「特定アドレスは固定値を返し、それ以外は既定値(0,0)を返す」を表現するため。先勝ち評価で『特定→既定』の順に判定できる。 |
応答に {field:TransactionId}・{field:UnitId} をエコー | Modbus/TCP では応答の TransactionId・UnitId を要求と一致させる必要があるから。 |
| FC06 は要求をそのままエコーする応答テンプレート | Modbus 仕様で「単一レジスタ書込の正常応答 = 要求と同一フレーム」だから。 |
samples/modbus-tcp/modbus-tcp.commsim(502)を「ファイル」→「ファイルから開く…」(Ctrl+O)。CommSim.Sample のシナリオ「Modbus」の「デモ送信」で FC03@0 → FC03@10 → FC04 → FC06 の 4 要求を一括確認できます。動作確認手順
- CommSim で
samples/modbus-tcp/modbus-tcp.commsimを開く(メニューバー「ファイル」→「ファイルから開く…」)。 - セッション「Modbus TCP スタブ」の「起動」ボタンをクリックし、待ち受け状態にする。
- クライアントから FC03@0 の要求を送る。
- ログペインに受信(Rx)が表示され、フィールド分解(TransactionId・UnitId・FunctionCode・StartAddress 等)が確認できる。
- 同タブに送信(Tx)としてレジスタ 100, 200 の応答が表示される。
- FC03@10・FC04・FC06 の要求を順に送り、それぞれ異なる応答が返ることを確認する。
実行ログサンプル
Rx 00 01 00 00 00 06 01 03 00 00 00 02
[TransactionId=0x0001] [ProtocolId=0x0000] [Length=0x0006]
[UnitId=0x01] [FunctionCode=3(ReadHoldingRegisters)] [StartAddress=0] [Quantity=2]
Tx 00 01 00 00 00 07 01 03 04 00 64 00 C8
Rx 00 02 00 00 00 06 01 03 00 0A 00 02
[TransactionId=0x0002] [FunctionCode=3(ReadHoldingRegisters)] [StartAddress=10] [Quantity=2]
Tx 00 02 00 00 00 07 01 03 04 12 34 56 78
Rx 00 03 00 00 00 06 01 04 00 00 00 01
[FunctionCode=4(ReadInputRegisters)]
Tx 00 03 00 00 00 05 01 04 02 00 FA
Rx 00 04 00 00 00 06 01 06 00 05 00 FF
[FunctionCode=6(WriteSingleRegister)] [RegisterAddress=5] [RegisterValue=255]
Tx 00 04 00 00 00 06 01 06 00 05 00 FFスクリプト応答版(503)
スクリプト応答は既定で無効です。初回はウィンドウ上部のバナー、またはプロジェクト設定の「スクリプト / プラグイン」タブで有効化してから起動してください(→ セキュリティについて)。基本的な書き方はチュートリアル「スクリプトで動的応答を作る」を参照してください。
samples/modbus-tcp/modbus-tcp-script.commsim(ポート 503)は、上記テンプレート版と同じ Modbus FC03 応答をインライン C# スクリプトで実装した比較教材です。テンプレートでは固定本数しか返せない制約を、スクリプトで動的長応答に変える実装例です。
テンプレート版との違い
テンプレート版は返すレジスタ本数(バイト数)が固定ですが、スクリプト版は受信から Quantity を読み取り、その本数分のレジスタ値を生成して MBAP 長さフィールドも正しく計算して返します。
FC03 @addr0 qty2 → 00 01 00 00 00 07 01 03 04 00 64 00 C8
FC03 @addr0 qty4 → 00 03 00 00 00 0B 01 03 08 00 64 00 C8 00 02 00 03
↑ Quantity に応じてバイト数・MBAP長が変化既知アドレスの応答値
| StartAddress | 返す値 |
|---|---|
| 0 | 100(0x0064) |
| 1 | 200(0x00C8) |
| 10 | 0x1234 |
| 11 | 0x5678 |
| その他 | アドレス値そのまま |
詳細は スクリプト応答 を参照してください。