From 32e370a562eef65ebff37ccfb27a22ec0cfb15de Mon Sep 17 00:00:00 2001 From: wznln <18435084+Xuwznln@users.noreply.github.com> Date: Tue, 6 May 2025 16:24:19 +0800 Subject: [PATCH] add: bind_parent_ids to resource create action fix: message convert string --- unilabos/app/web/utils/action_utils.py | 9 +++-- unilabos/ros/nodes/base_device_node.py | 38 +++++++++---------- unilabos/ros/nodes/presets/host_node.py | 14 +++---- .../action/ResourceCreateFromOuter.action | 1 + 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/unilabos/app/web/utils/action_utils.py b/unilabos/app/web/utils/action_utils.py index 1af458f5..be2baa3f 100644 --- a/unilabos/app/web/utils/action_utils.py +++ b/unilabos/app/web/utils/action_utils.py @@ -8,7 +8,7 @@ import traceback from typing import Dict, Any, Type, TypedDict, Optional from rclpy.action import ActionClient, ActionServer -from rosidl_parser.definition import UnboundedSequence, NamespacedType, BasicType +from rosidl_parser.definition import UnboundedSequence, NamespacedType, BasicType, UnboundedString from unilabos.ros.msgs.message_converter import msg_converter_manager from unilabos.ros.nodes.base_device_node import BaseROS2DeviceNode @@ -74,7 +74,6 @@ def get_yaml_from_goal_type(goal_type) -> str: for ind, slot_info in enumerate(goal_type._fields_and_field_types.items()): slot_name, slot_type = slot_info type_info = goal_type.SLOT_TYPES[ind] - default_value = "unknown" if isinstance(type_info, UnboundedSequence): inner_type = type_info.value_type if isinstance(inner_type, NamespacedType): @@ -83,8 +82,10 @@ def get_yaml_from_goal_type(goal_type) -> str: default_value = [get_ros_msg_instance_as_dict(type_class())] elif isinstance(inner_type, BasicType): default_value = [get_default_value_for_ros_type(inner_type.typename)] + elif isinstance(inner_type, UnboundedString): + default_value = [""] else: - default_value = "unknown" + default_value = [] elif isinstance(type_info, NamespacedType): cls_name = ".".join(type_info.namespaces) + ":" + type_info.name type_class = msg_converter_manager.get_class(cls_name) @@ -93,6 +94,8 @@ def get_yaml_from_goal_type(goal_type) -> str: default_value = get_ros_msg_instance_as_dict(type_class()) elif isinstance(type_info, BasicType): default_value = get_default_value_for_ros_type(type_info.typename) + elif isinstance(type_info, UnboundedString): + default_value = "" else: type_class = msg_converter_manager.search_class(slot_type, search_lower=True) if type_class is not None: diff --git a/unilabos/ros/nodes/base_device_node.py b/unilabos/ros/nodes/base_device_node.py index 92e0e479..24ee15b6 100644 --- a/unilabos/ros/nodes/base_device_node.py +++ b/unilabos/ros/nodes/base_device_node.py @@ -439,26 +439,26 @@ class BaseROS2DeviceNode(Node, Generic[T]): action_kwargs = convert_from_ros_msg_with_mapping(goal, action_value_mapping["goal"]) self.lab_logger().debug(f"接收到原始目标: {action_kwargs}") - # 向Host查询物料当前状态,如果是host本身的增加物料的请求,则直接跳过 - for k, v in goal.get_fields_and_field_types().items(): - if v in ["unilabos_msgs/Resource", "sequence"]: - self.lab_logger().info(f"查询资源状态: Key: {k} Type: {v}") - try: - r = ResourceGet.Request() - r.id = action_kwargs[k]["id"] if v == "unilabos_msgs/Resource" else action_kwargs[k][0]["id"] - r.with_children = True - response = await self._resource_clients["resource_get"].call_async(r) - except Exception: - logger.error(f"资源查询失败,默认使用本地资源") - # 删除对response.resources的检查,因为它总是存在 - resources_list = [convert_from_ros_msg(rs) for rs in response.resources] # type: ignore # FIXME - self.lab_logger().debug(f"资源查询结果: {len(resources_list)} 个资源") - type_hint = action_paramtypes[k] - final_type = get_type_class(type_hint) - # 判断 ACTION 是否需要特殊的物料类型如 pylabrobot.resources.Resource,并做转换 - final_resource = convert_resources_to_type(resources_list, final_type) - action_kwargs[k] = self.resource_tracker.figure_resource(final_resource) + if action_name != "add_resource_from_outer": + for k, v in goal.get_fields_and_field_types().items(): + if v in ["unilabos_msgs/Resource", "sequence"]: + self.lab_logger().info(f"查询资源状态: Key: {k} Type: {v}") + try: + r = ResourceGet.Request() + r.id = action_kwargs[k]["id"] if v == "unilabos_msgs/Resource" else action_kwargs[k][0]["id"] + r.with_children = True + response = await self._resource_clients["resource_get"].call_async(r) + except Exception: + logger.error(f"资源查询失败,默认使用本地资源") + # 删除对response.resources的检查,因为它总是存在 + resources_list = [convert_from_ros_msg(rs) for rs in response.resources] # type: ignore # FIXME + self.lab_logger().debug(f"资源查询结果: {len(resources_list)} 个资源") + type_hint = action_paramtypes[k] + final_type = get_type_class(type_hint) + # 判断 ACTION 是否需要特殊的物料类型如 pylabrobot.resources.Resource,并做转换 + final_resource = convert_resources_to_type(resources_list, final_type) + action_kwargs[k] = self.resource_tracker.figure_resource(final_resource) self.lab_logger().info(f"准备执行: {action_kwargs}, 函数: {ACTION.__name__}") time_start = time.time() diff --git a/unilabos/ros/nodes/presets/host_node.py b/unilabos/ros/nodes/presets/host_node.py index 88274b65..e694268e 100644 --- a/unilabos/ros/nodes/presets/host_node.py +++ b/unilabos/ros/nodes/presets/host_node.py @@ -7,11 +7,12 @@ import uuid from typing import Optional, Dict, Any, List, ClassVar, Set from action_msgs.msg import GoalStatus -from unilabos_msgs.msg import Resource # type: ignore -from unilabos_msgs.srv import ResourceAdd, ResourceGet, ResourceDelete, ResourceUpdate, ResourceList, SerialCommand # type: ignore from rclpy.action import ActionClient, get_action_server_names_and_types_by_node from rclpy.callback_groups import ReentrantCallbackGroup from rclpy.service import Service +from unilabos_msgs.msg import Resource # type: ignore +from unilabos_msgs.srv import ResourceAdd, ResourceGet, ResourceDelete, ResourceUpdate, ResourceList, \ + SerialCommand # type: ignore from unique_identifier_msgs.msg import UUID from unilabos.registry.registry import lab_registry @@ -23,11 +24,9 @@ from unilabos.ros.msgs.message_converter import ( convert_from_ros_msg, convert_to_ros_msg, msg_converter_manager, - ros_action_to_json_schema, ) from unilabos.ros.nodes.base_device_node import BaseROS2DeviceNode, ROS2DeviceNode, DeviceNodeResourceTracker from unilabos.ros.nodes.presets.controller_node import ControllerNode -from unilabos.utils.type_check import TypeEncoder class HostNode(BaseROS2DeviceNode): @@ -76,7 +75,7 @@ class HostNode(BaseROS2DeviceNode): driver_instance=self, device_id=device_id, status_types={}, - action_value_mappings={}, + action_value_mappings=lab_registry.device_type_registry["host_node"]["class"]["action_value_mappings"], hardware_interface={}, print_publish=False, resource_tracker=DeviceNodeResourceTracker(), # host node并不是通过initialize 包一层传进来的 @@ -268,8 +267,9 @@ class HostNode(BaseROS2DeviceNode): except Exception as e: self.lab_logger().error(f"[Host Node] Failed to create ActionClient for {action_id}: {str(e)}") - def add_resource_from_outer(self, resources: list["Resource"], device_ids): - print("111") + def add_resource_from_outer(self, resources: list["Resource"], device_ids: list[str], bind_parent_ids: list[str]): + for resource, device_id, bind_parent_id in zip(resources, device_ids, bind_parent_ids): + print("111") pass def initialize_device(self, device_id: str, device_config: Dict[str, Any]) -> None: diff --git a/unilabos_msgs/action/ResourceCreateFromOuter.action b/unilabos_msgs/action/ResourceCreateFromOuter.action index 8519158b..daf67c7d 100644 --- a/unilabos_msgs/action/ResourceCreateFromOuter.action +++ b/unilabos_msgs/action/ResourceCreateFromOuter.action @@ -1,5 +1,6 @@ Resource[] resources string[] device_ids +string[] bind_parent_ids --- bool success --- \ No newline at end of file