This commit is contained in:
Guangxin Zhang
2025-07-19 13:18:52 +08:00
20 changed files with 272 additions and 84 deletions

1
.gitignore vendored
View File

@@ -242,4 +242,3 @@ unilabos/device_mesh/view_robot.rviz
**/.certs
local_test2.py
ros-humble-unilabos-msgs-0.9.13-h6403a04_5.tar.bz2
ros-humble-unilabos-msgs-0.9.12-h6403a04_5.tar.bz2

View File

@@ -49,7 +49,7 @@ conda env update --file unilabos-[YOUR_OS].yml -n environment_name
# Currently, you need to install the `unilabos_msgs` package
# You can download the system-specific package from the Release page
conda install ros-humble-unilabos-msgs-0.9.13-xxxxx.tar.bz2
conda install ros-humble-unilabos-msgs-0.9.14-xxxxx.tar.bz2
# Install PyLabRobot and other prerequisites
git clone https://github.com/PyLabRobot/pylabrobot plr_repo

View File

@@ -49,7 +49,7 @@ conda env update --file unilabos-[YOUR_OS].yml -n 环境名
# 现阶段,需要安装 `unilabos_msgs` 包
# 可以前往 Release 页面下载系统对应的包进行安装
conda install ros-humble-unilabos-msgs-0.9.13-xxxxx.tar.bz2
conda install ros-humble-unilabos-msgs-0.9.14-xxxxx.tar.bz2
# 安装PyLabRobot等前置
git clone https://github.com/PyLabRobot/pylabrobot plr_repo

View File

@@ -1,6 +1,6 @@
package:
name: ros-humble-unilabos-msgs
version: 0.9.13
version: 0.9.14
source:
path: ../../unilabos_msgs
folder: ros-humble-unilabos-msgs/src/work

View File

@@ -1,6 +1,6 @@
package:
name: unilabos
version: "0.9.13"
version: "0.9.14"
source:
path: ../..

View File

@@ -4,7 +4,7 @@ package_name = 'unilabos'
setup(
name=package_name,
version='0.9.13',
version='0.9.14',
packages=find_packages(),
include_package_data=True,
install_requires=['setuptools'],

View File

@@ -21,7 +21,7 @@
"timeout": 10.0,
"axis": "Right",
"channel_num": 1,
"setup": false,
"setup": true,
"debug": false,
"simulator": false,
"matrix_id": "fd383e6d-2d0e-40b5-9c01-1b2870b1f1b1"

View File

@@ -18,10 +18,22 @@ def register_devices_and_resources(mqtt_client, lab_registry):
mqtt_client.publish_registry(device_info["id"], device_info, False)
logger.debug(f"[UniLab Register] 注册设备: {device_info['id']}")
# 注册资源信息
# 注册资源信息 - 使用HTTP方式
from unilabos.app.web.client import http_client
resources_to_register = {}
for resource_info in lab_registry.obtain_registry_resource_info():
mqtt_client.publish_registry(resource_info["id"], resource_info, False)
logger.debug(f"[UniLab Register] 注册资源: {resource_info['id']}")
resources_to_register[resource_info["id"]] = resource_info
logger.debug(f"[UniLab Register] 准备注册资源: {resource_info['id']}")
if resources_to_register:
start_time = time.time()
response = http_client.resource_registry(resources_to_register)
cost_time = time.time() - start_time
if response.status_code in [200, 201]:
logger.info(f"[UniLab Register] 成功通过HTTP注册 {len(resources_to_register)} 个资源 {cost_time}ms")
else:
logger.error(f"[UniLab Register] HTTP注册资源失败: {response.status_code}, {response.text} {cost_time}ms")
time.sleep(10)
@@ -53,11 +65,9 @@ def main():
help="是否补全注册表",
)
args = parser.parse_args()
load_config_from_file(args.config)
# 构建注册表
build_registry(args.registry, args.complete_registry)
load_config_from_file(args.config)
from unilabos.app.mq import mqtt_client
# 连接mqtt
@@ -70,4 +80,4 @@ def main():
if __name__ == "__main__":
main()
main()

View File

@@ -40,8 +40,9 @@ class HTTPClient:
Returns:
Response: API响应对象
"""
database_param = 1 if database_process_later else 0
response = requests.post(
f"{self.remote_addr}/lab/resource/edge/batch_create/?database_process_later={1 if database_process_later else 0}",
f"{self.remote_addr}/lab/resource/edge/batch_create/?database_process_later={database_param}",
json=resources,
headers={"Authorization": f"lab {self.auth}"},
timeout=100,
@@ -149,6 +150,26 @@ class HTTPClient:
)
return response
def resource_registry(self, registry_data: Dict[str, Any]) -> requests.Response:
"""
注册资源到服务器
Args:
registry_data: 注册表数据,格式为 {resource_id: resource_info}
Returns:
Response: API响应对象
"""
response = requests.post(
f"{self.remote_addr}/lab/registry/",
json=registry_data,
headers={"Authorization": f"lab {self.auth}"},
timeout=30,
)
if response.status_code not in [200, 201]:
logger.error(f"注册资源失败: {response.status_code}, {response.text}")
return response
# 创建默认客户端实例
http_client = HTTPClient()

View File

@@ -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 = list(vessel.values())[0].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(f"❌ vessel 参数无效必须包含id字段或直接提供容器ID. vessel: {vessel}")
raise ValueError("vessel 参数无效必须包含id字段或直接提供容器ID")
debug_print("=" * 60)
debug_print("🧪 开始生成pH调节协议")

View File

@@ -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)

View File

@@ -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}' 对应的容器")

View File

@@ -149,7 +149,7 @@ def extract_vessel_id(vessel: Union[str, dict]) -> str:
str: vessel_id
"""
if isinstance(vessel, dict):
vessel_id = vessel.get("id", "")
vessel_id = list(vessel.values())[0].get("id", "")
debug_print(f"🔧 从vessel字典提取ID: {vessel_id}")
return vessel_id
elif isinstance(vessel, str):

View File

@@ -165,7 +165,7 @@ def extract_vessel_id(vessel: Union[str, dict]) -> str:
str: vessel_id
"""
if isinstance(vessel, dict):
vessel_id = vessel.get("id", "")
vessel_id = list(vessel.values())[0].get("id", "")
debug_print(f"🔧 从vessel字典提取ID: {vessel_id}")
return vessel_id
elif isinstance(vessel, str):

View File

@@ -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)

View File

@@ -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({

View File

@@ -245,8 +245,13 @@ workstation:
feedback: {}
goal:
amount: amount
equiv: equiv
event: event
mass: mass
mol: mol
purpose: purpose
rate_spec: rate_spec
ratio: ratio
reagent: reagent
stir: stir
stir_speed: stir_speed
@@ -469,7 +474,12 @@ workstation:
goal:
ph_value: ph_value
reagent: reagent
settling_time: settling_time
stir: stir
stir_speed: stir_speed
stir_time: stir_time
vessel: vessel
volume: volume
goal_default:
ph_value: 0.0
reagent: ''
@@ -511,6 +521,8 @@ workstation:
data_type: resource
handler_key: VesselOut
label: Vessel
placeholder_keys:
vessel: unilabos_resources
result: {}
schema:
description: ''
@@ -674,6 +686,8 @@ workstation:
data_type: resource
handler_key: VesselOut
label: Vessel
placeholder_keys:
vessel: unilabos_resources
result: {}
schema:
description: ''
@@ -853,6 +867,8 @@ workstation:
data_type: resource
handler_key: VesselOut
label: Vessel
placeholder_keys:
vessel: unilabos_resources
result: {}
schema:
description: ''
@@ -1060,6 +1076,8 @@ workstation:
data_type: resource
handler_key: VesselOut
label: Vessel
placeholder_keys:
vessel: unilabos_resources
result: {}
schema:
description: ''
@@ -1191,6 +1209,10 @@ workstation:
feedback: {}
goal:
amount: amount
event: event
mass: mass
mol: mol
reagent: reagent
solvent: solvent
stir_speed: stir_speed
temp: temp
@@ -1246,6 +1268,8 @@ workstation:
data_type: resource
handler_key: VesselOut
label: Vessel
placeholder_keys:
vessel: unilabos_resources
result: {}
schema:
description: ''
@@ -1429,6 +1453,8 @@ workstation:
data_type: resource
handler_key: VesselOut
label: Vessel
placeholder_keys:
vessel: unilabos_resources
result: {}
schema:
description: ''
@@ -1585,6 +1611,8 @@ workstation:
data_type: resource
handler_key: VesselOut
label: Vessel
placeholder_keys:
vessel: unilabos_resources
result: {}
schema:
description: ''
@@ -1778,6 +1806,8 @@ workstation:
data_type: resource
handler_key: vessel_out
label: Evaporation Vessel
placeholder_keys:
vessel: unilabos_nodes
result: {}
schema:
description: ''
@@ -2014,6 +2044,9 @@ workstation:
data_type: resource
handler_key: filtrate_out
label: Filtrate Vessel
placeholder_keys:
filtrate_vessel: unilabos_resources
vessel: unilabos_nodes
result: {}
schema:
description: ''
@@ -2325,6 +2358,9 @@ workstation:
data_type: resource
handler_key: ToVesselOut
label: To Vessel
placeholder_keys:
from_vessel: unilabos_resources
to_vessel: unilabos_resources
result: {}
schema:
description: ''
@@ -2656,6 +2692,8 @@ workstation:
data_type: resource
handler_key: VesselOut
label: Vessel
placeholder_keys:
vessel: unilabos_resources
result: {}
schema:
description: ''
@@ -2835,6 +2873,8 @@ workstation:
data_type: resource
handler_key: VesselOut
label: Vessel
placeholder_keys:
vessel: unilabos_resources
result: {}
schema:
description: ''
@@ -2986,6 +3026,8 @@ workstation:
data_type: resource
handler_key: VesselOut
label: Vessel
placeholder_keys:
vessel: unilabos_resources
result: {}
schema:
description: ''
@@ -3135,6 +3177,8 @@ workstation:
data_type: resource
handler_key: VesselOut
label: Vessel
placeholder_keys:
vessel: unilabos_resources
result: {}
schema:
description: ''
@@ -3354,6 +3398,9 @@ workstation:
data_type: resource
handler_key: ToVesselOut
label: To Vessel
placeholder_keys:
from_vessel: unilabos_nodes
to_vessel: unilabos_nodes
result: {}
schema:
description: ''
@@ -3667,6 +3714,8 @@ workstation:
data_type: resource
handler_key: VesselOut
label: Vessel
placeholder_keys:
vessel: unilabos_resources
result: {}
schema:
description: ''
@@ -4019,6 +4068,10 @@ workstation:
data_type: resource
handler_key: ToVesselOut
label: To Vessel
placeholder_keys:
column: unilabos_devices
from_vessel: unilabos_resources
to_vessel: unilabos_resources
result: {}
schema:
description: ''
@@ -4422,6 +4475,11 @@ workstation:
data_type: resource
handler_key: ToVesselOut
label: To Vessel
placeholder_keys:
from_vessel: unilabos_resources
to_vessel: unilabos_resources
waste_phase_to_vessel: unilabos_resources
waste_vessel: unilabos_resources
result: {}
schema:
description: ''
@@ -5053,6 +5111,8 @@ workstation:
data_type: resource
handler_key: VesselOut
label: Vessel
placeholder_keys:
vessel: unilabos_resources
result: {}
schema:
description: ''
@@ -5225,6 +5285,8 @@ workstation:
data_type: resource
handler_key: VesselOut
label: Vessel
placeholder_keys:
vessel: unilabos_resources
result: {}
schema:
description: ''
@@ -5391,6 +5453,8 @@ workstation:
data_type: resource
handler_key: VesselOut
label: Vessel
placeholder_keys:
vessel: unilabos_resources
result: {}
schema:
description: ''
@@ -5556,6 +5620,9 @@ workstation:
data_type: resource
handler_key: ToVesselOut
label: To Vessel
placeholder_keys:
from_vessel: unilabos_nodes
to_vessel: unilabos_nodes
result: {}
schema:
description: ''
@@ -5722,6 +5789,9 @@ workstation:
data_type: resource
handler_key: filtrate_vessel_out
label: Filtrate Vessel
placeholder_keys:
filtrate_vessel: unilabos_resources
vessel: unilabos_resources
result: {}
schema:
description: ''

View File

@@ -127,6 +127,8 @@ class Registry:
},
},
"version": "1.0.0",
"category": [],
"config_info": [],
"icon": "icon_device.webp",
"registry_type": "device",
"handles": [],

View File

@@ -248,7 +248,7 @@ def dict_to_nested_dict(nodes: dict, devices_only: bool = False) -> dict:
root_nodes = {
node["id"]: node
for node in nodes_list
if node.get("parent", node.get("parent_name")) in [None, "", "None", np.nan]
if node.get("parent", node.get("parent_name")) in [None, "", "None", np.nan] or len(nodes_list) == 1
}
# 如果存在多个根节点,返回所有根节点

View File

@@ -189,45 +189,26 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
# # 🔧 完全禁用Host查询直接使用转换后的数据
# print(f"🔧 跳过Host查询直接使用转换后的数据")
# 🔧 额外验证确保vessel数据完整
if 'vessel' in protocol_kwargs:
vessel_data = protocol_kwargs['vessel']
#print(f"🔍 验证vessel数据: {vessel_data}")
# 如果vessel是空字典尝试重新构建
if not vessel_data or (isinstance(vessel_data, dict) and not vessel_data):
# print(f"⚠️ vessel数据为空尝试从原始goal重新提取...")
# 直接从原始goal提取vessel
if hasattr(goal, 'vessel') and goal.vessel:
# print(f"🔍 原始goal.vessel: {goal.vessel}")
# 手动转换vessel
vessel_data = {
'id': goal.vessel.id,
'name': goal.vessel.name,
'type': goal.vessel.type,
'category': goal.vessel.category,
'config': goal.vessel.config,
'data': goal.vessel.data
}
protocol_kwargs['vessel'] = vessel_data
# print(f"✅ 手动重建vessel数据: {vessel_data}")
else:
# print(f"❌ 无法从原始goal提取vessel数据")
# 创建一个基本的vessel
vessel_data = {'id': 'default_vessel'}
protocol_kwargs['vessel'] = vessel_data
# print(f"🔧 创建默认vessel: {vessel_data}")
# 向Host查询物料当前状态
for k, v in goal.get_fields_and_field_types().items():
if v in ["unilabos_msgs/Resource", "sequence<unilabos_msgs/Resource>"]:
r = ResourceGet.Request()
resource_id = (
protocol_kwargs[k]["id"] if v == "unilabos_msgs/Resource" else protocol_kwargs[k][0]["id"]
)
r.id = resource_id
r.with_children = True
response = await self._resource_clients["resource_get"].call_async(r)
protocol_kwargs[k] = list_to_nested_dict(
[convert_from_ros_msg(rs) for rs in response.resources]
)
#print(f"🔍 最终传递给协议的 protocol_kwargs: {protocol_kwargs}")
#print(f"🔍 最终的 vessel: {protocol_kwargs.get('vessel', 'NOT_FOUND')}")
self.lab_logger().info(f"🔍 最终传递给协议的 protocol_kwargs: {protocol_kwargs}")
self.lab_logger().info(f"🔍 最终的 vessel: {protocol_kwargs.get('vessel', 'NOT_FOUND')}")
from unilabos.resources.graphio import physical_setup_graph
self.lab_logger().info(f"Working on physical setup: {physical_setup_graph}")
self.lab_logger().info(f"Protocol kwargs: {goal}")
self.lab_logger().info(f"Protocol kwargs: {action_value_mapping}")
protocol_steps = protocol_steps_generator(G=physical_setup_graph, **protocol_kwargs)
self.lab_logger().info(f"Goal received: {protocol_kwargs}, running steps: \n{protocol_steps}")
@@ -263,14 +244,14 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
}
)
# # 向Host更新物料当前状态
# for k, v in goal.get_fields_and_field_types().items():
# if v in ["unilabos_msgs/Resource", "sequence<unilabos_msgs/Resource>"]:
# r = ResourceUpdate.Request()
# r.resources = [
# convert_to_ros_msg(Resource, rs) for rs in nested_dict_to_list(protocol_kwargs[k])
# ]
# response = await self._resource_clients["resource_update"].call_async(r)
# 向Host更新物料当前状态
for k, v in goal.get_fields_and_field_types().items():
if v in ["unilabos_msgs/Resource", "sequence<unilabos_msgs/Resource>"]:
r = ResourceUpdate.Request()
r.resources = [
convert_to_ros_msg(Resource, rs) for rs in nested_dict_to_list(protocol_kwargs[k])
]
response = await self._resource_clients["resource_update"].call_async(r)
# 设置成功状态和返回值
execution_success = True