ディレクトリ構成の再検討

ディレクトリ構成の再検討#

ツールの構成メンバー#

モジュール名

役割

configer

設定ファイル管理の担当者

sampler

データ取得実行の担当者

monitor

データ取得モニタリングの担当者

scanner

スレショルドスキャン実行の担当者

fitter

スレッショルド解析の担当者

converter

生データから解析データ変換の担当者

plotter

データ描画の担当者

logger

ロギングの担当者

keeper

データ保存の担当者

mocker

モックデータ作成の担当者

helper

ヘルプ表示の担当者

commander

コマンド統合の担当者

manager

イベント管理の担当者

ディレクトリ構成案#

haniwers/
├── __init__.py
├── cli.py                    # CLI統括(commander)
├── config/                   # 設定管理(configer)
│   ├── __init__.py
│   ├── loader.py
│   └── schema.py
├── daq/                      # データ取得まわり
│   ├── __init__.py
│   ├── device.py    # 物理接続とIO制御
│   │   ├── connect(config: DeviceConfig) -> serial.Serial    # 標準の接続入口
│   │   ├── is_available(config: DeviceConfig) -> bool    # ポートの確認
│   │   ├── read_line(device: serial.Serial) -> str    # デコード、strip処理
│   │   ├── write_line(device: serial.Serial, data) -> bool    # 閾値設定などで書き込み
│   │   ├── flush(device: serial.Serial) -> None    # 測定前のバッファクリア
│   │   ├── with_timeout(device: serial.Serial, sec: float) -> Generator[serial.Serial, None, None]    # 一時的なタイムアウト変更 @contextmanager として使う
│   │   ├── read_n_lines(device: serial.Serial, n: int) -> list[str]    # option: 初期化時のガベージ除去など
│   │   ├── disconnect(device: serial.Serial) -> bool    # option: 明示的に .close()
│   │   ├── get_port_description(device: serial.Serial) -> str    # option: ログやGUI表示に便利
│   │   ├── get_status(device: serial.Serial) -> dict    # option: 通信状況や設定確認用(高度)
│   ├── sampler.py    # イベント収集
│   │   ├── sample(device: serial.Serial) -> RawEvent
│   │   ├── for_events(device: serial.Serial, n: int) -> list[RawEvent]
│   │   ├── for_duration(device: serial.Serial, t: float) -> list[RawEvent]
│   │   ├── to_csv(events: list[RawEvent], path: Path) -> bool
│   │   ├── create_writer(path: Path) -> csv.writer    # option: to_csvの内部処理
│   │   ├── append_events(writer: csv.writer, events: list[RawEvent]) -> None    # option: to_csvの内部処理
│   │   ├── tqdm_wrapper(iterator, total: int | None) -> Iterator[RawEvent]    # option: 進捗バー付きのイテレータラッパー
│   │   ├── sanitize(event: RawEvent) -> RawEvent    # option: RawEvent の整形・欠損チェック・補正など
│   │   ├── parse_line(line: str) -> RawEvent    # option: 1行の生データをRawEventにパースする
│   │   ├── mock_sample(index: int = 0) -> RawEvent    # option: モックしたRawEventを取得
│   │   ├── run_session(device: serial.Serial, config: DaqConfig | ScanConfig) -> None    # option: 設定に従ってDAQランを実行・ファイル出力する
│   ├── mocker.py    # 開発用のデバイス模擬
│   │   ├── MockSerial(csv_path: Path, shuffle: bool = False, speed: float = 1.0)    # CSVベースの再現モック(旧FileBackedMockSerial)
│   │   ├── RandomMockSerial(rate: float = 1.0, jitter: float = 0.0)    # ランダムに行を生成(旧MockSerial)
│   │   ├── create_mock_device(config: DeviceConfig) -> serial.Serial    # config.port に応じて適切なモックを生成
│   │   ├── create_mock_from_file(path: Path, **kwargs) -> MockSerial    # 明示的にCSVモックを作成
│   │   ├── load_csv_lines(path: Path) -> list[str]    # CSVファイルから行データを抽出
│   │   ├── parse_timestamps(lines: list[str], column: int) -> list[datetime]    # ISO8601時刻列をdatetime型に変換
│   │   ├── compute_time_deltas(times: list[datetime]) -> list[float]    # イベント間の秒数差を計算
│   │   ├── is_mock_port(port: str) -> bool    # ポート名がモック用かどうか判定
│   │   ├── inject_noise(line: str, col_indices: list[int], stddev: float = 0.5) -> str    # 指定列にノイズを加える
│   │   ├── drop_columns(line: str, col_indices: list[int]) -> str    # 特定列を除去して行を再構成
├── thresholds/                # スレッショルドスキャン関係
│   ├── __init__.py
│   ├── scanner.py     # スレショルドスキャンを実行
│   │   ├── scan_channel(device: serial.Serial, channel: str, config: SensorConfig, duration: float) -> list[tuple[int, int]]    # Returns [ (threshold, count), ...]
│   │   ├── scan_all_channels_serial(device: serial.Serial, sensors: dict[str, SensorConfig], duration: float, mask_value: int = 500) -> dict[str, list[tuple[int, int]]]
│   │   ├── scan_all_channels_parallel(device: serial.Serial, sensors: dict[str, SensorConfig], duration: float) -> dict[str, list[tuple[int, int]]]
│   │   ├── to_csv(scan_result: dict[str, list[tuple[int, int]]], path: Path) -> None  # スキャン結果をCSV形式で保存
│   │   ├── from_csv(path: Path) -> dict[str, list[tuple[int, int]]]  # スキャン結果をCSVから読み込む
│   │   ├── _set_threshold(device: serial.Serial, channel: str, value: int) -> None
│   │   ├── _count_hits(device: serial.Serial, channel: str, duration: float) -> int
│   ├── fitter.py      # スキャン結果をフィットしてスレッショルド値を計算
│   │   ├── fit_scan_curve(x: list[float], y: list[int]) -> FittingResult
│   │   ├── estimate_thresholds(result: FittingResult, sigmas: list[float] = [0, 1, 3, 5]) -> dict[str, float]
│   │   ├── plot_fit_result(x: list[float], y: list[int], result: FittingResult, save_path: Optional[Path] = None) -> None
│   │   ├── FittingResult
│   ├── applier.py    # チャンネルごとにスレッショルドを設定(apply)
│   │   ├── from_csv(path: Path) -> dict[str, int]   # CSVからチャンネルごとの閾値を読み込む
│   │   ├── to_csv(thresholds: dict[str, int], path: Path) -> None  # 設定した値をCSVに書き込む(読み込みとは別のログ用)
│   │   ├── apply_thresholds(device: serial.Serial, thresholds: dict[str, int]) -> bool    # 複数チャンネルに順番に閾値を設定
│   │   ├── apply_threshold(device: serial.Serial, channel: str, value: int) -> bool  # 単一チャンネルの閾値設定
│   │   ├── verify_threshold(device: serial.Serial, channel: str) -> int  # 設定されている閾値を確認(デバイスが対応していないかも)
│   │   ├── interactive(device: serial.Serial, config: HaniwersConfig) -> None  # 対話的に適用
│   └── writer.py    # スレッショルド関係のファイル出力
├── preprocess/               # 前処理・変換(converter)
│   ├── __init__.py
│   └── converter.py
├── viz/                      # 可視化まわり(plotter)
│   ├── __init__.py
│   └── plotter.py
├── log/                      # ログ(logger)
│   ├── __init__.py
│   └── logger.py
├── monitor/
│   ├── watcher.py       # watchdogによるファイル監視
│   │   ├── watch_csv_file
│   ├── parser.py        # RawEvent / ProcessedEvent の生成
│   │   ├── parse_raw_event
│   │   ├── process_event
│   ├── dashboard.py     # streamlit or panel による可視化
│   │   ├── hvplot + panel
│   │   ├── hvplot + streamlit
│   ├── state.py         # 共有データ構造・キャッシュ管理
│   │   ├── Shared State
│   ├── remote.py    # WebSocket通知でリアルタイム配信
│   ├── summary.py    # 統計的処理や異常検知の自動化
│   ├── alerts.py    # 閾値越えをSlack等に通知