--- description: 协议编译器开发规范 globs: ["unilabos/compile/**/*.py"] --- # 协议编译器开发规范 ## 概述 协议编译器负责将高级实验操作(如 Stir、Add、Filter)编译为设备可执行的动作序列。 ## 文件命名 - 位置: `unilabos/compile/` - 命名: `{operation}_protocol.py` - 示例: `stir_protocol.py`, `add_protocol.py`, `filter_protocol.py` ## 协议函数模板 ```python from typing import List, Dict, Any, Union import networkx as nx import logging from .utils.unit_parser import parse_time_input from .utils.vessel_parser import extract_vessel_id logger = logging.getLogger(__name__) def generate_{operation}_protocol( G: nx.DiGraph, vessel: Union[str, dict], param1: Union[str, float] = "0", param2: float = 0.0, **kwargs ) -> List[Dict[str, Any]]: """ 生成{操作}协议序列 Args: G: 物理拓扑图 (NetworkX DiGraph) vessel: 容器ID或Resource字典 param1: 参数1(支持字符串单位,如 "5 min") param2: 参数2 **kwargs: 其他参数 Returns: List[Dict]: 动作序列 Raises: ValueError: 参数无效时 """ # 1. 提取 vessel_id vessel_id = extract_vessel_id(vessel) # 2. 验证参数 if not vessel_id: raise ValueError("vessel 参数不能为空") if vessel_id not in G.nodes(): raise ValueError(f"容器 '{vessel_id}' 不存在于系统中") # 3. 解析参数(支持单位) parsed_param1 = parse_time_input(param1) # "5 min" -> 300.0 # 4. 查找设备 device_id = find_connected_device(G, vessel_id, device_type="my_device") # 5. 生成动作序列 action_sequence = [] action = { "device_id": device_id, "action_name": "my_action", "action_kwargs": { "vessel": {"id": vessel_id}, # 始终使用字典格式 "param1": float(parsed_param1), "param2": float(param2), } } action_sequence.append(action) logger.info(f"生成协议: {len(action_sequence)} 个动作") return action_sequence def find_connected_device( G: nx.DiGraph, vessel_id: str, device_type: str = "" ) -> str: """ 查找与容器相连的设备 Args: G: 拓扑图 vessel_id: 容器ID device_type: 设备类型关键字 Returns: str: 设备ID """ # 查找所有匹配类型的设备 device_nodes = [] for node in G.nodes(): node_class = G.nodes[node].get('class', '') or '' if device_type.lower() in node_class.lower(): device_nodes.append(node) # 检查连接 if vessel_id and device_nodes: for device in device_nodes: if G.has_edge(device, vessel_id) or G.has_edge(vessel_id, device): return device # 返回第一个可用设备 if device_nodes: return device_nodes[0] # 默认设备 return f"{device_type}_1" ``` ## 关键规则 ### 1. vessel 参数处理 vessel 参数可能是字符串或字典,需要统一处理: ```python def extract_vessel_id(vessel: Union[str, dict]) -> str: """提取vessel_id""" if isinstance(vessel, dict): # 可能是 {"id": "xxx"} 或完整 Resource 对象 return vessel.get("id", list(vessel.values())[0].get("id", "")) return str(vessel) if vessel else "" ``` ### 2. action_kwargs 中的 vessel 始终使用 `{"id": vessel_id}` 格式传递 vessel: ```python # 正确 "action_kwargs": { "vessel": {"id": vessel_id}, # 字符串ID包装为字典 } # 避免 "action_kwargs": { "vessel": vessel_resource, # 不要传递完整 Resource 对象 } ``` ### 3. 单位解析 使用 `parse_time_input` 解析时间参数: ```python from .utils.unit_parser import parse_time_input # 支持格式: "5 min", "1 h", "300", "1.5 hours" time_seconds = parse_time_input("5 min") # -> 300.0 time_seconds = parse_time_input(120) # -> 120.0 time_seconds = parse_time_input("1 h") # -> 3600.0 ``` ### 4. 参数验证 所有参数必须进行验证和类型转换: ```python # 验证范围 if speed < 10.0 or speed > 1500.0: logger.warning(f"速度 {speed} 超出范围,修正为 300") speed = 300.0 # 类型转换 param = float(param) if not isinstance(param, (int, float)) else param ``` ### 5. 日志记录 使用项目日志记录器: ```python logger = logging.getLogger(__name__) def generate_protocol(...): logger.info(f"开始生成协议...") logger.debug(f"参数: vessel={vessel_id}, time={time}") logger.warning(f"参数修正: {old_value} -> {new_value}") ``` ## 便捷函数 为常用操作提供便捷函数: ```python def stir_briefly(G: nx.DiGraph, vessel: Union[str, dict], speed: float = 300.0) -> List[Dict[str, Any]]: """短时间搅拌(30秒)""" return generate_stir_protocol(G, vessel, time="30", stir_speed=speed) def stir_vigorously(G: nx.DiGraph, vessel: Union[str, dict], time: str = "5 min") -> List[Dict[str, Any]]: """剧烈搅拌""" return generate_stir_protocol(G, vessel, time=time, stir_speed=800.0) ``` ## 测试函数 每个协议文件应包含测试函数: ```python def test_{operation}_protocol(): """测试协议生成""" # 测试参数处理 vessel_dict = {"id": "flask_1", "name": "反应瓶1"} vessel_id = extract_vessel_id(vessel_dict) assert vessel_id == "flask_1" # 测试单位解析 time_s = parse_time_input("5 min") assert time_s == 300.0 if __name__ == "__main__": test_{operation}_protocol() ``` ## 现有协议参考 - `stir_protocol.py` - 搅拌操作 - `add_protocol.py` - 添加物料 - `filter_protocol.py` - 过滤操作 - `heatchill_protocol.py` - 加热/冷却 - `separate_protocol.py` - 分离操作 - `evaporate_protocol.py` - 蒸发操作