diff --git a/unilabos/app/register.py b/unilabos/app/register.py index 7d2f23c..40e4107 100644 --- a/unilabos/app/register.py +++ b/unilabos/app/register.py @@ -13,17 +13,17 @@ def register_devices_and_resources(mqtt_client, lab_registry): """ logger.info("[UniLab Register] 开始注册设备和资源...") - # # 注册设备信息 - # for device_info in lab_registry.obtain_registry_device_info(): - # mqtt_client.publish_registry(device_info["id"], device_info, False) - # logger.debug(f"[UniLab Register] 注册设备: {device_info['id']}") - # - # # 注册资源信息 - # for resource_info in lab_registry.obtain_registry_resource_info(): - # mqtt_client.publish_registry(resource_info["id"], resource_info, False) - # logger.debug(f"[UniLab Register] 注册资源: {resource_info['id']}") - # - # time.sleep(10) + # 注册设备信息 + for device_info in lab_registry.obtain_registry_device_info(): + mqtt_client.publish_registry(device_info["id"], device_info, False) + logger.debug(f"[UniLab Register] 注册设备: {device_info['id']}") + + # 注册资源信息 + for resource_info in lab_registry.obtain_registry_resource_info(): + mqtt_client.publish_registry(resource_info["id"], resource_info, False) + logger.debug(f"[UniLab Register] 注册资源: {resource_info['id']}") + + time.sleep(10) logger.info("[UniLab Register] 设备和资源注册完成.") diff --git a/unilabos/app/web/client.py b/unilabos/app/web/client.py index 9b9446a..09b6ede 100644 --- a/unilabos/app/web/client.py +++ b/unilabos/app/web/client.py @@ -40,7 +40,6 @@ class HTTPClient: Returns: Response: API响应对象 """ - return True response = requests.post( f"{self.remote_addr}/lab/resource/edge/batch_create/?database_process_later={1 if database_process_later else 0}", json=resources, @@ -61,7 +60,6 @@ class HTTPClient: Returns: Response: API响应对象 """ - return True response = requests.post( f"{self.remote_addr}/lab/resource/?database_process_later={1 if database_process_later else 0}", json=resources, diff --git a/unilabos/app/web/pages.py b/unilabos/app/web/pages.py index 51b109b..9f9a6c0 100644 --- a/unilabos/app/web/pages.py +++ b/unilabos/app/web/pages.py @@ -7,6 +7,7 @@ Web页面模块 import json import os import sys +import traceback from pathlib import Path from typing import Dict @@ -17,7 +18,7 @@ from jinja2 import Environment, FileSystemLoader from unilabos.config.config import BasicConfig from unilabos.registry.registry import lab_registry from unilabos.ros.msgs.message_converter import msg_converter_manager -from unilabos.utils.log import error +from unilabos.utils.log import error, debug from unilabos.utils.type_check import TypeEncoder from unilabos.app.web.utils.device_utils import get_registry_info from unilabos.app.web.utils.host_utils import get_host_node_info @@ -123,6 +124,7 @@ def setup_web_pages(router: APIRouter) -> None: return html except Exception as e: + debug(traceback.format_exc()) error(f"生成状态页面时出错: {str(e)}") raise HTTPException(status_code=500, detail=f"Error generating status page: {str(e)}") diff --git a/unilabos/registry/devices/liquid_handler.yaml b/unilabos/registry/devices/liquid_handler.yaml index 8fea092..eec9fc5 100644 --- a/unilabos/registry/devices/liquid_handler.yaml +++ b/unilabos/registry/devices/liquid_handler.yaml @@ -4899,7 +4899,7 @@ liquid_handler.biomek: none_keys: default: [] description: '参数: none_keys' - type: string + type: array protocol_author: description: '参数: protocol_author' type: string @@ -5287,7 +5287,7 @@ liquid_handler.biomek: none_keys: default: [] description: '参数: none_keys' - type: string + type: array offsets: description: '参数: offsets' type: string diff --git a/unilabos/registry/devices/virtual_device.yaml b/unilabos/registry/devices/virtual_device.yaml index 785b865..2538c57 100644 --- a/unilabos/registry/devices/virtual_device.yaml +++ b/unilabos/registry/devices/virtual_device.yaml @@ -402,7 +402,7 @@ virtual_column: properties: config: description: '参数: config' - type: string + type: object device_id: description: '参数: device_id' type: string @@ -1370,7 +1370,7 @@ virtual_heatchill: properties: config: description: '参数: config' - type: string + type: object device_id: description: '参数: device_id' type: string @@ -1893,7 +1893,7 @@ virtual_multiway_valve: module: unilabos.devices.virtual.virtual_multiway_valve:VirtualMultiwayValve status_types: current_position: int - get_available_ports: typing:Dict + get_available_ports: dict get_available_positions: list get_current_port: str get_current_position: int @@ -2372,7 +2372,7 @@ virtual_pump: properties: config: description: '参数: config' - type: string + type: object device_id: description: '参数: device_id' type: string @@ -3026,8 +3026,7 @@ virtual_solenoid_valve: auto-close: feedback: {} goal: {} - goal_default: - kwargs: null + goal_default: {} handles: [] result: {} schema: @@ -3036,12 +3035,8 @@ virtual_solenoid_valve: feedback: {} goal: description: UniLabJsonCommand close 的参数schema - properties: - kwargs: - description: '参数: kwargs' - type: string - required: - - kwargs + properties: {} + required: [] type: object result: {} required: @@ -3136,8 +3131,7 @@ virtual_solenoid_valve: auto-open: feedback: {} goal: {} - goal_default: - kwargs: null + goal_default: {} handles: [] result: {} schema: @@ -3146,12 +3140,8 @@ virtual_solenoid_valve: feedback: {} goal: description: UniLabJsonCommand open 的参数schema - properties: - kwargs: - description: '参数: kwargs' - type: string - required: - - kwargs + properties: {} + required: [] type: object result: {} required: @@ -3185,7 +3175,6 @@ virtual_solenoid_valve: goal: {} goal_default: command: null - kwargs: null handles: [] result: {} schema: @@ -3198,12 +3187,8 @@ virtual_solenoid_valve: command: description: '参数: command' type: string - kwargs: - description: '参数: kwargs' - type: string required: - command - - kwargs type: object result: {} required: @@ -3216,7 +3201,6 @@ virtual_solenoid_valve: goal: {} goal_default: command: null - kwargs: null handles: [] result: {} schema: @@ -3229,11 +3213,7 @@ virtual_solenoid_valve: command: description: '参数: command' type: string - kwargs: - description: '参数: kwargs' - type: string - required: - - kwargs + required: [] type: object result: {} required: @@ -3852,7 +3832,7 @@ virtual_stirrer: properties: config: description: '参数: config' - type: string + type: object device_id: description: '参数: device_id' type: string diff --git a/unilabos/ros/nodes/presets/host_node.py b/unilabos/ros/nodes/presets/host_node.py index cae1a9f..0cb3db7 100644 --- a/unilabos/ros/nodes/presets/host_node.py +++ b/unilabos/ros/nodes/presets/host_node.py @@ -459,6 +459,8 @@ class HostNode(BaseROS2DeviceNode): self.devices_instances[device_id] = d # noinspection PyProtectedMember for action_name, action_value_mapping in d._ros_node._action_value_mappings.items(): + if action_name.startswith("auto-"): + continue action_id = f"/devices/{device_id}/{action_name}" if action_id not in self._action_clients: action_type = action_value_mapping["type"] diff --git a/unilabos/utils/import_manager.py b/unilabos/utils/import_manager.py index b3968e1..ec1396b 100644 --- a/unilabos/utils/import_manager.py +++ b/unilabos/utils/import_manager.py @@ -216,15 +216,16 @@ class ImportManager: f"{module_path} 失败(将使用静态分析," f"建议修复导入错误,以实现更好的注册表识别效果!): {e}" ) - - # 尝试静态分析 - static_info = None - try: - static_info = self._get_static_class_info(module_path) - result["static_analysis_success"] = True - logger.debug(f"[ImportManager] 静态分析类 {module_path} 成功") - except Exception as e: - logger.warning(f"[ImportManager] 静态分析类 {module_path} 失败: {e}") + use_dynamic = False + if not use_dynamic: + # 尝试静态分析 + static_info = None + try: + static_info = self._get_static_class_info(module_path) + result["static_analysis_success"] = True + logger.debug(f"[ImportManager] 静态分析类 {module_path} 成功") + except Exception as e: + logger.warning(f"[ImportManager] 静态分析类 {module_path} 失败: {e}") # 合并信息(优先使用动态导入的信息) if dynamic_info: @@ -351,15 +352,32 @@ class ImportManager: return result def _analyze_method_signature(self, method) -> Dict[str, Any]: - """分析方法签名""" - signature = inspect.signature(method) + """ + 分析方法签名,提取具体的命名参数信息 + 注意:此方法会跳过*args和**kwargs,只提取具体的命名参数 + 这样可以确保通过**dict方式传参时的准确性 + + 示例用法: + method_info = self._analyze_method_signature(some_method) + params = {"param1": "value1", "param2": "value2"} + result = some_method(**params) # 安全的参数传递 + """ + signature = inspect.signature(method) args = [] num_required = 0 + for param_name, param in signature.parameters.items(): + # 跳过self参数 if param_name == "self": continue + # 跳过*args和**kwargs参数 + if param.kind == param.VAR_POSITIONAL: # *args + continue + if param.kind == param.VAR_KEYWORD: # **kwargs + continue + is_required = param.default == inspect.Parameter.empty if is_required: num_required += 1 @@ -392,10 +410,15 @@ class ImportManager: """将类型注解转换为字符串""" if annotation == inspect.Parameter.empty: return "Any" # 如果没有注解,返回Any - if annotation is None: return "None" # 明确的None类型 - + annotation_str = str(annotation) + # 处理typing模块的复杂类型 + if "typing." in annotation_str: + # 简化typing类型显示 + return ( + annotation_str.replace("typing.", "") if getattr(annotation, "_name", None) is None else annotation._name.lower() + ) # 如果是类型对象 if hasattr(annotation, "__name__"): # 如果是内置类型 @@ -404,22 +427,13 @@ class ImportManager: else: # 如果是自定义类,返回完整路径 return f"{annotation.__module__}:{annotation.__name__}" - # 如果是typing模块的类型 elif hasattr(annotation, "_name"): return annotation._name - # 如果是字符串形式的类型注解 elif isinstance(annotation, str): return annotation - - # 其他情况,尝试转换为字符串 else: - annotation_str = str(annotation) - # 处理typing模块的复杂类型 - if "typing." in annotation_str: - # 简化typing类型显示 - return annotation_str.replace("typing.", "") return annotation_str def _is_property_method(self, node: ast.FunctionDef) -> bool: