設定ガイド(v1)#
このガイドでは、v1 の ConfigLoader を使用して設定を読み込み、使用する方法を説明します。
概要#
Haniwers v1 では、Pydantic ベースの型安全な設定管理システムを提供しています。
設定モデル (
model.py):DeviceConfig、DaqConfig、SensorConfigなど設定ローダー (
loader.py): TOML ファイルの検索と読み込み環境変数オーバーライド: TOML ファイルの値を環境変数で上書き可能
基本的な使い方#
1. 設定ファイルの準備#
TOML 形式で hnw.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 で設定を読み込む#
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() で探す順序:
./hnw.toml(カレントディレクトリ)./config.toml(カレントディレクトリ)./config/*.toml(カレントディレクトリの config フォルダ、アルファベット順)~/.config/haniwers/hnw.toml(ユーザーディレクトリ)~/.config/haniwers/config.toml(ユーザーディレクトリ)~/.config/haniwers/*.toml(ユーザーディレクトリ、アルファベット順)
最初にマッチしたファイルが使用されます。
プラットフォーム別ユーザー設定ディレクトリ#
macOS:
~/Library/Application Support/haniwers/Linux:
~/.config/haniwers/Windows:
C:\Users\<username>\AppData\Roaming\haniwers\
環境変数オーバーライド#
設定ファイルの値を環境変数で上書きできます:
# シリアルポートをオーバーライド
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 の値から上書き)
サポートされている環境変数#
環境変数 |
型 |
説明 |
|---|---|---|
|
str |
シリアルポート |
|
int |
ボーレート |
|
float |
タイムアウト(秒) |
|
str |
DAQ と Scan の出力ディレクトリ |
CLI での使用例#
v1 DAQ コマンド#
# 設定ファイルを指定
haniwers-v1 daq --config custom.toml
# または、デフォルト設定ファイルを自動検出
haniwers-v1 daq
環境変数で上書き#
export HANIWERS_DEVICE_PORT=/dev/cu.usbserial-999
haniwers-v1 daq # 環境変数の値が使用される
コードでの使用パターン#
パターン 1: 最小限の使用#
from haniwers.v1.config.loader import ConfigLoader
loader = ConfigLoader()
cfg = loader.config
# デバイス設定を取得
port = cfg.device.port
baudrate = cfg.device.baudrate
パターン 2: エラーハンドリング#
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: センサー設定の反復#
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 デバイスの初期化#
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”#
設定ファイルが見つからない場合:
# 設定ファイルが存在することを確認
ls -la hnw.toml config.toml
# または、デフォルト検索パスを表示
haniwers-v1 --help # ドキュメント参照
# 明示的にパスを指定
python script.py --config /path/to/hnw.toml
“Invalid configuration format”#
TOML ファイルの形式が無効な場合:
TOML 構文を確認:
[section]の形式、key = valueの形式必須フィールドをすべて記入したか確認
値の型が正しいか確認(例:
baudrateは整数)
環境変数が反映されない#
# 環境変数が設定されているか確認
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” または数値)
スキャン範囲の計算例:
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 の終端に到達したらループ
ベストプラクティス#
推奨事項#
プロジェクトルートに
hnw.tomlを置く - 常に最初に検出される環境変数は一時的なオーバーライドに使用 - 恒久的な設定は TOML ファイルに
エラーハンドリングを実装 - 設定ファイルが見つからない場合の処理
設定値をログ出力 - デバッグ時に何が読み込まれたか確認
例:安全な設定読み込み#
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