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,
|
|||
|
|
)
|