デバイスレス開発ガイド:ConfigLoader でハードウェアなしで開発する#
目次#
概要
3つのデバイスタイプ
ConfigLoader の基本
Workflow 1:ローカル開発(RandomMocker)
Workflow 2:CI/CD テスト(Mocker)
Workflow 3:本番運用(Device)
よくある質問
1. 概要#
このガイドでは、ConfigLoader を使ってハードウェアなしで haniwers を開発・テストする方法を説明します。
キーコンセプト:
ConfigLoader が TOML ファイルから設定を読み込む
設定に基づいて Device、Mocker、RandomMocker を選択
コードを変更せず、設定ファイルだけでデバイスを切り替え可能
2. 3つのデバイスタイプ#
Device:本番運用(実ハードウェア)#
用途:OSECHI 検出器から実際のデータを取得
実行環境:実ハードウェアを接続したマシン
特徴:本物のセンサー値を取得
Mocker:再現テスト(記録済みデータ再生)#
用途:記録済み CSV ファイルからイベントを再生
実行環境:開発環境、CI/CD パイプライン
特徴:完全に再現可能、決定論的
RandomMocker:開発・ストレステスト(ランダム生成)#
用途:テストデータが不要な開発やストレステスト
実行環境:ローカル開発
特徴:ハードウェア不要、高速
3. ConfigLoader の基本#
ConfigLoader とは#
ConfigLoader は TOML 設定ファイルから HaniwersConfig を読み込み、すべてのコンポーネント設定を一元管理します。
基本的な使い方#
設定ファイルを明示的に指定する場合:
from pathlib import Path
from haniwers.v1.config.loader import ConfigLoader
# ConfigLoader インスタンスを作成
loader = ConfigLoader(Path("custom.toml"))
# config プロパティで HaniwersConfig を取得
config = loader.config
# 各設定にアクセス
print(config.device.port) # "/dev/cu.usbserial-140"
print(config.device.baudrate) # 115200
print(config.daq.workspace) # "./data"
print(config.mocker.speed) # 5.0
デフォルト設定を自動検索する場合:
from haniwers.v1.config.loader import ConfigLoader
# 設定ファイルを指定しない場合、自動検索
loader = ConfigLoader()
config = loader.config
設定の検索順序#
ConfigLoader は以下の順序で設定ファイルを探します:
./hnw.toml./config.toml./config/*.toml(アルファベット順)~/.config/haniwers/hnw.toml(macOS/Linux)~/.config/haniwers/config.toml(macOS/Linux)~/.config/haniwers/*.toml(アルファベット順)
見つからない場合: FileNotFoundError が発生
HaniwersConfig の構造#
ConfigLoader が返す config オブジェクトには以下のプロパティがあります:
config.device # DeviceConfig:デバイス接続設定
config.daq # DaqConfig:データ取得出力設定
config.scan # ScanConfig:スキャン設定
config.sensors # Dict[str, SensorConfig]:センサー設定
config.mocker # MockerConfig:モッカー設定
4. Workflow 1:ローカル開発(RandomMocker)#
シナリオ#
ハードウェアなしでデータ取得パイプラインを開発したい。
4.1 設定ファイルの作成#
config.local.toml:
[device]
label = "local-dev"
port = "/dev/ttyUSB0"
baudrate = 115200
timeout = 1.0
[daq]
label = "dev-daq"
workspace = "./sandbox"
filename_prefix = "random_mock"
filename_suffix = ".csv"
events_per_file = 1000
number_of_files = 5
stream_mode = true
[mocker]
csv_path = null
shuffle = false
speed = 5.0
jitter = 0.01
loop = false
[sensors.ch1]
label = "top"
step_size = 1
threshold = 500
center = 512
nsteps = 10
[sensors.ch2]
label = "mid"
step_size = 1
threshold = 300
center = 311
nsteps = 10
[sensors.ch3]
label = "btm"
step_size = 1
threshold = 400
center = 420
nsteps = 10
[scan]
label = "scan"
workspace = "./sandbox"
filename_prefix = "scan_data"
filename_suffix = ".csv"
events_per_file = 1000
number_of_files = 1
stream_mode = true
duration = 10.0
suppress_threshold = 1000
max_retry = 3
4.2 Python コード#
# custom_script.py
from pathlib import Path
from haniwers.v1.config.loader import ConfigLoader
from haniwers.v1.daq.mocker import RandomMocker
from haniwers.v1.daq.sampler import Sampler
def main():
# 設定を読み込み
loader = ConfigLoader(Path("config.local.toml"))
config = loader.config
# RandomMocker を作成
mocker = RandomMocker(config.mocker, seed=42)
# Sampler を作成
output_dir = Path(config.daq.workspace)
output_dir.mkdir(parents=True, exist_ok=True)
sampler = Sampler(mocker, config.daq, output_dir)
# データ取得実行
try:
sampler.acquire_by_count(
file_path=output_dir / f"{config.daq.filename_prefix}_001{config.daq.filename_suffix}",
event_count=config.daq.events_per_file
)
print("✅ データ取得完了")
finally:
mocker.close()
if __name__ == "__main__":
main()
4.3 実行方法#
# ローカル開発環境で実行
uv run python custom_script.py
5. Workflow 2:過去データを使ったデモ実行(Mocker)#
シナリオ#
記録済み CSV ファイルからイベントを再生してデータを取得し、結果をリアルタイムで表示したい。
5.1 設定ファイルの作成#
config.demo.toml:
[device]
label = "demo-replay"
port = "/dev/null"
baudrate = 115200
timeout = 1.0
[daq]
label = "demo-daq"
workspace = "./demo-output"
filename_prefix = "replay"
filename_suffix = ".csv"
events_per_file = 100
number_of_files = 10
stream_mode = true
[mocker]
csv_path = "./examples/data/cosmic_rays.csv"
shuffle = false
speed = 10.0
jitter = 0.0
loop = false
[sensors.ch1]
label = "top"
step_size = 1
threshold = 500
center = 512
nsteps = 10
[sensors.ch2]
label = "mid"
step_size = 1
threshold = 300
center = 311
nsteps = 10
[sensors.ch3]
label = "btm"
step_size = 1
threshold = 400
center = 420
nsteps = 10
[scan]
label = "scan"
workspace = "./demo-output"
filename_prefix = "scan_data"
filename_suffix = ".csv"
events_per_file = 1000
number_of_files = 1
stream_mode = true
duration = 10.0
suppress_threshold = 1000
max_retry = 3
5.2 Python コード#
# demo_replay.py
from pathlib import Path
from haniwers.v1.config.loader import ConfigLoader
from haniwers.v1.daq.mocker import Mocker
from haniwers.v1.daq.sampler import Sampler
import subprocess
import threading
import time
def main():
# 設定を読み込み
loader = ConfigLoader(Path("config.demo.toml"))
config = loader.config
# 出力ディレクトリを作成
output_dir = Path(config.daq.workspace)
output_dir.mkdir(parents=True, exist_ok=True)
# Mocker を作成(CSV ファイルから再生)
mocker = Mocker(config.mocker, seed=None)
# Sampler を作成
sampler = Sampler(mocker, config.daq, output_dir)
# データ取得実行
try:
output_file = output_dir / f"{config.daq.filename_prefix}_001{config.daq.filename_suffix}"
sampler.acquire_by_count(
file_path=output_file,
event_count=config.daq.events_per_file * config.daq.number_of_files
)
print("✅ デモ実行完了")
finally:
mocker.close()
if __name__ == "__main__":
main()
5.3 実行方法#
# デモを実行
uv run python demo_replay.py
# 別のセッションからtail -f でリアルタイム表示
tail -f demo-output/replay_001.csv
6. Workflow 3:本番運用(Device)#
シナリオ#
実OSECHI検出器を接続して、本物のセンサーデータを取得する。
6.1 ポート確認#
まず、OSECHI検出器が接続されているシリアルポートを確認します:
# 利用可能なシリアルポートを一覧表示
uv run haniwers-v1 port list
# デバイスの接続確認
uv run haniwers-v1 port test /dev/cu.usbserial-14230
6.2 設定ファイルの作成#
config.prod.toml:
[device]
label = "osechi-main"
port = "/dev/cu.usbserial-14230"
baudrate = 115200
timeout = 5.0
[daq]
label = "production"
workspace = "./data/production"
filename_prefix = "osechi_data"
filename_suffix = ".csv"
events_per_file = 5000
number_of_files = 10
stream_mode = true
[sensors.ch1]
label = "top"
step_size = 1
threshold = 500
center = 512
nsteps = 10
[sensors.ch2]
label = "mid"
step_size = 1
threshold = 300
center = 311
nsteps = 10
[sensors.ch3]
label = "btm"
step_size = 1
threshold = 400
center = 420
nsteps = 10
[scan]
label = "scan"
workspace = "./data/production"
filename_prefix = "scan_data"
filename_suffix = ".csv"
events_per_file = 1000
number_of_files = 1
stream_mode = true
duration = 10.0
suppress_threshold = 1000
max_retry = 3
6.3 Python コード#
# production_daq.py
from pathlib import Path
from haniwers.v1.config.loader import ConfigLoader
from haniwers.v1.daq.device import Device
from haniwers.v1.daq.sampler import Sampler
def main():
# 設定を読み込み
loader = ConfigLoader(Path("config.prod.toml"))
config = loader.config
# 実デバイスに接続
device = Device(config.device)
# 出力ディレクトリを作成
output_dir = Path(config.daq.workspace)
output_dir.mkdir(parents=True, exist_ok=True)
# Sampler を作成
sampler = Sampler(device, config.daq, output_dir)
# データ取得実行
device.connect()
try:
output_file = output_dir / f"{config.daq.filename_prefix}_001{config.daq.filename_suffix}"
sampler.acquire_by_count(
file_path=output_file,
event_count=config.daq.events_per_file * config.daq.number_of_files
)
print("✅ 本番データ取得完了")
finally:
device.disconnect()
if __name__ == "__main__":
main()
6.4 実行方法#
# 本番データ取得
uv run python production_daq.py
7. よくある質問#
Q: RandomMocker と Mocker の違いは?#
項目 |
RandomMocker |
Mocker |
|---|---|---|
データ源 |
ランダム生成 |
CSV ファイル |
再現性 |
低い(seed で部分的に制御可能) |
高い(完全に再現可能) |
セットアップ |
CSV ファイル不要 |
CSV ファイル必須 |
速度 |
高速(メモリ内で生成) |
中速(ファイル読み込み) |
用途 |
ローカル開発、ストレステスト |
テスト、デバッグ、デモ |
設定ファイル例 |
|
|
Q: Device と RandomMocker を切り替えるには?#
設定ファイルのポート設定を変更するだけです。コードは変わりません:
# RandomMocker を使う(ハードウェアなし)
[device]
port = "/dev/null"
# Device を使う(ハードウェアあり)
[device]
port = "/dev/cu.usbserial-14230"
Python コードは同じ:
device = Device(config.device) # ポート設定に応じて自動選択
Q: 設定ファイルが見つからないエラーが出た#
ConfigLoader は以下の場所を順に探します:
指定したファイル(例:
config.prod.toml)./hnw.toml./config.toml./config/*.tomlホームディレクトリの haniwers 設定
見つからない場合は、明示的に設定ファイルを指定してください:
loader = ConfigLoader(Path("config.local.toml"))
または、以下のコマンドで確認:
ls -la hnw.toml config.toml config/*.toml 2>/dev/null
関連資料#
src/haniwers/v1/config/loader.py- ConfigLoader の実装src/haniwers/v1/config/model.py- 設定モデルsrc/haniwers/v1/daq/device.py- Device クラスsrc/haniwers/v1/daq/mocker.py- Mocker/RandomMockersrc/haniwers/v1/daq/sampler.py- Sampler クラスexamples/- 設定ファイルの例