Newer
Older
RobotCar / src / pc / steering / auto_params.py
"""
auto_params
パラメータの自動保存・読み込みを管理するモジュール
アプリ起動時に前回のパラメータを復元し,変更時に自動保存する

ファイル構成:
    params/
    ├── control.json              PD 制御 + 最後に使用した手法
    ├── pursuit.json              パシュート制御パラメータ
    ├── ts_pd.json                Theil-Sen PD 制御パラメータ
    ├── recovery.json             コースアウト復帰パラメータ
    ├── overlay.json              オーバーレイ表示フラグ
    ├── detect_current.json       現行手法の二値化パラメータ
    ├── detect_blackhat.json      案A の二値化パラメータ
    ├── detect_dual_norm.json     案B の二値化パラメータ
    └── detect_robust.json        案C の二値化パラメータ
"""

from dataclasses import asdict

from common.json_utils import PARAMS_DIR, read_json, write_json
from pc.steering.pd_control import PdParams
from pc.steering.pursuit_control import PursuitParams
from pc.steering.recovery import RecoveryParams
from pc.steering.ts_pd_control import TsPdParams
from pc.vision.line_detector import ImageParams
from pc.vision.overlay import OverlayFlags

# PD 制御パラメータファイル
_CONTROL_FILE = PARAMS_DIR / "control.json"

# パシュート制御パラメータファイル
_PURSUIT_FILE = PARAMS_DIR / "pursuit.json"

# Theil-Sen PD 制御パラメータファイル
_TS_PD_FILE = PARAMS_DIR / "ts_pd.json"

# コースアウト復帰パラメータファイル
_RECOVERY_FILE = PARAMS_DIR / "recovery.json"

# オーバーレイ表示フラグファイル
_OVERLAY_FILE = PARAMS_DIR / "overlay.json"

# 検出手法ごとのファイル名
_DETECT_FILES: dict[str, str] = {
    "current": "detect_current.json",
    "blackhat": "detect_blackhat.json",
    "dual_norm": "detect_dual_norm.json",
    "robust": "detect_robust.json",
    "valley": "detect_valley.json",
}


def save_control(
    params: PdParams,
    method: str,
    steering_method: str = "pd",
) -> None:
    """PD 制御パラメータと最後に使用した手法を保存する

    Args:
        params: PD 制御パラメータ
        method: 最後に使用した検出手法の識別子
        steering_method: 最後に使用した制御手法("pd" or "pursuit")
    """
    data = asdict(params)
    data["last_method"] = method
    data["last_steering_method"] = steering_method
    write_json(_CONTROL_FILE, data)


def load_control() -> tuple[PdParams, str, str]:
    """PD 制御パラメータと最後に使用した手法を読み込む

    Returns:
        (PD 制御パラメータ, 検出手法の識別子, 制御手法の識別子)
    """
    if not _CONTROL_FILE.exists():
        return PdParams(), "current", "pd"

    data = read_json(_CONTROL_FILE)
    method = data.pop("last_method", "current")
    steering_method = data.pop(
        "last_steering_method", "pd",
    )
    known = PdParams.__dataclass_fields__
    filtered = {
        k: v for k, v in data.items()
        if k in known
    }
    return PdParams(**filtered), method, steering_method


def save_pursuit(params: PursuitParams) -> None:
    """パシュート制御パラメータを保存する

    Args:
        params: パシュート制御パラメータ
    """
    write_json(_PURSUIT_FILE, asdict(params))


def load_pursuit() -> PursuitParams:
    """パシュート制御パラメータを読み込む

    Returns:
        パシュート制御パラメータ(ファイルがない場合はデフォルト)
    """
    if not _PURSUIT_FILE.exists():
        return PursuitParams()

    data = read_json(_PURSUIT_FILE)
    known = PursuitParams.__dataclass_fields__
    filtered = {
        k: v for k, v in data.items()
        if k in known
    }
    return PursuitParams(**filtered)


def save_ts_pd(params: TsPdParams) -> None:
    """Theil-Sen PD 制御パラメータを保存する

    Args:
        params: Theil-Sen PD 制御パラメータ
    """
    write_json(_TS_PD_FILE, asdict(params))


def load_ts_pd() -> TsPdParams:
    """Theil-Sen PD 制御パラメータを読み込む

    Returns:
        Theil-Sen PD 制御パラメータ(ファイルがない場合はデフォルト)
    """
    if not _TS_PD_FILE.exists():
        return TsPdParams()

    data = read_json(_TS_PD_FILE)
    known = TsPdParams.__dataclass_fields__
    filtered = {
        k: v for k, v in data.items()
        if k in known
    }
    return TsPdParams(**filtered)


def save_recovery(params: RecoveryParams) -> None:
    """コースアウト復帰パラメータを保存する

    Args:
        params: コースアウト復帰パラメータ
    """
    write_json(_RECOVERY_FILE, asdict(params))


def load_recovery() -> RecoveryParams:
    """コースアウト復帰パラメータを読み込む

    Returns:
        復帰パラメータ(ファイルがない場合はデフォルト)
    """
    if not _RECOVERY_FILE.exists():
        return RecoveryParams()

    data = read_json(_RECOVERY_FILE)
    known = RecoveryParams.__dataclass_fields__
    filtered = {
        k: v for k, v in data.items()
        if k in known
    }
    return RecoveryParams(**filtered)


def save_overlay(flags: OverlayFlags) -> None:
    """オーバーレイ表示フラグを保存する

    Args:
        flags: オーバーレイ表示フラグ
    """
    write_json(_OVERLAY_FILE, asdict(flags))


def load_overlay() -> OverlayFlags:
    """オーバーレイ表示フラグを読み込む

    Returns:
        オーバーレイ表示フラグ(ファイルがない場合はデフォルト)
    """
    if not _OVERLAY_FILE.exists():
        return OverlayFlags()

    data = read_json(_OVERLAY_FILE)
    known = OverlayFlags.__dataclass_fields__
    filtered = {
        k: v for k, v in data.items()
        if k in known
    }
    return OverlayFlags(**filtered)


def save_detect_params(
    method: str, params: ImageParams,
) -> None:
    """検出手法のパラメータを保存する

    Args:
        method: 検出手法の識別子
        params: 二値化パラメータ
    """
    filename = _DETECT_FILES.get(method)
    if filename is None:
        return
    data = asdict(params)
    data["method"] = method
    write_json(PARAMS_DIR / filename, data)


def load_detect_params(method: str) -> ImageParams:
    """検出手法のパラメータを読み込む

    Args:
        method: 検出手法の識別子

    Returns:
        二値化パラメータ(ファイルがない場合はデフォルト)
    """
    filename = _DETECT_FILES.get(method)
    if filename is None:
        return ImageParams(method=method)

    path = PARAMS_DIR / filename
    if not path.exists():
        return ImageParams(method=method)

    data = read_json(path)
    known = ImageParams.__dataclass_fields__
    filtered = {
        k: v for k, v in data.items()
        if k in known
    }
    filtered["method"] = method
    return ImageParams(**filtered)