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