simplify resource system

This commit is contained in:
Junhan Chang
2025-08-27 11:13:56 +08:00
parent 1ec642ee3a
commit 332b33c6f4
3 changed files with 407 additions and 287 deletions

View File

@@ -0,0 +1,184 @@
# 工作站抽象基类物料系统架构说明
## 设计理念
基于用户需求"请你帮我系统思考一下,工作站抽象基类的物料系统基类该如何构建",我们最终确定了一个**PyLabRobot Deck为中心**的简化架构。
### 核心原则
1. **PyLabRobot为物料管理核心**使用PyLabRobot的Deck系统作为物料管理的基础利用其成熟的Resource体系
2. **Graphio转换函数集成**使用graphio中的`resource_ulab_to_plr`等转换函数实现UniLab与PLR格式的无缝转换
3. **关注点分离**基类专注核心物料系统HTTP服务等功能在子类中实现
4. **外部系统集成模式**通过ResourceSynchronizer抽象类提供外部物料系统对接模式
## 架构组成
### 1. WorkstationBase基类
**文件**: `workstation_base.py`
**核心功能**
- 使用deck_config和children通过`resource_ulab_to_plr`转换为PLR物料self.deck
- 基础的资源查找和管理功能
- 抽象的工作流执行接口
- ResourceSynchronizer集成点
**关键代码**
```python
def _initialize_material_system(self, deck_config: Dict[str, Any], children_config: Dict[str, Any] = None):
"""初始化基于PLR的物料系统"""
# 合并deck_config和children
complete_config = self._merge_deck_and_children_config(deck_config, children_config)
# 使用graphio转换函数转换为PLR资源
self.deck = resource_ulab_to_plr(complete_config)
```
### 2. ResourceSynchronizer外部系统集成抽象类
**定义在**: `workstation_base.py`
**设计目的**
- 提供外部物料系统如Bioyong、LIMS等集成的标准接口
- 双向同步从外部系统同步到本地deck以及将本地变更同步到外部系统
- 处理外部系统的变更通知
**核心方法**
```python
async def sync_from_external(self) -> bool:
"""从外部系统同步物料到本地deck"""
async def sync_to_external(self, plr_resource) -> bool:
"""将本地物料同步到外部系统"""
async def handle_external_change(self, change_info: Dict[str, Any]) -> bool:
"""处理外部系统的变更通知"""
```
### 3. WorkstationWithHTTP子类示例
**文件**: `workstation_with_http_example.py`
**扩展功能**
- HTTP报送接收服务集成
- 具体工作流实现(液体转移、板洗等)
- Bioyong物料系统同步器示例
- 外部报送处理方法
## 技术栈
### 核心依赖
- **PyLabRobot**: 物料资源管理核心Deck, Resource, Coordinate
- **GraphIO转换函数**: UniLab ↔ PLR格式转换
- `resource_ulab_to_plr`: UniLab格式转PLR格式
- `resource_plr_to_ulab`: PLR格式转UniLab格式
- `convert_resources_to_type`: 通用资源类型转换
- **ROS2**: 基础设备节点通信BaseROS2DeviceNode
### 可选依赖
- **HTTP服务**: 仅在需要外部报送接收的子类中使用
- **外部系统API**: 根据具体集成需求添加
## 使用示例
### 1. 简单工作站仅PLR物料系统
```python
from unilabos.devices.workstation.workstation_base import WorkstationBase
# Deck配置
deck_config = {
"size_x": 1200.0,
"size_y": 800.0,
"size_z": 100.0
}
# 子资源配置
children_config = {
"source_plate": {
"name": "source_plate",
"type": "plate",
"position": {"x": 100, "y": 100, "z": 10},
"config": {"size_x": 127.8, "size_y": 85.5, "size_z": 14.4}
}
}
# 创建工作站
workstation = WorkstationBase(
device_id="simple_workstation",
deck_config=deck_config,
children_config=children_config
)
# 查找资源
plate = workstation.find_resource_by_name("source_plate")
```
### 2. 带HTTP服务的工作站
```python
from unilabos.devices.workstation.workstation_with_http_example import WorkstationWithHTTP
# HTTP服务配置
http_service_config = {
"enabled": True,
"host": "127.0.0.1",
"port": 8081
}
# 创建带HTTP服务的工作站
workstation = WorkstationWithHTTP(
device_id="http_workstation",
deck_config=deck_config,
children_config=children_config,
http_service_config=http_service_config
)
# 执行工作流
success = workstation.execute_workflow("liquid_transfer", {
"volume": 100.0,
"source_wells": ["A1", "A2"],
"dest_wells": ["B1", "B2"]
})
```
### 3. 外部系统集成
```python
class BioyongResourceSynchronizer(ResourceSynchronizer):
"""Bioyong系统同步器"""
async def sync_from_external(self) -> bool:
# 从Bioyong API获取物料
external_materials = await self._fetch_bioyong_materials()
# 转换并添加到本地deck
for material in external_materials:
await self._add_material_to_deck(material)
return True
```
## 设计优势
### 1. **简洁性**
- 基类只专注核心物料管理,没有冗余功能
- 使用成熟的PyLabRobot作为物料管理基础
### 2. **可扩展性**
- 通过子类添加HTTP服务、特定工作流等功能
- ResourceSynchronizer模式支持任意外部系统集成
### 3. **标准化**
- PLR Deck提供标准的资源管理接口
- Graphio转换函数确保格式一致性
### 4. **灵活性**
- 可选择性使用HTTP服务和外部系统集成
- 支持不同类型的工作站需求
## 发展历程
1. **初始设计**: 复杂的统一物料系统包含HTTP服务和多种功能
2. **PyLabRobot集成**: 引入PLR Deck管理但保留了ResourceTracker复杂性
3. **Graphio转换**: 使用graphio转换函数简化初始化
4. **最终简化**: 专注核心PLR物料系统HTTP服务移至子类
这个架构体现了"用PyLabRobot Deck来管理物料会更好但是要做好和外部物料系统的对接"的设计理念,以及"现在我只需要在工作站创建的时候整体使用deck_config和children一起通过resource_ulab_to_plr转换为plr物料self.deck即可"的简化要求。

View File

@@ -606,12 +606,12 @@ class ProxyWorkflowExecutor(WorkflowExecutor):
"""执行代理工作流"""
try:
# 通过协议节点调用目标设备的工作流
if self.workstation._protocol_node:
return self.workstation._protocol_node.call_device_method(
if self.workstation._workstation_node:
return self.workstation._workstation_node.call_device_method(
self.device_id, 'execute_workflow', workflow_name, parameters
)
else:
logger.error("代理模式需要protocol_node")
logger.error("代理模式需要workstation_node")
return False
except Exception as e:
@@ -621,12 +621,12 @@ class ProxyWorkflowExecutor(WorkflowExecutor):
def stop_workflow(self, emergency: bool = False) -> bool:
"""停止代理工作流"""
try:
if self.workstation._protocol_node:
return self.workstation._protocol_node.call_device_method(
if self.workstation._workstation_node:
return self.workstation._workstation_node.call_device_method(
self.device_id, 'stop_workflow', emergency
)
else:
logger.error("代理模式需要protocol_node")
logger.error("代理模式需要workstation_node")
return False
except Exception as e:

View File

@@ -1,24 +1,24 @@
"""
工作站基类
Workstation Base Class - 单接口模式
Workstation Base Class - 简化版
基于单一硬件接口的简化工作站架构
支持直接模式和代理模式的自动工作流执行器选择
基于PLR Deck的简化工作站架构
专注于核心物料系统和工作流管理
"""
import time
import traceback
from typing import Dict, Any, List, Optional, Union, TYPE_CHECKING
from typing import Dict, Any, List, Optional, Union
from abc import ABC, abstractmethod
from dataclasses import dataclass
from enum import Enum
if TYPE_CHECKING:
from unilabos.ros.nodes.presets.protocol_node import ROS2WorkstationNode
try:
from pylabrobot.resources import Deck, Resource as PLRResource
PYLABROBOT_AVAILABLE = True
except ImportError:
PYLABROBOT_AVAILABLE = False
class Deck: pass
class PLRResource: pass
from unilabos.devices.work_station.workstation_material_management import MaterialManagementBase
from unilabos.devices.work_station.workstation_http_service import (
WorkstationHTTPService, WorkstationReportRequest, MaterialUsage
)
from unilabos.utils.log import logger
@@ -45,43 +45,69 @@ class WorkflowInfo:
parameters_schema: Dict[str, Any] # 参数架构
class WorkstationBase(ABC):
"""工作站基类 - 单接口模式
class ResourceSynchronizer(ABC):
"""资源同步器基类
核心设计原则:
1. 每个工作站只有一个 hardware_interface
2. 根据接口类型自动选择工作流执行器
3. 支持直接模式和代理模式
4. 统一的设备操作接口
负责与外部物料系统的同步,并对 self.deck 做修改
"""
def __init__(self, workstation: 'WorkstationBase'):
self.workstation = workstation
@abstractmethod
async def sync_from_external(self) -> bool:
"""从外部系统同步物料到本地deck"""
pass
@abstractmethod
async def sync_to_external(self, plr_resource: PLRResource) -> bool:
"""将本地物料同步到外部系统"""
pass
@abstractmethod
async def handle_external_change(self, change_info: Dict[str, Any]) -> bool:
"""处理外部系统的变更通知"""
pass
class WorkstationBase(ABC):
"""工作站基类 - 简化版
核心功能:
1. 基于 PLR Deck 的物料系统,支持格式转换
2. 可选的资源同步器支持外部物料系统
3. 简化的工作流管理
"""
def __init__(
self,
device_id: str,
deck_config: Optional[Dict[str, Any]] = None,
http_service_config: Optional[Dict[str, Any]] = None,
deck_config: Dict[str, Any],
children: Optional[Dict[str, Any]] = None,
resource_synchronizer: Optional[ResourceSynchronizer] = None,
*args,
**kwargs,
):
if not PYLABROBOT_AVAILABLE:
raise ImportError("PyLabRobot 未安装,无法创建工作站")
# 基本配置
self.device_id = device_id
self.deck_config = deck_config or {"size_x": 1000.0, "size_y": 1000.0, "size_z": 500.0}
self.deck_config = deck_config
self.children = children or {}
# HTTP服务配置
self.http_service_config = http_service_config or {
"enabled": True,
"host": "127.0.0.1",
"port": 8081
}
# PLR 物料系统
self.deck: Optional[Deck] = None
self.plr_resources: Dict[str, PLRResource] = {}
# 单一硬件接口 - 可以是具体客户端对象或代理字符串
# 资源同步器(可选)
self.resource_synchronizer = resource_synchronizer
# 硬件接口
self.hardware_interface: Union[Any, str] = None
# 协议节点引用(用于代理模式)
self._protocol_node: Optional['ROS2WorkstationNode'] = None
# 工作流执行器(基于通信接口类型自动选择)
self.workflow_executor: Optional['WorkflowExecutor'] = None
self._workstation_node: Optional['ROS2WorkstationNode'] = None
# 工作流状态
self.current_workflow_status = WorkflowStatus.IDLE
@@ -89,90 +115,136 @@ class WorkstationBase(ABC):
self.workflow_start_time = None
self.workflow_parameters = {}
# 错误处理
self.error_history = []
self.action_results = {}
# 支持的工作流(静态预定义)
self.supported_workflows: Dict[str, WorkflowInfo] = {}
# 初始化工作站模块
self.material_management: MaterialManagementBase = self._create_material_management_module()
# 初始化物料系统
self._initialize_material_system()
# 注册支持的工作流
self._register_supported_workflows()
# 启动HTTP报送接收服务
self.http_service = None
self._start_http_service()
logger.info(f"工作站 {device_id} 初始化完成(简化版)")
logger.info(f"工作站 {device_id} 初始化完成(单接口模式)")
def _initialize_material_system(self):
"""初始化物料系统 - 使用 graphio 转换"""
try:
from unilabos.resources.graphio import resource_ulab_to_plr
# 1. 合并 deck_config 和 children 创建完整的资源树
complete_resource_config = self._create_complete_resource_config()
# 2. 使用 graphio 转换为 PLR 资源
self.deck = resource_ulab_to_plr(complete_resource_config, plr_model=True)
# 3. 建立资源映射
self._build_resource_mappings(self.deck)
# 4. 如果有资源同步器,执行初始同步
if self.resource_synchronizer:
# 这里可以异步执行,暂时跳过
pass
logger.info(f"工作站 {self.device_id} 物料系统初始化成功,创建了 {len(self.plr_resources)} 个资源")
except Exception as e:
logger.error(f"工作站 {self.device_id} 物料系统初始化失败: {e}")
raise
def _create_complete_resource_config(self) -> Dict[str, Any]:
"""创建完整的资源配置 - 合并 deck_config 和 children"""
# 创建主 deck 配置
deck_resource = {
"id": f"{self.device_id}_deck",
"name": f"{self.device_id}_deck",
"type": "deck",
"position": {"x": 0, "y": 0, "z": 0},
"config": {
"size_x": self.deck_config.get("size_x", 1000.0),
"size_y": self.deck_config.get("size_y", 1000.0),
"size_z": self.deck_config.get("size_z", 100.0),
**{k: v for k, v in self.deck_config.items() if k not in ["size_x", "size_y", "size_z"]}
},
"data": {},
"children": [],
"parent": None
}
# 添加子资源
if self.children:
children_list = []
for child_id, child_config in self.children.items():
child_resource = self._normalize_child_resource(child_id, child_config, deck_resource["id"])
children_list.append(child_resource)
deck_resource["children"] = children_list
return deck_resource
def _normalize_child_resource(self, resource_id: str, config: Dict[str, Any], parent_id: str) -> Dict[str, Any]:
"""标准化子资源配置"""
return {
"id": resource_id,
"name": config.get("name", resource_id),
"type": config.get("type", "container"),
"position": self._normalize_position(config.get("position", {})),
"config": config.get("config", {}),
"data": config.get("data", {}),
"children": [], # 简化版本:只支持一层子资源
"parent": parent_id
}
def _normalize_position(self, position: Any) -> Dict[str, float]:
"""标准化位置信息"""
if isinstance(position, dict):
return {
"x": float(position.get("x", 0)),
"y": float(position.get("y", 0)),
"z": float(position.get("z", 0))
}
elif isinstance(position, (list, tuple)) and len(position) >= 2:
return {
"x": float(position[0]),
"y": float(position[1]),
"z": float(position[2]) if len(position) > 2 else 0.0
}
else:
return {"x": 0.0, "y": 0.0, "z": 0.0}
def _build_resource_mappings(self, deck: Deck):
"""递归构建资源映射"""
def add_resource_recursive(resource: PLRResource):
if hasattr(resource, 'name'):
self.plr_resources[resource.name] = resource
if hasattr(resource, 'children'):
for child in resource.children:
add_resource_recursive(child)
add_resource_recursive(deck)
# ============ 硬件接口管理 ============
def set_hardware_interface(self, hardware_interface: Union[Any, str]):
"""设置硬件接口"""
self.hardware_interface = hardware_interface
# 根据接口类型自动创建工作流执行器
self._setup_workflow_executor()
logger.info(f"工作站 {self.device_id} 硬件接口设置: {type(hardware_interface).__name__}")
def set_protocol_node(self, protocol_node: 'ROS2WorkstationNode'):
def set_workstation_node(self, workstation_node: 'ROS2WorkstationNode'):
"""设置协议节点引用(用于代理模式)"""
self._protocol_node = protocol_node
self._workstation_node = workstation_node
logger.info(f"工作站 {self.device_id} 关联协议节点")
def _setup_workflow_executor(self):
"""根据硬件接口类型自动设置工作流执行器"""
if self.hardware_interface is None:
return
# 动态导入工作流执行器类
try:
from unilabos.devices.work_station.workflow_executors import (
ProxyWorkflowExecutor, ModbusWorkflowExecutor,
HttpWorkflowExecutor, PyLabRobotWorkflowExecutor
)
except ImportError:
logger.warning("工作流执行器模块未找到,将使用基础执行器")
self.workflow_executor = None
return
# 检查是否为代理字符串
if isinstance(self.hardware_interface, str) and self.hardware_interface.startswith("proxy:"):
self.workflow_executor = ProxyWorkflowExecutor(self)
logger.info(f"工作站 {self.device_id} 使用代理工作流执行器")
# 检查是否为Modbus客户端
elif hasattr(self.hardware_interface, 'write_register') and hasattr(self.hardware_interface, 'read_register'):
self.workflow_executor = ModbusWorkflowExecutor(self)
logger.info(f"工作站 {self.device_id} 使用Modbus工作流执行器")
# 检查是否为HTTP客户端
elif hasattr(self.hardware_interface, 'post') or hasattr(self.hardware_interface, 'get'):
self.workflow_executor = HttpWorkflowExecutor(self)
logger.info(f"工作站 {self.device_id} 使用HTTP工作流执行器")
# 检查是否为PyLabRobot设备
elif hasattr(self.hardware_interface, 'transfer_liquid') or hasattr(self.hardware_interface, 'pickup_tips'):
self.workflow_executor = PyLabRobotWorkflowExecutor(self)
logger.info(f"工作站 {self.device_id} 使用PyLabRobot工作流执行器")
else:
logger.warning(f"工作站 {self.device_id} 无法识别硬件接口类型: {type(self.hardware_interface)}")
self.workflow_executor = None
# ============ 统一的设备操作接口 ============
# ============ 设备操作接口 ============
def call_device_method(self, method: str, *args, **kwargs) -> Any:
"""调用设备方法的统一接口"""
# 1. 代理模式:通过协议节点转发
if isinstance(self.hardware_interface, str) and self.hardware_interface.startswith("proxy:"):
if not self._protocol_node:
raise RuntimeError("代理模式需要设置protocol_node")
if not self._workstation_node:
raise RuntimeError("代理模式需要设置workstation_node")
device_id = self.hardware_interface[6:] # 移除 "proxy:" 前缀
return self._protocol_node.call_device_method(device_id, method, *args, **kwargs)
return self._workstation_node.call_device_method(device_id, method, *args, **kwargs)
# 2. 直接模式:直接调用硬件接口方法
elif self.hardware_interface and hasattr(self.hardware_interface, method):
@@ -201,22 +273,54 @@ class WorkstationBase(ABC):
except:
return False
# ============ 工作流控制接口 ============
# ============ 物料系统接口 ============
def execute_workflow(self, workflow_name: str, parameters: Dict[str, Any]) -> bool:
"""执行工作流 - 委托给工作流执行器"""
if not self.workflow_executor:
logger.error(f"工作站 {self.device_id} 工作流执行器未初始化")
def get_deck(self) -> Deck:
"""获取主 Deck"""
return self.deck
def get_all_resources(self) -> Dict[str, PLRResource]:
"""获取所有 PLR 资源"""
return self.plr_resources.copy()
def find_resource_by_name(self, name: str) -> Optional[PLRResource]:
"""按名称查找资源"""
return self.plr_resources.get(name)
def find_resources_by_type(self, resource_type: type) -> List[PLRResource]:
"""按类型查找资源"""
return [res for res in self.plr_resources.values()
if isinstance(res, resource_type)]
async def sync_with_external_system(self) -> bool:
"""与外部物料系统同步"""
if not self.resource_synchronizer:
logger.info(f"工作站 {self.device_id} 没有配置资源同步器")
return True
try:
success = await self.resource_synchronizer.sync_from_external()
if success:
logger.info(f"工作站 {self.device_id} 外部同步成功")
else:
logger.warning(f"工作站 {self.device_id} 外部同步失败")
return success
except Exception as e:
logger.error(f"工作站 {self.device_id} 外部同步异常: {e}")
return False
# ============ 简化的工作流控制 ============
def execute_workflow(self, workflow_name: str, parameters: Dict[str, Any]) -> bool:
"""执行工作流"""
try:
# 设置工作流状态
self.current_workflow_status = WorkflowStatus.INITIALIZING
self.workflow_parameters = parameters
self.workflow_start_time = time.time()
# 委托给工作流执行器
success = self.workflow_executor.execute_workflow(workflow_name, parameters)
# 委托给子类实现
success = self._execute_workflow_impl(workflow_name, parameters)
if success:
self.current_workflow_status = WorkflowStatus.RUNNING
@@ -232,16 +336,8 @@ class WorkstationBase(ABC):
logger.error(f"工作站 {self.device_id} 执行工作流失败: {e}")
return False
def start_workflow(self, workflow_type: str, parameters: Dict[str, Any] = None) -> bool:
"""启动工作流 - 兼容旧接口"""
return self.execute_workflow(workflow_type, parameters or {})
def stop_workflow(self, emergency: bool = False) -> bool:
"""停止工作流"""
if not self.workflow_executor:
logger.warning(f"工作站 {self.device_id} 工作流执行器未初始化")
return True
try:
if self.current_workflow_status in [WorkflowStatus.IDLE, WorkflowStatus.STOPPED]:
logger.warning(f"工作站 {self.device_id} 没有正在运行的工作流")
@@ -249,8 +345,8 @@ class WorkstationBase(ABC):
self.current_workflow_status = WorkflowStatus.STOPPING
# 委托给工作流执行器
success = self.workflow_executor.stop_workflow(emergency)
# 委托给子类实现
success = self._stop_workflow_impl(emergency)
if success:
self.current_workflow_status = WorkflowStatus.STOPPED
@@ -289,179 +385,19 @@ class WorkstationBase(ABC):
return 0.0
return time.time() - self.workflow_start_time
@property
def error_count(self) -> int:
"""获取错误计数"""
return len(self.error_history)
@property
def last_error(self) -> Optional[Dict[str, Any]]:
"""获取最后一个错误"""
return self.error_history[-1] if self.error_history else None
# ============ 抽象方法 - 子类必须实现 ============
@abstractmethod
def _create_material_management_module(self) -> MaterialManagementBase:
"""创建物料管理模块 - 子类必须实现"""
pass
@abstractmethod
def _register_supported_workflows(self):
"""注册支持的工作流 - 子类必须实现"""
pass
# ============ HTTP服务管理 ============
def _start_http_service(self):
"""启动HTTP报送接收服务"""
try:
if not self.http_service_config.get("enabled", True):
logger.info(f"工作站 {self.device_id} HTTP报送接收服务已禁用")
return
host = self.http_service_config.get("host", "127.0.0.1")
port = self.http_service_config.get("port", 8081)
self.http_service = WorkstationHTTPService(
workstation_handler=self,
host=host,
port=port
)
logger.info(f"工作站 {self.device_id} HTTP报送接收服务启动成功: {host}:{port}")
except Exception as e:
logger.error(f"工作站 {self.device_id} 启动HTTP报送接收服务失败: {e}")
self.http_service = None
def _stop_http_service(self):
"""停止HTTP报送接收服务"""
try:
if self.http_service:
self.http_service.stop()
self.http_service = None
logger.info(f"工作站 {self.device_id} HTTP报送接收服务已停止")
except Exception as e:
logger.error(f"工作站 {self.device_id} 停止HTTP报送接收服务失败: {e}")
# ============ 报送处理方法 ============
def process_material_change_report(self, report) -> Dict[str, Any]:
"""处理物料变更报送"""
try:
logger.info(f"处理物料变更报送: {report.workstation_id} -> {report.resource_id} ({report.change_type})")
result = {
'processed': True,
'resource_id': report.resource_id,
'change_type': report.change_type,
'timestamp': time.time()
}
# 更新本地物料管理系统
if hasattr(self, 'material_management'):
try:
self.material_management.sync_external_material_change(report)
except Exception as e:
logger.warning(f"同步物料变更到本地管理系统失败: {e}")
return result
except Exception as e:
logger.error(f"处理物料变更报送失败: {e}")
return {'processed': False, 'error': str(e)}
def process_step_finish_report(self, request: WorkstationReportRequest) -> Dict[str, Any]:
"""处理步骤完成报送统一LIMS协议规范"""
try:
data = request.data
logger.info(f"处理步骤完成报送: {data['orderCode']} - {data['stepName']}")
result = {
'processed': True,
'order_code': data['orderCode'],
'step_id': data['stepId'],
'timestamp': time.time()
}
return result
except Exception as e:
logger.error(f"处理步骤完成报送失败: {e}")
return {'processed': False, 'error': str(e)}
def process_sample_finish_report(self, request: WorkstationReportRequest) -> Dict[str, Any]:
"""处理样品完成报送"""
try:
data = request.data
logger.info(f"处理样品完成报送: {data['sampleId']}")
result = {
'processed': True,
'sample_id': data['sampleId'],
'timestamp': time.time()
}
return result
except Exception as e:
logger.error(f"处理样品完成报送失败: {e}")
return {'processed': False, 'error': str(e)}
def process_order_finish_report(self, request: WorkstationReportRequest, used_materials: List[MaterialUsage]) -> Dict[str, Any]:
"""处理订单完成报送"""
try:
data = request.data
logger.info(f"处理订单完成报送: {data['orderCode']}")
result = {
'processed': True,
'order_code': data['orderCode'],
'used_materials': len(used_materials),
'timestamp': time.time()
}
return result
except Exception as e:
logger.error(f"处理订单完成报送失败: {e}")
return {'processed': False, 'error': str(e)}
def handle_external_error(self, error_request):
"""处理外部错误报告"""
try:
logger.error(f"收到外部错误报告: {error_request}")
# 记录错误
error_record = {
'timestamp': time.time(),
'error_type': error_request.get('error_type', 'unknown'),
'error_message': error_request.get('message', ''),
'source': error_request.get('source', 'external'),
'context': error_request.get('context', {})
}
self.error_history.append(error_record)
# 处理紧急停止情况
if error_request.get('emergency_stop', False):
self._trigger_emergency_stop(error_record['error_message'])
return {'processed': True, 'error_id': len(self.error_history)}
except Exception as e:
logger.error(f"处理外部错误失败: {e}")
return {'processed': False, 'error': str(e)}
def _trigger_emergency_stop(self, reason: str):
"""触发紧急停止"""
logger.critical(f"触发紧急停止: {reason}")
self.stop_workflow(emergency=True)
def __del__(self):
"""清理资源"""
try:
self._stop_http_service()
except:
@abstractmethod
def _execute_workflow_impl(self, workflow_name: str, parameters: Dict[str, Any]) -> bool:
"""执行工作流的具体实现 - 子类必须实现"""
pass
@abstractmethod
def _stop_workflow_impl(self, emergency: bool = False) -> bool:
"""停止工作流的具体实现 - 子类必须实现"""
pass