スクリプト応答(C#)

よくある応答は画面から数項目を埋めるだけ。計算・分岐・状態保持が必要な複雑な応答は C# スクリプトで。簡単なものは簡単に、難しいものも実現できる二段構えです。

どれを使うか — 簡単なものは画面から

CommSim は応答の作り方を 3 段階で用意しています。多くのケースはコード不要。スクリプトは「どうしてもコードが要る」場面の受け皿です。

画面だけ

テンプレート

固定値・長さ・CRC・受信エコーを {len} / {crc} / {echo} で埋め込む宣言的な応答。よくある応答はこれで十分。

画面だけ

バイナリ構造応答

フィールド条件(= など)で応答を出し分け。コマンドやアドレスで分岐する固定フレームのバイナリ機器向け。

迷ったら、まずテンプレート → 足りなければバイナリ構造応答 → それでも無理ならスクリプト、の順で十分です。

スクリプトでできること

受信データに応じて、毎回違う・長さが変わる・状態で変わる応答を生成できます。

動的なバイト長

要求の数量フィールドに応じて応答の本数・長さを変える

#

状態保持

セッション内で値を保持し、リクエストごとにカウンタを進める

条件分岐

受信の先頭バイトや値で応答を切り替える

状態機械

IDLE→READY→RUNNING のように状態を遷移させる

計算結果

受信値から算出した値・チェックサムを組み立てる

条件付き無応答

null を返してそのときだけ応答しない

使える変数・ヘルパ

コードはメソッド本体だけを書きます(class / using 不要)。byte[]return すると送信、null または空配列で無送信です。次の変数・ヘルパを無修飾で使えます。

使える変数

変数内容
Receivedbyte[]トリガー時の受信データ(生バイト列)。パケット一致以外では空
ReceivedHexstring受信を "AA 01 FF" 形式にした文字列
ReceivedTextstring受信をセッションのエンコーディングでテキスト化
Iterationint繰り返し周回(0 起算)。{seq} と同等
EndiannessEndiannessプロジェクトのバイト順序(Little / Big)
ParsedParsedMessage?構造体パース結果(フレーミング有効時のみ非 null)
StateIDictionary<string,object?>セッション内で保持される可変辞書(カウンタ・状態機械)

使えるヘルパ

ヘルパ説明
Hex("AA 01 FF")HEX 文字列を byte[] に変換
Utf8("OK\r\n")UTF-8 文字列を byte[] に変換
Concat(a, b, ...)複数の byte[] を連結
Log("メッセージ")ログタブへ出力(デバッグ用)

実例

「構文チェック」でコンパイル確認してから保存できます。下のコードはそのまま貼り付けて試せます。

受信の先頭バイトで応答を切り替え、未知コマンドは無応答にする。

if (Received.Length == 0) return null;
return Received[0] switch
{
    0x01 => Hex("AA 81 00"),                // コマンド01へ ACK
    0x02 => Concat(Hex("AA 82"), Received), // コマンド02はエコー
    _    => null,                           // 未知コマンドは無応答
};

リクエストのたびにカウンタを進め、応答末尾に付ける(State で保持)。

var n = (int)(State.TryGetValue("count", out var v) ? v : 0);
State["count"] = n + 1;
Log($"カウンタ = {n}");
return Concat(Hex("AA 81"), new[] { (byte)n });

IDLE → READY → RUNNING と状態を遷移させ、現在状態を返す。

var phase = (string)(State.TryGetValue("phase", out var p) ? p : "IDLE");
State["phase"] = phase switch { "IDLE" => "READY", "READY" => "RUNNING", _ => "IDLE" };
return Utf8($"STATUS:{State["phase"]}\r\n");

同梱サンプル modbus-tcp-script.commsim(ポート 503)の実例。Modbus FC03 で、要求の Quantity に応じた動的バイト長の応答を生成します(テンプレートでは固定長しか返せない部分)。

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 長さが変化

受信から Quantity を読み取り、その本数分のレジスタ値を生成し、MBAP 長さフィールドも正しく計算して返しています。Modbus サンプルでテンプレート版と比較できます。

有効化(既定は無効)

スクリプトはアプリと同じ権限で動くため、既定で無効です。自分で書いた・信頼できるコードのみ有効化してください。

  1. プロジェクト設定を開く

    メニューバー「プロジェクト」→「プロジェクト設定...」→「スクリプト / プラグイン」タブ。

  2. 有効化トグルをオン

    「スクリプト応答を有効にする」をオン。タイムアウト既定(1000ms)もここで設定。

  3. ステップ種別を「スクリプト応答」に

    オートメーション編集の送信ステップで種別を変更し、「スクリプト編集...」でコードを記述。

有効化(トラスト)はこの PC の設定にプロジェクトのパス単位で記録され、.commsim 自体には書き込まれません。他人が作ったプロジェクトを受け取っても、受け取った側が明示的に有効化しない限りスクリプトは動きません。

制限とセキュリティ

🔒

参照の制限

System.IO / System.Net / System.Diagnostics などは既定で参照不可(安全な既定セットのみ)

タイムアウト

時間内に返さないとそのステップはスキップ+ログ記録(アプリは継続)

🛟

例外に強い

実行時例外もスキップ+ログで継続。アプリは落ちない

📦

サイズ上限

1 MiB を超える応答は送信されない(ログに通知)

Roslyn(C# スクリプティング)は完全なサンドボックスではありません。既定の参照制限は事故防止のためのもので、悪意あるコードからの完全防御を保証しません。自分が信頼できるコードのみ有効化してください。

動く実例で確かめたいなら、スクリプト版 Modbus を含むサンプル集へ。

サンプル集を見る