ルールの優先度・重複・層間競合(バイナリ構造応答の出し分け)

このチュートリアルは 1 つの構造定義に複数のバイナリ構造応答ルールを優先度付きで重ね、重なったときに何が返るかを観察するためのものです。題材はセンサーハブ SensorHub(バイナリ)。同梱プロジェクトを開いて起動すると、特定値・範囲・キャッチオール(既定)の先勝ちキャッチオールの置き場所の落とし穴パケット一致オートメーションによる層間抑止を一連で試せて、そこから「自分の機器のコマンドごとに応答をどう出し分けるか」を掴めます。

センサーハブ SensorHubTCP 9620バイナリ(Stx+Type+Len+Payload+Sum8)優先度で先勝ち重複・キャッチオール層間競合実機不要

このページでできること(段階ゲート)

このチュートリアルの主役は 2 つです。①だけでも実用的に学べ、②まで行くとオートメーションの先勝ちを「層をまたいで」理解できます。

  • ① 複数ルールの優先度・重複(ステップ 1〜2・約 5 分で完結):1 つの構造に特定値ルール・範囲ルール・キャッチオール(既定)ルールを優先度付きで重ね、同じ受信に複数が当たっても優先度で 1 件が先勝ちする様子を観察する。キャッチオールを置く位置を間違えると「全部が既定応答になる」落とし穴も確認する。
  • ② 層間競合(ステップ 3):特定のフレームにパケット一致オートメーション(層①)を当て、それが構造体ルール(層②)を抑止する様子を観察する。オートメーションの有効/無効で同じフレームの応答が変わる。
前提:このチュートリアルは メッセージ構造定義を白紙から作る を済ませていることを前提にします(構造定義のパースが土台です)。オートメーションを使いこなす も済ませていると、ステップ 3(層間競合)の理解がスムーズです。
フィールド条件・比較演算子・応答テンプレート・チェックサムなどの網羅的な説明バイナリ構造応答メッセージ構造定義 を参照してください。このページは手順とルールの並べ方の観察に絞っています。

題材プロトコル SensorHub と 3 ルール+1 オートメーション

SensorHub は、本チュートリアル用のセンサーハブ機器プロトコルです。バイナリで、フレームは Stx(0xAA) + Type + Len + Payload(Len バイト) + Sum8。機器は Type(種別コード)で内容を区別し、種別ごとに違う応答を返します。この 1 台に、Type で出し分ける 3 つの構造体ルールと、メンテ用の 1 つのパケット一致オートメーションが同居しています。

#名前条件(Type優先度返すもの教える概念
R1② 構造体ルール温度センサEq 1(=0x01)10AA 81 02 00 64 91特定値マッチ・先勝ちで勝つ
R2② 構造体ルールアナログ群Ge 16(0x10)かつ Le 31(0x1F)20AA 90 01 «Type» «Sum8»範囲条件(2 条件 AND)・種別エコー
R3② 構造体ルール既定=未対応条件なし(空)100AA EE 01 00 99キャッチオール(全フレームに一致)・最低優先度
AX① パケット一致メンテ Ping生バイト列に AA F0 を含む10AA FA 01 01 A6層間競合(①が②の R3 を抑止)
3 つの読みどころ
重複は優先度で 1 件Type=0x01 は R1(特定値)と R3(キャッチオール)の両方に当たりますが、優先度が小さい R1 だけが返ります。Type=0x15 は R2(範囲)と R3 に当たりますが R2 が勝ちます。
キャッチオールは最低優先度に置く。R3 は条件なし=どのフレームにも一致します。だから優先度を最低(100)に置いて「他に当たらなかったときの既定」にします。優先度を小さくすると全部を食ってしまう(ステップ 2 で確認)。
①パケット一致は層が違う。AX はパース前の生バイト列AA F0)を見て、R1〜R3 はパース後のフィールドType)を見ます。受信は ①パケット一致 → ②構造体ルール の順で評価され、①が当たると②は評価されません(先勝ち抑止)。
優先度の向きに注意:CommSim の優先度は数字が小さいほど先に評価されます(昇順)。「優先度 10 のほうが優先度 100 より先に当たる」という向きです。
Sum8 について:フレーム末尾の Sum8それ以前の全バイトの 8bit 総和です。受信パースでは照合しません(パーサは読むだけ)。応答テンプレートの {crc:Sum8} が送信時に自動計算します。«Type» は受信した Type の 1 バイトをエコーする {field:Type} です。

用意するもの

ファイル内容
rule-priority/rule-priority.commsimSensorHub 機器スタブのプロジェクト(TCP 9620・3 構造体ルール R1/R2/R3+パケット一致オートメーション AX が設定済み)

これは CommSim.Sample の実行フォルダ内 samples/rule-priority/ に同梱されています。必要なのは CommSim 本体と CommSim.Sample の 2 本だけで、上流の実機は不要です(CommSim.Sample のクライアント役がアプリ役を演じます)。

ステップ1:3 ルールを一望して起動、先勝ちを見る(まず動かす)

① CommSim.Sampleテスト対象アプリ役:Type 違いを送信
CommSim 本体SensorHub 機器役(スタブ):3 ルールで出し分け
⇠ 実機不要
実機使わない
  1. CommSim 本体で samples/rule-priority/rule-priority.commsim を開く(「ファイル」→「ファイルから開く…」/Ctrl+O)。
  2. バイナリ構造応答」タブを開き、3 ルール(R1/R2/R3)を優先度順に一望する。条件(Type の Eq/範囲/空)と優先度(10/20/100)の違いを眺める。「オートメーション」タブで AX(メンテ Ping) も確認する。
  3. セッション「SensorHub スタブ TCP 9620」を「起動」する。
  4. CommSim.Sample を起動し、「動かすサンプル」で「バイナリ構造応答の優先度・重複(センサーハブ)」を選ぶ。① ペインを「接続」(9620 へ接続)して「デモ送信」を押す(Type 違いのフレームが順に送られます)。
「自動応答」タブ ②バイナリ構造応答:R1 温度センサ(Type=1) 優先10、R2 アナログ群(Type 0x10-0x1F) 優先20、R3 既定=未対応(キャッチオール) 優先100 が一覧表示され、概要列に各テンプレートが出る
「バイナリ構造応答」タブ。3 ルールが優先度順に並ぶ:R1 温度センサ1 条件・優先 10)、R2 アナログ群2 条件・優先 20・テンプレに {field:Type} の種別エコー)、R3 既定=キャッチオール0 条件=全フレーム一致・優先 100)。条件なしの R3 を最低優先度に置くのがキャッチオール設計の要点。

観察できること

  • AA 01 00 AB① が送信:Type=0x01
  • AA 81 02 00 64 91R1(温度)が先勝ち。R3 も当たるが優先度で R1
  • AA 15 00 BF① が送信:Type=0x15
  • AA 90 01 15 50R2(範囲 0x10-0x1F)。«Type»=15 をエコー
  • AA 99 00 43① が送信:Type=0x99
  • AA EE 01 00 99どの特定/範囲にも当たらず R3(既定)
ログ:Type=1 受信に AA 81 02 00 64 91(R1)、Type=0x15 受信に AA 90 01 15 50(R2・15 をエコー)、Type=0x99 受信に AA EE 01 00 99(R3 既定)が自動応答される
受信の Type でルールが先勝ちで出し分けられるログ。Type=1R1AA 81 02 00 64 91・温度)、Type=0x15R2AA 90 01 15 50{field:Type}15 をエコー)、Type=0x99R3AA EE 01 00 99・既定)。いずれも R3 も条件上は一致するが、優先度の小さいルールが先勝ちするため Type=1/0x15 では R3 は発火していない。末尾 1 バイトは {crc:Sum8} が自動計算。
ここがステップ 1(まず動かす)の到達点です。同じ仕組みに 3 つの応答が同居していて、受信の Type によって違うルールが先勝ちしています。どのルールが発火したかは CommSim 本体のログで確認できます(Type=0x01 で R1 の応答が出ていれば、R3 のキャッチオールは発火していません=先勝ち)。

ステップ2:キャッチオールの落とし穴を確認する

R3(既定=キャッチオール)は条件なし=どのフレームにも一致します。だから優先度を最低(100)に置いて「他に当たらなかったときだけ」効くようにしています。ここで、わざと優先度を上げて壊してみます。

  1. 「バイナリ構造応答」タブで R3(既定=未対応) を編集し、優先度を 5(R1=10・R2=20 より小さく)に変える。
  2. セッションを「起動」し直し、① で再度「デモ送信」する。

観察できること

  • AA 01 00 ABType=0x01(本来は R1 温度)
  • AA EE 01 00 99R3 が先勝ち=既定(温度応答が出ない!)
  • AA 15 00 BFType=0x15(本来は R2 アナログ)
  • AA EE 01 00 99こちらも R3=既定に食われる
これがキャッチオールの落とし穴です。条件なしのルールは全フレームに一致するので、優先度を小さくすると R1・R2 より先に当たり、すべての応答が既定になってしまいます。キャッチオール(既定応答)は必ず最低優先度に置きます。
思い出すべきは 「数字が小さいほど先に評価される」 こと。観察し終わったら R3 の優先度を 100 に戻してステップ 3 に進みます。

ステップ3:層間競合を見る(パケット一致が構造体ルールを抑止)

ステップ 2 で見た R3(キャッチオール) は、構造体ルール(層②)の中での既定でした。実は、その R3 すら層①のパケット一致オートメーションに抑止されることがあります。メンテ用フレーム AA F0 … で観察します。

  1. R3 の優先度を 100 に戻した状態で、セッションを起動する。
  2. ① の送信欄に AA F0 00 9A を入力して送る(デモ送信の 4 通目にも含まれます)。

観察できること(AX 有効時)

  • AA F0 00 9AType=0xF0(メンテ)
  • AA FA 01 01 A6AX(層①)が先勝ち。R3 は評価されない
ログ:AA F0 受信に「オートメーション開始: メンテPing(層①・先勝ちでR3抑止)」→「[AUTO] 送信: AA FA 01 01 A6」が記録され、R3 のキャッチオール(AA EE)は出ない
AA F0(メンテ)受信時のログ。オートメーション開始: メンテPing(層①・先勝ちで R3 抑止)[AUTO] 送信: AA FA 01 01 A6 が記録され、構造体ルール R3 のキャッチオール(AA EE)は出ない。AX が生バイト列 AA F0(パース前)で一致し、層②(Type で見る R1〜R3)より先に処理されている。AX を無効にして再送すると、ここが AA EE 01 00 99(R3 既定)に切り替わる。

次に、AX を無効化して同じフレームを送ります。

  1. 「オートメーション」タブで AX(メンテ Ping) の有効チェックを外す
  2. セッションを起動し直し、再度 AA F0 00 9A を送る。

観察できること(AX 無効時)

  • AA F0 00 9AType=0xF0(メンテ)
  • AA EE 01 00 99②の R3 キャッチオールに落ちる
これが層間競合(先勝ち抑止)です。ステップ 2 で見たキャッチオール R3 は、層①のパケット一致オートメーションにも抑止されます。CommSim は受信を ①パケット一致オートメーション → ②構造体ルール → ③既定転送 の順で評価し、①が当たった時点で②(R3 を含む)の評価そのものを行いません。だから AX を有効/無効にすると同じフレームの応答が AA FA(メンテ)と AA EE(既定)で切り替わります
AX は AA F0 という生バイト列で一致します(パース前)。R1〜R3 の Type 条件(パース後)とは層が違う、という非対称がここで効いています。

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

自分の機器の「コマンドごとの応答の出し分け」を、どう組むかの判断の糸口です。

やりたいこと選ぶ条件/設定
特定の 1 値(コマンド・コード)に応答するフィールド条件 Eq小さい優先度(キャッチオールより先に当てる)
連続した値の範囲に応答するGeLe の 2 条件(AND)で下限・上限を指定。受信値を返すなら {field:…} でエコー
当たらなかったときの既定応答(エラー・未対応)を返す条件なし(空)のキャッチオール最低優先度に置く
同じ受信に複数ルールが当たる優先度で先勝ちを制御(数字が小さい 1 件だけが返る)
特定のフレームだけ別系統で処理する(メンテ・割り込み)パケット一致オートメーション(層①)。生バイト列の HexPattern で当て、構造体ルールより先に処理する
ヒント:応答にチェックサムや受信フィールドのエコーが要るなら、応答テンプレートで {crc:Sum8}(や {crc:Crc16Modbus})・{field:名前} を使います。CommSim が送信時に自動計算・差し込みします(→ バイナリ構造応答)。

次に読む