254 lines
10 KiB
Python
254 lines
10 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
Action 数据模型 & 状态机
|
||
─────────────────────────────────────────────────────────────────
|
||
Action —— 核心行为对象,携带类型/参数/处理函数
|
||
ActionResult—— 执行结果(含产生的 Event 列表)
|
||
ActionState —— 状态机枚举
|
||
─────────────────────────────────────────────────────────────────
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import logging
|
||
import time
|
||
import uuid
|
||
from dataclasses import dataclass, field
|
||
from datetime import datetime
|
||
from enum import Enum
|
||
from typing import Any, Callable, Dict, List, Optional, Union
|
||
|
||
from uas.action.action_type import ActionType, ActionTypeMeta, action_registry
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
# ─────────────────────────────────────────────────────────────────
|
||
# Action 状态机
|
||
# ─────────────────────────────────────────────────────────────────
|
||
|
||
class ActionState(Enum):
|
||
CREATED = "created" # 已创建
|
||
QUEUED = "queued" # 已入队等待
|
||
RUNNING = "running" # 执行中
|
||
PAUSED = "paused" # 已暂停
|
||
COMPLETED = "completed" # 执行完成
|
||
FAILED = "failed" # 执行失败
|
||
CANCELLED = "cancelled" # 已取消
|
||
TIMEOUT = "timeout" # 执行超时
|
||
INTERRUPTED = "interrupted" # 被中断
|
||
|
||
|
||
# ─────────────────────────────────────────────────────────────────
|
||
# Action 执行结果
|
||
# ─────────────────────────────────────────────────────────────────
|
||
|
||
@dataclass
|
||
class ActionResult:
|
||
"""
|
||
Action 执行结果
|
||
核心特性:携带处理函数产生的 Event 列表
|
||
"""
|
||
action_id: str
|
||
action_type: str
|
||
success: bool
|
||
data: Any = None # 处理函数返回的业务数据
|
||
error: str = ""
|
||
elapsed_ms: float = 0.0
|
||
events: List[Any] = field(default_factory=list) # 产生的 Event 对象列表
|
||
|
||
@property
|
||
def event_count(self) -> int:
|
||
return len(self.events)
|
||
|
||
def __repr__(self):
|
||
status = "✅" if self.success else "❌"
|
||
return (f"ActionResult({status} {self.action_type} "
|
||
f"{self.elapsed_ms:.1f}ms events={self.event_count})")
|
||
|
||
|
||
# ─────────────────────────────────────────────────────────────────
|
||
# 核心 Action 对象
|
||
# ─────────────────────────────────────────────────────────────────
|
||
|
||
@dataclass
|
||
class Action:
|
||
"""
|
||
核心 Action 对象
|
||
─────────────────────────────────────────────────────────────
|
||
字段说明:
|
||
action_type —— 行为类型(ActionType 枚举 或 自定义字符串)
|
||
handler —— 处理函数 fn(action, context) -> ActionResult
|
||
处理函数内部可产生一个或多个 Event 对象
|
||
params —— 行为参数(任意字典)
|
||
source —— 触发来源(设备ID/模块名)
|
||
priority —— 执行优先级(数值越小越先)
|
||
─────────────────────────────────────────────────────────────
|
||
"""
|
||
|
||
action_type: Union[ActionType, str]
|
||
handler: Optional[Callable] = None
|
||
params: Dict[str, Any] = field(default_factory=dict)
|
||
source: str = "unknown"
|
||
priority: int = 0
|
||
description: str = ""
|
||
|
||
# 自动填充
|
||
action_id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
||
created_at: float = field(default_factory=time.time)
|
||
state: ActionState = field(default=ActionState.CREATED)
|
||
|
||
# 运行时字段
|
||
result: Optional[ActionResult] = field(default=None, repr=False)
|
||
_start_time: Optional[float] = field(default=None, repr=False)
|
||
_end_time: Optional[float] = field(default=None, repr=False)
|
||
_cancel_flag: bool = field(default=False, repr=False)
|
||
_pause_flag: bool = field(default=False, repr=False)
|
||
|
||
def __post_init__(self):
|
||
# 若未设置 description,从注册表读取
|
||
if not self.description:
|
||
meta = action_registry.get(self.type_key)
|
||
if meta:
|
||
self.description = meta.description
|
||
|
||
# ── 属性 ──────────────────────────────────────────────────────
|
||
|
||
@property
|
||
def type_key(self) -> str:
|
||
return (self.action_type.value
|
||
if isinstance(self.action_type, ActionType)
|
||
else str(self.action_type))
|
||
|
||
@property
|
||
def meta(self) -> Optional[ActionTypeMeta]:
|
||
return action_registry.get(self.type_key)
|
||
|
||
@property
|
||
def elapsed_ms(self) -> float:
|
||
if self._start_time and self._end_time:
|
||
return (self._end_time - self._start_time) * 1000
|
||
return 0.0
|
||
|
||
@property
|
||
def is_running(self) -> bool:
|
||
return self.state == ActionState.RUNNING
|
||
|
||
@property
|
||
def is_done(self) -> bool:
|
||
return self.state in (
|
||
ActionState.COMPLETED, ActionState.FAILED,
|
||
ActionState.CANCELLED, ActionState.TIMEOUT,
|
||
ActionState.INTERRUPTED,
|
||
)
|
||
|
||
@property
|
||
def created_datetime(self) -> str:
|
||
return datetime.fromtimestamp(self.created_at).strftime("%H:%M:%S.%f")[:-3]
|
||
|
||
# ── 状态操作 ──────────────────────────────────────────────────
|
||
|
||
def mark_queued(self):
|
||
self.state = ActionState.QUEUED
|
||
|
||
def mark_running(self):
|
||
self.state = ActionState.RUNNING
|
||
self._start_time = time.time()
|
||
|
||
def mark_completed(self, result: ActionResult):
|
||
self.state = ActionState.COMPLETED
|
||
self.result = result
|
||
self._end_time = time.time()
|
||
|
||
def mark_failed(self, error: str):
|
||
self.state = ActionState.FAILED
|
||
self._end_time = time.time()
|
||
self.result = ActionResult(
|
||
action_id = self.action_id,
|
||
action_type = self.type_key,
|
||
success = False,
|
||
error = error,
|
||
elapsed_ms = self.elapsed_ms,
|
||
)
|
||
|
||
def mark_cancelled(self):
|
||
self._cancel_flag = True
|
||
self.state = ActionState.CANCELLED
|
||
self._end_time = time.time()
|
||
|
||
def mark_timeout(self):
|
||
self.state = ActionState.TIMEOUT
|
||
self._end_time = time.time()
|
||
|
||
def request_cancel(self) -> bool:
|
||
"""请求取消(仅 interruptible 类型可取消)"""
|
||
meta = self.meta
|
||
if meta and not meta.interruptible:
|
||
logger.warning(f"Action {self.type_key} 不可中断,取消请求被拒绝")
|
||
return False
|
||
self._cancel_flag = True
|
||
return True
|
||
|
||
def request_pause(self):
|
||
self._pause_flag = True
|
||
|
||
def request_resume(self):
|
||
self._pause_flag = False
|
||
if self.state == ActionState.PAUSED:
|
||
self.state = ActionState.RUNNING
|
||
|
||
@property
|
||
def cancel_requested(self) -> bool:
|
||
return self._cancel_flag
|
||
|
||
@property
|
||
def pause_requested(self) -> bool:
|
||
return self._pause_flag
|
||
|
||
# ── 序列化 ────────────────────────────────────────────────────
|
||
|
||
def to_dict(self) -> dict:
|
||
return {
|
||
"action_id": self.action_id,
|
||
"action_type": self.type_key,
|
||
"source": self.source,
|
||
"description": self.description,
|
||
"params": self.params,
|
||
"priority": self.priority,
|
||
"state": self.state.value,
|
||
"created_at": self.created_datetime,
|
||
"elapsed_ms": self.elapsed_ms,
|
||
"result": str(self.result) if self.result else None,
|
||
}
|
||
|
||
def __repr__(self):
|
||
return (f"Action(id={self.action_id[:8]}, type={self.type_key!r}, "
|
||
f"src={self.source!r}, state={self.state.value})")
|
||
|
||
|
||
# ─────────────────────────────────────────────────────────────────
|
||
# 便捷工厂函数
|
||
# ─────────────────────────────────────────────────────────────────
|
||
|
||
def make_action(
|
||
action_type: Union[ActionType, str],
|
||
handler: Optional[Callable] = None,
|
||
source: str = "system",
|
||
priority: int = 0,
|
||
**params
|
||
) -> Action:
|
||
"""
|
||
快速创建 Action
|
||
示例:
|
||
a = make_action(ActionType.CAPTURE_PHOTO,
|
||
handler=photo_handler,
|
||
source="camera-01",
|
||
resolution="4K", format="jpg")
|
||
"""
|
||
return Action(
|
||
action_type = action_type,
|
||
handler = handler,
|
||
source = source,
|
||
priority = priority,
|
||
params = params,
|
||
) |