mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-04 21:35:09 +00:00
stir和adjustph的中的bug修不好
This commit is contained in:
@@ -49,7 +49,7 @@ conda env update --file unilabos-[YOUR_OS].yml -n environment_name
|
|||||||
|
|
||||||
# Currently, you need to install the `unilabos_msgs` package
|
# Currently, you need to install the `unilabos_msgs` package
|
||||||
# You can download the system-specific package from the Release page
|
# You can download the system-specific package from the Release page
|
||||||
conda install ros-humble-unilabos-msgs-0.9.10-xxxxx.tar.bz2
|
conda install ros-humble-unilabos-msgs-0.9.12-xxxxx.tar.bz2
|
||||||
|
|
||||||
# Install PyLabRobot and other prerequisites
|
# Install PyLabRobot and other prerequisites
|
||||||
git clone https://github.com/PyLabRobot/pylabrobot plr_repo
|
git clone https://github.com/PyLabRobot/pylabrobot plr_repo
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ def get_vessel_display_info(vessel: Union[str, dict]) -> str:
|
|||||||
|
|
||||||
def generate_stir_protocol(
|
def generate_stir_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: Union[str, dict], # 🔧 修改:支持vessel字典
|
vessel: Union[str, dict], # 支持vessel字典或字符串
|
||||||
time: Union[str, float, int] = "300",
|
time: Union[str, float, int] = "300",
|
||||||
stir_time: Union[str, float, int] = "0",
|
stir_time: Union[str, float, int] = "0",
|
||||||
time_spec: str = "",
|
time_spec: str = "",
|
||||||
@@ -190,28 +190,35 @@ def generate_stir_protocol(
|
|||||||
settling_time: Union[str, float] = "60",
|
settling_time: Union[str, float] = "60",
|
||||||
**kwargs
|
**kwargs
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""生成搅拌操作的协议序列 - 修复vessel参数传递"""
|
||||||
生成搅拌操作的协议序列 - 支持vessel字典
|
|
||||||
|
|
||||||
Args:
|
# 🔧 核心修改:正确处理vessel参数
|
||||||
G: 有向图,节点为设备和容器,边为流体管道
|
|
||||||
vessel: 搅拌容器字典(从XDL传入)或容器ID字符串
|
|
||||||
time: 搅拌时间(支持 "300", "5 min", "1 h" 等格式)
|
|
||||||
stir_time: 指定搅拌时间(优先级比time低)
|
|
||||||
time_spec: 时间规格(优先级最高)
|
|
||||||
event: 事件描述
|
|
||||||
stir_speed: 搅拌速度(RPM)
|
|
||||||
settling_time: 沉降时间
|
|
||||||
**kwargs: 其他可选参数
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
List[Dict[str, Any]]: 搅拌操作的动作序列
|
|
||||||
"""
|
|
||||||
|
|
||||||
# 🔧 核心修改:从vessel参数中提取vessel_id
|
|
||||||
vessel_id = extract_vessel_id(vessel)
|
vessel_id = extract_vessel_id(vessel)
|
||||||
vessel_display = get_vessel_display_info(vessel)
|
vessel_display = get_vessel_display_info(vessel)
|
||||||
|
|
||||||
|
# 🔧 关键修复:确保vessel_resource是完整的Resource对象
|
||||||
|
if isinstance(vessel, dict):
|
||||||
|
vessel_resource = vessel # 已经是完整的Resource字典
|
||||||
|
debug_print(f"✅ 使用传入的vessel Resource对象")
|
||||||
|
else:
|
||||||
|
# 如果只是字符串,构建一个基本的Resource对象
|
||||||
|
vessel_resource = {
|
||||||
|
"id": vessel,
|
||||||
|
"name": "",
|
||||||
|
"category": "",
|
||||||
|
"children": [],
|
||||||
|
"config": "",
|
||||||
|
"data": "",
|
||||||
|
"parent": "",
|
||||||
|
"pose": {
|
||||||
|
"orientation": {"w": 1.0, "x": 0.0, "y": 0.0, "z": 0.0},
|
||||||
|
"position": {"x": 0.0, "y": 0.0, "z": 0.0}
|
||||||
|
},
|
||||||
|
"sample_id": "",
|
||||||
|
"type": ""
|
||||||
|
}
|
||||||
|
debug_print(f"🔧 构建了基本的vessel Resource对象: {vessel}")
|
||||||
|
|
||||||
debug_print("🌪️" * 20)
|
debug_print("🌪️" * 20)
|
||||||
debug_print("🚀 开始生成搅拌协议(支持vessel字典)✨")
|
debug_print("🚀 开始生成搅拌协议(支持vessel字典)✨")
|
||||||
debug_print(f"📝 输入参数:")
|
debug_print(f"📝 输入参数:")
|
||||||
@@ -293,13 +300,14 @@ def generate_stir_protocol(
|
|||||||
"device_id": stirrer_id,
|
"device_id": stirrer_id,
|
||||||
"action_name": "stir",
|
"action_name": "stir",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"vessel": vessel_id, # 🔧 使用 vessel_id
|
# 🔧 关键修复:传递vessel_id字符串,而不是完整的Resource对象
|
||||||
"time": str(time), # 保持原始格式
|
"vessel": vessel_id, # 传递字符串ID,不是Resource对象
|
||||||
|
"time": str(time),
|
||||||
"event": event,
|
"event": event,
|
||||||
"time_spec": time_spec,
|
"time_spec": time_spec,
|
||||||
"stir_time": float(parsed_time), # 确保是数字
|
"stir_time": float(parsed_time),
|
||||||
"stir_speed": float(stir_speed), # 确保是数字
|
"stir_speed": float(stir_speed),
|
||||||
"settling_time": float(parsed_settling_time) # 确保是数字
|
"settling_time": float(parsed_settling_time)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
action_sequence.append(stir_action)
|
action_sequence.append(stir_action)
|
||||||
@@ -324,36 +332,47 @@ def generate_stir_protocol(
|
|||||||
|
|
||||||
def generate_start_stir_protocol(
|
def generate_start_stir_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: Union[str, dict], # 🔧 修改:支持vessel字典
|
vessel: Union[str, dict],
|
||||||
stir_speed: float = 300.0,
|
stir_speed: float = 300.0,
|
||||||
purpose: str = "",
|
purpose: str = "",
|
||||||
**kwargs
|
**kwargs
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""生成开始搅拌操作的协议序列 - 修复vessel参数传递"""
|
||||||
生成开始搅拌操作的协议序列 - 支持vessel字典
|
|
||||||
|
|
||||||
Args:
|
# 🔧 核心修改:正确处理vessel参数
|
||||||
G: 有向图
|
|
||||||
vessel: 搅拌容器字典或容器ID字符串
|
|
||||||
stir_speed: 搅拌速度(RPM)
|
|
||||||
purpose: 搅拌目的描述
|
|
||||||
**kwargs: 其他可选参数
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
List[Dict[str, Any]]: 开始搅拌操作的动作序列
|
|
||||||
"""
|
|
||||||
|
|
||||||
# 🔧 核心修改:从vessel参数中提取vessel_id
|
|
||||||
vessel_id = extract_vessel_id(vessel)
|
vessel_id = extract_vessel_id(vessel)
|
||||||
vessel_display = get_vessel_display_info(vessel)
|
vessel_display = get_vessel_display_info(vessel)
|
||||||
|
|
||||||
debug_print("🔄 开始生成启动搅拌协议(支持vessel字典)✨")
|
# 🔧 关键修复:确保vessel_resource是完整的Resource对象
|
||||||
|
if isinstance(vessel, dict):
|
||||||
|
vessel_resource = vessel # 已经是完整的Resource字典
|
||||||
|
debug_print(f"✅ 使用传入的vessel Resource对象")
|
||||||
|
else:
|
||||||
|
# 如果只是字符串,构建一个基本的Resource对象
|
||||||
|
vessel_resource = {
|
||||||
|
"id": vessel,
|
||||||
|
"name": "",
|
||||||
|
"category": "",
|
||||||
|
"children": [],
|
||||||
|
"config": "",
|
||||||
|
"data": "",
|
||||||
|
"parent": "",
|
||||||
|
"pose": {
|
||||||
|
"orientation": {"w": 1.0, "x": 0.0, "y": 0.0, "z": 0.0},
|
||||||
|
"position": {"x": 0.0, "y": 0.0, "z": 0.0}
|
||||||
|
},
|
||||||
|
"sample_id": "",
|
||||||
|
"type": ""
|
||||||
|
}
|
||||||
|
debug_print(f"🔧 构建了基本的vessel Resource对象: {vessel}")
|
||||||
|
|
||||||
|
debug_print("🔄 开始生成启动搅拌协议(修复vessel参数)✨")
|
||||||
debug_print(f"🥽 vessel: {vessel_display} (ID: {vessel_id})")
|
debug_print(f"🥽 vessel: {vessel_display} (ID: {vessel_id})")
|
||||||
debug_print(f"🌪️ speed: {stir_speed} RPM")
|
debug_print(f"🌪️ speed: {stir_speed} RPM")
|
||||||
debug_print(f"🎯 purpose: {purpose}")
|
debug_print(f"🎯 purpose: {purpose}")
|
||||||
|
|
||||||
# 基础验证
|
# 基础验证
|
||||||
if not vessel_id or vessel_id not in G.nodes(): # 🔧 使用 vessel_id
|
if not vessel_id or vessel_id not in G.nodes():
|
||||||
debug_print("❌ 容器验证失败!")
|
debug_print("❌ 容器验证失败!")
|
||||||
raise ValueError("vessel 参数无效")
|
raise ValueError("vessel 参数无效")
|
||||||
|
|
||||||
@@ -363,14 +382,15 @@ def generate_start_stir_protocol(
|
|||||||
stir_speed = 300.0
|
stir_speed = 300.0
|
||||||
|
|
||||||
# 查找设备
|
# 查找设备
|
||||||
stirrer_id = find_connected_stirrer(G, vessel_id) # 🔧 使用 vessel_id
|
stirrer_id = find_connected_stirrer(G, vessel_id)
|
||||||
|
|
||||||
# 生成动作
|
# 🔧 关键修复:传递vessel_id字符串
|
||||||
action_sequence = [{
|
action_sequence = [{
|
||||||
"device_id": stirrer_id,
|
"device_id": stirrer_id,
|
||||||
"action_name": "start_stir",
|
"action_name": "start_stir",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"vessel": vessel_id, # 🔧 使用 vessel_id
|
# 🔧 关键修复:传递vessel_id字符串,而不是完整的Resource对象
|
||||||
|
"vessel": vessel_id, # 传递字符串ID,不是Resource对象
|
||||||
"stir_speed": stir_speed,
|
"stir_speed": stir_speed,
|
||||||
"purpose": purpose or f"启动搅拌 {stir_speed} RPM"
|
"purpose": purpose or f"启动搅拌 {stir_speed} RPM"
|
||||||
}
|
}
|
||||||
@@ -381,42 +401,56 @@ def generate_start_stir_protocol(
|
|||||||
|
|
||||||
def generate_stop_stir_protocol(
|
def generate_stop_stir_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: Union[str, dict], # 🔧 修改:支持vessel字典
|
vessel: Union[str, dict],
|
||||||
**kwargs
|
**kwargs
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""生成停止搅拌操作的协议序列 - 修复vessel参数传递"""
|
||||||
生成停止搅拌操作的协议序列 - 支持vessel字典
|
|
||||||
|
|
||||||
Args:
|
# 🔧 核心修改:正确处理vessel参数
|
||||||
G: 有向图
|
|
||||||
vessel: 搅拌容器字典或容器ID字符串
|
|
||||||
**kwargs: 其他可选参数
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
List[Dict[str, Any]]: 停止搅拌操作的动作序列
|
|
||||||
"""
|
|
||||||
|
|
||||||
# 🔧 核心修改:从vessel参数中提取vessel_id
|
|
||||||
vessel_id = extract_vessel_id(vessel)
|
vessel_id = extract_vessel_id(vessel)
|
||||||
vessel_display = get_vessel_display_info(vessel)
|
vessel_display = get_vessel_display_info(vessel)
|
||||||
|
|
||||||
debug_print("🛑 开始生成停止搅拌协议(支持vessel字典)✨")
|
# 🔧 关键修复:确保vessel_resource是完整的Resource对象
|
||||||
|
if isinstance(vessel, dict):
|
||||||
|
vessel_resource = vessel # 已经是完整的Resource字典
|
||||||
|
debug_print(f"✅ 使用传入的vessel Resource对象")
|
||||||
|
else:
|
||||||
|
# 如果只是字符串,构建一个基本的Resource对象
|
||||||
|
vessel_resource = {
|
||||||
|
"id": vessel,
|
||||||
|
"name": "",
|
||||||
|
"category": "",
|
||||||
|
"children": [],
|
||||||
|
"config": "",
|
||||||
|
"data": "",
|
||||||
|
"parent": "",
|
||||||
|
"pose": {
|
||||||
|
"orientation": {"w": 1.0, "x": 0.0, "y": 0.0, "z": 0.0},
|
||||||
|
"position": {"x": 0.0, "y": 0.0, "z": 0.0}
|
||||||
|
},
|
||||||
|
"sample_id": "",
|
||||||
|
"type": ""
|
||||||
|
}
|
||||||
|
debug_print(f"🔧 构建了基本的vessel Resource对象: {vessel}")
|
||||||
|
|
||||||
|
debug_print("🛑 开始生成停止搅拌协议(修复vessel参数)✨")
|
||||||
debug_print(f"🥽 vessel: {vessel_display} (ID: {vessel_id})")
|
debug_print(f"🥽 vessel: {vessel_display} (ID: {vessel_id})")
|
||||||
|
|
||||||
# 基础验证
|
# 基础验证
|
||||||
if not vessel_id or vessel_id not in G.nodes(): # 🔧 使用 vessel_id
|
if not vessel_id or vessel_id not in G.nodes():
|
||||||
debug_print("❌ 容器验证失败!")
|
debug_print("❌ 容器验证失败!")
|
||||||
raise ValueError("vessel 参数无效")
|
raise ValueError("vessel 参数无效")
|
||||||
|
|
||||||
# 查找设备
|
# 查找设备
|
||||||
stirrer_id = find_connected_stirrer(G, vessel_id) # 🔧 使用 vessel_id
|
stirrer_id = find_connected_stirrer(G, vessel_id)
|
||||||
|
|
||||||
# 生成动作
|
# 🔧 关键修复:传递vessel_id字符串
|
||||||
action_sequence = [{
|
action_sequence = [{
|
||||||
"device_id": stirrer_id,
|
"device_id": stirrer_id,
|
||||||
"action_name": "stop_stir",
|
"action_name": "stop_stir",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"vessel": vessel_id # 🔧 使用 vessel_id
|
# 🔧 关键修复:传递vessel_id字符串,而不是完整的Resource对象
|
||||||
|
"vessel": vessel_id # 传递字符串ID,不是Resource对象
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
|||||||
@@ -3650,3 +3650,176 @@ 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
|
||||||
@@ -509,68 +509,68 @@ def convert_from_ros_msg_with_mapping(ros_msg: Any, value_mapping: Dict[str, str
|
|||||||
"""
|
"""
|
||||||
data: Dict[str, Any] = {}
|
data: Dict[str, Any] = {}
|
||||||
|
|
||||||
# 🔧 添加调试信息
|
# # 🔧 添加调试信息
|
||||||
print(f"🔍 convert_from_ros_msg_with_mapping 开始")
|
# print(f"🔍 convert_from_ros_msg_with_mapping 开始")
|
||||||
print(f"🔍 ros_msg 类型: {type(ros_msg)}")
|
# print(f"🔍 ros_msg 类型: {type(ros_msg)}")
|
||||||
print(f"🔍 ros_msg 内容: {ros_msg}")
|
# print(f"🔍 ros_msg 内容: {ros_msg}")
|
||||||
print(f"🔍 value_mapping: {value_mapping}")
|
# print(f"🔍 value_mapping: {value_mapping}")
|
||||||
print("-" * 60)
|
# print("-" * 60)
|
||||||
|
|
||||||
for msg_name, attr_name in value_mapping.items():
|
for msg_name, attr_name in value_mapping.items():
|
||||||
print(f"🔍 处理映射: {msg_name} -> {attr_name}")
|
# print(f"🔍 处理映射: {msg_name} -> {attr_name}")
|
||||||
|
|
||||||
msg_path = msg_name.split(".")
|
msg_path = msg_name.split(".")
|
||||||
current = ros_msg
|
current = ros_msg
|
||||||
|
|
||||||
print(f"🔍 msg_path: {msg_path}")
|
# print(f"🔍 msg_path: {msg_path}")
|
||||||
print(f"🔍 current 初始值: {current} (类型: {type(current)})")
|
# print(f"🔍 current 初始值: {current} (类型: {type(current)})")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not attr_name.endswith("[]"):
|
if not attr_name.endswith("[]"):
|
||||||
# 处理单值映射
|
# 处理单值映射
|
||||||
print(f"🔍 处理单值映射")
|
# print(f"🔍 处理单值映射")
|
||||||
for i, name in enumerate(msg_path):
|
for i, name in enumerate(msg_path):
|
||||||
print(f"🔍 步骤 {i}: 获取属性 '{name}' 从 {type(current)}")
|
# print(f"🔍 步骤 {i}: 获取属性 '{name}' 从 {type(current)}")
|
||||||
if hasattr(current, name):
|
if hasattr(current, name):
|
||||||
current = getattr(current, name)
|
current = getattr(current, name)
|
||||||
print(f"🔍 获取到: {current} (类型: {type(current)})")
|
# print(f"🔍 获取到: {current} (类型: {type(current)})")
|
||||||
else:
|
else:
|
||||||
print(f"❌ 属性 '{name}' 不存在于 {type(current)}")
|
# print(f"❌ 属性 '{name}' 不存在于 {type(current)}")
|
||||||
break
|
break
|
||||||
|
|
||||||
converted_value = convert_from_ros_msg(current)
|
converted_value = convert_from_ros_msg(current)
|
||||||
print(f"🔍 转换后的值: {converted_value} (类型: {type(converted_value)})")
|
# print(f"🔍 转换后的值: {converted_value} (类型: {type(converted_value)})")
|
||||||
data[attr_name] = converted_value
|
data[attr_name] = converted_value
|
||||||
print(f"✅ 设置 data['{attr_name}'] = {converted_value}")
|
# print(f"✅ 设置 data['{attr_name}'] = {converted_value}")
|
||||||
else:
|
else:
|
||||||
# 处理列表值映射
|
# 处理列表值映射
|
||||||
print(f"🔍 处理列表值映射")
|
# print(f"🔍 处理列表值映射")
|
||||||
for i, name in enumerate(msg_path):
|
for i, name in enumerate(msg_path):
|
||||||
print(f"🔍 列表步骤 {i}: 处理 '{name}' 从 {type(current)}")
|
# print(f"🔍 列表步骤 {i}: 处理 '{name}' 从 {type(current)}")
|
||||||
if name.endswith("[]"):
|
if name.endswith("[]"):
|
||||||
base_name = name[:-2]
|
base_name = name[:-2]
|
||||||
print(f"🔍 数组字段 base_name: '{base_name}'")
|
# print(f"🔍 数组字段 base_name: '{base_name}'")
|
||||||
if hasattr(current, base_name):
|
if hasattr(current, base_name):
|
||||||
current = list(getattr(current, base_name))
|
current = list(getattr(current, base_name))
|
||||||
print(f"🔍 获取数组: {current} (长度: {len(current)})")
|
# print(f"🔍 获取数组: {current} (长度: {len(current)})")
|
||||||
else:
|
else:
|
||||||
print(f"❌ 数组字段 '{base_name}' 不存在")
|
# print(f"❌ 数组字段 '{base_name}' 不存在")
|
||||||
current = []
|
current = []
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
if isinstance(current, list):
|
if isinstance(current, list):
|
||||||
print(f"🔍 从列表中获取属性 '{name}'")
|
# print(f"🔍 从列表中获取属性 '{name}'")
|
||||||
next_level = []
|
next_level = []
|
||||||
for item in current:
|
for item in current:
|
||||||
if hasattr(item, name):
|
if hasattr(item, name):
|
||||||
next_level.append(getattr(item, name))
|
next_level.append(getattr(item, name))
|
||||||
current = next_level
|
current = next_level
|
||||||
print(f"🔍 列表处理结果: {current} (长度: {len(current)})")
|
# print(f"🔍 列表处理结果: {current} (长度: {len(current)})")
|
||||||
elif hasattr(current, name):
|
elif hasattr(current, name):
|
||||||
current = getattr(current, name)
|
current = getattr(current, name)
|
||||||
print(f"🔍 获取到属性: {current} (类型: {type(current)})")
|
# print(f"🔍 获取到属性: {current} (类型: {type(current)})")
|
||||||
else:
|
else:
|
||||||
print(f"❌ 属性 '{name}' 不存在")
|
# print(f"❌ 属性 '{name}' 不存在")
|
||||||
current = []
|
current = []
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -578,20 +578,20 @@ def convert_from_ros_msg_with_mapping(ros_msg: Any, value_mapping: Dict[str, str
|
|||||||
if current:
|
if current:
|
||||||
converted_list = [convert_from_ros_msg(item) for item in current]
|
converted_list = [convert_from_ros_msg(item) for item in current]
|
||||||
data[attr_key] = converted_list
|
data[attr_key] = converted_list
|
||||||
print(f"✅ 设置 data['{attr_key}'] = {converted_list}")
|
# print(f"✅ 设置 data['{attr_key}'] = {converted_list}")
|
||||||
else:
|
else:
|
||||||
print(f"⚠️ 列表为空,跳过 '{attr_key}'")
|
print(f"⚠️ 列表为空,跳过 '{attr_key}'")
|
||||||
except (AttributeError, TypeError) as e:
|
except (AttributeError, TypeError) as e:
|
||||||
print(f"❌ 映射转换错误 {msg_name} -> {attr_name}: {e}")
|
# print(f"❌ 映射转换错误 {msg_name} -> {attr_name}: {e}")
|
||||||
logger.debug(f"Mapping conversion error for {msg_name} -> {attr_name}")
|
logger.debug(f"Mapping conversion error for {msg_name} -> {attr_name}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print(f"🔍 当前 data 状态: {data}")
|
# print(f"🔍 当前 data 状态: {data}")
|
||||||
print("-" * 40)
|
# print("-" * 40)
|
||||||
|
|
||||||
print(f"🔍 convert_from_ros_msg_with_mapping 结束")
|
#print(f"🔍 convert_from_ros_msg_with_mapping 结束")
|
||||||
print(f"🔍 最终 data: {data}")
|
#print(f"🔍 最终 data: {data}")
|
||||||
print("=" * 60)
|
#print("=" * 60)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -629,7 +629,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
|||||||
execution_success = False
|
execution_success = False
|
||||||
action_return_value = None
|
action_return_value = None
|
||||||
|
|
||||||
self.lab_logger().info(f"执行动作: {action_name}")
|
##### self.lab_logger().info(f"执行动作: {action_name}")
|
||||||
goal = goal_handle.request
|
goal = goal_handle.request
|
||||||
|
|
||||||
# 从目标消息中提取参数, 并调用对应的方法
|
# 从目标消息中提取参数, 并调用对应的方法
|
||||||
@@ -685,14 +685,14 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
|||||||
final_resource = [convert_resources_to_type([i], final_type)[0] for i in resources_list]
|
final_resource = [convert_resources_to_type([i], final_type)[0] for i in resources_list]
|
||||||
action_kwargs[k] = self.resource_tracker.figure_resource(final_resource)
|
action_kwargs[k] = self.resource_tracker.figure_resource(final_resource)
|
||||||
|
|
||||||
self.lab_logger().info(f"准备执行: {action_kwargs}, 函数: {ACTION.__name__}")
|
##### self.lab_logger().info(f"准备执行: {action_kwargs}, 函数: {ACTION.__name__}")
|
||||||
time_start = time.time()
|
time_start = time.time()
|
||||||
time_overall = 100
|
time_overall = 100
|
||||||
|
|
||||||
# 将阻塞操作放入线程池执行
|
# 将阻塞操作放入线程池执行
|
||||||
if asyncio.iscoroutinefunction(ACTION):
|
if asyncio.iscoroutinefunction(ACTION):
|
||||||
try:
|
try:
|
||||||
self.lab_logger().info(f"异步执行动作 {ACTION}")
|
##### self.lab_logger().info(f"异步执行动作 {ACTION}")
|
||||||
future = ROS2DeviceNode.run_async_func(ACTION, trace_error=False, **action_kwargs)
|
future = ROS2DeviceNode.run_async_func(ACTION, trace_error=False, **action_kwargs)
|
||||||
|
|
||||||
def _handle_future_exception(fut):
|
def _handle_future_exception(fut):
|
||||||
@@ -702,7 +702,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
|||||||
execution_success = True
|
execution_success = True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
execution_error = traceback.format_exc()
|
execution_error = traceback.format_exc()
|
||||||
error(f"异步任务 {ACTION.__name__} 报错了")
|
##### error(f"异步任务 {ACTION.__name__} 报错了")
|
||||||
error(traceback.format_exc())
|
error(traceback.format_exc())
|
||||||
|
|
||||||
future.add_done_callback(_handle_future_exception)
|
future.add_done_callback(_handle_future_exception)
|
||||||
@@ -710,7 +710,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
|||||||
self.lab_logger().error(f"创建异步任务失败: {traceback.format_exc()}")
|
self.lab_logger().error(f"创建异步任务失败: {traceback.format_exc()}")
|
||||||
raise e
|
raise e
|
||||||
else:
|
else:
|
||||||
self.lab_logger().info(f"同步执行动作 {ACTION}")
|
##### self.lab_logger().info(f"同步执行动作 {ACTION}")
|
||||||
future = self._executor.submit(ACTION, **action_kwargs)
|
future = self._executor.submit(ACTION, **action_kwargs)
|
||||||
|
|
||||||
def _handle_future_exception(fut):
|
def _handle_future_exception(fut):
|
||||||
@@ -765,7 +765,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
|||||||
self.lab_logger().info(f"动作 {action_name} 已取消")
|
self.lab_logger().info(f"动作 {action_name} 已取消")
|
||||||
return action_type.Result()
|
return action_type.Result()
|
||||||
|
|
||||||
self.lab_logger().info(f"动作执行完成: {action_name}")
|
##### self.lab_logger().info(f"动作执行完成: {action_name}")
|
||||||
del future
|
del future
|
||||||
|
|
||||||
# 向Host更新物料当前状态
|
# 向Host更新物料当前状态
|
||||||
@@ -801,7 +801,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
|||||||
|
|
||||||
# 发布结果
|
# 发布结果
|
||||||
goal_handle.succeed()
|
goal_handle.succeed()
|
||||||
self.lab_logger().info(f"设置动作成功: {action_name}")
|
##### self.lab_logger().info(f"设置动作成功: {action_name}")
|
||||||
|
|
||||||
result_values = {}
|
result_values = {}
|
||||||
for msg_name, attr_name in action_value_mapping["result"].items():
|
for msg_name, attr_name in action_value_mapping["result"].items():
|
||||||
@@ -820,7 +820,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
|||||||
elif attr_name == "return_info":
|
elif attr_name == "return_info":
|
||||||
setattr(result_msg, attr_name, serialize_result_info(execution_error, execution_success, action_return_value))
|
setattr(result_msg, attr_name, serialize_result_info(execution_error, execution_success, action_return_value))
|
||||||
|
|
||||||
self.lab_logger().info(f"动作 {action_name} 完成并返回结果")
|
##### self.lab_logger().info(f"动作 {action_name} 完成并返回结果")
|
||||||
return result_msg
|
return result_msg
|
||||||
|
|
||||||
return execute_callback
|
return execute_callback
|
||||||
|
|||||||
@@ -562,9 +562,9 @@ class HostNode(BaseROS2DeviceNode):
|
|||||||
for bridge in self.bridges:
|
for bridge in self.bridges:
|
||||||
if hasattr(bridge, "publish_device_status"):
|
if hasattr(bridge, "publish_device_status"):
|
||||||
bridge.publish_device_status(self.device_status, device_id, property_name)
|
bridge.publish_device_status(self.device_status, device_id, property_name)
|
||||||
#self.lab_logger().debug(
|
self.lab_logger().debug(
|
||||||
# f"[Host Node] Status updated: {device_id}.{property_name} = {msg.data}"
|
f"[Host Node] Status updated: {device_id}.{property_name} = {msg.data}"
|
||||||
#)
|
)
|
||||||
|
|
||||||
def send_goal(
|
def send_goal(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -183,25 +183,25 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
|
|||||||
goal = goal_handle.request
|
goal = goal_handle.request
|
||||||
protocol_kwargs = convert_from_ros_msg_with_mapping(goal, action_value_mapping["goal"])
|
protocol_kwargs = convert_from_ros_msg_with_mapping(goal, action_value_mapping["goal"])
|
||||||
|
|
||||||
# 🔧 添加调试信息
|
# # 🔧 添加调试信息
|
||||||
print(f"🔍 转换后的 protocol_kwargs: {protocol_kwargs}")
|
# print(f"🔍 转换后的 protocol_kwargs: {protocol_kwargs}")
|
||||||
print(f"🔍 vessel 在转换后: {protocol_kwargs.get('vessel', 'NOT_FOUND')}")
|
# print(f"🔍 vessel 在转换后: {protocol_kwargs.get('vessel', 'NOT_FOUND')}")
|
||||||
|
|
||||||
# 🔧 完全禁用Host查询,直接使用转换后的数据
|
# # 🔧 完全禁用Host查询,直接使用转换后的数据
|
||||||
print(f"🔧 跳过Host查询,直接使用转换后的数据")
|
# print(f"🔧 跳过Host查询,直接使用转换后的数据")
|
||||||
|
|
||||||
# 🔧 额外验证:确保vessel数据完整
|
# 🔧 额外验证:确保vessel数据完整
|
||||||
if 'vessel' in protocol_kwargs:
|
if 'vessel' in protocol_kwargs:
|
||||||
vessel_data = protocol_kwargs['vessel']
|
vessel_data = protocol_kwargs['vessel']
|
||||||
print(f"🔍 验证vessel数据: {vessel_data}")
|
#print(f"🔍 验证vessel数据: {vessel_data}")
|
||||||
|
|
||||||
# 如果vessel是空字典,尝试重新构建
|
# 如果vessel是空字典,尝试重新构建
|
||||||
if not vessel_data or (isinstance(vessel_data, dict) and not vessel_data):
|
if not vessel_data or (isinstance(vessel_data, dict) and not vessel_data):
|
||||||
print(f"⚠️ vessel数据为空,尝试从原始goal重新提取...")
|
# print(f"⚠️ vessel数据为空,尝试从原始goal重新提取...")
|
||||||
|
|
||||||
# 直接从原始goal提取vessel
|
# 直接从原始goal提取vessel
|
||||||
if hasattr(goal, 'vessel') and goal.vessel:
|
if hasattr(goal, 'vessel') and goal.vessel:
|
||||||
print(f"🔍 原始goal.vessel: {goal.vessel}")
|
# print(f"🔍 原始goal.vessel: {goal.vessel}")
|
||||||
# 手动转换vessel
|
# 手动转换vessel
|
||||||
vessel_data = {
|
vessel_data = {
|
||||||
'id': goal.vessel.id,
|
'id': goal.vessel.id,
|
||||||
@@ -212,16 +212,16 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
|
|||||||
'data': goal.vessel.data
|
'data': goal.vessel.data
|
||||||
}
|
}
|
||||||
protocol_kwargs['vessel'] = vessel_data
|
protocol_kwargs['vessel'] = vessel_data
|
||||||
print(f"✅ 手动重建vessel数据: {vessel_data}")
|
# print(f"✅ 手动重建vessel数据: {vessel_data}")
|
||||||
else:
|
else:
|
||||||
print(f"❌ 无法从原始goal提取vessel数据")
|
# print(f"❌ 无法从原始goal提取vessel数据")
|
||||||
# 创建一个基本的vessel
|
# 创建一个基本的vessel
|
||||||
vessel_data = {'id': 'default_vessel'}
|
vessel_data = {'id': 'default_vessel'}
|
||||||
protocol_kwargs['vessel'] = vessel_data
|
protocol_kwargs['vessel'] = vessel_data
|
||||||
print(f"🔧 创建默认vessel: {vessel_data}")
|
# print(f"🔧 创建默认vessel: {vessel_data}")
|
||||||
|
|
||||||
print(f"🔍 最终传递给协议的 protocol_kwargs: {protocol_kwargs}")
|
#print(f"🔍 最终传递给协议的 protocol_kwargs: {protocol_kwargs}")
|
||||||
print(f"🔍 最终的 vessel: {protocol_kwargs.get('vessel', 'NOT_FOUND')}")
|
#print(f"🔍 最终的 vessel: {protocol_kwargs.get('vessel', 'NOT_FOUND')}")
|
||||||
|
|
||||||
from unilabos.resources.graphio import physical_setup_graph
|
from unilabos.resources.graphio import physical_setup_graph
|
||||||
|
|
||||||
@@ -315,7 +315,7 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
|
|||||||
serialize_result_info(execution_error, execution_success, protocol_return_value),
|
serialize_result_info(execution_error, execution_success, protocol_return_value),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.lab_logger().info(f"协议 {protocol_name} 完成并返回结果")
|
self.lab_logger().info(f"🤩🤩🤩🤩🤩🤩协议 {protocol_name} 完成并返回结果😎😎😎😎😎😎")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
return execute_protocol
|
return execute_protocol
|
||||||
@@ -337,7 +337,7 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
|
|||||||
action_client = self._action_clients[action_id]
|
action_client = self._action_clients[action_id]
|
||||||
goal_msg = convert_to_ros_msg(action_client._action_type.Goal(), action_kwargs)
|
goal_msg = convert_to_ros_msg(action_client._action_type.Goal(), action_kwargs)
|
||||||
|
|
||||||
self.lab_logger().info(f"发送动作请求到: {action_id}")
|
##### self.lab_logger().info(f"发送动作请求到: {action_id}")
|
||||||
action_client.wait_for_server()
|
action_client.wait_for_server()
|
||||||
|
|
||||||
# 等待动作完成
|
# 等待动作完成
|
||||||
@@ -349,7 +349,7 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
result_future = await handle.get_result_async()
|
result_future = await handle.get_result_async()
|
||||||
self.lab_logger().info(f"动作完成: {action_name}")
|
##### self.lab_logger().info(f"动作完成: {action_name}")
|
||||||
|
|
||||||
return result_future.result
|
return result_future.result
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user