スクリプト応答(C# による動的応答生成)
Phase 1(インライン C#)は現バージョンで利用可能です。 スクリプト実行を有効化するには、プロジェクト設定の「スクリプト / プラグイン」タブで「スクリプト応答を有効にする」をオンにしてください。DLL プラグイン方式(
PluginResponseGenerator)は今後の Phase 2 で対応予定です。
スクリプト応答は、オートメーションの送信ステップの一種です。C# のコードを直接記述し、受信データに応じた計算・分岐・状態保持を使った動的な応答バイト列を生成できます。
固定値やテンプレートでは表現できない「受信値で分岐して計算した結果を返す」「カウンタを保持して毎回異なる値を返す」「状態機械を実現する」といった用途に適しています。
セキュリティ注意: スクリプトはこのアプリと同じ権限で実行されます。自分が記述・信頼できるコードのみ有効化してください。詳しくはセキュリティについてを参照してください。
どれを使えばよいか
| やりたいこと | 使う種別 |
|---|---|
| 固定値・長さ/CRC/受信エコー等の宣言的な値埋め込み | 送信ステップ(テンプレート {..}) |
| 固定フレーム構造に対してフィールド条件で応答を出し分ける(バイナリ機器) | バイナリ構造応答(SendStructured) |
| 計算・条件分岐・状態(カウンタ / 状態機械)が必要な手続き的応答 | スクリプト応答(本機能・利用可能) |
スクリプト応答を使う手順
1. スクリプト実行を有効化する
スクリプト応答は既定で無効です。はじめに有効化してください。
- メニューバーの「プロジェクト」→「プロジェクト設定...」を開きます。
- 「スクリプト / プラグイン」タブを選択します。
- 「スクリプト応答を有効にする」トグルをオンにします。
ここで有効化したプロジェクト(ファイルパス単位)は次回以降も有効化状態が維持されます(別名保存・移動後は再度有効化が必要です)。
2. オートメーションにスクリプト応答ステップを追加する
- セッション画面の「オートメーション」タブを開きます。
- 「追加」または「編集」をクリックし、オートメーション編集ダイアログを開きます。
- 送信データのステップ行で「種別」を「スクリプト応答」に変更します。
- 行内の「スクリプト編集...」ボタンをクリックし、スクリプト編集ダイアログを開きます。
3. C# コードを記述する
スクリプト編集ダイアログのコード欄に C# を記述します。メソッド本体のみを書いてください(クラス定義・using 宣言は不要です)。
byte[] を return すると、その内容を送信します。null または空配列を返すと、このステップでは何も送信しません。
使える変数(無修飾で参照できます):
| 変数 | 型 | 内容 |
|---|---|---|
Received | byte[] | トリガー時の受信データ(生バイト列)。パケット一致トリガー以外では空 |
ReceivedHex | string | 受信データを "AA 01 FF" 形式で表したもの |
ReceivedText | string | 受信データを UTF-8 でテキスト化したもの(バイナリの場合は Received / ReceivedHex を使用) |
Iteration | int | 繰り返し周回(0 起算)。{seq} テンプレートと同等 |
Endianness | Endianness | プロジェクトのバイト順序(Little / Big) |
Parsed | ParsedMessage? | 受信を構造体パースした結果(Matched / Fields / Success など)。プロジェクトに構造体定義があれば供給。未構成・空受信時は null |
State | IDictionary<string, object?> | セッション内で共有・保持される可変辞書(カウンタ・状態機械に使用) |
使えるヘルパ(無修飾で呼べます):
| ヘルパ | 説明 |
|---|---|
Hex("AA 01 FF") | HEX 文字列を byte[] に変換する |
Utf8("OK\r\n") | UTF-8 文字列を byte[] に変換する |
Concat(a, b, ...) | 複数の byte[] を連結する |
Log("メッセージ") | ログペインにメッセージを出力する(デバッグ用) |
4. 構文チェックをする
「構文チェック」ボタンをクリックすると、コードをコンパイルして構文エラーの有無を確認できます。
- エラーなし →
OKと表示 - エラーあり →
行N: エラー内容と表示
「OK」ボタンを押す前に構文チェックしておくことをおすすめします。構文エラーがある状態で「OK」を押した場合は、保存確認ダイアログが表示されます(保存は可能ですが、実行時にこのステップはスキップされます)。
5. タイムアウトを設定する
既定タイムアウトはプロジェクト設定の「スクリプト / プラグイン」タブで設定した値(既定: 1000ms)が使われます。ステップごとに「タイムアウト上書き」で別の値を指定することもできます。
コード例
例 1: 受信バイトで分岐して応答する
// 受信の先頭バイトで分岐して応答フレームを返す
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, // 未知コマンドは無応答
};
例 2: 状態を保持してカウンタを返す
// リクエストのたびにカウンタを進め、応答末尾に付ける
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 });
例 3: 状態機械(起動フロー)
// 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");
例 4: 構造体パース結果(Parsed)で分岐する
プロジェクトに構造体定義があると、受信は自動でパースされ Parsed に入ります。フィールド値で分岐できます。
// Cmd フィールドの値に応じて応答を変える(構造体未定義時は Parsed が null)
if (Parsed == null || !Parsed.Success) return null;
var cmd = Parsed.Fields.FirstOrDefault(f => f.Name == "Cmd");
return (cmd?.Value is long c && c == 0x10) ? Hex("AA 90 01") : Hex("AA 90 00");
実行時の動作
- 応答なし:
nullまたは空配列を返すと、このステップでは何も送信しません(オートメーション自体は次のステップへ続行します)。 - タイムアウト: 指定時間内に
returnしない場合、このステップはスキップされログにタイムアウトが記録されます。アプリ・オートメーションは継続します。 - 実行時例外: 例外が発生した場合も同様にスキップ+ログで継続します。アプリは落ちません。
- 応答サイズ上限: 1 MiB を超える byte[] は送信されません(ログに通知)。
注意:待機・繰り返しはスクリプト内で行わず、ステップ側で組んでください。 スクリプトは C# なので
Task.Delayやループで「待つ・繰り返す」ことも技術的には書けますが、おすすめしません。
- スクリプトはタイムアウト付きで実行され、長い待機やループはタイムアウトで打ち切られるか、タイムアウトを延ばして安全網を外すことになります。
- スクリプト内の待機・ループは「操作」列の ■(停止)で即座に止められないことがあります(Roslyn はサンドボックスではなく、協調キャンセルを見ないループはスレッドを占有し続けます)。一方、Delay ステップやオートメーションの繰り返し設定はキャンセルに正しく追従し、停止操作できれいに止まります。
スクリプトの役割は「今回送る応答バイト列を1回計算して返す」ことに留め、「一定間隔で送る/N回繰り返す/受信を待つ」といったタイミング制御は Delay・繰り返し・受信待機の各ステップに任せるのが安全で、各ステップ・各周回がログや進捗に個別表示される利点もあります。
セキュリティについて
既定で無効・明示的な有効化
スクリプト応答は既定で無効です。プロジェクト設定で「スクリプト応答を有効にする」をオンにした場合のみ実行されます。
トラストはローカル・パス単位
有効化の状態(トラスト)は このPCの app-settings.json にプロジェクトファイルのパス単位で記録されます。プロジェクトファイル(.commsim)自体にはトラストを書きません。
そのため、他の人が作ったプロジェクトを受け取っても、受け取った側が明示的に有効化しない限りスクリプトは動きません。
未トラストのプロジェクトを開いたとき
スクリプトを含むプロジェクトで、まだトラストされていない場合は、ウィンドウ上部にバナーが表示されます。
このプロジェクトはスクリプトを含みます。実行するには有効化してください。
[ 有効化する ] [ 詳細 ]
「有効化する」をクリックすると確認ダイアログが表示され、承認するとトラスト登録されます。一度有効化したプロジェクトは次回以降バナーが表示されません。
参照とサンドボックスについて
インライン C# のスクリプトは Roslyn(C# スクリプティング)で実行されます。System.IO・System.Net.Http 等、.NET のフレームワーク参照一式が利用可能です。
注意: Roslyn は完全なサンドボックスではありません。実行できる処理の範囲を技術的に限定する仕組みではなく、事故を防ぐためのトラストゲート(既定オフ・明示的有効化)・タイムアウト・応答サイズ上限が防衛線です。悪意あるコードからの完全な防御を保証するものではないため、自分が信頼できるコードのみ有効化してください。
試せるサンプル
今すぐスクリプト応答を試せる実例は、同梱サンプル samples/script-response/script-response.commsim(ポート 7400)です。テキスト行プロトコル CounterDev の仮想機器として、READ→VAL:1/2/3…(State でカウンタ保持)・PING/RESET/未知コマンド分岐を実装したインライン C# の実例を確認・編集できます。
手順の詳細はチュートリアル「スクリプトで動的応答を作る」を参照してください。
もう 1 つの実例として、samples/modbus-tcp/modbus-tcp-script.commsim(ポート 503)では Modbus FC03 の動的長応答をスクリプトで実装しています(構造体ルール版のポート 502 との比較教材)。詳細は Modbus-TCP を参照してください。
DLL プラグイン(将来対応・Phase 2)
IResponseGenerator インターフェースを実装した DLL を所定フォルダに置いて選択する方式は、Phase 2 で対応予定です(現時点では未実装)。インライン C# が複雑になった場合の受け皿として将来提供します。