スクリプトで動的応答を作る(C# で状態保持・分岐)
このチュートリアルは 固定値やテンプレートでは表現できない「状態保持・計算・分岐」を、インライン C# スクリプトによる動的応答で実現することを試すためのものです。題材は小さなテキスト行プロトコル CounterDev。同梱プロジェクトを開いてスクリプト実行を有効化(トラスト)し、起動すると、リクエストのたびに増えるカウンタ・コマンドでの分岐・状態のリセットを一連で試せて、そこから「自分の機器の動的な応答をどう書くか」を掴めます。
CounterDevTCP 7400テキスト行(READ / PING / RESET)State でカウンタ保持受信で分岐トラスト(有効化)実機不要
このページでできること
スクリプト応答の主役は 3 つです。①と②でスクリプトならではの価値を実感でき、③まで行くと自分で書き換える足がかりになります。
- ① 状態保持(State でカウンタ):
READを送るたびにVAL:1 → VAL:2 → VAL:3 …と毎回違う値が返る。固定値テンプレートでは絶対にできない「前回値を覚えて次を返す」をスクリプトで実現する。 - ② 受信での分岐:
PING→PONG、RESET→ カウンタを 0 に戻す、未知コマンド → 受信内容をエコー。1 つのスクリプトが受信内容を見て応答を組み立てる。 - ③ 書き換え(構文チェック):応答文字列やコマンドを自分で書き換え、「構文チェック」で確認してから反映する。
題材プロトコル CounterDev とスクリプト
CounterDev は、本チュートリアル用の小さなテキスト行プロトコルです。クライアントが 1 行のコマンド(末尾 \r\n)を送ると、CommSim 本体の1 つのインライン C# スクリプトが内容に応じて応答を生成します。この 1 台に、あらゆる受信で起動するパケット一致オートメーションが 1 つあり、その送信ステップが Script(スクリプト応答)です。
| 受信(① が送る) | スクリプトの応答 | 教える概念 |
|---|---|---|
READ | VAL:1 → VAL:2 → VAL:3 …(毎回 +1) | State でカウンタ保持(テンプレートでは不可能) |
PING | PONG | 受信での分岐 |
RESET | OK reset(カウンタを 0 へ) | State の書き換え+Log() |
上記以外(例 HELLO) | ERR unknown:HELLO | 受信エコー・未知コマンド処理 |
スクリプト本体(メソッド本体だけを書きます。クラス定義・using は不要):
// 受信した1行コマンドに応じて動的に応答する(State でカウンタを保持)
var cmd = ReceivedText.Trim();
if (cmd == "PING") return Utf8("PONG\r\n");
if (cmd == "RESET") { State["seq"] = 0; Log("カウンタをリセットしました"); return Utf8("OK reset\r\n"); }
if (cmd == "READ")
{
var n = (int)(State.TryGetValue("seq", out var v) ? v! : 0) + 1;
State["seq"] = n; // セッション内で持続(毎回違う値)
return Utf8($"VAL:{n}\r\n");
}
return Utf8($"ERR unknown:{cmd}\r\n"); // 未知コマンドは受信内容をエコー
①
State はセッション内で持続する辞書。READ のたびに State["seq"] を +1 して返すので、毎回違う値になります(セッションを停止するとクリア)。②
ReceivedText は受信を UTF-8 でテキスト化したもの。.Trim() で末尾の \r\n を落としてコマンドを判定します。③
Utf8(...) / Hex(...) は応答バイト列を作るヘルパ。byte[] を return すると送信、null を返すと無応答です。使える変数・ヘルパの一覧は スクリプト応答 を参照。用意するもの
| ファイル | 内容 |
|---|---|
script-response/script-response.commsim | CounterDev スタブのプロジェクト(TCP 7400・あらゆる受信で起動するパケット一致オートメーション+Script ステップが設定済み) |
これは CommSim.Sample の実行フォルダ内 samples/script-response/ に同梱されています。必要なのは CommSim 本体と CommSim.Sample の 2 本だけで、上流の実機は不要です(CommSim.Sample のクライアント役がアプリ役を演じます)。
ステップ1:スクリプト実行を有効化する(トラスト)
スクリプト応答は既定で無効です。スクリプトを含むプロジェクトを開くと、ウィンドウ上部に確認バナーが出ます。まずこれを有効化します。
- CommSim 本体で
samples/script-response/script-response.commsimを開く(「ファイル」→「ファイルから開く…」/Ctrl+O)。 - 上部のバナー「このプロジェクトはスクリプトを含みます。実行するには有効化してください」の [有効化する] をクリックし、確認ダイアログで承認する。
(または「プロジェクト」→「プロジェクト設定...」→「スクリプト / プラグイン」タブ →「スクリプト応答を有効にする」をオンにする。)
.commsim 自体には書かれない(他人に渡したプロジェクトは受け取った側が有効化しない限り動かない)。一度有効化すれば次回以降バナーは出ない。ステップ2:動かしてカウンタと分岐を見る(すぐ試せる)
- 「オートメーション」タブを開き、「コマンド応答(スクリプト)」を選んで [編集]→ 送信データの Script ステップの [編集…] でコードを一望する(§題材プロトコルのコードが入っています)。
- セッション「スクリプト応答スタブ TCP 7400」を「起動」する。
- CommSim.Sample を起動し、「動かすサンプル」で「スクリプト応答(C# で動的応答・CounterDev)」を選ぶ。① ペインを「接続」(7400 へ接続)して「デモ送信」を押す(
READ×3 →PING→RESET→READ→HELLOが順に送られます)。
観察できること
- ◀READ① が送信(1 回目)
- ▶VAL:1カウンタ=1
- ◀READ① が送信(2 回目)
- ▶VAL:2State で +1 されて 2
- ◀READ① が送信(3 回目)
- ▶VAL:33(毎回違う=テンプレ不可)
- ◀PING分岐
- ▶PONGPING → PONG
- ◀RESET状態を書き換え
- ▶OK resetカウンタを 0 に
- ◀READRESET 後の READ
- ▶VAL:11 に戻る=State が効いた証明
- ◀HELLO未知コマンド
- ▶ERR unknown:HELLO受信内容をエコー
READ のたびに VAL:1 → VAL:2 → VAL:3 と増えていく(State のカウンタ)。PING→PONG、RESET で OK reset、その後の READ が VAL:1 に戻る(State が効いている証明)、未知の HELLO は ERR unknown:HELLO とエコー。[SCRIPT] 行は Log() 出力。VAL:1 に戻るのが、State が「セッション内で生きている」何よりの証拠です。ステップ3:スクリプトを少し書き換える
スクリプトは自分で書き換えられます。応答を変えてみましょう。
- 「オートメーション」タブ →「コマンド応答(スクリプト)」→ [編集]→ Script ステップの [編集…]を開く。
- たとえば
READの応答をreturn Utf8($"VAL:{n} (count)\r\n");に変える、PINGの応答を"PONG v2\r\n"にする、新しいコマンドif (cmd == "HELLO") return Utf8("HI THERE\r\n");をREADの前に足す、など。 - [構文チェック]を押してエラーが無いことを確認する(
OK表示)。エラーがあれば行N: 内容が出ます。 - [OK]で保存し、セッションを起動し直してから ① で再度デモ送信して、応答が変わったことを確認する。
・応答が返らない → セッションが起動しているか/スクリプトが有効化(トラスト)されているか(ステップ 1)を確認。未トラストだと Script ステップはスキップされ、ログに「スクリプト未許可のためスキップ」と出ます。
・カウンタが増えない →
State の読み書きキーが一致しているか確認。セッションを停止すると State はクリアされます。・
VAL: が READ\r\n 全体に反応してしまう → ReceivedText.Trim() で末尾の改行を落としているか確認。自分のケースへの当てはめ方
自分の機器の「動的な応答」を、どう書くかの判断の糸口です。
| やりたいこと | 書き方 |
|---|---|
| 固定値・長さ・受信エコーなど宣言的な応答 | スクリプトではなく送信ステップのテンプレート({..})が簡単(→ オートメーション) |
| リクエストのたびに違う値(連番・センサ値)を返す | State に値を持って読み書き(本チュートリアルの seq) |
| 受信内容で応答を分岐する | Received / ReceivedText を見て if / switch で組み立て |
| 状態機械(起動フロー・ロック状態など)を再現する | State に現在状態を持ち、受信で遷移させる(→ コード例3) |
| 構造体フィールドで分岐・計算する | プロジェクトに構造定義があれば Parsed.Fields でフィールド値を参照(→ コード例4) |
| 応答にチェックサムや複数バイト列の連結が要る | Hex("AA 01") でバイト列化し Concat(a, b, …) で連結 |
次に読む
- スクリプト応答 — 使える変数・ヘルパ、コード例、実行時の動作、セキュリティの詳細(記述の正)
- スクリプト応答の編集 — 編集ダイアログの画面項目
- オートメーションを使いこなす — トリガー(パケット一致)と送信ステップ(スクリプトはその一種)
- ← チュートリアル一覧に戻る