> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ionworks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# ラボビュー

> チャンネル占有状況のプロジェクトスコープダッシュボード。実行中の内容、チャンネルの詳細、ウォッチ、SDK からの照会

**ラボビュー**は[装置ツリー](/ja/operate/equipment)を対象とした
プロジェクトスコープのダッシュボードで、テスト装置内のすべてのチャンネルと
その使用状況を表示します。「いま何が動いていて、チャンネルはいつ空くのか」
という問いに答えるために使います。

## チャンネル占有状況

ラボビューは、各チャンネルの状態をリンクされた測定から導出します — ライブテレメトリはありません。チャンネルは次の 4 つの状態のいずれかを取ります:

| 状態                  | 意味                                                                                          |
| ------------------- | ------------------------------------------------------------------------------------------- |
| `free`              | リンクされた測定がない、またはリンクされたすべての測定に `end_time` が設定されています。新しいテストに利用可能です。                            |
| `occupied`          | リンクされた測定に `end_time` がなく、鮮度ウィンドウ内で更新されています。セルが現在テスト中です。                                     |
| `stale`             | リンクされた測定に `end_time` がありませんが、鮮度ウィンドウ内で更新されていません。停止または暗黙的に失敗したテスト、あるいは完了マークを忘れたテストの可能性があります。 |
| `out_of_commission` | チャンネルが意図的にサービスから外れています（故障またはメンテナンス中）。上記の派生状態を上書きします。                                        |

鮮度ウィンドウは **48 時間**に固定されています。これは典型的なサイクラーの日次アップロード頻度より十分に大きく、およそ 2 回分の想定更新をスキップしたときにのみチャンネルが `stale` に切り替わります — 短時間の遅延ではなく、真の「停止・忘却」シグナルです。

`stale` チャンネルを解放するには、リンクされた測定に `end_time` を設定する（テストを完了としてマークする）か、その測定を削除してください。

## チャンネルをテーブルで表示する

ラボビューは、既定のカードレイアウト（サイクラーごとにグループ化されたチャンネル）と、
プロジェクト内のすべてのサイクラーを横断して **1 行につき 1 チャンネル**を表示する
**テーブルビュー**を切り替えられます。ラボビュー上部のレイアウト切替でモードを変更できますが、
選択は保存されません — 更新やページ遷移を行うとカード表示に戻ります。

次のような場合にテーブルビューを使ってください。

* サイクラーごとのグリッドではなく、プロジェクト内のすべてのチャンネルを 1 枚のフラットなリストで一覧したい。
* サイクラーを横断してチャンネルを絞り込み・比較したい — 例：すべての `stale` チャンネルを表示する、電気的定格を比較する。
* ラボが現在どう稼働しているかを、チャンネル単位のスナップショットとしてコピーしたい。

各行にはチャンネルカードと同じ情報 — サイト、サイクラー、チャンネル名、占有状態、電気的定格、
アクティブな測定（あれば）とその `estimated_end_time`、および `out_of_commission` フラグ — が
表示されるので、レイアウトを切り替えても隠れる情報はありません。行をクリックすると、
カードをクリックしたときと同じ[チャンネルページ](#チャンネルを詳しく見る)が開き、
同じ `channel:read` 権限でアクセスが制御されます。

テーブルは `client.lab.status()` が公開しているのと同じ集計から読み込まれるため、
Python から同じフラットな 1 行 1 チャンネルのビューが欲しい場合は、
ツリーを自分で平坦化してください。

```python theme={null}
status = client.lab.status(client.project_id)

for site in status.sites:
    for cycler in site.cyclers:
        for channel in cycler.channels:
            print(site.name, cycler.name, channel.name, channel.state)
```

## チャンネルを詳しく見る

ラボビューでチャンネルをクリックすると、その装置のいまと過去をまとめた
**チャンネルページ**が開きます。Ionworks Studio の他の場所ですでに表示されている
データを一つの画面に集約しているため、複数の測定を行き来しなくても、そのチャンネル
で何が起きているかを把握できます。

チャンネルページには次の情報が表示されます。

* 現在の状態（`free`、`occupied`、`stale`、または `out_of_commission`）と電気的定格。
* **アクティブな測定**（存在する場合）— セル、プロトコル、開始時刻、推定終了時刻 — に加え、測定に `end_time` を設定してチャンネルを解放する **Mark complete**（完了としてマーク）アクション。
* アクティブな測定の直近のデータを 2 つのタブで表示：
  * **Time series**（時系列）— 生信号の直近ウィンドウ（デフォルトは電圧。プロット側のセレクタから電流、温度、その他の列も追加できます）。x 軸は測定の `start_time` を起点としたカレンダー日付軸なので、経過秒ではなく実際の日付でグラフを読めます。
  * **Cycles**（サイクル）— 測定全体を通したサイクル単位のサマリ指標。
* このチャンネルで実行された **直近 100 件の測定履歴**（開始時刻が新しい順）と、各測定の詳細ページへのリンク。

時系列ウィンドウはユーザーが制御します — 日数（デフォルトは 7 日）を設定して
**Refresh** をクリックすると再取得します。自動ポーリングはしないので、プロットは
明示的に更新したときだけ変わり、「as of」タイムスタンプはそのスナップショットを
取得した時刻を示します。プロットするデータがあるのは `occupied` または `stale` の
アクティブな測定を持つチャンネルだけで、`free` や `out_of_commission` のチャンネル
では状態バッジと履歴のみが表示されます。

チャンネルページからの完了マークは、SDK からの `end_time` パッチと等価で、
チャンネルは即座に `free` に戻ります。

```python theme={null}
from datetime import datetime, timezone

client.cell_measurement.update(
    measurement_id,
    {"end_time": datetime.now(timezone.utc).isoformat()},
)
```

チャンネルページへのアクセスは `channel:read` 権限に従います — 権限を持たない
ユーザーはラボビューで装置ツリーを閲覧できますが、個々のチャンネルにドリルダウン
することはできません。

## SDK から占有状況を照会する

`client.lab` は、ラボビューが描画に使うのと同じ集約 API をラップし、「いまラボはどう動いているか」という定番の質問に Python から答えます。読み取り専用です — 装置を変更するには `client.site`、`client.cycler`、`client.channel` を使ってください。

| メソッド                                                                      | 答える内容                                                                               |
| ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| `client.lab.status(project_id)`                                           | 各チャンネルの導出状態とテスト中の測定、およびプロジェクトレベルの占有カウントを含む、サイト → サイクラー → チャンネルの完全なツリー。              |
| `client.lab.utilization(project_id)`                                      | ビジー率（`occupied` + `stale`、`out_of_commission` を分母に含める）と生のカウント。ラボウォールに表示される数値と一致します。 |
| `client.lab.free_channels(project_id, *, min_amps, min_volts, max_volts)` | 空きチャンネル。必要な電気的定格で任意にフィルタできます。「このセルはどこで回せるか」に答えます。                                   |
| `client.lab.stale_channels(project_id)`                                   | リンクされたテストが静かになったチャンネル — 停止・暗黙的失敗・忘れられたテストが装置を占有している可能性があります。                        |
| `client.lab.on_channel(project_id, channel_id)`                           | チャンネルで現在テスト中の測定、またはチャンネルが空き・運用停止・プロジェクト外のときは `None`。                                |

ネットワークにアクセスするのは `status()` だけです。他のメソッドはそのペイロードに対するクライアント側ビューで、デフォルトでは各呼び出しが独自にスナップショットを取得するため、1 回の呼び出しは常に内部整合的です。

```python theme={null}
util = client.lab.utilization(project_id)
print(f"{util.percent}% ビジー — {util.occupied}/{util.total} チャンネル稼働中")

# 4 A、0〜5 V ウィンドウのセル
candidates = client.lab.free_channels(
    project_id,
    min_amps=4.0,
    min_volts=0.0,
    max_volts=5.0,
)
for fc in candidates:
    print(f"{fc.site_name} / {fc.cycler_name} / {fc.channel.name}")

for fc in client.lab.stale_channels(project_id):
    m = fc.channel.measurement
    print(f"Stale: {fc.cycler_name}/{fc.channel.name} — 最終更新 {m.updated_at}")
```

別々の呼び出しは別々のスナップショットを取得するため、その間に装置が変化すると結果が食い違うことがあります。同じ**瞬間**の複数のビューが必要な場合（例: 稼働率と空きチャンネル一覧を同時に見る）は、スナップショットを 1 度だけ取得し、`status` キーワードで渡してください:

```python theme={null}
snapshot = client.lab.status(project_id)
util = client.lab.utilization(project_id, status=snapshot)
free = client.lab.free_channels(project_id, status=snapshot)  # 同じ瞬間
```

定格のないチャンネルは、その定格をフィルタとして指定した `free_channels` の結果から除外されます — 定格のないチャンネルが要件を満たすとは示せないためです。定格のないチャンネルを含めたい場合はフィルタを外してください。

## チャンネルをウォッチする

チャンネル上で現在実行中のテストを追いかけたい場合は、そのチャンネルを **ウォッチ** します。エンジニアは通常、自分のセルを動かしている少数のチャンネルをウォッチしておき、ラボウォールで **Only watched** フィルタを切り替えると、他をすべて隠して、気にしている実行だけを掘り下げられます。

ウォッチはチャンネルそのものではなく、現在そのチャンネル上にある特定の [セル測定](/ja/data/measurements) に紐付いています — したがってその測定が終了した瞬間（`end_time` がセットされた瞬間）、ウォッチは **自動的に解除** されます。同じチャンネルで新しいテストが始まっても、暗黙のうちにあなたのウォッチリストに戻されることはありません。新しい実行が始まったら、あらためてオプトインしてください。

ウォッチはチャンネルページまたはラボウォールから設定します — 稼働中の `occupied` または `stale` な測定があるチャンネルにのみ **Watch** トグルが表示されます（`free` または `out_of_commission` のチャンネルにはウォッチ対象がありません）。ウォッチはユーザーごとに管理されるため、各メンバーは他のメンバーに影響を与えることなく、自分のウォッチリストをキュレーションできます。

## スケジューリング不変条件

ラボビューと装置レコードの整合性を保つため、サーバーはいくつかのスケジューリング不変条件を強制します。時間順序や種別に関するルールに違反するリクエストは HTTP 400 で拒否され、チャンネルのスケジューリング競合（重複、サービス外）は HTTP 409 を返します。いずれの場合も書き込みは行われず、チャンネル状態も変わりません。

| 不変条件                                  | 発生タイミング                                                                                           | ステータス |
| ------------------------------------- | ------------------------------------------------------------------------------------------------- | ----- |
| チャンネルあたりアクティブな測定は 1 件のみ               | `end_time` が未設定の測定が既にリンクされているチャンネルに、別の測定を（作成時または `channel_id` の更新で）紐付けようとしたとき。                    | 409   |
| `out_of_commission` チャンネルへのスケジューリング不可 | `out_of_commission` が `true` のチャンネルに測定の `channel_id` を設定しようとしたとき。後からサービス外に切り替えた場合、既存のリンクは保持されます。  | 409   |
| `time_series` 測定のみがチャンネルを参照可能         | `properties` または `file` 測定（物理的なチャンネル占有を持たない）に `channel_id` を指定したとき。                               | 400   |
| `end_time` は `start_time` より前にできない    | `end_time` が `start_time` より前になるようにパッチしたとき。このチェックはチャンネルスケジューリングより前から存在し、リンクの有無に関わらずすべての測定に適用されます。 | 400   |

オーバーラップチェックは `end_time` のない測定のみを対象にします — 完了済みのテストが新しいテストをブロックすることはありません。使用中のチャンネルを再スケジュールするには、まず実行中の測定を閉じる（`end_time` をパッチする）か別のチャンネルに移してから、新しい測定を紐付けてください。

```python theme={null}
from datetime import datetime, timezone

# 次のテストを開始する前にチャンネルを解放する。
client.cell_measurement.update(
    previous_measurement_id,
    {"end_time": datetime.now(timezone.utc).isoformat()},
)

client.cell_measurement.update(new_measurement_id, {"channel_id": ch01.id})
```

チャンネルをサービスに戻す必要がある場合は、新しい測定を割り当てる前に `out_of_commission` を解除してください:

```python theme={null}
client.channel.update(channel_id, {"out_of_commission": False})
client.cell_measurement.update(measurement_id, {"channel_id": channel_id})
```

## 次のステップ

<CardGroup cols={2}>
  <Card title="チャンネルサービス" icon="wrench" href="/ja/operate/channel-service">
    チャンネルをサービス外にし、停止履歴を確認します。
  </Card>

  <Card title="計画済み測定" icon="calendar-check" href="/ja/operate/planned-measurements">
    テストをリクエストし、チャンネルにスケジュールし、実測定を紐付けます。
  </Card>
</CardGroup>
