# 設定ガイド（v1）

このガイドでは、v1 の ConfigLoader を使用して設定を読み込み、使用する方法を説明します。

## 概要

Haniwers v1 では、Pydantic ベースの型安全な設定管理システムを提供しています。

- **設定モデル** (`model.py`): `DeviceConfig`、`DaqConfig`、`SensorConfig` など
- **設定ローダー** (`loader.py`): TOML ファイルの検索と読み込み
- **環境変数オーバーライド**: TOML ファイルの値を環境変数で上書き可能

## 基本的な使い方

### 1. 設定ファイルの準備

TOML 形式で `hnw.toml` を作成：

```toml
[device]
label = "OSECHI"
port = "/dev/cu.usbserial-140"
baudrate = 115200
timeout = 1.0

[daq]
label = "main"
workspace = "."
filename_prefix = "osechi_data"
filename_suffix = ".csv"
events_per_file = 10000
number_of_files = 10000
stream_mode = true

[sensors.ch1]
label = "top"
center = 512
nsteps = 10
step_size = 1
threshold = "none"

[sensors.ch2]
label = "mid"
center = 311
nsteps = 10
step_size = 1
threshold = "none"

[sensors.ch3]
label = "btm"
center = 420
nsteps = 10
step_size = 1
threshold = "none"

[scan]
label = "main"
workspace = "."
filename_prefix = "scan_data"
filename_suffix = ".csv"
events_per_file = 10000
number_of_files = 10
stream_mode = false
duration = 10.0
suppress_threshold = 1000
max_retry = 3

[mocker]
csv_path = null
shuffle = false
speed = 1.0
jitter = 0.0
loop = true
```

### 2. ConfigLoader で設定を読み込む

```python
from pathlib import Path
from haniwers.v1.config.loader import ConfigLoader

# 明示的にパスを指定
config_path = Path("custom.toml")
loader = ConfigLoader(config_path)
cfg = loader.config

# または、デフォルト検索パスから自動検出
loader = ConfigLoader()  # hnw.toml を自動検索
cfg = loader.config

# 設定にアクセス
print(cfg.device.port)           # "/dev/cu.usbserial-140"
print(cfg.device.label)          # "OSECHI_Main"
print(cfg.sensors["ch1"].center)  # 512
print(cfg.daq.workspace)         # "."
```

## 設定ファイルの検索順序

ConfigLoader が `get_default_config_path()` で探す順序：

1. `./hnw.toml`（カレントディレクトリ）
2. `./config.toml`（カレントディレクトリ）
3. `./config/*.toml`（カレントディレクトリの config フォルダ、アルファベット順）
4. `~/.config/haniwers/hnw.toml`（ユーザーディレクトリ）
5. `~/.config/haniwers/config.toml`（ユーザーディレクトリ）
6. `~/.config/haniwers/*.toml`（ユーザーディレクトリ、アルファベット順）

最初にマッチしたファイルが使用されます。

### プラットフォーム別ユーザー設定ディレクトリ

- **macOS**: `~/Library/Application Support/haniwers/`
- **Linux**: `~/.config/haniwers/`
- **Windows**: `C:\Users\<username>\AppData\Roaming\haniwers\`

## 環境変数オーバーライド

設定ファイルの値を環境変数で上書きできます：

```bash
# シリアルポートをオーバーライド
export HANIWERS_DEVICE_PORT=/dev/cu.usbserial-999
export HANIWERS_DEVICE_BAUDRATE=9600
export HANIWERS_DEVICE_TIMEOUT=2.0

# ワークスペース（DAQ と Scan の両方）
export HANIWERS_WORKSPACE=/tmp/daq_data

# Python で使用
loader = ConfigLoader()
cfg = loader.config
print(cfg.device.port)  # "/dev/cu.usbserial-999"（TOML の値から上書き）
```

### サポートされている環境変数

| 環境変数 | 型 | 説明 |
|---------|-----|------|
| `HANIWERS_DEVICE_PORT` | str | シリアルポート |
| `HANIWERS_DEVICE_BAUDRATE` | int | ボーレート |
| `HANIWERS_DEVICE_TIMEOUT` | float | タイムアウト（秒） |
| `HANIWERS_WORKSPACE` | str | DAQ と Scan の出力ディレクトリ |

## CLI での使用例

### v1 DAQ コマンド

```bash
# 設定ファイルを指定
haniwers-v1 daq --config custom.toml

# または、デフォルト設定ファイルを自動検出
haniwers-v1 daq
```

### 環境変数で上書き

```bash
export HANIWERS_DEVICE_PORT=/dev/cu.usbserial-999
haniwers-v1 daq  # 環境変数の値が使用される
```

## コードでの使用パターン

### パターン 1: 最小限の使用

```python
from haniwers.v1.config.loader import ConfigLoader

loader = ConfigLoader()
cfg = loader.config

# デバイス設定を取得
port = cfg.device.port
baudrate = cfg.device.baudrate
```

### パターン 2: エラーハンドリング

```python
from pathlib import Path
from haniwers.v1.config.loader import ConfigLoader

try:
    loader = ConfigLoader(Path("hnw.toml"))
    cfg = loader.config
except FileNotFoundError as e:
    print(f"設定ファイルが見つかりません: {e}")
    # フォールバック処理
except ValueError as e:
    print(f"設定ファイルが無効です: {e}")
    # エラー処理
```

### パターン 3: センサー設定の反復

```python
from haniwers.v1.config.loader import ConfigLoader

loader = ConfigLoader()
cfg = loader.config

# すべてのセンサーを反復
for name, sensor in cfg.sensors.items():
    print(f"センサー: {name}")
    print(f"  ラベル: {sensor.label}")
    print(f"  スキャン範囲: {list(sensor.threshold_range())}")
    print(f"  しきい値: {sensor.threshold}")
```

### パターン 4: DAQ デバイスの初期化

```python
from pathlib import Path
from haniwers.v1.config.loader import ConfigLoader
from haniwers.v1.daq.device import Device
from haniwers.v1.daq.mocker import RandomMocker

loader = ConfigLoader()
cfg = loader.config

# 実ハードウェア を使用
device = Device(
    port=cfg.device.port,
    baudrate=cfg.device.baudrate,
    timeout=cfg.device.timeout
)

# または、モック デバイス を使用（テスト用）
mock_device = RandomMocker(cfg.mocker, seed=42)
```

## トラブルシューティング

### "No config file found in expected locations"

設定ファイルが見つからない場合：

```bash
# 設定ファイルが存在することを確認
ls -la hnw.toml config.toml

# または、デフォルト検索パスを表示
haniwers-v1 --help  # ドキュメント参照

# 明示的にパスを指定
python script.py --config /path/to/hnw.toml
```

### "Invalid configuration format"

TOML ファイルの形式が無効な場合：

1. TOML 構文を確認：`[section]` の形式、`key = value` の形式
2. 必須フィールドをすべて記入したか確認
3. 値の型が正しいか確認（例：`baudrate` は整数）

### 環境変数が反映されない

```bash
# 環境変数が設定されているか確認
echo $HANIWERS_DEVICE_PORT

# Python スクリプトで直接確認
import os
print(os.environ.get("HANIWERS_DEVICE_PORT"))
```

## 設定クラスの詳細

### DeviceConfig

OSECHI ハードウェアの接続設定：

- `label`: デバイス名（"OSECHI_Main" など）
- `port`: シリアルポート（"/dev/cu.usbserial-140" など）
- `baudrate`: 通信速度（通常 115200）
- `timeout`: タイムアウト（秒、通常 1.0）

### SensorConfig

センサーチャンネルのスキャン設定：

- `label`: センサー名（"top"、"mid"、"btm"）
- `center`: スキャンの中心値（1-1023）
- `nsteps`: 中心からのステップ数
- `step_size`: ステップサイズ（ADC単位）
- `threshold`: 現在のしきい値（"none" または数値）

**スキャン範囲の計算例**:
```python
sensor = cfg.sensors["ch1"]
# center=512, nsteps=10, step_size=1
# 範囲: [502, 503, 504, ..., 521, 522] (21 個の値)
scan_range = list(sensor.threshold_range())
```

### DaqConfig

データ取得出力の設定：

- `label`: 設定名
- `workspace`: 出力ディレクトリ
- `filename_prefix`: ファイル名の前付け
- `filename_suffix`: ファイル拡張子
- `events_per_file`: ファイルあたりのイベント数
- `number_of_files`: 作成する最大ファイル数
- `stream_mode`: true でリアルタイム出力、false でバッファリング

### ScanConfig

閾値スキャン実験の設定：

- 基本的には DaqConfig と同じ、加えて：
- `duration`: 各閾値レベルでのデータ収集時間（秒）
- `suppress_threshold`: ノイズフロア（デフォルト: 1000）
- `max_retry`: 失敗時の最大リトライ回数（デフォルト: 3）

### MockerConfig

モックデバイスの設定：

- `csv_path`: リプレイ用 CSV ファイルパス（null で ランダム生成）
- `shuffle`: 再生前にイベントをシャッフル
- `speed`: 再生速度倍率（1.0 = リアルタイム、2.0 = 2倍速）
- `jitter`: タイミング変動（標準偏差、秒）
- `loop`: CSV の終端に到達したらループ

## ベストプラクティス

### 推奨事項

1. **プロジェクトルートに `hnw.toml` を置く** - 常に最初に検出される
2. **環境変数は一時的なオーバーライドに使用** - 恒久的な設定は TOML ファイルに
3. **エラーハンドリングを実装** - 設定ファイルが見つからない場合の処理
4. **設定値をログ出力** - デバッグ時に何が読み込まれたか確認

### 例：安全な設定読み込み

```python
from pathlib import Path
from haniwers.v1.config.loader import ConfigLoader
from haniwers.v1.log.logger import logger

def load_config(config_path=None):
    """安全に設定を読み込む"""
    try:
        if config_path:
            logger.info(f"Loading config from: {config_path}")
            loader = ConfigLoader(Path(config_path))
        else:
            logger.info("Loading config from default search paths")
            loader = ConfigLoader()

        cfg = loader.config
        logger.info(f"Device: {cfg.device.label} on {cfg.device.port}")
        return cfg

    except FileNotFoundError as e:
        logger.error(f"Config not found: {e}")
        raise
    except ValueError as e:
        logger.error(f"Invalid config: {e}")
        raise
```

## 参考資料

- [モデル詳細](../../src/haniwers/v1/config/model.py) - Pydantic モデルの定義
- [設定ローダー実装](../../src/haniwers/v1/config/loader.py) - ConfigLoader の実装
- [テスト例](../../tests/v1/unit/config/) - 設定テストの例
