base_agent/uas/uas_control/lifecycle.py

187 lines
7.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""
装备模型生命周期状态机
─────────────────────────────────────────────────────────────────
LifecycleState 枚举CREATED / READY / RUNNING / PAUSED /
STOPPED / ERROR
LifecycleFSM 有限状态机:管理合法状态转移,非法转移抛出异常
─────────────────────────────────────────────────────────────────
"""
from __future__ import annotations
from enum import Enum
from typing import Dict, Set, Callable, Optional
# ─────────────────────────────────────────────────────────────────
# 生命周期状态枚举
# ─────────────────────────────────────────────────────────────────
class LifecycleState(Enum):
CREATED = "CREATED" # 刚创建,参数未就绪
READY = "READY" # 参数就绪,可启动
RUNNING = "RUNNING" # 仿真运行中
PAUSED = "PAUSED" # 暂停(可恢复)
STOPPED = "STOPPED" # 已停止(不可恢复,需 reset
ERROR = "ERROR" # 发生错误
# ─────────────────────────────────────────────────────────────────
# 合法状态转移表
# ─────────────────────────────────────────────────────────────────
# { 当前状态 → 可转移到的目标状态集合 }
_TRANSITIONS: Dict[LifecycleState, Set[LifecycleState]] = {
LifecycleState.CREATED: {
LifecycleState.READY,
LifecycleState.ERROR,
},
LifecycleState.READY: {
LifecycleState.RUNNING,
LifecycleState.ERROR,
},
LifecycleState.RUNNING: {
LifecycleState.PAUSED,
LifecycleState.STOPPED,
LifecycleState.ERROR,
LifecycleState.READY, # reset 时
},
LifecycleState.PAUSED: {
LifecycleState.RUNNING,
LifecycleState.STOPPED,
LifecycleState.ERROR,
LifecycleState.READY, # reset 时
},
LifecycleState.STOPPED: {
LifecycleState.READY, # reset 后可重新启动
LifecycleState.ERROR,
},
LifecycleState.ERROR: {
LifecycleState.READY, # 错误恢复后 reset
},
}
# ─────────────────────────────────────────────────────────────────
# 生命周期有限状态机
# ─────────────────────────────────────────────────────────────────
class LifecycleFSM:
"""
装备模型生命周期有限状态机
职责:
- 维护当前状态
- 校验状态转移合法性
- 触发转移回调on_enter / on_exit
- 记录状态历史
"""
def __init__(
self,
initial: LifecycleState = LifecycleState.CREATED,
on_changed: Optional[Callable[[str, str], None]] = None,
):
self._state: LifecycleState = initial
self._history: list = [(initial, 0.0)]
self._on_changed = on_changed # (old, new) 回调
# ── 属性 ──────────────────────────────────────────────────────
@property
def state(self) -> LifecycleState:
return self._state
@property
def history(self) -> list:
return list(self._history)
# ── 状态查询 ──────────────────────────────────────────────────
@property
def is_running(self) -> bool:
return self._state == LifecycleState.RUNNING
@property
def is_paused(self) -> bool:
return self._state == LifecycleState.PAUSED
@property
def is_stopped(self) -> bool:
return self._state == LifecycleState.STOPPED
@property
def is_ready(self) -> bool:
return self._state == LifecycleState.READY
@property
def is_error(self) -> bool:
return self._state == LifecycleState.ERROR
@property
def can_step(self) -> bool:
"""是否可以执行 step()"""
return self._state == LifecycleState.RUNNING
# ── 状态转移 ──────────────────────────────────────────────────
def transition(
self,
target: LifecycleState,
timestamp: float = 0.0,
force: bool = False,
) -> bool:
"""
执行状态转移
:param target: 目标状态
:param timestamp: 当前仿真时刻(用于历史记录)
:param force: True=强制转移(跳过合法性检查,仅内部使用)
:return: True=转移成功False=已在目标状态
:raises ValueError: 非法状态转移
"""
if self._state == target:
return False
if not force:
allowed = _TRANSITIONS.get(self._state, set())
if target not in allowed:
raise ValueError(
f"非法状态转移: {self._state.value}{target.value} "
f"(允许: {[s.value for s in allowed]}"
)
old_state = self._state
self._state = target
self._history.append((target, timestamp))
if self._on_changed:
self._on_changed(old_state.value, target.value)
return True
# ── 快捷转移方法 ──────────────────────────────────────────────
def to_ready(self, t: float = 0.0) -> bool:
return self.transition(LifecycleState.READY, t)
def to_running(self, t: float = 0.0) -> bool:
return self.transition(LifecycleState.RUNNING, t)
def to_paused(self, t: float = 0.0) -> bool:
return self.transition(LifecycleState.PAUSED, t)
def to_stopped(self, t: float = 0.0) -> bool:
return self.transition(LifecycleState.STOPPED, t)
def to_error(self, t: float = 0.0) -> bool:
return self.transition(LifecycleState.ERROR, t, force=True)
# ── 工具 ──────────────────────────────────────────────────────
def reset(self, t: float = 0.0) -> bool:
"""强制重置到 READY任意状态均可"""
return self.transition(LifecycleState.READY, t, force=True)
def __repr__(self) -> str:
return f"LifecycleFSM(state={self._state.value})"