ルールの優先度・重複・層間競合(バイナリ構造応答の出し分け)
このチュートリアルは 1 つの構造定義に複数のバイナリ構造応答ルールを優先度付きで重ね、重なったときに何が返るかを観察するためのものです。題材はセンサーハブ SensorHub(バイナリ)。同梱プロジェクトを開いて起動すると、特定値・範囲・キャッチオール(既定)の先勝ち、キャッチオールの置き場所の落とし穴、パケット一致オートメーションによる層間抑止を一連で試せて、そこから「自分の機器のコマンドごとに応答をどう出し分けるか」を掴めます。
センサーハブ SensorHubTCP 9620バイナリ(Stx+Type+Len+Payload+Sum8)優先度で先勝ち重複・キャッチオール層間競合実機不要
このページでできること(段階ゲート)
このチュートリアルの主役は 2 つです。①だけでも実用的に学べ、②まで行くとオートメーションの先勝ちを「層をまたいで」理解できます。
- ① 複数ルールの優先度・重複(ステップ 1〜2・約 5 分で完結):1 つの構造に特定値ルール・範囲ルール・キャッチオール(既定)ルールを優先度付きで重ね、同じ受信に複数が当たっても優先度で 1 件が先勝ちする様子を観察する。キャッチオールを置く位置を間違えると「全部が既定応答になる」落とし穴も確認する。
- ② 層間競合(ステップ 3):特定のフレームにパケット一致オートメーション(層①)を当て、それが構造体ルール(層②)を抑止する様子を観察する。オートメーションの有効/無効で同じフレームの応答が変わる。
題材プロトコル SensorHub と 3 ルール+1 オートメーション
SensorHub は、本チュートリアル用のセンサーハブ機器プロトコルです。バイナリで、フレームは Stx(0xAA) + Type + Len + Payload(Len バイト) + Sum8。機器は Type(種別コード)で内容を区別し、種別ごとに違う応答を返します。この 1 台に、Type で出し分ける 3 つの構造体ルールと、メンテ用の 1 つのパケット一致オートメーションが同居しています。
| # | 層 | 名前 | 条件(Type) | 優先度 | 返すもの | 教える概念 |
|---|---|---|---|---|---|---|
| R1 | ② 構造体ルール | 温度センサ | Eq 1(=0x01) | 10 | AA 81 02 00 64 91 | 特定値マッチ・先勝ちで勝つ |
| R2 | ② 構造体ルール | アナログ群 | Ge 16(0x10)かつ Le 31(0x1F) | 20 | AA 90 01 «Type» «Sum8» | 範囲条件(2 条件 AND)・種別エコー |
| R3 | ② 構造体ルール | 既定=未対応 | 条件なし(空) | 100 | AA EE 01 00 99 | キャッチオール(全フレームに一致)・最低優先度 |
| AX | ① パケット一致 | メンテ Ping | 生バイト列に AA F0 を含む | 10 | AA FA 01 01 A6 | 層間競合(①が②の R3 を抑止) |
① 重複は優先度で 1 件。
Type=0x01 は R1(特定値)と R3(キャッチオール)の両方に当たりますが、優先度が小さい R1 だけが返ります。Type=0x15 は R2(範囲)と R3 に当たりますが R2 が勝ちます。② キャッチオールは最低優先度に置く。R3 は条件なし=どのフレームにも一致します。だから優先度を最低(100)に置いて「他に当たらなかったときの既定」にします。優先度を小さくすると全部を食ってしまう(ステップ 2 で確認)。
③ ①パケット一致は層が違う。AX はパース前の生バイト列(
AA F0)を見て、R1〜R3 はパース後のフィールド(Type)を見ます。受信は ①パケット一致 → ②構造体ルール の順で評価され、①が当たると②は評価されません(先勝ち抑止)。Sum8 について:フレーム末尾の
Sum8 はそれ以前の全バイトの 8bit 総和です。受信パースでは照合しません(パーサは読むだけ)。応答テンプレートの {crc:Sum8} が送信時に自動計算します。«Type» は受信した Type の 1 バイトをエコーする {field:Type} です。用意するもの
| ファイル | 内容 |
|---|---|
rule-priority/rule-priority.commsim | SensorHub 機器スタブのプロジェクト(TCP 9620・3 構造体ルール R1/R2/R3+パケット一致オートメーション AX が設定済み) |
これは CommSim.Sample の実行フォルダ内 samples/rule-priority/ に同梱されています。必要なのは CommSim 本体と CommSim.Sample の 2 本だけで、上流の実機は不要です(CommSim.Sample のクライアント役がアプリ役を演じます)。
ステップ1:3 ルールを一望して起動、先勝ちを見る(まず動かす)
- CommSim 本体で
samples/rule-priority/rule-priority.commsimを開く(「ファイル」→「ファイルから開く…」/Ctrl+O)。 - 「バイナリ構造応答」タブを開き、3 ルール(R1/R2/R3)を優先度順に一望する。条件(
Typeの Eq/範囲/空)と優先度(10/20/100)の違いを眺める。「オートメーション」タブで AX(メンテ Ping) も確認する。 - セッション「SensorHub スタブ TCP 9620」を「起動」する。
- CommSim.Sample を起動し、「動かすサンプル」で「バイナリ構造応答の優先度・重複(センサーハブ)」を選ぶ。① ペインを「接続」(9620 へ接続)して「デモ送信」を押す(
Type違いのフレームが順に送られます)。
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 でルールが先勝ちで出し分けられるログ。Type=1→R1(AA 81 02 00 64 91・温度)、Type=0x15→R2(AA 90 01 15 50・{field:Type} で 15 をエコー)、Type=0x99→R3(AA EE 01 00 99・既定)。いずれも R3 も条件上は一致するが、優先度の小さいルールが先勝ちするため Type=1/0x15 では R3 は発火していない。末尾 1 バイトは {crc:Sum8} が自動計算。Type によって違うルールが先勝ちしています。どのルールが発火したかは CommSim 本体のログで確認できます(Type=0x01 で R1 の応答が出ていれば、R3 のキャッチオールは発火していません=先勝ち)。ステップ2:キャッチオールの落とし穴を確認する
R3(既定=キャッチオール)は条件なし=どのフレームにも一致します。だから優先度を最低(100)に置いて「他に当たらなかったときだけ」効くようにしています。ここで、わざと優先度を上げて壊してみます。
- 「バイナリ構造応答」タブで R3(既定=未対応) を編集し、優先度を
5(R1=10・R2=20 より小さく)に変える。 - セッションを「起動」し直し、① で再度「デモ送信」する。
観察できること
- ◀AA 01 00 ABType=0x01(本来は R1 温度)
- ▶AA EE 01 00 99R3 が先勝ち=既定(温度応答が出ない!)
- ◀AA 15 00 BFType=0x15(本来は R2 アナログ)
- ▶AA EE 01 00 99こちらも R3=既定に食われる
思い出すべきは 「数字が小さいほど先に評価される」 こと。観察し終わったら R3 の優先度を
100 に戻してステップ 3 に進みます。ステップ3:層間競合を見る(パケット一致が構造体ルールを抑止)
ステップ 2 で見た R3(キャッチオール) は、構造体ルール(層②)の中での既定でした。実は、その R3 すら層①のパケット一致オートメーションに抑止されることがあります。メンテ用フレーム AA F0 … で観察します。
- R3 の優先度を
100に戻した状態で、セッションを起動する。 - ① の送信欄に
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)は出ない。AX が生バイト列 AA F0(パース前)で一致し、層②(Type で見る R1〜R3)より先に処理されている。AX を無効にして再送すると、ここが AA EE 01 00 99(R3 既定)に切り替わる。次に、AX を無効化して同じフレームを送ります。
- 「オートメーション」タブで AX(メンテ Ping) の有効チェックを外す。
- セッションを起動し直し、再度
AA F0 00 9Aを送る。
観察できること(AX 無効時)
- ◀AA F0 00 9AType=0xF0(メンテ)
- ▶AA EE 01 00 99②の R3 キャッチオールに落ちる
AA FA(メンテ)と AA EE(既定)で切り替わります。AX は
AA F0 という生バイト列で一致します(パース前)。R1〜R3 の Type 条件(パース後)とは層が違う、という非対称がここで効いています。自分のケースへの当てはめ方
自分の機器の「コマンドごとの応答の出し分け」を、どう組むかの判断の糸口です。
| やりたいこと | 選ぶ条件/設定 |
|---|---|
| 特定の 1 値(コマンド・コード)に応答する | フィールド条件 Eq+小さい優先度(キャッチオールより先に当てる) |
| 連続した値の範囲に応答する | Ge と Le の 2 条件(AND)で下限・上限を指定。受信値を返すなら {field:…} でエコー |
| 当たらなかったときの既定応答(エラー・未対応)を返す | 条件なし(空)のキャッチオールを最低優先度に置く |
| 同じ受信に複数ルールが当たる | 優先度で先勝ちを制御(数字が小さい 1 件だけが返る) |
| 特定のフレームだけ別系統で処理する(メンテ・割り込み) | パケット一致オートメーション(層①)。生バイト列の HexPattern で当て、構造体ルールより先に処理する |
{crc:Sum8}(や {crc:Crc16Modbus})・{field:名前} を使います。CommSim が送信時に自動計算・差し込みします(→ バイナリ構造応答)。次に読む
- バイナリ構造応答 — フィールド条件・優先度・応答テンプレート・チェックサムの詳細(設定の正)
- メッセージ構造定義 — フレーミング・フィールド・長さ駆動・判別子の詳細
- メッセージ構造定義を白紙から作る — 本チュートリアルの前提(構造の作り方)
- オートメーションを使いこなす — パケット一致の先勝ち(層間競合の前提)
- ← チュートリアル一覧に戻る