155 lines
5.7 KiB
Python
155 lines
5.7 KiB
Python
|
|
# -*- coding: utf-8 -*-
|
|||
|
|
"""
|
|||
|
|
Action 执行上下文
|
|||
|
|
─────────────────────────────────────────────────────────────────
|
|||
|
|
ActionContext 在处理函数执行期间提供:
|
|||
|
|
- 共享数据存储(跨 Action 传递状态)
|
|||
|
|
- Event 收集器(处理函数产生的 Event 暂存于此)
|
|||
|
|
- 进度上报接口
|
|||
|
|
- 取消/暂停检查接口
|
|||
|
|
─────────────────────────────────────────────────────────────────
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import logging
|
|||
|
|
import threading
|
|||
|
|
from typing import Any, Callable, Dict, List, Optional
|
|||
|
|
|
|||
|
|
logger = logging.getLogger(__name__)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ActionContext:
|
|||
|
|
"""
|
|||
|
|
Action 执行上下文(每次执行独立实例)
|
|||
|
|
|
|||
|
|
处理函数签名:
|
|||
|
|
def my_handler(action: Action, ctx: ActionContext) -> Any:
|
|||
|
|
# 产生事件
|
|||
|
|
ctx.emit_event(make_event(EventType.DEVICE_DATA_RECEIVED, ...))
|
|||
|
|
ctx.emit_event(make_event(EventType.DEVICE_STATUS_CHANGED, ...))
|
|||
|
|
# 上报进度
|
|||
|
|
ctx.report_progress(50, "处理中...")
|
|||
|
|
# 检查取消
|
|||
|
|
if ctx.is_cancelled:
|
|||
|
|
return None
|
|||
|
|
return {"result": "ok"}
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def __init__(
|
|||
|
|
self,
|
|||
|
|
action, # Action 对象
|
|||
|
|
shared_store: Optional[Dict] = None, # 全局共享数据
|
|||
|
|
progress_cb: Optional[Callable] = None,
|
|||
|
|
):
|
|||
|
|
self._action = action
|
|||
|
|
self._events: List[Any] = [] # 收集产生的 Event 对象
|
|||
|
|
self._store: Dict[str, Any] = shared_store if shared_store is not None else {}
|
|||
|
|
self._lock = threading.Lock()
|
|||
|
|
self._progress_cb = progress_cb
|
|||
|
|
self._progress: int = 0
|
|||
|
|
self._status_msg: str = ""
|
|||
|
|
self.logger = logging.getLogger(
|
|||
|
|
f"ActionContext[{action.type_key}:{action.action_id[:8]}]"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# ── Event 收集 ────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
def emit_event(self, event: Any) -> None:
|
|||
|
|
"""
|
|||
|
|
在处理函数中产生一个 Event 对象
|
|||
|
|
Event 将被收集后统一由 ActionExecutor 提交到 EventBus
|
|||
|
|
"""
|
|||
|
|
with self._lock:
|
|||
|
|
self._events.append(event)
|
|||
|
|
self.logger.debug(f"产生事件: {event}")
|
|||
|
|
|
|||
|
|
def emit_events(self, events: List[Any]) -> None:
|
|||
|
|
"""批量产生多个 Event 对象"""
|
|||
|
|
for event in events:
|
|||
|
|
self.emit_event(event)
|
|||
|
|
|
|||
|
|
@property
|
|||
|
|
def collected_events(self) -> List[Any]:
|
|||
|
|
"""获取所有已收集的 Event 对象(只读副本)"""
|
|||
|
|
with self._lock:
|
|||
|
|
return list(self._events)
|
|||
|
|
|
|||
|
|
# ── 进度上报 ──────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
def report_progress(self, percent: int, message: str = "") -> None:
|
|||
|
|
"""
|
|||
|
|
上报执行进度(0~100)
|
|||
|
|
:param percent: 进度百分比
|
|||
|
|
:param message: 进度描述
|
|||
|
|
"""
|
|||
|
|
self._progress = max(0, min(100, percent))
|
|||
|
|
self._status_msg = message
|
|||
|
|
if self._progress_cb:
|
|||
|
|
self._progress_cb(self._action.action_id, self._progress, message)
|
|||
|
|
self.logger.debug(f"进度 {self._progress}% - {message}")
|
|||
|
|
|
|||
|
|
@property
|
|||
|
|
def progress(self) -> int:
|
|||
|
|
return self._progress
|
|||
|
|
|
|||
|
|
@property
|
|||
|
|
def status_message(self) -> str:
|
|||
|
|
return self._status_msg
|
|||
|
|
|
|||
|
|
# ── 取消/暂停检查 ─────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
@property
|
|||
|
|
def is_cancelled(self) -> bool:
|
|||
|
|
"""处理函数内部轮询此属性以响应取消请求"""
|
|||
|
|
return self._action.cancel_requested
|
|||
|
|
|
|||
|
|
@property
|
|||
|
|
def is_paused(self) -> bool:
|
|||
|
|
return self._action.pause_requested
|
|||
|
|
|
|||
|
|
def check_cancel(self) -> bool:
|
|||
|
|
"""
|
|||
|
|
检查取消标志,若已取消则抛出 ActionCancelledError
|
|||
|
|
在处理函数关键节点调用:ctx.check_cancel()
|
|||
|
|
"""
|
|||
|
|
if self.is_cancelled:
|
|||
|
|
raise ActionCancelledError(
|
|||
|
|
f"Action {self._action.type_key} 已被取消"
|
|||
|
|
)
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# ── 共享数据存储 ──────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
def set(self, key: str, value: Any) -> None:
|
|||
|
|
"""写入共享数据"""
|
|||
|
|
with self._lock:
|
|||
|
|
self._store[key] = value
|
|||
|
|
|
|||
|
|
def get(self, key: str, default: Any = None) -> Any:
|
|||
|
|
"""读取共享数据"""
|
|||
|
|
with self._lock:
|
|||
|
|
return self._store.get(key, default)
|
|||
|
|
|
|||
|
|
def update(self, data: dict) -> None:
|
|||
|
|
with self._lock:
|
|||
|
|
self._store.update(data)
|
|||
|
|
|
|||
|
|
@property
|
|||
|
|
def store(self) -> dict:
|
|||
|
|
with self._lock:
|
|||
|
|
return dict(self._store)
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ─────────────────────────────────────────────────────────────────
|
|||
|
|
# 自定义异常
|
|||
|
|
# ─────────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
class ActionCancelledError(Exception):
|
|||
|
|
"""Action 被取消时抛出"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ActionTimeoutError(Exception):
|
|||
|
|
"""Action 执行超时时抛出"""
|
|||
|
|
pass
|