Newer
Older
RobotCar / src / pc / steering / param_store.py
"""
param_store
パラメータプリセットの保存・読み込みを管理するモジュール
画像処理・PD 制御・Theil-Sen PD 制御パラメータを独立して管理する
"""

from dataclasses import asdict, dataclass

from common.json_utils import PARAMS_DIR, read_json, write_json
from pc.steering.pd_control import PdParams
from pc.steering.ts_pd_control import TsPdParams
from pc.vision.line_detector import ImageParams

_PD_FILE = PARAMS_DIR / "presets_pd.json"
_TS_PD_FILE = PARAMS_DIR / "presets_ts_pd.json"
_IMAGE_FILE = PARAMS_DIR / "presets_image.json"


# ── PD 制御プリセット ─────────────────────────


@dataclass
class PdPreset:
    """PD 制御パラメータのプリセット

    Attributes:
        title: プリセットのタイトル
        memo: メモ
        params: PD 制御パラメータ
    """

    title: str
    memo: str
    params: PdParams


def load_pd_presets() -> list[PdPreset]:
    """PD 制御プリセット一覧を読み込む"""
    return _load_presets(
        _PD_FILE, PdPreset, "params", PdParams,
    )


def add_pd_preset(preset: PdPreset) -> None:
    """PD 制御プリセットを追加する"""
    presets = load_pd_presets()
    presets.append(preset)
    _save_presets(
        _PD_FILE, presets, "params",
    )


def delete_pd_preset(index: int) -> None:
    """PD 制御プリセットを削除する"""
    presets = load_pd_presets()
    if 0 <= index < len(presets):
        presets.pop(index)
        _save_presets(
            _PD_FILE, presets, "params",
        )


# ── Theil-Sen PD 制御プリセット ────────────────


@dataclass
class TsPdPreset:
    """Theil-Sen PD 制御パラメータのプリセット

    Attributes:
        title: プリセットのタイトル
        memo: メモ
        params: Theil-Sen PD 制御パラメータ
    """

    title: str
    memo: str
    params: TsPdParams


def load_ts_pd_presets() -> list[TsPdPreset]:
    """Theil-Sen PD 制御プリセット一覧を読み込む"""
    return _load_presets(
        _TS_PD_FILE, TsPdPreset,
        "params", TsPdParams,
    )


def add_ts_pd_preset(preset: TsPdPreset) -> None:
    """Theil-Sen PD 制御プリセットを追加する"""
    presets = load_ts_pd_presets()
    presets.append(preset)
    _save_presets(
        _TS_PD_FILE, presets, "params",
    )


def delete_ts_pd_preset(index: int) -> None:
    """Theil-Sen PD 制御プリセットを削除する"""
    presets = load_ts_pd_presets()
    if 0 <= index < len(presets):
        presets.pop(index)
        _save_presets(
            _TS_PD_FILE, presets, "params",
        )


# ── 画像処理プリセット ────────────────────────


@dataclass
class ImagePreset:
    """画像処理パラメータのプリセット

    Attributes:
        title: プリセットのタイトル
        memo: メモ
        image_params: 画像処理パラメータ
    """

    title: str
    memo: str
    image_params: ImageParams


def load_image_presets() -> list[ImagePreset]:
    """画像処理プリセット一覧を読み込む"""
    return _load_presets(
        _IMAGE_FILE, ImagePreset,
        "image_params", ImageParams,
    )


def add_image_preset(preset: ImagePreset) -> None:
    """画像処理プリセットを追加する"""
    presets = load_image_presets()
    presets.append(preset)
    _save_presets(
        _IMAGE_FILE, presets, "image_params",
    )


def delete_image_preset(index: int) -> None:
    """画像処理プリセットを削除する"""
    presets = load_image_presets()
    if 0 <= index < len(presets):
        presets.pop(index)
        _save_presets(
            _IMAGE_FILE, presets, "image_params",
        )


# ── 共通処理 ──────────────────────────────────


def _load_presets(path, preset_cls, params_key, params_cls):
    """プリセットファイルを読み込む"""
    if not path.exists():
        return []

    data = read_json(path)

    known = params_cls.__dataclass_fields__
    presets = []
    for item in data:
        if params_key in item:
            filtered = {
                k: v
                for k, v in item[params_key].items()
                if k in known
            }
            params = params_cls(**filtered)
        else:
            params = params_cls()

        presets.append(preset_cls(
            title=item["title"],
            memo=item["memo"],
            **{params_key: params},
        ))
    return presets


def _save_presets(path, presets, params_key):
    """プリセットファイルに保存する"""
    data = []
    for preset in presets:
        data.append({
            "title": preset.title,
            "memo": preset.memo,
            params_key: asdict(
                getattr(preset, params_key),
            ),
        })

    write_json(path, data)