メッセージ構造定義を白紙から作る

このサンプルは 自分の独自バイナリプロトコルの「構造定義」を、白紙の構造エディタから組み立てて検証するためのものです。題材は小さな自作プロトコル MiniCtrl。フィールドを 1 つずつ並べ、プレビューでフィールド分解を検算し、最後に(任意で)バイナリ構造応答ルールに載せて、応答テンプレートにチェックサムを自動計算させるところまでを一本の動線でたどれます。

自作プロトコル MiniCtrlTCP 7300構造定義をゼロからプレビュー検証自動 CRC実機不要

このページでできること

  • 白紙の構造エディタから、自作プロトコル MiniCtrl のフィールドを 1 つずつ定義する(型・役割・長さ駆動・判別)。
  • プレビューにサンプル電文を貼って、各フィールドが正しい位置・長さで分解され、長さフィールドが可変ペイロードを駆動することを検算する。
  • 完成形をインポートして自分の定義と答え合わせする。
  • (任意)定義をバイナリ構造応答ルールに載せ、クライアント役が送る電文をライブでパース・応答させる。応答テンプレートの {crc:Crc16Modbus}チェックサムを自動計算する様子を見る。
  • 最後に「自分のプロトコルではどう作るか」を判断フローで掴む。
フィールド種別・フレーミング方式・チェックサム種別などの網羅的な説明メッセージ構造定義 を参照してください。このページは手順に絞っています。
入口は 2 通り:ステップ A・B(白紙から作る・答え合わせ)は 新規プロジェクトで始めます。ステップ C(動かす)は構造・応答ルールが設定済みの同梱 structure-builder.commsim を開きます。

題材プロトコル MiniCtrl

MiniCtrl は、本チュートリアル用の小さな機器制御プロトコルです。先頭マーカ 0x7E で始まり、コマンド・連番・長さ・可変ペイロード・CRC が並びます。これを白紙から組み立てます。

#フィールド役割意味・エディタでの設定
1StxU8判別(Discriminator)フレーム先頭マーカ 0x7E。「判別フィールドで適用を分岐する」を ON にし、判別フィールド名 Stx・一致値 0x7E
2CmdU8なしコマンド(0x10 Open/0x20 Status/0x90 Ack/0xA0 Report)。ラベルは完成形に同梱(白紙では数値のまま)
3SeqU8なし連番。応答で {field:Seq} としてエコーする
4LenU8Lengthペイロードのバイト数。役割を Length にする
5PayloadHexなし可変長データ部。「長さ元」欄に Len と入力して可変長を駆動する
6CrcU16ChecksumCRC16/Modbus。役割を Checksum にする(後述:チェックサムの検証はプレビューでは行われません。実 CRC は応答テンプレートが計算します)
2 つの大事な性質
LenPayload 長を駆動します(Len=1 なら Payload は 1 バイト)。可変長プロトコルの肝です。
チェックサムは「送信時に CommSim が計算してくれる」ものです。受信パース(プレビュー)では CRC を検証しません。CRC の確認はステップ C の応答({crc:Crc16Modbus})で行います。

用意するもの

ファイル内容
structure-builder/structure-builder.commsim-structMiniCtrl 構造定義の完成形(答え合わせ用にインポートする)
structure-builder/structure-builder.commsimステップ C 用のプロジェクト(TCP 7300 スタブ・MiniCtrl 構造・Status→Report 応答ルール・フレーミングが設定済み)

これらは CommSim.Sample の実行フォルダ内 samples/structure-builder/ に同梱されています。ステップ A・B(白紙から作る・答え合わせ)は CommSim 本体 1 本だけで行えます。ステップ C(ライブ実演)のときだけ CommSim.Sample を相手役として使います(実機は不要)。

ステップA:白紙から構造を組んでプレビューで検証する(まず動かす)

新規プロジェクトを作り、構造エディタを白紙から開いて MiniCtrl の 6 フィールドを並べます。

  1. CommSim 本体で新規プロジェクトを作成する(「ファイル」→「新規」)。
  2. 構造体定義(プロジェクト共有)」を開く → 「追加」をクリックすると、白紙の構造エディタが開く。
  3. 行追加」で 6 行作り、題材プロトコル MiniCtrl の表のとおり各行の名前・型・役割を設定する。
    • Stx:型 U8。上部の「判別フィールドで適用を分岐する」を ON にし、判別フィールド名 Stx・一致値 0x7E
    • Len:型 U8・役割 Length
    • Payload:型 Hex「長さ元」欄に Len
    • Crc:型 U16・役割 Checksum
  4. エディタ下部のプレビュー欄(スペース区切り HEX)に、Status 要求フレームを入れて「パース」を押す。
    7E 20 01 00 18 56
構造エディタ:上部に判別フィールド(Stx・一致値 0x7E)、中央に 6 フィールドのグリッド(名前・型・役割・長さ元)、下部のプレビューに 7E 20 01 00 18 56 を当てたパース結果
構造エディタで MiniCtrl の 6 フィールドを並べた状態。判別フィールドStx0x7E)を ON にし、Len の役割を LengthPayload長さ元Len にしているのがポイント。下部のプレビューに 7E 20 01 00 18 56 を入れて「パース」すると、各フィールドが 名前 = 値 @オフセット+長さ で分解される(Crc 行はグリッドを下にスクロールすると現れる)。

観察できること(プレビュー結果)

各フィールドが 名前 = 値 @オフセット+長さ の形で分解されます。

  • --OK
  • ·Stx = 126 @0+1先頭マーカ 0x7E
  • ·Cmd = 32 @1+10x20 = Status
  • ·Seq = 1 @2+1連番
  • ·Len = 0 @3+1ペイロード長 = 0
  • ·Payload = @4+0Len=0 なので 0 バイト
  • ·Crc = … @4+2末尾 2 バイト
これがステップ A(まず動かす)の到達点です。長さ駆動を体感するには、ペイロード付きの Open 要求 7E 10 02 01 01 69 5A も貼ってみてください。Len = 1 になり Payload@4+1(1 バイト)に変わります。さらに Len を含むどこか 1 バイトを書き換えると、後続のオフセットがずれるのが分かります。
なお、プレビューはフィールドの並び・位置・長さ駆動を検証するもので、チェックサムは検証しません(CRC の確認はステップ C で)。

ステップB:完成形と答え合わせ(オプション)

自作した定義を、同梱の完成形と見比べます。

  1. 「構造体定義(プロジェクト共有)」で「インポート」をクリックし、samples/structure-builder/structure-builder.commsim-struct を取り込む。
  2. 取り込まれた「MiniCtrl フレーム」を開いて、フィールド構成・型・役割・長さ元・判別が自分の定義と一致するか確認する。
構造体定義(プロジェクト共有)ダイアログ:一覧に「MiniCtrl フレーム」が並び、追加・編集・複製・削除・インポート・エクスポートのボタンがある
「構造体定義(プロジェクト共有)」ダイアログ。インポートすると一覧に「MiniCtrl フレーム」が現れる(ステップ A では空の一覧から追加で白紙エディタを開く)。一覧で選んで編集を押すと、上の構造エディタが開き、自分の定義と答え合わせできる。
完成形には、白紙では設定しなかった Enum ラベルCmd0x20Status など)が入っています。受信ログでコマンドが数値ではなく名前で読めるようになり可読性が上がります。Enum ラベルやチェックサム種別は定義に含めて持ち回れる仕上げ要素で、白紙のエディタではまず構造の骨格(並び・型・役割・長さ駆動・判別)を作ります。

ステップC:実トラフィックで動かす(自動 CRC 応答)(オプション)

作った構造定義をバイナリ構造応答ルールに載せ、実際の通信でパース・応答させます。ここで CommSim がチェックサムを自動計算してくれるうれしさを確認します。実機は要りません(クライアント役は CommSim.Sample が演じます)。

① CommSim.Sampleテスト対象アプリ役:MiniCtrl 要求を連投
CommSim 本体機器役(スタブ):構造定義でパース+応答
⇠ 実機不要
実機使わない
  1. CommSim 本体で同梱の samples/structure-builder/structure-builder.commsim を開く(構造・フレーミング・応答ルールが設定済みのスタータ)。
  2. バイナリ構造応答」タブを開き、ルール「Status(0x20) → Report(0xA0) 応答」を確認する。応答テンプレートは次のとおり。
    7E A0 {field:Seq} 01 2A {crc:Crc16Modbus}
    {field:Seq} が受信の連番をエコーし、末尾の {crc:Crc16Modbus}それ以前の全バイトの CRC を自動計算して付けます。
構造体ルールの編集ダイアログ:対象構造体定義 MiniCtrl フレーム、フィールド条件 Cmd Eq 32、応答テンプレート 7E A0 {field:Seq} 01 2A {crc:Crc16Modbus}
バイナリ構造応答ルール「Status(0x20) → Report(0xA0) 応答」。フィールド条件 Cmd Eq 32(=0x20 Status)でこのルールが発火し、応答テンプレート{field:Seq} が受信連番をエコー、末尾の {crc:Crc16Modbus} が CRC を自動計算する。Open(0x10)は条件に合わないので応答しない。
  1. セッション「MiniCtrl スタブ TCP 7300」を「起動」する。
  2. CommSim.Sample を起動し、「動かすサンプル」で「構造定義(自作プロトコルを白紙から)」を選ぶ。① ペインを「起動」(7300 へ接続)→「デモ送信」を押す。MiniCtrl の Status/Open 要求が連投される。
  3. CommSim 本体の「ログ」で、受信フレームが MiniCtrl にパース・デコード表示され、Status にだけ Report 応答が返る(Open には返らない)のを確認する。

観察できること

  • 受信 Status:7E 20 01 00 18 56Cmd=0x20
  • 応答 Report:7E A0 01 01 2A FF 85末尾 FF 85 = 自動計算された CRC16
  • 受信 Open:7E 10 02 01 01 69 5ACmd=0x10 → 応答なし
ログペイン:受信 7E 20 01 00 18 56 が MiniCtrl フレームにパースされ、自動応答 7E A0 01 01 2A FF 85 を返す。Open 受信には応答がない
ログペインの実際の往復。受信した 7E 20 01 00 18 56[構造体] MiniCtrl フレームにパース・デコードされ、自動応答 7E A0 01 01 2A FF 85(末尾 FF 85 =自動計算された CRC16)を返す。Open 受信(7E 10 …)には応答が出ない。連番が変わった 2 回目の Status(Seq=03)では応答 CRC が 5E 45付け直される——テンプレートが毎回 CRC を再計算している証拠。
ここが構造定義のうれしさ:応答の末尾 FF 85 は手で計算したものではなく、{crc:Crc16Modbus} が送信時に自動で求めたものです。ペイロードや連番を変えても、CRC は常に正しく付き直します。
なぜ Status だけ応答するのか:ルールのフィールド条件が Cmd == 0x20(Status) だからです。Open(0x10)は条件に合わないので応答しません。複数のコマンドや定義を出し分ける判別・優先度・重複の詳しい扱いは メッセージ構造定義 を参照してください。

自分のケースへの当てはめ方

このサンプルの流れを、自分の独自プロトコルに応用するときの判断の糸口です。

問いはいいいえ
プロトコルにフレーム同期(STX・固定ヘッダ)はあるか?判別フィールド+プロジェクトのフレーミング設定で先頭を合わせるフレーミングを None に(受信チャンク=1 メッセージ)
可変長のペイロードはあるか?長さフィールドに役割 Length、ペイロードの「長さ元」にその名前を指定する固定長(型 Hex +固定長)で表す
応答も返すか?バイナリ構造応答ルール(フィールド条件+テンプレート {field:}{len:}{crc:KIND})。チェックサムはテンプレートが自動計算受信のパース表示だけでよい(ルール不要)
構造定義の作り方の要点は「先頭から順にフィールドを並べる」「可変長は長さフィールドで駆動する」「応答のチェックサムはテンプレートに任せる」の 3 点です。まず小さく作ってプレビューで検算し、動かしながら広げていくのが近道です。

次に読む