diff --git a/unilabos/resources/graphio.py b/unilabos/resources/graphio.py index 507f8b6b..3a8d2e46 100644 --- a/unilabos/resources/graphio.py +++ b/unilabos/resources/graphio.py @@ -340,14 +340,14 @@ def convert_resources_to_type( Returns: List of resources in the given type. """ - if resource_type == dict: + if resource_type == dict or resource_type == str: return list_to_nested_dict(resources_list) elif isinstance(resource_type, type) and issubclass(resource_type, ResourcePLR): if isinstance(resources_list, dict): return resource_ulab_to_plr(resources_list, plr_model) resources_tree = dict_to_tree({r["id"]: r for r in resources_list}) return resource_ulab_to_plr(resources_tree[0], plr_model) - elif isinstance(resource_type, list) : + elif isinstance(resource_type, list): if all((get_origin(t) is Union) for t in resource_type): resources_tree = dict_to_tree({r["id"]: r for r in resources_list}) return [resource_ulab_to_plr(r, plr_model) for r in resources_tree] diff --git a/unilabos/ros/nodes/base_device_node.py b/unilabos/ros/nodes/base_device_node.py index 1ed4bd6e..199cae88 100644 --- a/unilabos/ros/nodes/base_device_node.py +++ b/unilabos/ros/nodes/base_device_node.py @@ -656,6 +656,7 @@ class BaseROS2DeviceNode(Node, Generic[T]): action_kwargs = convert_from_ros_msg_with_mapping(goal, action_value_mapping["goal"]) self.lab_logger().debug(f"任务 {ACTION.__name__} 接收到原始目标: {action_kwargs}") + error_skip = False # 向Host查询物料当前状态,如果是host本身的增加物料的请求,则直接跳过 if action_name not in ["create_resource_detailed", "create_resource"]: for k, v in goal.get_fields_and_field_types().items(): @@ -665,7 +666,7 @@ class BaseROS2DeviceNode(Node, Generic[T]): # TODO: resource后面需要分组 only_one_resource = False try: - if len(action_kwargs[k]) > 1: + if isinstance(action_kwargs[k], list) and len(action_kwargs[k]) > 1: for i in action_kwargs[k]: r = ResourceGet.Request() r.id = i["id"] # splash optional @@ -695,17 +696,43 @@ class BaseROS2DeviceNode(Node, Generic[T]): final_resource = convert_resources_to_type(resources_list, final_type) else: 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) + try: + action_kwargs[k] = self.resource_tracker.figure_resource(final_resource) + except Exception as e: + self.lab_logger().error(f"物料实例获取失败: {e}\n{traceback.format_exc()}") + error_skip = True + execution_error = traceback.format_exc() + break ##### self.lab_logger().info(f"准备执行: {action_kwargs}, 函数: {ACTION.__name__}") time_start = time.time() time_overall = 100 future = None - # 将阻塞操作放入线程池执行 - if asyncio.iscoroutinefunction(ACTION): - try: - ##### self.lab_logger().info(f"异步执行动作 {ACTION}") - future = ROS2DeviceNode.run_async_func(ACTION, trace_error=False, **action_kwargs) + if not error_skip: + # 将阻塞操作放入线程池执行 + if asyncio.iscoroutinefunction(ACTION): + try: + ##### self.lab_logger().info(f"异步执行动作 {ACTION}") + future = ROS2DeviceNode.run_async_func(ACTION, trace_error=False, **action_kwargs) + + def _handle_future_exception(fut): + nonlocal execution_error, execution_success, action_return_value + try: + action_return_value = fut.result() + execution_success = True + except Exception as e: + execution_error = traceback.format_exc() + error(f"异步任务 {ACTION.__name__} 报错了\n{traceback.format_exc()}\n原始输入:{action_kwargs}") + error(traceback.format_exc()) + + future.add_done_callback(_handle_future_exception) + except Exception as e: + execution_error = traceback.format_exc() + execution_success = False + self.lab_logger().error(f"创建异步任务失败: {traceback.format_exc()}") + else: + ##### self.lab_logger().info(f"同步执行动作 {ACTION}") + future = self._executor.submit(ACTION, **action_kwargs) def _handle_future_exception(fut): nonlocal execution_error, execution_success, action_return_value @@ -713,28 +740,9 @@ class BaseROS2DeviceNode(Node, Generic[T]): action_return_value = fut.result() execution_success = True except Exception as e: - execution_error = traceback.format_exc() - error(f"异步任务 {ACTION.__name__} 报错了\n{traceback.format_exc()}\n原始输入:{action_kwargs}") - error(traceback.format_exc()) + error(f"同步任务 {ACTION.__name__} 报错了\n{traceback.format_exc()}\n原始输入:{action_kwargs}") future.add_done_callback(_handle_future_exception) - except Exception as e: - execution_error = traceback.format_exc() - execution_success = False - self.lab_logger().error(f"创建异步任务失败: {traceback.format_exc()}") - else: - ##### self.lab_logger().info(f"同步执行动作 {ACTION}") - future = self._executor.submit(ACTION, **action_kwargs) - - def _handle_future_exception(fut): - nonlocal execution_error, execution_success, action_return_value - try: - action_return_value = fut.result() - execution_success = True - except Exception as e: - error(f"同步任务 {ACTION.__name__} 报错了\n{traceback.format_exc()}\n原始输入:{action_kwargs}") - - future.add_done_callback(_handle_future_exception) action_type = action_value_mapping["type"] feedback_msg_types = action_type.Feedback.get_fields_and_field_types() diff --git a/unilabos/ros/nodes/presets/protocol_node.py b/unilabos/ros/nodes/presets/protocol_node.py index ddb08451..23462142 100644 --- a/unilabos/ros/nodes/presets/protocol_node.py +++ b/unilabos/ros/nodes/presets/protocol_node.py @@ -146,6 +146,7 @@ class ROS2ProtocolNode(BaseROS2DeviceNode): # 为子设备的每个动作创建动作客户端 if d is not None and hasattr(d, "ros_node_instance"): node = d.ros_node_instance + node.resource_tracker = self.resource_tracker # 站内应当共享资源跟踪器 for action_name, action_mapping in node._action_value_mappings.items(): if action_name.startswith("auto-") or str(action_mapping.get("type", "")).startswith("UniLabJsonCommand"): continue diff --git a/unilabos/ros/nodes/resource_tracker.py b/unilabos/ros/nodes/resource_tracker.py index e6054ef0..45355f0d 100644 --- a/unilabos/ros/nodes/resource_tracker.py +++ b/unilabos/ros/nodes/resource_tracker.py @@ -37,6 +37,8 @@ class DeviceNodeResourceTracker(object): def figure_resource(self, query_resource, try_mode=False): if isinstance(query_resource, list): return [self.figure_resource(r) for r in query_resource] + elif isinstance(query_resource, dict) and "id" not in query_resource and "name" not in query_resource: # 临时处理,要删除的,driver有太多类型错误标注 + return [self.figure_resource(r) for r in query_resource.values()] res_id = query_resource.id if hasattr(query_resource, "id") else (query_resource.get("id") if isinstance(query_resource, dict) else None) res_name = query_resource.name if hasattr(query_resource, "name") else (query_resource.get("name") if isinstance(query_resource, dict) else None) res_identifier = res_id if res_id else res_name diff --git a/unilabos/ros/utils/driver_creator.py b/unilabos/ros/utils/driver_creator.py index c76cc3b4..862f04c1 100644 --- a/unilabos/ros/utils/driver_creator.py +++ b/unilabos/ros/utils/driver_creator.py @@ -51,7 +51,7 @@ class DeviceClassCreator(Generic[T]): """ if self.device_instance is not None: for c in self.children.values(): - if c["type"] == "container": + if c["type"] != "device": self.resource_tracker.add_resource(c)