fix protocol node log_message, added create_resource return value

This commit is contained in:
Xuwznln
2025-09-19 05:31:49 +08:00
parent 01f8816597
commit da4bd927e0
7 changed files with 43 additions and 17 deletions

View File

@@ -1093,7 +1093,7 @@ class WebSocketClient(BaseCommunicationClient):
}, },
} }
self.message_processor.send_message(message) self.message_processor.send_message(message)
logger.debug(f"[WebSocketClient] Device status published: {device_id}.{property_name}") logger.trace(f"[WebSocketClient] Device status published: {device_id}.{property_name}")
def publish_job_status( def publish_job_status(
self, feedback_data: dict, item: QueueItem, status: str, return_info: Optional[dict] = None self, feedback_data: dict, item: QueueItem, status: str, return_info: Optional[dict] = None

View File

@@ -294,7 +294,7 @@ class Registry:
logger.warning(f"[UniLab Registry] 设备 {device_id}{field_name} 类型为空,跳过替换") logger.warning(f"[UniLab Registry] 设备 {device_id}{field_name} 类型为空,跳过替换")
return type_name return type_name
convert_manager = { # 将python基本对象转为ros2基本对象 convert_manager = { # 将python基本对象转为ros2基本对象
"builtins:str": "String", "str": "String",
"bool": "Bool", "bool": "Bool",
"int": "Int64", "int": "Int64",
"float": "Float64", "float": "Float64",

View File

@@ -430,7 +430,7 @@ def resource_ulab_to_plr(resource: dict, plr_model=False) -> "ResourcePLR":
return resource_plr return resource_plr
def resource_plr_to_ulab(resource_plr: "ResourcePLR", parent_name: str = None): def resource_plr_to_ulab(resource_plr: "ResourcePLR", parent_name: str = None, with_children=True):
def replace_plr_type_to_ulab(source: str): def replace_plr_type_to_ulab(source: str):
replace_info = { replace_info = {
"plate": "plate", "plate": "plate",
@@ -445,12 +445,12 @@ def resource_plr_to_ulab(resource_plr: "ResourcePLR", parent_name: str = None):
else: else:
print("转换pylabrobot的时候出现未知类型", source) print("转换pylabrobot的时候出现未知类型", source)
return "container" return "container"
def resource_plr_to_ulab_inner(d: dict, all_states: dict) -> dict: def resource_plr_to_ulab_inner(d: dict, all_states: dict, child=True) -> dict:
r = { r = {
"id": d["name"], "id": d["name"],
"name": d["name"], "name": d["name"],
"sample_id": None, "sample_id": None,
"children": [resource_plr_to_ulab_inner(child, all_states) for child in d["children"]], "children": [resource_plr_to_ulab_inner(child, all_states) for child in d["children"]] if child else [],
"parent": d["parent_name"] if d["parent_name"] else parent_name if parent_name else None, "parent": d["parent_name"] if d["parent_name"] else parent_name if parent_name else None,
"type": replace_plr_type_to_ulab(d.get("category")), # FIXME plr自带的type是python class name "type": replace_plr_type_to_ulab(d.get("category")), # FIXME plr自带的type是python class name
"class": d.get("class", ""), "class": d.get("class", ""),
@@ -465,7 +465,7 @@ def resource_plr_to_ulab(resource_plr: "ResourcePLR", parent_name: str = None):
return r return r
d = resource_plr.serialize() d = resource_plr.serialize()
all_states = resource_plr.serialize_all_state() all_states = resource_plr.serialize_all_state()
r = resource_plr_to_ulab_inner(d, all_states) r = resource_plr_to_ulab_inner(d, all_states, with_children)
return r return r

View File

@@ -12,6 +12,7 @@ import asyncio
import rclpy import rclpy
import yaml import yaml
from msgcenterpy import ROS2MessageInstance
from rclpy.node import Node from rclpy.node import Node
from rclpy.action import ActionServer, ActionClient from rclpy.action import ActionServer, ActionClient
from rclpy.action.server import ServerGoalHandle from rclpy.action.server import ServerGoalHandle
@@ -177,7 +178,7 @@ class PropertyPublisher:
try: try:
self.publisher_ = node.create_publisher(msg_type, f"{name}", 10) self.publisher_ = node.create_publisher(msg_type, f"{name}", 10)
except AttributeError as ex: except AttributeError as ex:
logger.error(f"创建发布者失败,可能由于注册表有误,类型: {msg_type},错误: {ex}\n{traceback.format_exc()}") self.node.lab_logger().error(f"创建发布者 {name} 失败,可能由于注册表有误,类型: {msg_type},错误: {ex}\n{traceback.format_exc()}")
self.timer = node.create_timer(self.timer_period, self.publish_property) self.timer = node.create_timer(self.timer_period, self.publish_property)
self.__loop = get_event_loop() self.__loop = get_event_loop()
str_msg_type = str(msg_type)[8:-2] str_msg_type = str(msg_type)[8:-2]
@@ -399,9 +400,13 @@ class BaseROS2DeviceNode(Node, Generic[T]):
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}")
response = await rclient.call_async(request) response: ResourceAdd.Response = await rclient.call_async(request)
# 应该先add_resource了 # 应该先add_resource了
res.response = "OK" final_response = {
"created_resources": [ROS2MessageInstance(i).get_python_dict() for i in request.resources],
"liquid_input_resources": []
}
res.response = json.dumps(final_response)
# 如果driver自己就有assign的方法那就使用driver自己的assign方法 # 如果driver自己就有assign的方法那就使用driver自己的assign方法
if hasattr(self.driver_instance, "create_resource"): if hasattr(self.driver_instance, "create_resource"):
create_resource_func = getattr(self.driver_instance, "create_resource") create_resource_func = getattr(self.driver_instance, "create_resource")
@@ -418,7 +423,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
) )
res.response = get_result_info_str("", True, ret) res.response = get_result_info_str("", True, ret)
except Exception as e: except Exception as e:
traceback.print_exc() self.lab_logger().error(f"运行设备的create_resource出错{create_resource_func}\n{traceback.format_exc()}")
res.response = get_result_info_str(traceback.format_exc(), False, {}) res.response = get_result_info_str(traceback.format_exc(), False, {})
return res return res
# 接下来该根据bind_parent_id进行assign了目前只有plr可以进行assign不然没有办法输入到物料系统中 # 接下来该根据bind_parent_id进行assign了目前只有plr可以进行assign不然没有办法输入到物料系统中
@@ -438,6 +443,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
# resources.list() # resources.list()
resources_tree = dict_to_tree(copy.deepcopy({r["id"]: r for r in resources})) resources_tree = dict_to_tree(copy.deepcopy({r["id"]: r for r in resources}))
plr_instance = resource_ulab_to_plr(resources_tree[0], contain_model) plr_instance = resource_ulab_to_plr(resources_tree[0], contain_model)
if isinstance(plr_instance, Plate): if isinstance(plr_instance, Plate):
empty_liquid_info_in = [(None, 0)] * plr_instance.num_items empty_liquid_info_in = [(None, 0)] * plr_instance.num_items
for liquid_type, liquid_volume, liquid_input_slot in zip( for liquid_type, liquid_volume, liquid_input_slot in zip(
@@ -445,6 +451,11 @@ class BaseROS2DeviceNode(Node, Generic[T]):
): ):
empty_liquid_info_in[liquid_input_slot] = (liquid_type, liquid_volume) empty_liquid_info_in[liquid_input_slot] = (liquid_type, liquid_volume)
plr_instance.set_well_liquids(empty_liquid_info_in) plr_instance.set_well_liquids(empty_liquid_info_in)
input_wells_ulr = [
convert_to_ros_msg(Resource, resource_plr_to_ulab(plr_instance.get_well(LIQUID_INPUT_SLOT), with_children=False)) for r in LIQUID_INPUT_SLOT
]
final_response["liquid_input_resources"] = [ROS2MessageInstance(i).get_python_dict() for i in input_wells_ulr]
res.response = json.dumps(final_response)
if isinstance(resource, OTDeck) and "slot" in other_calling_param: if isinstance(resource, OTDeck) and "slot" in other_calling_param:
other_calling_param["slot"] = int(other_calling_param["slot"]) other_calling_param["slot"] = int(other_calling_param["slot"])
resource.assign_child_at_slot(plr_instance, **other_calling_param) resource.assign_child_at_slot(plr_instance, **other_calling_param)

View File

@@ -13,6 +13,7 @@ from geometry_msgs.msg import Point
from rclpy.action import ActionClient, get_action_server_names_and_types_by_node from rclpy.action import ActionClient, get_action_server_names_and_types_by_node
from rclpy.callback_groups import ReentrantCallbackGroup from rclpy.callback_groups import ReentrantCallbackGroup
from rclpy.service import Service from rclpy.service import Service
from rosidl_runtime_py import set_message_fields
from unilabos_msgs.msg import Resource # type: ignore from unilabos_msgs.msg import Resource # type: ignore
from unilabos_msgs.srv import ( from unilabos_msgs.srv import (
ResourceAdd, ResourceAdd,
@@ -366,7 +367,7 @@ class HostNode(BaseROS2DeviceNode):
bind_parent_ids: list[str], bind_parent_ids: list[str],
bind_locations: list[Point], bind_locations: list[Point],
other_calling_params: list[str], other_calling_params: list[str],
): ) -> List[str]:
responses = [] responses = []
for resource, device_id, bind_parent_id, bind_location, other_calling_param in zip( for resource, device_id, bind_parent_id, bind_location, other_calling_param in zip(
resources, device_ids, bind_parent_ids, bind_locations, other_calling_params resources, device_ids, bind_parent_ids, bind_locations, other_calling_params
@@ -403,8 +404,8 @@ class HostNode(BaseROS2DeviceNode):
}, },
ensure_ascii=False, ensure_ascii=False,
) )
response = await sclient.call_async(request) response: SerialCommand.Response = await sclient.call_async(request)
responses.append(response) responses.append(response.response)
return responses return responses
async def create_resource( async def create_resource(
@@ -463,11 +464,23 @@ class HostNode(BaseROS2DeviceNode):
) )
] ]
response = await self.create_resource_detailed( response: List[str] = await self.create_resource_detailed(
resources, device_ids, bind_parent_id, bind_location, other_calling_param resources, device_ids, bind_parent_id, bind_location, other_calling_param
) )
return response try:
new_li = []
for i in response:
res = json.loads(i)
new_li.append(res)
return {
"resources": new_li,
"liquid_input_resources": new_li
}
except Exception as ex:
pass
_n = "\n"
raise ValueError(f"创建资源时失败!\n{_n.join(response)}")
def initialize_device(self, device_id: str, device_config: Dict[str, Any]) -> None: def initialize_device(self, device_id: str, device_config: Dict[str, Any]) -> None:
""" """

View File

@@ -218,7 +218,7 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
[convert_from_ros_msg(rs) for rs in response.resources] [convert_from_ros_msg(rs) for rs in response.resources]
) )
self.lab_logger().info(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
@@ -325,6 +325,8 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
async def execute_single_action(self, device_id, action_name, action_kwargs): async def execute_single_action(self, device_id, action_name, action_kwargs):
"""执行单个动作""" """执行单个动作"""
# 构建动作ID # 构建动作ID
if action_name == "log_message":
self.lab_logger().info(f"[Protocol Log] {action_kwargs}")
if device_id in ["", None, "self"]: if device_id in ["", None, "self"]:
action_id = f"/devices/{self.device_id}/{action_name}" action_id = f"/devices/{self.device_id}/{action_name}"
else: else:

View File

@@ -19,7 +19,7 @@ class EnvironmentChecker:
# 包导入名 : pip安装名 # 包导入名 : pip安装名
# "pymodbus.framer.FramerType": "pymodbus==3.9.2", # "pymodbus.framer.FramerType": "pymodbus==3.9.2",
"websockets": "websockets", "websockets": "websockets",
"paho.mqtt": "paho-mqtt", "msgcenterpy": "msgcenterpy",
"opentrons_shared_data": "opentrons_shared_data", "opentrons_shared_data": "opentrons_shared_data",
} }