> ## 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.

# ラボビュー

> 測定データの取得元となる物理的なラボ装置 — サイト、サイクラー、チャンネル — を管理し、各測定を実行したチャンネルに紐付け、プロジェクト単位でチャンネル占有状況を一目で把握します

Ionworks Studio では、測定データの取得元となった物理的なラボ装置 — サイト、サイクラー、チャンネル — を追跡できます。これにより、[セル測定](/ja/data/measurements)を実行したチャンネルに直接リンクでき、データポイントからそれを生成した物理ハードウェアまでのエンドツーエンドのリンクが得られます。この装置モデルの上に **ラボビュー** が構築されています — テスト装置内のすべてのチャンネルと、それが現在使用中かどうかを表示するプロジェクトスコープのダッシュボードです。

## 使い分け

装置の記録は任意です — `channel_id` を設定しなくても測定は問題なく動作します。次のような場合にサイト、サイクラー、チャンネルをセットアップします:

* サイクラーやラボをまたいで収集したデータを比較したい。
* 測定の一部が異常に見えたときに、疑わしいチャンネルを特定したい。
* 規制対象のデータや公開データについて、長期的なプロベナンスを維持したい。
* どのチャンネルが空いている・使用中・サービス外かを一目で把握したい。

サイクラーが 1 台のみで、ここまでの詳細が不要であれば、各測定の[`test_setup`](/ja/data/measurements#共通フィールド)フィールドをそのまま使ってください。

## 装置階層

試験装置はセルデータ階層と同じ構造です:

```
Organization
└── Site           (ラボ / 施設 — 組織スコープ、共有)
    └── Cycler     (バッテリーサイクラー — 単一プロジェクトに所属)
        └── Channel  (サイクラー上の単一テストチャンネル — 親サイクラーのプロジェクトを継承)
             └── channel_id 経由でセル測定にリンク
```

| リソース      | スコープ         | 説明                                                                     |
| --------- | ------------ | ---------------------------------------------------------------------- |
| `site`    | Organization | 物理的なラボまたは施設。組織内で名前が一意です（大文字小文字を区別しません）。                                |
| `cycler`  | Project      | サイトに設置されたバッテリーサイクリング装置。作成時に指定した単一プロジェクトが所有します。                         |
| `channel` | Project (継承) | サイクラー上の単一テストチャンネル。親サイクラーのプロジェクトを継承します — `project_id` を自分で設定することはありません。 |

チャンネルと**同じプロジェクト**のセル測定のみをリンクできます。サイトを削除するとそのサイクラーもカスケード削除され、サイクラーを削除するとそのチャンネルもカスケード削除されます。

### サイト

**サイト**はサイクラーを保有するラボまたは施設です。サイトは**組織スコープ**です。

| フィールド      | 説明                 |
| ---------- | ------------------ |
| `name`     | サイトの名前（必須、組織内で一意）  |
| `location` | 物理的な所在地（例: 住所やラボ名） |
| `notes`    | 自由形式のメモ            |

### サイクラー

**サイクラー**は、ちょうど 1 つのサイトに設置されたサイクリング装置です。サイクラーは**プロジェクトスコープ**で、各サイクラーは作成時に指定した 1 つの[プロジェクト](/ja/core-concepts/projects-studies)によって所有されます。サイクラー名はプロジェクト内で一意（大文字小文字を区別しない）なので、異なる 2 つのプロジェクトがそれぞれ `"Maccor-1"` という名前のサイクラーを持つことができます。

所有プロジェクトは作成時に固定されます。同じ物理的な装置を 2 つのプロジェクトで使用する場合は、プロジェクトごとに 1 つのサイクラー行を作成してください。

| フィールド          | 説明                                   |
| -------------- | ------------------------------------ |
| `name`         | サイクラーの名前（必須、プロジェクト内で一意）              |
| `project_id`   | サイクラーを所有するプロジェクト（必須）                 |
| `manufacturer` | 例: `"Arbin"`、`"Maccor"`、`"Biologic"` |
| `model`        | 例: `"S4000"`                         |
| `notes`        | 自由形式のメモ                              |

### チャンネル

**チャンネル**はサイクラー上の物理チャンネルです。チャンネルはちょうど 1 つのサイクラーに属し、そのサイクラーのプロジェクトを継承します。チャンネル名はサイクラー内で一意（大文字小文字を区別しない）なので、異なる 2 つのサイクラーがそれぞれ `"CH1"` という名前のチャンネルを持つことができます。

| フィールド   | 説明                     |
| ------- | ---------------------- |
| `name`  | チャンネルの名前（必須、サイクラー内で一意） |
| `notes` | 自由形式のメモ                |

チャンネルには任意の電気的定格と `out_of_commission` フラグもあります — 下記の[チャンネル定格](#チャンネル定格)を参照してください。

## 装置ツリーの構築

サイト・サイクラー・チャンネルはラボごとに一度作成します。定常運用ではチャンネルのみを操作します — `out_of_commission` の切り替え、定格の調整、測定のリンクなどです。

```python theme={null}
from ionworks import Ionworks

client = Ionworks(project_id="your-project-id")

# 1. サイトを作成（またはフェッチ）— 組織スコープでプロジェクト間で共有。
site = client.site.create_or_get({
    "name": "Main lab",
    "location": "Building 3, Room 210",
})

# 2. サイトの下にサイクラーを作成。project_id は必須 — サイクラーは
#    そのプロジェクトが所有します。
cycler = client.cycler.create_or_get(
    site.id,
    {
        "name": "Cycler-01",
        "manufacturer": "Arbin",
        "model": "LBT-5V-6A",
        "project_id": client.project_id,
    },
)

# 3. サイクラー上にチャンネルを作成し、任意で電気的定格を設定します。
for i in range(1, 9):
    client.channel.create_or_get(
        cycler.id,
        {
            "name": f"Ch-{i:02d}",
            "max_amps": 6.0,
            "min_volts": 0.0,
            "max_volts": 5.0,
        },
    )
```

<Note>
  名前はサイトなら組織内、サイクラーならプロジェクト内、チャンネルならサイクラー内で、それぞれ大文字小文字を区別せず一意ですが、`create_or_get` は名前が完全に一致する場合にのみ既存レコードへの競合解決を行います。既に存在する名前と大文字小文字が異なる名前（例: `"Boston Lab"` が存在するときに `"boston lab"`）で呼び出すと、既存のレコードを返す代わりに `ValueError` が発生します。作成時と同じ完全な名前を再利用してください。
</Note>

更新と削除はいずれも同じ方法です:

```python theme={null}
# 更新（部分更新）。新しい site_id を渡すと、同じ組織内の別サイトに
# サイクラーを移動できます。所有プロジェクトは変更できません。新しい
# cycler_id を渡すとチャンネルの親を付け替えられますが、同じプロジェクト内の
# 別サイクラーに限ります。
client.cycler.update(cycler.id, {"model": "S4000M"})

# 削除 — 階層を下方向にカスケードします（サイト -> サイクラー -> チャンネル）。
# チャンネルを削除すると、それを参照していた測定の channel_id は NULL に戻ります。
# 測定自体は削除されません。
client.cycler.delete(cycler.id)
```

| 削除対象  | 影響                                               |
| ----- | ------------------------------------------------ |
| サイト   | 配下のすべてのサイクラーとそのチャンネルを削除                          |
| サイクラー | 配下のすべてのチャンネルを削除                                  |
| チャンネル | これを参照していた測定の `channel_id` を `NULL` に設定。測定は保持されます |

## 測定をチャンネルに紐付ける

すべての[セル測定](/ja/data/measurements)には、試験を実行した物理チャンネルを記録する null 許容の `channel_id` があります。測定を作成するときに設定するか、後から更新で付け替えられます。

チャンネルは測定と**同じプロジェクト**に属している必要があります（測定はそのセルインスタンスからプロジェクトを継承します）。別のプロジェクトのチャンネルを指定するとエラー（HTTP 400）になります。

このリンクは意図的にゆるく作られています: 後からチャンネルを削除すると `channel_id` は `NULL` に戻り、測定はそのまま残ります。

```python theme={null}
channels = client.channel.list(cycler.id)
ch01 = next(c for c in channels if c.name == "Ch-01")

bundle = client.cell_measurement.create(
    cell_instance.id,
    {
        "measurement": {
            "name": "Formation Cycle 1",
            "channel_id": ch01.id,
        },
        "time_series": time_series,
    },
)

# 既存の測定にチャンネルを付ける／付け替える
client.cell_measurement.update(bundle.id, {"channel_id": ch01.id})

# 測定を削除せずに切り離す
client.cell_measurement.update(bundle.id, {"channel_id": None})
```

テスト実行中は測定の `end_time` を未設定のままにし、鮮度の高いデータをアップロードし続けます — チャンネルは `occupied` として表示されます。テストが完了したら、測定を `end_time` でパッチすると、チャンネルは `free` に戻ります。

## チャンネル占有状況

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

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

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

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

## チャンネル定格

チャンネルには、ハードウェアの能力を記述する任意の電気的定格があります。ラボ UI は新規テスト用のチャンネルを選ぶ際にこれらでフィルタリングします。

| フィールド               | 型               | 説明                                                      |
| ------------------- | --------------- | ------------------------------------------------------- |
| `max_amps`          | `float \| null` | 最大定格電流 (A)。`null` は未指定を意味します。                           |
| `min_volts`         | `float \| null` | 最小定格電圧 (V)。`null` は未指定を意味します。                           |
| `max_volts`         | `float \| null` | 最大定格電圧 (V)。`null` は未指定を意味します。                           |
| `out_of_commission` | `bool`          | `true` はチャンネルをサービス外としてマークします。派生占有状態を上書きします。既定は `false`。 |

定格は UI ではなく API または SDK 経由で設定します。

## チャンネルをサービス外にする

`out_of_commission` を切り替えると、チャンネルを削除せず、参照している過去の測定を壊さずに、プールから除外できます。

```python theme={null}
client.channel.update(
    channel_id,
    {"out_of_commission": True, "notes": "セルホルダー交換 — 校正待ち"},
)
```

進行中の測定があるかどうかに関係なく、ラボビューはチャンネルを `out_of_commission` として表示します。

## フィルタリング、ページング、名前による解決

サイト・サイクラー・チャンネルのリストエンドポイントは、API 全体で共通のフィルタ形式に従います — `name` に対する大文字小文字を区別しない部分一致、`name_exact` による完全一致、ISO 日時レンジフィルタ（`created_after` / `updated_before` / ...）、および `order_by` / `order` によるソートです。

```python theme={null}
# 現在のプロジェクトが所有するサイト内のすべてのサイクラー
cyclers = client.cycler.list(site.id, project_id=client.project_id)

# サイクラー上のすべてのチャンネルを 1 ページで取得
channels = client.channel.list(cycler.id, limit=100)
print(channels.total)
```

サイトとサイクラーについては、`client.site.detail(site_id)` および `client.cycler.detail(cycler_id)` がリソースとその子要素すべてを 1 レスポンスで返し、ページングも自動処理します。

`name_exact` は大文字小文字を区別するサーバー側の完全一致です — 人間が読めるサイト名からチャンネル ID までたどる最も簡単な方法です:

```python theme={null}
site = client.site.list(name_exact="Boston Lab")[0]
cycler = client.cycler.list(site.id, name_exact="Cycler-01")[0]
channel = client.channel.list(cycler.id, name_exact="Ch-01")[0]

print(site.id, cycler.id, channel.id)
```

`name_exact` を使った場合でも各 `list()` は `PaginatedList` を返すため、名前が存在しない可能性がある場合は空のケースを扱ってください。

## 次のステップ

<CardGroup cols={2}>
  <Card title="測定" icon="flask" href="/ja/data/measurements">
    測定を作成し、`channel_id` でチャンネルにリンクします。
  </Card>

  <Card title="Python API クライアント" icon="code" href="/ja/api-client">
    `client.site`、`client.cycler`、`client.channel` を設定します。
  </Card>
</CardGroup>
