mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-14 11:45:11 +00:00
Compare commits
2 Commits
eac9b8ab3d
...
f791c1a342
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f791c1a342 | ||
|
|
ea60cbe891 |
@@ -170,12 +170,15 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"volume": 1000.0,
|
"max_volume": 1000.0
|
||||||
"reagent": "DMF"
|
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"current_volume": 1000.0,
|
"liquids": [
|
||||||
"reagent_name": "DMF"
|
{
|
||||||
|
"liquid_type": "DMF",
|
||||||
|
"liquid_volume": 1000.0
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -191,12 +194,15 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"volume": 1000.0,
|
"max_volume": 1000.0
|
||||||
"reagent": "ethyl_acetate"
|
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"current_volume": 1000.0,
|
"liquids": [
|
||||||
"reagent_name": "ethyl_acetate"
|
{
|
||||||
|
"liquid_type": "ethyl_acetate",
|
||||||
|
"liquid_volume": 1000.0
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -212,12 +218,15 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"volume": 1000.0,
|
"max_volume": 1000.0
|
||||||
"reagent": "hexane"
|
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"current_volume": 1000.0,
|
"liquids": [
|
||||||
"reagent_name": "hexane"
|
{
|
||||||
|
"liquid_type": "hexane",
|
||||||
|
"liquid_volume": 1000.0
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -233,12 +242,15 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"volume": 1000.0,
|
"max_volume": 1000.0
|
||||||
"reagent": "methanol"
|
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"current_volume": 1000.0,
|
"liquids": [
|
||||||
"reagent_name": "methanol"
|
{
|
||||||
|
"liquid_type": "methanol",
|
||||||
|
"liquid_volume": 1000.0
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -254,12 +266,15 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"volume": 1000.0,
|
"max_volume": 1000.0
|
||||||
"reagent": "water"
|
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"current_volume": 1000.0,
|
"liquids": [
|
||||||
"reagent_name": "water"
|
{
|
||||||
|
"liquid_type": "water",
|
||||||
|
"liquid_volume": 1000.0
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -319,15 +334,15 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"volume": 500.0,
|
"max_volume": 500.0,
|
||||||
"max_temp": 200.0,
|
"max_temp": 200.0,
|
||||||
"min_temp": -20.0,
|
"min_temp": -20.0,
|
||||||
"has_stirrer": true,
|
"has_stirrer": true,
|
||||||
"has_heater": true
|
"has_heater": true
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"current_volume": 0.0,
|
"liquids": [
|
||||||
"current_temp": 25.0
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -404,10 +419,11 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"volume": 2000.0
|
"max_volume": 2000.0
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"current_volume": 0.0
|
"liquids": [
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -423,10 +439,11 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"volume": 2000.0
|
"max_volume": 2000.0
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"current_volume": 0.0
|
"liquids": [
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -632,10 +649,11 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"volume": 250.0
|
"max_volume": 250.0
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"current_volume": 0.0
|
"liquids": [
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -651,10 +669,11 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"volume": 250.0
|
"max_volume": 250.0
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"current_volume": 0.0
|
"liquids": [
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -670,10 +689,11 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"volume": 250.0
|
"max_volume": 250.0
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"current_volume": 0.0
|
"liquids": [
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -712,7 +732,7 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"volume": 500.0,
|
"max_volume": 500.0,
|
||||||
"reagent": "sodium_chloride",
|
"reagent": "sodium_chloride",
|
||||||
"physical_state": "solid"
|
"physical_state": "solid"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -160,8 +160,8 @@ def generate_filter_protocol(
|
|||||||
# 使用pump protocol转移液体到过滤器
|
# 使用pump protocol转移液体到过滤器
|
||||||
transfer_actions = generate_pump_protocol_with_rinsing(
|
transfer_actions = generate_pump_protocol_with_rinsing(
|
||||||
G=G,
|
G=G,
|
||||||
from_vessel=vessel_id, # 🔧 使用 vessel_id
|
from_vessel={"id": vessel_id}, # 🔧 使用 vessel_id
|
||||||
to_vessel=filter_device,
|
to_vessel={"id": filter_device},
|
||||||
volume=0.0, # 转移所有液体
|
volume=0.0, # 转移所有液体
|
||||||
amount="",
|
amount="",
|
||||||
time=0.0,
|
time=0.0,
|
||||||
@@ -212,8 +212,8 @@ def generate_filter_protocol(
|
|||||||
# 构建过滤动作参数
|
# 构建过滤动作参数
|
||||||
debug_print(" ⚙️ 构建过滤参数...")
|
debug_print(" ⚙️ 构建过滤参数...")
|
||||||
filter_kwargs = {
|
filter_kwargs = {
|
||||||
"vessel": filter_device, # 过滤器设备
|
"vessel": {"id": filter_device}, # 过滤器设备
|
||||||
"filtrate_vessel": filtrate_vessel_id, # 滤液容器(可能为空)
|
"filtrate_vessel": {"id": filtrate_vessel_id}, # 滤液容器(可能为空)
|
||||||
"stir": kwargs.get("stir", False),
|
"stir": kwargs.get("stir", False),
|
||||||
"stir_speed": kwargs.get("stir_speed", 0.0),
|
"stir_speed": kwargs.get("stir_speed", 0.0),
|
||||||
"temp": kwargs.get("temp", 25.0),
|
"temp": kwargs.get("temp", 25.0),
|
||||||
@@ -244,7 +244,7 @@ def generate_filter_protocol(
|
|||||||
# === 收集滤液(如果需要)===
|
# === 收集滤液(如果需要)===
|
||||||
debug_print("📍 步骤5: 收集滤液... 💧")
|
debug_print("📍 步骤5: 收集滤液... 💧")
|
||||||
|
|
||||||
if filtrate_vessel:
|
if filtrate_vessel_id and filtrate_vessel_id not in G.neighbors(filter_device):
|
||||||
debug_print(f" 🧪 收集滤液: {filter_device} → {filtrate_vessel_id} 💧")
|
debug_print(f" 🧪 收集滤液: {filter_device} → {filtrate_vessel_id} 💧")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -125,6 +125,29 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
|||||||
"""
|
"""
|
||||||
debug_print(f"🔍 正在查找溶剂 '{solvent}' 的容器... 🧪")
|
debug_print(f"🔍 正在查找溶剂 '{solvent}' 的容器... 🧪")
|
||||||
|
|
||||||
|
# 第四步:通过数据中的试剂信息匹配
|
||||||
|
debug_print(" 🧪 步骤1: 数据试剂信息匹配...")
|
||||||
|
for node_id in G.nodes():
|
||||||
|
debug_print(f"查找 id {node_id}, type={G.nodes[node_id].get('type')}, data={G.nodes[node_id].get('data', {})} 的容器...")
|
||||||
|
if G.nodes[node_id].get('type') == 'container':
|
||||||
|
vessel_data = G.nodes[node_id].get('data', {})
|
||||||
|
|
||||||
|
# 检查 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', []) or vessel_data.get('liquids', [])
|
||||||
|
for liquid in liquids:
|
||||||
|
if isinstance(liquid, dict):
|
||||||
|
liquid_type = (liquid.get('liquid_type') or liquid.get('name', '')).lower()
|
||||||
|
|
||||||
|
if solvent.lower() == liquid_type or solvent.lower() in liquid_type:
|
||||||
|
debug_print(f" 🎉 通过液体类型匹配找到容器: {node_id} (液体类型: {liquid_type}) ✨")
|
||||||
|
return node_id
|
||||||
|
|
||||||
# 构建可能的容器名称
|
# 构建可能的容器名称
|
||||||
possible_names = [
|
possible_names = [
|
||||||
f"flask_{solvent}",
|
f"flask_{solvent}",
|
||||||
@@ -140,14 +163,14 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
|||||||
debug_print(f"📋 候选容器名称: {possible_names[:3]}... (共{len(possible_names)}个) 📝")
|
debug_print(f"📋 候选容器名称: {possible_names[:3]}... (共{len(possible_names)}个) 📝")
|
||||||
|
|
||||||
# 第一步:通过容器名称匹配
|
# 第一步:通过容器名称匹配
|
||||||
debug_print(" 🎯 步骤1: 精确名称匹配...")
|
debug_print(" 🎯 步骤2: 精确名称匹配...")
|
||||||
for vessel_name in possible_names:
|
for vessel_name in possible_names:
|
||||||
if vessel_name in G.nodes():
|
if vessel_name in G.nodes():
|
||||||
debug_print(f" 🎉 通过名称匹配找到容器: {vessel_name} ✨")
|
debug_print(f" 🎉 通过名称匹配找到容器: {vessel_name} ✨")
|
||||||
return vessel_name
|
return vessel_name
|
||||||
|
|
||||||
# 第二步:通过模糊匹配(节点ID和名称)
|
# 第二步:通过模糊匹配(节点ID和名称)
|
||||||
debug_print(" 🔍 步骤2: 模糊名称匹配...")
|
debug_print(" 🔍 步骤3: 模糊名称匹配...")
|
||||||
for node_id in G.nodes():
|
for node_id in G.nodes():
|
||||||
if G.nodes[node_id].get('type') == 'container':
|
if G.nodes[node_id].get('type') == 'container':
|
||||||
node_name = G.nodes[node_id].get('name', '').lower()
|
node_name = G.nodes[node_id].get('name', '').lower()
|
||||||
@@ -157,7 +180,7 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
|||||||
return node_id
|
return node_id
|
||||||
|
|
||||||
# 第三步:通过配置中的试剂信息匹配
|
# 第三步:通过配置中的试剂信息匹配
|
||||||
debug_print(" 🧪 步骤3: 配置试剂信息匹配...")
|
debug_print(" 🧪 步骤4: 配置试剂信息匹配...")
|
||||||
for node_id in G.nodes():
|
for node_id in G.nodes():
|
||||||
if G.nodes[node_id].get('type') == 'container':
|
if G.nodes[node_id].get('type') == 'container':
|
||||||
# 检查 config 中的 reagent 字段
|
# 检查 config 中的 reagent 字段
|
||||||
@@ -168,28 +191,6 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
|||||||
debug_print(f" 🎉 通过config.reagent匹配找到容器: {node_id} (试剂: {config_reagent}) ✨")
|
debug_print(f" 🎉 通过config.reagent匹配找到容器: {node_id} (试剂: {config_reagent}) ✨")
|
||||||
return node_id
|
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', {})
|
|
||||||
|
|
||||||
# 检查 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()
|
|
||||||
|
|
||||||
if solvent.lower() in liquid_type:
|
|
||||||
debug_print(f" 🎉 通过液体类型匹配找到容器: {node_id} (液体类型: {liquid_type}) ✨")
|
|
||||||
return node_id
|
|
||||||
|
|
||||||
# 第五步:部分匹配(如果前面都没找到)
|
# 第五步:部分匹配(如果前面都没找到)
|
||||||
debug_print(" 🔍 步骤5: 部分匹配...")
|
debug_print(" 🔍 步骤5: 部分匹配...")
|
||||||
for node_id in G.nodes():
|
for node_id in G.nodes():
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import logging
|
|||||||
import time as time_module
|
import time as time_module
|
||||||
from typing import Dict, Any, Optional
|
from typing import Dict, Any, Optional
|
||||||
|
|
||||||
|
from unilabos.compile.utils.vessel_parser import get_vessel
|
||||||
|
|
||||||
|
|
||||||
class VirtualFilter:
|
class VirtualFilter:
|
||||||
"""Virtual filter device - 完全按照 Filter.action 规范 🌊"""
|
"""Virtual filter device - 完全按照 Filter.action 规范 🌊"""
|
||||||
@@ -40,7 +42,6 @@ class VirtualFilter:
|
|||||||
"progress": 0.0, # Filter.action feedback
|
"progress": 0.0, # Filter.action feedback
|
||||||
"current_temp": 25.0, # Filter.action feedback
|
"current_temp": 25.0, # Filter.action feedback
|
||||||
"filtered_volume": 0.0, # Filter.action feedback
|
"filtered_volume": 0.0, # Filter.action feedback
|
||||||
"current_status": "Ready for filtration", # Filter.action feedback
|
|
||||||
"message": "Ready for filtration"
|
"message": "Ready for filtration"
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -52,9 +53,7 @@ class VirtualFilter:
|
|||||||
self.logger.info(f"🧹 清理虚拟过滤器 {self.device_id} 🔚")
|
self.logger.info(f"🧹 清理虚拟过滤器 {self.device_id} 🔚")
|
||||||
|
|
||||||
self.data.update({
|
self.data.update({
|
||||||
"status": "Offline",
|
"status": "Offline"
|
||||||
"current_status": "System offline",
|
|
||||||
"message": "System offline"
|
|
||||||
})
|
})
|
||||||
|
|
||||||
self.logger.info(f"✅ 过滤器 {self.device_id} 清理完成 💤")
|
self.logger.info(f"✅ 过滤器 {self.device_id} 清理完成 💤")
|
||||||
@@ -62,8 +61,8 @@ class VirtualFilter:
|
|||||||
|
|
||||||
async def filter(
|
async def filter(
|
||||||
self,
|
self,
|
||||||
vessel: str,
|
vessel: dict,
|
||||||
filtrate_vessel: str = "",
|
filtrate_vessel: dict = {},
|
||||||
stir: bool = False,
|
stir: bool = False,
|
||||||
stir_speed: float = 300.0,
|
stir_speed: float = 300.0,
|
||||||
temp: float = 25.0,
|
temp: float = 25.0,
|
||||||
@@ -71,6 +70,8 @@ class VirtualFilter:
|
|||||||
volume: float = 0.0
|
volume: float = 0.0
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Execute filter action - 完全按照 Filter.action 参数 🌊"""
|
"""Execute filter action - 完全按照 Filter.action 参数 🌊"""
|
||||||
|
vessel_id, _ = get_vessel(vessel)
|
||||||
|
filtrate_vessel_id, _ = get_vessel(filtrate_vessel) if filtrate_vessel else (f"{vessel_id}_filtrate", {})
|
||||||
|
|
||||||
# 🔧 新增:温度自动调整
|
# 🔧 新增:温度自动调整
|
||||||
original_temp = temp
|
original_temp = temp
|
||||||
@@ -81,7 +82,7 @@ class VirtualFilter:
|
|||||||
temp = 4.0 # 小于4度自动设置为4度
|
temp = 4.0 # 小于4度自动设置为4度
|
||||||
self.logger.info(f"🌡️ 温度自动调整: {original_temp}°C → {temp}°C (最低温度) ❄️")
|
self.logger.info(f"🌡️ 温度自动调整: {original_temp}°C → {temp}°C (最低温度) ❄️")
|
||||||
|
|
||||||
self.logger.info(f"🌊 开始过滤操作: {vessel} → {filtrate_vessel} 🚰")
|
self.logger.info(f"🌊 开始过滤操作: {vessel_id} → {filtrate_vessel_id} 🚰")
|
||||||
self.logger.info(f" 🌪️ 搅拌: {stir} ({stir_speed} RPM)")
|
self.logger.info(f" 🌪️ 搅拌: {stir} ({stir_speed} RPM)")
|
||||||
self.logger.info(f" 🌡️ 温度: {temp}°C")
|
self.logger.info(f" 🌡️ 温度: {temp}°C")
|
||||||
self.logger.info(f" 💧 体积: {volume}mL")
|
self.logger.info(f" 💧 体积: {volume}mL")
|
||||||
@@ -93,7 +94,6 @@ class VirtualFilter:
|
|||||||
self.logger.error(f"❌ {error_msg}")
|
self.logger.error(f"❌ {error_msg}")
|
||||||
self.data.update({
|
self.data.update({
|
||||||
"status": f"Error: 温度超出范围 ⚠️",
|
"status": f"Error: 温度超出范围 ⚠️",
|
||||||
"current_status": f"Error: 温度超出范围 ⚠️",
|
|
||||||
"message": error_msg
|
"message": error_msg
|
||||||
})
|
})
|
||||||
return False
|
return False
|
||||||
@@ -103,7 +103,6 @@ class VirtualFilter:
|
|||||||
self.logger.error(f"❌ {error_msg}")
|
self.logger.error(f"❌ {error_msg}")
|
||||||
self.data.update({
|
self.data.update({
|
||||||
"status": f"Error: 搅拌速度超出范围 ⚠️",
|
"status": f"Error: 搅拌速度超出范围 ⚠️",
|
||||||
"current_status": f"Error: 搅拌速度超出范围 ⚠️",
|
|
||||||
"message": error_msg
|
"message": error_msg
|
||||||
})
|
})
|
||||||
return False
|
return False
|
||||||
@@ -112,8 +111,7 @@ class VirtualFilter:
|
|||||||
error_msg = f"💧 过滤体积 {volume} mL 超出范围 (0-{self._max_volume} mL) ⚠️"
|
error_msg = f"💧 过滤体积 {volume} mL 超出范围 (0-{self._max_volume} mL) ⚠️"
|
||||||
self.logger.error(f"❌ {error_msg}")
|
self.logger.error(f"❌ {error_msg}")
|
||||||
self.data.update({
|
self.data.update({
|
||||||
"status": f"Error: 体积超出范围 ⚠️",
|
"status": f"Error",
|
||||||
"current_status": f"Error: 体积超出范围 ⚠️",
|
|
||||||
"message": error_msg
|
"message": error_msg
|
||||||
})
|
})
|
||||||
return False
|
return False
|
||||||
@@ -123,12 +121,11 @@ class VirtualFilter:
|
|||||||
self.logger.info(f"🚀 开始过滤 {filter_volume}mL 液体 💧")
|
self.logger.info(f"🚀 开始过滤 {filter_volume}mL 液体 💧")
|
||||||
|
|
||||||
self.data.update({
|
self.data.update({
|
||||||
"status": f"🌊 过滤中: {vessel}",
|
"status": f"Running",
|
||||||
"current_temp": temp,
|
"current_temp": temp,
|
||||||
"filtered_volume": 0.0,
|
"filtered_volume": 0.0,
|
||||||
"progress": 0.0,
|
"progress": 0.0,
|
||||||
"current_status": f"🌊 Filtering {vessel} → {filtrate_vessel}",
|
"message": f"🚀 Starting filtration: {vessel_id} → {filtrate_vessel_id}"
|
||||||
"message": f"🚀 Starting filtration: {vessel} → {filtrate_vessel}"
|
|
||||||
})
|
})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -164,8 +161,7 @@ class VirtualFilter:
|
|||||||
"progress": progress, # Filter.action feedback
|
"progress": progress, # Filter.action feedback
|
||||||
"current_temp": temp, # Filter.action feedback
|
"current_temp": temp, # Filter.action feedback
|
||||||
"filtered_volume": current_filtered, # Filter.action feedback
|
"filtered_volume": current_filtered, # Filter.action feedback
|
||||||
"current_status": f"🌊 Filtering: {progress:.1f}% complete", # Filter.action feedback
|
"status": "Running",
|
||||||
"status": status_msg,
|
|
||||||
"message": f"🌊 Filtering: {progress:.1f}% complete, {current_filtered:.1f}mL filtered"
|
"message": f"🌊 Filtering: {progress:.1f}% complete, {current_filtered:.1f}mL filtered"
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -190,11 +186,10 @@ class VirtualFilter:
|
|||||||
"progress": 100.0, # Filter.action feedback
|
"progress": 100.0, # Filter.action feedback
|
||||||
"current_temp": final_temp, # Filter.action feedback
|
"current_temp": final_temp, # Filter.action feedback
|
||||||
"filtered_volume": filter_volume, # Filter.action feedback
|
"filtered_volume": filter_volume, # Filter.action feedback
|
||||||
"current_status": f"✅ Filtration completed: {filter_volume}mL", # Filter.action feedback
|
"message": f"✅ Filtration completed: {filter_volume}mL filtered from {vessel_id}"
|
||||||
"message": f"✅ Filtration completed: {filter_volume}mL filtered from {vessel}"
|
|
||||||
})
|
})
|
||||||
|
|
||||||
self.logger.info(f"🎉 过滤完成! 💧 {filter_volume}mL 从 {vessel} 过滤到 {filtrate_vessel} ✨")
|
self.logger.info(f"🎉 过滤完成! 💧 {filter_volume}mL 从 {vessel_id} 过滤到 {filtrate_vessel_id} ✨")
|
||||||
self.logger.info(f"📊 最终状态: 温度 {final_temp}°C | 进度 100% | 体积 {filter_volume}mL 🏁")
|
self.logger.info(f"📊 最终状态: 温度 {final_temp}°C | 进度 100% | 体积 {filter_volume}mL 🏁")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -202,8 +197,7 @@ class VirtualFilter:
|
|||||||
error_msg = f"过滤过程中发生错误: {str(e)} 💥"
|
error_msg = f"过滤过程中发生错误: {str(e)} 💥"
|
||||||
self.logger.error(f"❌ {error_msg}")
|
self.logger.error(f"❌ {error_msg}")
|
||||||
self.data.update({
|
self.data.update({
|
||||||
"status": f"❌ 过滤错误: {str(e)}",
|
"status": f"Error",
|
||||||
"current_status": f"❌ Filtration failed: {str(e)}",
|
|
||||||
"message": f"❌ Filtration failed: {str(e)}"
|
"message": f"❌ Filtration failed: {str(e)}"
|
||||||
})
|
})
|
||||||
return False
|
return False
|
||||||
@@ -228,11 +222,6 @@ class VirtualFilter:
|
|||||||
"""Filter.action feedback 字段 💧"""
|
"""Filter.action feedback 字段 💧"""
|
||||||
return self.data.get("filtered_volume", 0.0)
|
return self.data.get("filtered_volume", 0.0)
|
||||||
|
|
||||||
@property
|
|
||||||
def current_status(self) -> str:
|
|
||||||
"""Filter.action feedback 字段 📋"""
|
|
||||||
return self.data.get("current_status", "")
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def message(self) -> str:
|
def message(self) -> str:
|
||||||
return self.data.get("message", "")
|
return self.data.get("message", "")
|
||||||
|
|||||||
@@ -21,19 +21,6 @@ class VirtualMultiwayValve:
|
|||||||
self._current_position = 0 # 默认在0号位(transfer pump位置)
|
self._current_position = 0 # 默认在0号位(transfer pump位置)
|
||||||
self._target_position = 0
|
self._target_position = 0
|
||||||
|
|
||||||
# 位置映射说明
|
|
||||||
self.position_map = {
|
|
||||||
0: "transfer_pump", # 0号位连接转移泵
|
|
||||||
1: "port_1", # 1号位
|
|
||||||
2: "port_2", # 2号位
|
|
||||||
3: "port_3", # 3号位
|
|
||||||
4: "port_4", # 4号位
|
|
||||||
5: "port_5", # 5号位
|
|
||||||
6: "port_6", # 6号位
|
|
||||||
7: "port_7", # 7号位
|
|
||||||
8: "port_8" # 8号位
|
|
||||||
}
|
|
||||||
|
|
||||||
print(f"🔄 === 虚拟多通阀门已创建 === ✨")
|
print(f"🔄 === 虚拟多通阀门已创建 === ✨")
|
||||||
print(f"🎯 端口: {port} | 📊 位置范围: 0-{self.max_positions} | 🏠 初始位置: 0 (transfer_pump)")
|
print(f"🎯 端口: {port} | 📊 位置范围: 0-{self.max_positions} | 🏠 初始位置: 0 (transfer_pump)")
|
||||||
self.logger.info(f"🔧 多通阀门初始化: 端口={port}, 最大位置={self.max_positions}")
|
self.logger.info(f"🔧 多通阀门初始化: 端口={port}, 最大位置={self.max_positions}")
|
||||||
@@ -60,7 +47,7 @@ class VirtualMultiwayValve:
|
|||||||
|
|
||||||
def get_current_port(self) -> str:
|
def get_current_port(self) -> str:
|
||||||
"""获取当前连接的端口名称 🔌"""
|
"""获取当前连接的端口名称 🔌"""
|
||||||
return self.position_map.get(self._current_position, "unknown")
|
return self._current_position
|
||||||
|
|
||||||
def set_position(self, command: Union[int, str]):
|
def set_position(self, command: Union[int, str]):
|
||||||
"""
|
"""
|
||||||
@@ -115,7 +102,7 @@ class VirtualMultiwayValve:
|
|||||||
old_position = self._current_position
|
old_position = self._current_position
|
||||||
old_port = self.get_current_port()
|
old_port = self.get_current_port()
|
||||||
|
|
||||||
self.logger.info(f"🔄 阀门切换: {old_position}({old_port}) → {pos}({self.position_map.get(pos, 'unknown')}) {pos_emoji}")
|
self.logger.info(f"🔄 阀门切换: {old_position}({old_port}) → {pos} {pos_emoji}")
|
||||||
|
|
||||||
self._status = "Busy"
|
self._status = "Busy"
|
||||||
self._valve_state = "Moving"
|
self._valve_state = "Moving"
|
||||||
@@ -190,6 +177,17 @@ class VirtualMultiwayValve:
|
|||||||
"""获取阀门位置 - 兼容性方法 📍"""
|
"""获取阀门位置 - 兼容性方法 📍"""
|
||||||
return self._current_position
|
return self._current_position
|
||||||
|
|
||||||
|
def set_valve_position(self, command: Union[int, str]):
|
||||||
|
"""
|
||||||
|
设置阀门位置 - 兼容pump_protocol调用 🎯
|
||||||
|
这是set_position的别名方法,用于兼容pump_protocol.py
|
||||||
|
|
||||||
|
Args:
|
||||||
|
command: 目标位置 (0-8) 或位置字符串
|
||||||
|
"""
|
||||||
|
# 删除debug日志:self.logger.debug(f"🎯 兼容性调用: set_valve_position({command})")
|
||||||
|
return self.set_position(command)
|
||||||
|
|
||||||
def is_at_position(self, position: int) -> bool:
|
def is_at_position(self, position: int) -> bool:
|
||||||
"""检查是否在指定位置 🎯"""
|
"""检查是否在指定位置 🎯"""
|
||||||
result = self._current_position == position
|
result = self._current_position == position
|
||||||
@@ -210,17 +208,6 @@ class VirtualMultiwayValve:
|
|||||||
# 删除debug日志:self.logger.debug(f"🔌 端口{port_number}检查: {port_status} (当前位置: {self._current_position})")
|
# 删除debug日志:self.logger.debug(f"🔌 端口{port_number}检查: {port_status} (当前位置: {self._current_position})")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_available_positions(self) -> list:
|
|
||||||
"""获取可用位置列表 📋"""
|
|
||||||
positions = list(range(0, self.max_positions + 1))
|
|
||||||
# 删除debug日志:self.logger.debug(f"📋 可用位置: {positions}")
|
|
||||||
return positions
|
|
||||||
|
|
||||||
def get_available_ports(self) -> Dict[int, str]:
|
|
||||||
"""获取可用端口映射 🗺️"""
|
|
||||||
# 删除debug日志:self.logger.debug(f"🗺️ 端口映射: {self.position_map}")
|
|
||||||
return self.position_map.copy()
|
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"""重置阀门到transfer pump位置(0号位)🔄"""
|
"""重置阀门到transfer pump位置(0号位)🔄"""
|
||||||
self.logger.info(f"🔄 重置阀门到泵位置...")
|
self.logger.info(f"🔄 重置阀门到泵位置...")
|
||||||
@@ -259,17 +246,6 @@ class VirtualMultiwayValve:
|
|||||||
|
|
||||||
return f"🔄 VirtualMultiwayValve({status_emoji} 位置: {self._current_position}/{self.max_positions}, 端口: {current_port}, 状态: {self._status})"
|
return f"🔄 VirtualMultiwayValve({status_emoji} 位置: {self._current_position}/{self.max_positions}, 端口: {current_port}, 状态: {self._status})"
|
||||||
|
|
||||||
def set_valve_position(self, command: Union[int, str]):
|
|
||||||
"""
|
|
||||||
设置阀门位置 - 兼容pump_protocol调用 🎯
|
|
||||||
这是set_position的别名方法,用于兼容pump_protocol.py
|
|
||||||
|
|
||||||
Args:
|
|
||||||
command: 目标位置 (0-8) 或位置字符串
|
|
||||||
"""
|
|
||||||
# 删除debug日志:self.logger.debug(f"🎯 兼容性调用: set_valve_position({command})")
|
|
||||||
return self.set_position(command)
|
|
||||||
|
|
||||||
|
|
||||||
# 使用示例
|
# 使用示例
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -291,10 +267,6 @@ if __name__ == "__main__":
|
|||||||
print(f"\n🔌 切换到2号位: {valve.set_to_port(2)}")
|
print(f"\n🔌 切换到2号位: {valve.set_to_port(2)}")
|
||||||
print(f"📍 当前状态: {valve}")
|
print(f"📍 当前状态: {valve}")
|
||||||
|
|
||||||
# 显示所有可用位置
|
|
||||||
print(f"\n📋 可用位置: {valve.get_available_positions()}")
|
|
||||||
print(f"🗺️ 端口映射: {valve.get_available_ports()}")
|
|
||||||
|
|
||||||
# 测试切换功能
|
# 测试切换功能
|
||||||
print(f"\n🔄 智能切换测试:")
|
print(f"\n🔄 智能切换测试:")
|
||||||
print(f"当前位置: {valve._current_position}")
|
print(f"当前位置: {valve._current_position}")
|
||||||
|
|||||||
@@ -99,8 +99,8 @@ class VirtualRotavap:
|
|||||||
self.logger.error(f"❌ 时间参数类型无效: {type(time)},使用默认值180.0秒")
|
self.logger.error(f"❌ 时间参数类型无效: {type(time)},使用默认值180.0秒")
|
||||||
time = 180.0
|
time = 180.0
|
||||||
|
|
||||||
# 确保time是float类型
|
# 确保time是float类型; 并加速
|
||||||
time = float(time)
|
time = float(time) / 10.0
|
||||||
|
|
||||||
# 🔧 简化处理:如果vessel就是设备自己,直接操作
|
# 🔧 简化处理:如果vessel就是设备自己,直接操作
|
||||||
if vessel == self.device_id:
|
if vessel == self.device_id:
|
||||||
|
|||||||
@@ -48,20 +48,6 @@ class VirtualSolenoidValve:
|
|||||||
"""获取阀门位置状态"""
|
"""获取阀门位置状态"""
|
||||||
return "OPEN" if self._is_open else "CLOSED"
|
return "OPEN" if self._is_open else "CLOSED"
|
||||||
|
|
||||||
@property
|
|
||||||
def state(self) -> dict:
|
|
||||||
"""获取阀门完整状态"""
|
|
||||||
return {
|
|
||||||
"device_id": self.device_id,
|
|
||||||
"port": self.port,
|
|
||||||
"voltage": self.voltage,
|
|
||||||
"response_time": self.response_time,
|
|
||||||
"is_open": self._is_open,
|
|
||||||
"valve_state": self._valve_state,
|
|
||||||
"status": self._status,
|
|
||||||
"position": self.valve_position
|
|
||||||
}
|
|
||||||
|
|
||||||
async def set_valve_position(self, command: str = None, **kwargs):
|
async def set_valve_position(self, command: str = None, **kwargs):
|
||||||
"""
|
"""
|
||||||
设置阀门位置 - ROS动作接口
|
设置阀门位置 - ROS动作接口
|
||||||
|
|||||||
@@ -2161,8 +2161,6 @@ virtual_multiway_valve:
|
|||||||
type: SendCmd
|
type: SendCmd
|
||||||
module: unilabos.devices.virtual.virtual_multiway_valve:VirtualMultiwayValve
|
module: unilabos.devices.virtual.virtual_multiway_valve:VirtualMultiwayValve
|
||||||
status_types:
|
status_types:
|
||||||
available_ports: dict
|
|
||||||
available_positions: list
|
|
||||||
current_port: str
|
current_port: str
|
||||||
current_position: int
|
current_position: int
|
||||||
flow_path: str
|
flow_path: str
|
||||||
@@ -2268,10 +2266,6 @@ virtual_multiway_valve:
|
|||||||
type: object
|
type: object
|
||||||
data:
|
data:
|
||||||
properties:
|
properties:
|
||||||
available_ports:
|
|
||||||
type: object
|
|
||||||
available_positions:
|
|
||||||
type: array
|
|
||||||
current_port:
|
current_port:
|
||||||
type: string
|
type: string
|
||||||
current_position:
|
current_position:
|
||||||
@@ -2293,8 +2287,6 @@ virtual_multiway_valve:
|
|||||||
- target_position
|
- target_position
|
||||||
- current_port
|
- current_port
|
||||||
- valve_position
|
- valve_position
|
||||||
- available_positions
|
|
||||||
- available_ports
|
|
||||||
- flow_path
|
- flow_path
|
||||||
type: object
|
type: object
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
@@ -3775,7 +3767,6 @@ virtual_solenoid_valve:
|
|||||||
module: unilabos.devices.virtual.virtual_solenoid_valve:VirtualSolenoidValve
|
module: unilabos.devices.virtual.virtual_solenoid_valve:VirtualSolenoidValve
|
||||||
status_types:
|
status_types:
|
||||||
is_open: bool
|
is_open: bool
|
||||||
state: dict
|
|
||||||
status: str
|
status: str
|
||||||
valve_position: str
|
valve_position: str
|
||||||
valve_state: str
|
valve_state: str
|
||||||
@@ -3813,8 +3804,6 @@ virtual_solenoid_valve:
|
|||||||
properties:
|
properties:
|
||||||
is_open:
|
is_open:
|
||||||
type: boolean
|
type: boolean
|
||||||
state:
|
|
||||||
type: object
|
|
||||||
status:
|
status:
|
||||||
type: string
|
type: string
|
||||||
valve_position:
|
valve_position:
|
||||||
@@ -3826,7 +3815,6 @@ virtual_solenoid_valve:
|
|||||||
- valve_state
|
- valve_state
|
||||||
- is_open
|
- is_open
|
||||||
- valve_position
|
- valve_position
|
||||||
- state
|
|
||||||
type: object
|
type: object
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
virtual_solid_dispenser:
|
virtual_solid_dispenser:
|
||||||
|
|||||||
@@ -395,6 +395,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
|||||||
if "data" not in resource:
|
if "data" not in resource:
|
||||||
resource["data"] = {}
|
resource["data"] = {}
|
||||||
resource["data"].update(json.loads(container_instance.data))
|
resource["data"].update(json.loads(container_instance.data))
|
||||||
|
request.resources[0].name = resource["name"]
|
||||||
logger.info(f"更新物料{container_query_dict['name']}的数据{resource['data']} dict")
|
logger.info(f"更新物料{container_query_dict['name']}的数据{resource['data']} dict")
|
||||||
else:
|
else:
|
||||||
logger.info(f"更新物料{container_query_dict['name']}出现不支持的数据类型{type(resource)} {resource}")
|
logger.info(f"更新物料{container_query_dict['name']}出现不支持的数据类型{type(resource)} {resource}")
|
||||||
|
|||||||
@@ -404,13 +404,14 @@ class HostNode(BaseROS2DeviceNode):
|
|||||||
class_name: str,
|
class_name: str,
|
||||||
parent: str,
|
parent: str,
|
||||||
bind_locations: Point,
|
bind_locations: Point,
|
||||||
liquid_input_slot: list[int],
|
liquid_input_slot: list[int] = [],
|
||||||
liquid_type: list[str],
|
liquid_type: list[str] = [],
|
||||||
liquid_volume: list[int],
|
liquid_volume: list[int] = [],
|
||||||
slot_on_deck: str,
|
slot_on_deck: str = "",
|
||||||
):
|
):
|
||||||
# 暂不支持多对同名父子同时存在
|
# 暂不支持多对同名父子同时存在
|
||||||
res_creation_input = {
|
res_creation_input = {
|
||||||
|
"id": res_id.split("/")[-1],
|
||||||
"name": res_id.split("/")[-1],
|
"name": res_id.split("/")[-1],
|
||||||
"class": class_name,
|
"class": class_name,
|
||||||
"parent": parent.split("/")[-1],
|
"parent": parent.split("/")[-1],
|
||||||
@@ -424,8 +425,10 @@ class HostNode(BaseROS2DeviceNode):
|
|||||||
res_creation_input.update(
|
res_creation_input.update(
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
|
"liquids": [{
|
||||||
"liquid_type": liquid_type[0] if liquid_type else None,
|
"liquid_type": liquid_type[0] if liquid_type else None,
|
||||||
"liquid_volume": liquid_volume[0] if liquid_volume else None,
|
"liquid_volume": liquid_volume[0] if liquid_volume else None,
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user