mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-05 14:05:12 +00:00
stir和adjustph的中的bug修不好
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import networkx as nx
|
||||
import logging
|
||||
from typing import List, Dict, Any
|
||||
from typing import List, Dict, Any, Union
|
||||
from .pump_protocol import generate_pump_protocol_with_rinsing
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -216,7 +216,7 @@ def calculate_reagent_volume(target_ph_value: float, reagent: str, vessel_volume
|
||||
|
||||
def generate_adjust_ph_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
vessel:Union[dict,str], # 🔧 修改:从字符串改为字典类型
|
||||
ph_value: float,
|
||||
reagent: str,
|
||||
**kwargs
|
||||
@@ -235,8 +235,17 @@ def generate_adjust_ph_protocol(
|
||||
List[Dict[str, Any]]: 动作序列
|
||||
"""
|
||||
|
||||
# 🔧 核心修改:从字典中提取容器ID
|
||||
vessel_id = vessel["id"]
|
||||
# 统一处理vessel参数
|
||||
if isinstance(vessel, dict):
|
||||
vessel_id = vessel.get("id", "")
|
||||
vessel_data = vessel.get("data", {})
|
||||
else:
|
||||
vessel_id = str(vessel)
|
||||
vessel_data = G.nodes[vessel_id].get("data", {}) if vessel_id in G.nodes() else {}
|
||||
|
||||
if not vessel_id:
|
||||
debug_print("❌ vessel 参数无效,必须包含id字段或直接提供容器ID")
|
||||
raise ValueError("vessel 参数无效,必须包含id字段或直接提供容器ID")
|
||||
|
||||
debug_print("=" * 60)
|
||||
debug_print("🧪 开始生成pH调节协议")
|
||||
|
||||
@@ -22,7 +22,7 @@ def parse_time_input(time_input: Union[str, float]) -> float:
|
||||
"""
|
||||
if isinstance(time_input, (int, float)):
|
||||
debug_print(f"⏱️ 时间输入为数字: {time_input}s ✨")
|
||||
return float(time_input)
|
||||
return float(time_input) # 🔧 确保返回float
|
||||
|
||||
if not time_input or not str(time_input).strip():
|
||||
debug_print(f"⚠️ 时间输入为空,使用默认值: 180s (3分钟) 🕐")
|
||||
@@ -48,7 +48,7 @@ def parse_time_input(time_input: Union[str, float]) -> float:
|
||||
try:
|
||||
value = float(time_str)
|
||||
debug_print(f"✅ 时间解析成功: {time_str} → {value}s(无单位,默认秒)⏰")
|
||||
return value
|
||||
return float(value) # 🔧 确保返回float
|
||||
except ValueError:
|
||||
debug_print(f"❌ 无法解析时间: '{time_str}',使用默认值180s (3分钟) 😅")
|
||||
return 180.0
|
||||
@@ -70,7 +70,7 @@ def parse_time_input(time_input: Union[str, float]) -> float:
|
||||
time_sec = value # 已经是s
|
||||
debug_print(f"🕐 时间转换: {value}s → {time_sec}s (已是秒) ⏰")
|
||||
|
||||
return time_sec
|
||||
return float(time_sec) # 🔧 确保返回float
|
||||
|
||||
def find_rotavap_device(G: nx.DiGraph, vessel: str = None) -> Optional[str]:
|
||||
"""
|
||||
@@ -389,12 +389,12 @@ def generate_evaporate_protocol(
|
||||
"device_id": rotavap_device,
|
||||
"action_name": "evaporate",
|
||||
"action_kwargs": {
|
||||
"vessel": target_vessel, # 使用 target_vessel
|
||||
"pressure": pressure,
|
||||
"temp": temp,
|
||||
"time": final_time,
|
||||
"stir_speed": stir_speed,
|
||||
"solvent": solvent
|
||||
"vessel": target_vessel,
|
||||
"pressure": float(pressure),
|
||||
"temp": float(temp),
|
||||
"time": float(final_time), # 🔧 强制转换为float类型
|
||||
"stir_speed": float(stir_speed),
|
||||
"solvent": str(solvent)
|
||||
}
|
||||
}
|
||||
action_sequence.append(evaporate_action)
|
||||
|
||||
@@ -170,33 +170,94 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
||||
debug_print(f" 🎉 通过名称匹配找到容器: {vessel_name} ✨")
|
||||
return vessel_name
|
||||
|
||||
# 第二步:通过模糊匹配
|
||||
# 第二步:通过模糊匹配(节点ID和名称)
|
||||
debug_print(" 🔍 步骤2: 模糊名称匹配...")
|
||||
for node_id in G.nodes():
|
||||
if G.nodes[node_id].get('type') == 'container':
|
||||
node_name = G.nodes[node_id].get('name', '').lower()
|
||||
|
||||
if solvent.lower() in node_id.lower() or solvent.lower() in node_name:
|
||||
debug_print(f" 🎉 通过模糊匹配找到容器: {node_id} ✨")
|
||||
debug_print(f" 🎉 通过模糊匹配找到容器: {node_id} (名称: {node_name}) ✨")
|
||||
return node_id
|
||||
|
||||
# 第三步:通过液体类型匹配
|
||||
debug_print(" 🧪 步骤3: 液体类型匹配...")
|
||||
# 第三步:通过配置中的试剂信息匹配
|
||||
debug_print(" 🧪 步骤3: 配置试剂信息匹配...")
|
||||
for node_id in G.nodes():
|
||||
if G.nodes[node_id].get('type') == 'container':
|
||||
# 检查 config 中的 reagent 字段
|
||||
node_config = G.nodes[node_id].get('config', {})
|
||||
config_reagent = node_config.get('reagent', '').lower()
|
||||
|
||||
if config_reagent and solvent.lower() == config_reagent:
|
||||
debug_print(f" 🎉 通过config.reagent匹配找到容器: {node_id} (试剂: {config_reagent}) ✨")
|
||||
return node_id
|
||||
|
||||
# 第四步:通过数据中的试剂信息匹配
|
||||
debug_print(" 🧪 步骤4: 数据试剂信息匹配...")
|
||||
for node_id in G.nodes():
|
||||
if G.nodes[node_id].get('type') == 'container':
|
||||
vessel_data = G.nodes[node_id].get('data', {})
|
||||
liquids = vessel_data.get('liquid', [])
|
||||
|
||||
# 检查 data 中的 reagent_name 字段
|
||||
reagent_name = vessel_data.get('reagent_name', '').lower()
|
||||
if reagent_name and solvent.lower() == reagent_name:
|
||||
debug_print(f" 🎉 通过data.reagent_name匹配找到容器: {node_id} (试剂: {reagent_name}) ✨")
|
||||
return node_id
|
||||
|
||||
# 检查 data 中的液体信息
|
||||
liquids = vessel_data.get('liquid', [])
|
||||
for liquid in liquids:
|
||||
if isinstance(liquid, dict):
|
||||
liquid_type = (liquid.get('liquid_type') or liquid.get('name', '')).lower()
|
||||
reagent_name = vessel_data.get('reagent_name', '').lower()
|
||||
|
||||
if solvent.lower() in liquid_type or solvent.lower() in reagent_name:
|
||||
debug_print(f" 🎉 通过液体类型匹配找到容器: {node_id} ✨")
|
||||
if solvent.lower() in liquid_type:
|
||||
debug_print(f" 🎉 通过液体类型匹配找到容器: {node_id} (液体类型: {liquid_type}) ✨")
|
||||
return node_id
|
||||
|
||||
# 第五步:部分匹配(如果前面都没找到)
|
||||
debug_print(" 🔍 步骤5: 部分匹配...")
|
||||
for node_id in G.nodes():
|
||||
if G.nodes[node_id].get('type') == 'container':
|
||||
node_config = G.nodes[node_id].get('config', {})
|
||||
node_data = G.nodes[node_id].get('data', {})
|
||||
node_name = G.nodes[node_id].get('name', '').lower()
|
||||
|
||||
config_reagent = node_config.get('reagent', '').lower()
|
||||
data_reagent = node_data.get('reagent_name', '').lower()
|
||||
|
||||
# 检查是否包含溶剂名称
|
||||
if (solvent.lower() in config_reagent or
|
||||
solvent.lower() in data_reagent or
|
||||
solvent.lower() in node_name or
|
||||
solvent.lower() in node_id.lower()):
|
||||
debug_print(f" 🎉 通过部分匹配找到容器: {node_id} ✨")
|
||||
debug_print(f" - 节点名称: {node_name}")
|
||||
debug_print(f" - 配置试剂: {config_reagent}")
|
||||
debug_print(f" - 数据试剂: {data_reagent}")
|
||||
return node_id
|
||||
|
||||
# 调试信息:列出所有容器
|
||||
debug_print(" 🔎 调试信息:列出所有容器...")
|
||||
container_list = []
|
||||
for node_id in G.nodes():
|
||||
if G.nodes[node_id].get('type') == 'container':
|
||||
node_config = G.nodes[node_id].get('config', {})
|
||||
node_data = G.nodes[node_id].get('data', {})
|
||||
node_name = G.nodes[node_id].get('name', '')
|
||||
|
||||
container_info = {
|
||||
'id': node_id,
|
||||
'name': node_name,
|
||||
'config_reagent': node_config.get('reagent', ''),
|
||||
'data_reagent': node_data.get('reagent_name', '')
|
||||
}
|
||||
container_list.append(container_info)
|
||||
debug_print(f" - 容器: {node_id}, 名称: {node_name}, config试剂: {node_config.get('reagent', '')}, data试剂: {node_data.get('reagent_name', '')}")
|
||||
|
||||
debug_print(f"❌ 找不到溶剂 '{solvent}' 对应的容器 😭")
|
||||
debug_print(f"🔍 查找的溶剂: '{solvent}' (小写: '{solvent.lower()}')")
|
||||
debug_print(f"📊 总共发现 {len(container_list)} 个容器")
|
||||
|
||||
raise ValueError(f"找不到溶剂 '{solvent}' 对应的容器")
|
||||
|
||||
|
||||
|
||||
@@ -70,11 +70,32 @@ class VirtualMultiwayValve:
|
||||
command: 目标位置 (0-8) 或位置字符串
|
||||
0: transfer pump位置
|
||||
1-8: 其他设备位置
|
||||
'default': 默认位置(0号位)
|
||||
"""
|
||||
try:
|
||||
# 如果是字符串形式的位置,先转换为数字
|
||||
# 🔧 处理特殊字符串命令
|
||||
if isinstance(command, str):
|
||||
pos = int(command)
|
||||
command_lower = command.lower().strip()
|
||||
|
||||
# 处理特殊命令
|
||||
if command_lower in ['default', 'pump', 'transfer_pump', 'home']:
|
||||
pos = 0 # 默认位置为0号位(transfer pump)
|
||||
self.logger.info(f"🔧 特殊命令 '{command}' 映射到位置 {pos}")
|
||||
elif command_lower in ['open']:
|
||||
pos = 0 # open命令也映射到0号位
|
||||
self.logger.info(f"🔧 OPEN命令映射到位置 {pos}")
|
||||
elif command_lower in ['close', 'closed']:
|
||||
# 关闭命令保持当前位置
|
||||
pos = self._current_position
|
||||
self.logger.info(f"🔧 CLOSE命令保持当前位置 {pos}")
|
||||
else:
|
||||
# 尝试转换为数字
|
||||
try:
|
||||
pos = int(command)
|
||||
except ValueError:
|
||||
error_msg = f"无法识别的命令: '{command}'"
|
||||
self.logger.error(f"❌ {error_msg}")
|
||||
raise ValueError(error_msg)
|
||||
else:
|
||||
pos = int(command)
|
||||
|
||||
|
||||
@@ -88,6 +88,20 @@ class VirtualRotavap:
|
||||
) -> bool:
|
||||
"""Execute evaporate action - 简化版 🌪️"""
|
||||
|
||||
# 🔧 新增:确保time参数是数值类型
|
||||
if isinstance(time, str):
|
||||
try:
|
||||
time = float(time)
|
||||
except ValueError:
|
||||
self.logger.error(f"❌ 无法转换时间参数 '{time}' 为数值,使用默认值180.0秒")
|
||||
time = 180.0
|
||||
elif not isinstance(time, (int, float)):
|
||||
self.logger.error(f"❌ 时间参数类型无效: {type(time)},使用默认值180.0秒")
|
||||
time = 180.0
|
||||
|
||||
# 确保time是float类型
|
||||
time = float(time)
|
||||
|
||||
# 🔧 简化处理:如果vessel就是设备自己,直接操作
|
||||
if vessel == self.device_id:
|
||||
debug_print(f"🎯 在设备 {self.device_id} 上直接执行蒸发操作")
|
||||
@@ -158,7 +172,7 @@ class VirtualRotavap:
|
||||
})
|
||||
return False
|
||||
|
||||
# 开始蒸发
|
||||
# 开始蒸发 - 🔧 现在time已经确保是float类型
|
||||
self.logger.info(f"🚀 启动蒸发程序! 预计用时 {time/60:.1f}分钟 ⏱️")
|
||||
|
||||
self.data.update({
|
||||
|
||||
@@ -5993,3 +5993,176 @@ virtual_vacuum_pump:
|
||||
- status
|
||||
type: object
|
||||
version: 0.0.1
|
||||
virtual_solid_dispenser:
|
||||
class:
|
||||
action_value_mappings:
|
||||
auto-cleanup:
|
||||
feedback: {}
|
||||
goal: {}
|
||||
goal_default: {}
|
||||
handles: []
|
||||
result: {}
|
||||
schema:
|
||||
description: cleanup的参数schema
|
||||
properties:
|
||||
feedback: {}
|
||||
goal:
|
||||
properties: {}
|
||||
type: object
|
||||
result: {}
|
||||
required:
|
||||
- goal
|
||||
title: cleanup参数
|
||||
type: object
|
||||
type: UniLabJsonCommandAsync
|
||||
auto-initialize:
|
||||
feedback: {}
|
||||
goal: {}
|
||||
goal_default: {}
|
||||
handles: []
|
||||
result: {}
|
||||
schema:
|
||||
description: initialize的参数schema
|
||||
properties:
|
||||
feedback: {}
|
||||
goal:
|
||||
properties: {}
|
||||
type: object
|
||||
result: {}
|
||||
required:
|
||||
- goal
|
||||
title: initialize参数
|
||||
type: object
|
||||
type: UniLabJsonCommandAsync
|
||||
add_solid:
|
||||
feedback:
|
||||
current_status: status
|
||||
progress: progress
|
||||
goal:
|
||||
vessel: vessel
|
||||
reagent: reagent
|
||||
mass: mass
|
||||
mol: mol
|
||||
purpose: purpose
|
||||
event: event
|
||||
rate_spec: rate_spec
|
||||
equiv: equiv
|
||||
ratio: ratio
|
||||
goal_default:
|
||||
vessel: ''
|
||||
reagent: ''
|
||||
mass: ''
|
||||
mol: ''
|
||||
purpose: ''
|
||||
event: ''
|
||||
rate_spec: ''
|
||||
equiv: ''
|
||||
ratio: ''
|
||||
handles: []
|
||||
result:
|
||||
success: success
|
||||
message: message
|
||||
return_info: return_info
|
||||
schema:
|
||||
description: ''
|
||||
properties:
|
||||
feedback:
|
||||
properties:
|
||||
current_status:
|
||||
type: string
|
||||
progress:
|
||||
type: number
|
||||
type: object
|
||||
goal:
|
||||
properties:
|
||||
vessel:
|
||||
type: string
|
||||
reagent:
|
||||
type: string
|
||||
mass:
|
||||
type: string
|
||||
mol:
|
||||
type: string
|
||||
purpose:
|
||||
type: string
|
||||
event:
|
||||
type: string
|
||||
rate_spec:
|
||||
type: string
|
||||
equiv:
|
||||
type: string
|
||||
ratio:
|
||||
type: string
|
||||
type: object
|
||||
result:
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
message:
|
||||
type: string
|
||||
return_info:
|
||||
type: string
|
||||
type: object
|
||||
required:
|
||||
- goal
|
||||
title: AddSolid
|
||||
type: object
|
||||
type: Add # 🔧 使用 Add action type
|
||||
module: unilabos.devices.virtual.virtual_solid_dispenser:VirtualSolidDispenser
|
||||
status_types:
|
||||
status: str
|
||||
current_reagent: str
|
||||
dispensed_amount: float
|
||||
total_operations: int
|
||||
type: python
|
||||
description: Virtual Solid Dispenser for Add Protocol Testing - supports mass and molar additions
|
||||
handles:
|
||||
- data_key: solid_out
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
description: 固体试剂输出口
|
||||
handler_key: SolidOut
|
||||
io_type: source
|
||||
label: SolidOut
|
||||
side: SOUTH
|
||||
- data_key: solid_in
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
description: 固体试剂输入口(连接试剂瓶)
|
||||
handler_key: SolidIn
|
||||
io_type: target
|
||||
label: SolidIn
|
||||
side: NORTH
|
||||
icon: ''
|
||||
init_param_schema:
|
||||
config:
|
||||
properties:
|
||||
max_capacity:
|
||||
default: 100.0
|
||||
type: number
|
||||
precision:
|
||||
default: 0.001
|
||||
type: number
|
||||
config:
|
||||
type: object
|
||||
device_id:
|
||||
type: string
|
||||
required: []
|
||||
type: object
|
||||
data:
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
current_reagent:
|
||||
type: string
|
||||
dispensed_amount:
|
||||
type: number
|
||||
total_operations:
|
||||
type: integer
|
||||
required:
|
||||
- status
|
||||
- current_reagent
|
||||
- dispensed_amount
|
||||
- total_operations
|
||||
type: object
|
||||
version: 0.0.1
|
||||
Reference in New Issue
Block a user