# 実機自動テスト計画 (Hardware-in-the-Loop Test Plan)

## 目的 (Purpose)

[PLAN_05](PLAN_05_テスト網羅性向上計画.md) の 4 層モデルで「層④: 実機」に残っている確認（SDK の実呼び出し・実画像）を，**Claude が実機を操作して自動で確認する**ようにする．人間の役割を「機材をつなぐ・チャートを置く・テストしてよいと合図する」という物理的な準備だけに絞る．

## 前提 (Preconditions)

2026-10-06 にユーザーと確認した．

| 項目 | 内容 |
| --- | --- |
| 接続 | カメラ（ImagingSource・Lumenera）と光源は，Claude が動作している開発 PC に接続できる |
| 機材 | 両機種がそろっている |
| 実行してよい条件 | **ユーザーが会話の中で明示的に合図したときだけ**．撮影の本番中に機材を操作しないため，合図なしに実機テストを実行しない |

## 仕組み (Design)

既存のセルフテスト（`TIASshot.exe --selftest`．偽カメラ・ヘッドレス）と同じ考え方で，実機版の検査モードを追加する．

- **起動**: `TIASshot.exe --hwtest --camera <lucam|iscam> [--with-chart] --hwtest-out <結果ファイル>`．ランナー `scripts/verify/run_hwtest.py` がビルドから実行・判定までを行う（`run_selftest.py` と同じ形）
- **ビルドフラグ**: `HWTEST`（Debug 系の構成のみ）．診断・評価機能を Release に含めない方針（CLAUDE.md・[ENV_06](../02_ENV/ENV_06_ビルド構成とビルドフラグ.md)）に従う
- **フォーム**: ImagingSource は SDK の画面部品（`ICImagingControl`）を使うため，ヘッドレスではなく**実際の `Form1` を表示して**動かす．人間がその場にいる前提なので表示してよい
- **カメラの選択**: 両機種を同時につないでも，`--camera` で指定した機種だけを使う（通常起動の `Form1_Load` は Lumenera を優先して探すため，そのままでは ImagingSource を検査できない）
- **結果**: 項目ごとの PASS / FAIL と測定値を結果ファイルに書き，終了コード（0=合格，1=不合格あり，2=実機テスト自体の異常，3=タイムアウト）で返す．セルフテストの結果ファイルと同じ書式にする

### 守ること (Safety)

- 保存先は一時フォルダ（`%TEMP%` 配下）にする．本番の保存先（`C:\TIAS_Data` 等）には書き込まない
- 同梱の `config.xml` は変更しない（一時フォルダに複製して保存先等を差し替える）
- カメラのデバイス状態ファイル（`{DeviceName}.xml`）は読むだけで，上書きしない．露光・ゲイン等を検査で変えた場合は，終了時に元の値へ戻す
- 光源は検査の最後に必ず消灯してポートを閉じる（失敗した場合も）
- 待機には必ずタイムアウトを付ける

## 検査項目 (Test Items)

### チャート不要（`--with-chart` なし）

| ID | 確認内容 | 判定の仕方 |
| --- | --- | --- |
| `connect` | 指定した機種に接続でき，機種名・シリアル番号が取れる | `Connect()` の成否 |
| `preview` | プレビューが流れる | 一定時間に届いたフレーム数（フレームレートの下限） |
| `exposure-gain` | 露光・ゲインの設定がフレームに効く | 設定を上げ下げしたときのフレームの平均輝度の変化の向き |
| `light` | 光源が点灯・消灯し，閉じた後に開き直せる | 点灯時と消灯時のフレームの平均輝度の差，ポートの再オープンの成否 |
| `close-preview` | プレビュー中に閉じる処理を呼んでもすぐ戻る | 後始末の所要時間（上限つき）と例外の有無 |
| `reconnect` | 切断した後に再接続できる | 2 回目の `Connect()` の成否とフレームの到着 |

### チャートが必要（`--with-chart`．人間がチャートをカメラの前に置いてから実行する）

| ID | 確認内容 | 判定の仕方 |
| --- | --- | --- |
| `calibrate` | 実物の舌診チャートで自動校正が完了する | 校正完了までの時間（上限つき）と，変換後の色票と参照値の色差 |
| `shot` | 単発・連続撮影で保存される | 保存されたファイル・Info.csv の中身，撮影時刻の間隔（設定値との差） |
| `close-saving` | 保存中に閉じようとすると取り消され，保存後は閉じられる | 取り消しの成否と表示文，保存完了後の後始末の順序 |
| `close-calibrating` | 校正中に閉じてもすぐ戻る | 後始末の所要時間と例外の有無 |

※ 判定のしきい値（フレームレートの下限，色差の上限，撮影間隔の許容誤差等）は，最初の実行で実測値を見てから決める．

## 進め方 (Workflow)

1. 人間が機材をつなぎ（チャートが要る検査ではチャートを置き），「実機テストして」等と合図する
2. Claude（verifier）が `python scripts/verify/run_hwtest.py --camera <機種> [--with-chart]` を実行し，結果ファイルと終了コードで判定する
3. 2 機種とも行う場合は，機種ごとに実行する
4. 人間に残る確認は，画面の見た目の最終確認など，数値で置き換えられないものだけにする

## 変更が必要なもの (Changes)

| 対象 | 変更内容 |
| --- | --- |
| `TIASshot`（本体） | `HWTEST` フラグの追加，`--hwtest` の実装（検査項目・結果ファイル），カメラを機種指定で接続する口 |
| `scripts/verify/run_hwtest.py` | ビルド・実行・判定のランナー |
| `.claude/verify-profile.md` | 実機カメラ・光源・シリアルポートへの接続を，「ユーザーが会話の中で合図したとき」に限って許可する．合図がなければ実機テストは実行せず「検証不能（合図なし）」と報告する |
| `.claude/rules/testing.md` | 層④のうち実機テストで確認できる項目を明記する |
| [TEST_01](../06_TEST/TEST_01_テスト仕様.md) | 手動テストの項目を，実機テストで置き換えた分だけ減らす |
| [ENV_04](../02_ENV/ENV_04_開発コマンド.md)・[ENV_06](../02_ENV/ENV_06_ビルド構成とビルドフラグ.md) | 実機テストのコマンドと `HWTEST` フラグの説明 |

## フェーズ (Phases)

### フェーズ 1: 基盤とチャート不要の検査

- `HWTEST` フラグ，`--hwtest`，機種指定の接続，結果ファイル，ランナーを作る
- チャート不要の検査（`connect`・`preview`・`exposure-gain`・`light`・`close-preview`・`reconnect`）を実装する
- 検証プロファイルを更新する
- 完了条件: 両機種で人間の合図のもと実行でき，しきい値を実測から決めて全項目が合格する

### フェーズ 2: チャートが必要な検査

- `calibrate`・`shot`・`close-saving`・`close-calibrating` を実装する
- 完了条件: 両機種で実物のチャートを使って全項目が合格する

### フェーズ 3: 手動テストの縮小

- TEST_01 の手動テストと PLAN_05 の「手動確認の目標形」を，実機テストで置き換えた分だけ縮める

## PLAN_05 との関係 (Relation to PLAN_05)

- PLAN_05 フェーズ 3b（SDK アダプタからの判断ロジックの切り出し）とは独立に進められる．3b で切り出したロジックは層①で，SDK の呼び出しそのものは本計画の実機テストで確認する，という分担になる
- PLAN_05 フェーズ 4（実機録画データによる回帰）の撮影は，本計画の実機テストの仕組みを使って自動化できる可能性がある（録画フレームの保存を検査モードに加える）

## リスクと対策 (Risks)

| リスク | 対策 |
| --- | --- |
| 撮影の本番中に Claude が機材を操作してしまう | 実行はユーザーの合図があったときだけ．検証プロファイルで明記し，合図がなければ検証不能として報告する |
| 検査でカメラの設定が変わったまま残る | デバイス状態ファイルは上書きしない．変えた露光・ゲインは終了時に元に戻す |
| 光源が点いたまま残る | 検査の最後（失敗時も）に消灯してポートを閉じる |
| 実機の個体差・照明で判定が不安定になる | しきい値は実測から余裕を持って決める．輝度は絶対値ではなく変化の向きで判定する |
| SDK がハングして検査が終わらない | 検査全体と各項目にタイムアウトを付け，ランナー側でもプロセスを打ち切る |
