mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-06 23:15:10 +00:00
fix sub-resource query in protocol node compiling
This commit is contained in:
@@ -237,14 +237,14 @@ def generate_adjust_ph_protocol(
|
|||||||
|
|
||||||
# 统一处理vessel参数
|
# 统一处理vessel参数
|
||||||
if isinstance(vessel, dict):
|
if isinstance(vessel, dict):
|
||||||
vessel_id = vessel.get("id", "")
|
vessel_id = list(vessel.values())[0].get("id", "")
|
||||||
vessel_data = vessel.get("data", {})
|
vessel_data = vessel.get("data", {})
|
||||||
else:
|
else:
|
||||||
vessel_id = str(vessel)
|
vessel_id = str(vessel)
|
||||||
vessel_data = G.nodes[vessel_id].get("data", {}) if vessel_id in G.nodes() else {}
|
vessel_data = G.nodes[vessel_id].get("data", {}) if vessel_id in G.nodes() else {}
|
||||||
|
|
||||||
if not vessel_id:
|
if not vessel_id:
|
||||||
debug_print("❌ vessel 参数无效,必须包含id字段或直接提供容器ID")
|
debug_print(f"❌ vessel 参数无效,必须包含id字段或直接提供容器ID. vessel: {vessel}")
|
||||||
raise ValueError("vessel 参数无效,必须包含id字段或直接提供容器ID")
|
raise ValueError("vessel 参数无效,必须包含id字段或直接提供容器ID")
|
||||||
|
|
||||||
debug_print("=" * 60)
|
debug_print("=" * 60)
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ def extract_vessel_id(vessel: Union[str, dict]) -> str:
|
|||||||
str: vessel_id
|
str: vessel_id
|
||||||
"""
|
"""
|
||||||
if isinstance(vessel, dict):
|
if isinstance(vessel, dict):
|
||||||
vessel_id = vessel.get("id", "")
|
vessel_id = list(vessel.values())[0].get("id", "")
|
||||||
debug_print(f"🔧 从vessel字典提取ID: {vessel_id}")
|
debug_print(f"🔧 从vessel字典提取ID: {vessel_id}")
|
||||||
return vessel_id
|
return vessel_id
|
||||||
elif isinstance(vessel, str):
|
elif isinstance(vessel, str):
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ def extract_vessel_id(vessel: Union[str, dict]) -> str:
|
|||||||
str: vessel_id
|
str: vessel_id
|
||||||
"""
|
"""
|
||||||
if isinstance(vessel, dict):
|
if isinstance(vessel, dict):
|
||||||
vessel_id = vessel.get("id", "")
|
vessel_id = list(vessel.values())[0].get("id", "")
|
||||||
debug_print(f"🔧 从vessel字典提取ID: {vessel_id}")
|
debug_print(f"🔧 从vessel字典提取ID: {vessel_id}")
|
||||||
return vessel_id
|
return vessel_id
|
||||||
elif isinstance(vessel, str):
|
elif isinstance(vessel, str):
|
||||||
|
|||||||
@@ -4548,7 +4548,6 @@ virtual_solid_dispenser:
|
|||||||
feedback: {}
|
feedback: {}
|
||||||
goal:
|
goal:
|
||||||
properties: {}
|
properties: {}
|
||||||
required: []
|
|
||||||
type: object
|
type: object
|
||||||
result: {}
|
result: {}
|
||||||
required:
|
required:
|
||||||
@@ -4556,30 +4555,6 @@ virtual_solid_dispenser:
|
|||||||
title: cleanup参数
|
title: cleanup参数
|
||||||
type: object
|
type: object
|
||||||
type: UniLabJsonCommandAsync
|
type: UniLabJsonCommandAsync
|
||||||
auto-find_solid_reagent_bottle:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
reagent_name: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: ''
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
reagent_name:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- reagent_name
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: find_solid_reagent_bottle参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-initialize:
|
auto-initialize:
|
||||||
feedback: {}
|
feedback: {}
|
||||||
goal: {}
|
goal: {}
|
||||||
@@ -4592,7 +4567,6 @@ virtual_solid_dispenser:
|
|||||||
feedback: {}
|
feedback: {}
|
||||||
goal:
|
goal:
|
||||||
properties: {}
|
properties: {}
|
||||||
required: []
|
|
||||||
type: object
|
type: object
|
||||||
result: {}
|
result: {}
|
||||||
required:
|
required:
|
||||||
@@ -4600,58 +4574,9 @@ virtual_solid_dispenser:
|
|||||||
title: initialize参数
|
title: initialize参数
|
||||||
type: object
|
type: object
|
||||||
type: UniLabJsonCommandAsync
|
type: UniLabJsonCommandAsync
|
||||||
auto-parse_mass_string:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
mass_str: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: ''
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
mass_str:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- mass_str
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: parse_mass_string参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
auto-parse_mol_string:
|
|
||||||
feedback: {}
|
|
||||||
goal: {}
|
|
||||||
goal_default:
|
|
||||||
mol_str: null
|
|
||||||
handles: []
|
|
||||||
result: {}
|
|
||||||
schema:
|
|
||||||
description: ''
|
|
||||||
properties:
|
|
||||||
feedback: {}
|
|
||||||
goal:
|
|
||||||
properties:
|
|
||||||
mol_str:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- mol_str
|
|
||||||
type: object
|
|
||||||
result: {}
|
|
||||||
required:
|
|
||||||
- goal
|
|
||||||
title: parse_mol_string参数
|
|
||||||
type: object
|
|
||||||
type: UniLabJsonCommand
|
|
||||||
module: unilabos.devices.virtual.virtual_solid_dispenser:VirtualSolidDispenser
|
module: unilabos.devices.virtual.virtual_solid_dispenser:VirtualSolidDispenser
|
||||||
status_types:
|
status_types:
|
||||||
current_reagent: str
|
current_reagent: str
|
||||||
device_info: dict
|
|
||||||
dispensed_amount: float
|
dispensed_amount: float
|
||||||
status: str
|
status: str
|
||||||
total_operations: int
|
total_operations: int
|
||||||
@@ -4683,14 +4608,18 @@ virtual_solid_dispenser:
|
|||||||
type: object
|
type: object
|
||||||
device_id:
|
device_id:
|
||||||
type: string
|
type: string
|
||||||
|
max_capacity:
|
||||||
|
default: 100.0
|
||||||
|
type: number
|
||||||
|
precision:
|
||||||
|
default: 0.001
|
||||||
|
type: number
|
||||||
required: []
|
required: []
|
||||||
type: object
|
type: object
|
||||||
data:
|
data:
|
||||||
properties:
|
properties:
|
||||||
current_reagent:
|
current_reagent:
|
||||||
type: string
|
type: string
|
||||||
device_info:
|
|
||||||
type: object
|
|
||||||
dispensed_amount:
|
dispensed_amount:
|
||||||
type: number
|
type: number
|
||||||
status:
|
status:
|
||||||
@@ -4702,7 +4631,6 @@ virtual_solid_dispenser:
|
|||||||
- current_reagent
|
- current_reagent
|
||||||
- dispensed_amount
|
- dispensed_amount
|
||||||
- total_operations
|
- total_operations
|
||||||
- device_info
|
|
||||||
type: object
|
type: object
|
||||||
version: 0.0.1
|
version: 0.0.1
|
||||||
virtual_stirrer:
|
virtual_stirrer:
|
||||||
@@ -5993,176 +5921,3 @@ virtual_vacuum_pump:
|
|||||||
- status
|
- status
|
||||||
type: object
|
type: object
|
||||||
version: 0.0.1
|
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
|
|
||||||
@@ -248,7 +248,7 @@ def dict_to_nested_dict(nodes: dict, devices_only: bool = False) -> dict:
|
|||||||
root_nodes = {
|
root_nodes = {
|
||||||
node["id"]: node
|
node["id"]: node
|
||||||
for node in nodes_list
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
# 如果存在多个根节点,返回所有根节点
|
# 如果存在多个根节点,返回所有根节点
|
||||||
|
|||||||
@@ -189,45 +189,26 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
|
|||||||
|
|
||||||
# # 🔧 完全禁用Host查询,直接使用转换后的数据
|
# # 🔧 完全禁用Host查询,直接使用转换后的数据
|
||||||
# print(f"🔧 跳过Host查询,直接使用转换后的数据")
|
# print(f"🔧 跳过Host查询,直接使用转换后的数据")
|
||||||
|
# 向Host查询物料当前状态
|
||||||
# 🔧 额外验证:确保vessel数据完整
|
for k, v in goal.get_fields_and_field_types().items():
|
||||||
if 'vessel' in protocol_kwargs:
|
if v in ["unilabos_msgs/Resource", "sequence<unilabos_msgs/Resource>"]:
|
||||||
vessel_data = protocol_kwargs['vessel']
|
r = ResourceGet.Request()
|
||||||
#print(f"🔍 验证vessel数据: {vessel_data}")
|
resource_id = (
|
||||||
|
protocol_kwargs[k]["id"] if v == "unilabos_msgs/Resource" else protocol_kwargs[k][0]["id"]
|
||||||
# 如果vessel是空字典,尝试重新构建
|
)
|
||||||
if not vessel_data or (isinstance(vessel_data, dict) and not vessel_data):
|
r.id = resource_id
|
||||||
# print(f"⚠️ vessel数据为空,尝试从原始goal重新提取...")
|
r.with_children = True
|
||||||
|
response = await self._resource_clients["resource_get"].call_async(r)
|
||||||
# 直接从原始goal提取vessel
|
protocol_kwargs[k] = list_to_nested_dict(
|
||||||
if hasattr(goal, 'vessel') and goal.vessel:
|
[convert_from_ros_msg(rs) for rs in response.resources]
|
||||||
# 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}")
|
|
||||||
|
|
||||||
#print(f"🔍 最终传递给协议的 protocol_kwargs: {protocol_kwargs}")
|
self.lab_logger().info(f"🔍 最终传递给协议的 protocol_kwargs: {protocol_kwargs}")
|
||||||
#print(f"🔍 最终的 vessel: {protocol_kwargs.get('vessel', 'NOT_FOUND')}")
|
self.lab_logger().info(f"🔍 最终的 vessel: {protocol_kwargs.get('vessel', 'NOT_FOUND')}")
|
||||||
|
|
||||||
from unilabos.resources.graphio import physical_setup_graph
|
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"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)
|
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}")
|
self.lab_logger().info(f"Goal received: {protocol_kwargs}, running steps: \n{protocol_steps}")
|
||||||
@@ -263,14 +244,14 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# # 向Host更新物料当前状态
|
# 向Host更新物料当前状态
|
||||||
# for k, v in goal.get_fields_and_field_types().items():
|
for k, v in goal.get_fields_and_field_types().items():
|
||||||
# if v in ["unilabos_msgs/Resource", "sequence<unilabos_msgs/Resource>"]:
|
if v in ["unilabos_msgs/Resource", "sequence<unilabos_msgs/Resource>"]:
|
||||||
# r = ResourceUpdate.Request()
|
r = ResourceUpdate.Request()
|
||||||
# r.resources = [
|
r.resources = [
|
||||||
# convert_to_ros_msg(Resource, rs) for rs in nested_dict_to_list(protocol_kwargs[k])
|
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)
|
response = await self._resource_clients["resource_update"].call_async(r)
|
||||||
|
|
||||||
# 设置成功状态和返回值
|
# 设置成功状态和返回值
|
||||||
execution_success = True
|
execution_success = True
|
||||||
|
|||||||
Reference in New Issue
Block a user