From d47dedbd0d5f686f8969c3fddcf82f868a614d8f Mon Sep 17 00:00:00 2001 From: Xuwznln <18435084+Xuwznln@users.noreply.github.com> Date: Wed, 19 Aug 2026 18:56:24 +0800 Subject: [PATCH] fix(registry): stabilize ROS distro action schema --- .github/workflows/ci-check.yml | 2 +- tests/ros/test_action_schema_compat.py | 24 +++++++++++++++ .../registry/devices/robot_linear_motion.yaml | 12 ++------ unilabos/registry/registry.py | 4 +-- unilabos/ros/msgs/message_converter.py | 29 ++++++++++++++++++- 5 files changed, 57 insertions(+), 14 deletions(-) create mode 100644 tests/ros/test_action_schema_compat.py diff --git a/.github/workflows/ci-check.yml b/.github/workflows/ci-check.yml index a07d5c648..d28ee26b1 100644 --- a/.github/workflows/ci-check.yml +++ b/.github/workflows/ci-check.yml @@ -109,7 +109,7 @@ jobs: call conda activate check-env call install\unilabos_msgs\setup.bat echo Running HostLink, ROS2 domain and networking runtime tests... - python -m pytest -q tests\hostlink tests\networking tests\basic tests\device_runtime tests\app\test_backend_selection.py tests\app\test_rclpy_dll_patch.py tests\registry\test_backend_metadata.py tests\ros\test_domain_init.py tests\ros\test_device_node_contract.py -p no:launch_testing -p no:launch_ros + python -m pytest -q tests\hostlink tests\networking tests\basic tests\device_runtime tests\app\test_backend_selection.py tests\app\test_rclpy_dll_patch.py tests\registry\test_backend_metadata.py tests\ros\test_action_schema_compat.py tests\ros\test_domain_init.py tests\ros\test_device_node_contract.py -p no:launch_testing -p no:launch_ros - name: Run check mode (AST registry validation) # check_mode 会真实 import 所有设备类(连带 matplotlib/opencv 等原生库)。 diff --git a/tests/ros/test_action_schema_compat.py b/tests/ros/test_action_schema_compat.py new file mode 100644 index 000000000..be37026e1 --- /dev/null +++ b/tests/ros/test_action_schema_compat.py @@ -0,0 +1,24 @@ +from unilabos.ros.msgs.message_converter import ( + NavigateThroughPoses, + ros_action_result_mapping, + ros_action_to_json_schema, +) + + +def test_navigate_through_poses_result_contract_is_distro_independent() -> None: + """Humble/Jazzy 的原始 Result 不同,但 UniLabOS 对外合同必须一致。""" + native_fields = set( + NavigateThroughPoses.Result.get_fields_and_field_types() + ) + assert native_fields in ({"result"}, {"error_code", "error_msg"}) + + assert ros_action_result_mapping(NavigateThroughPoses) == {} + result_schema = ros_action_to_json_schema(NavigateThroughPoses)["properties"][ + "result" + ] + assert result_schema == { + "title": "NavigateThroughPoses_Result", + "type": "object", + "properties": {}, + "additionalProperties": False, + } diff --git a/unilabos/registry/devices/robot_linear_motion.yaml b/unilabos/registry/devices/robot_linear_motion.yaml index 49ce7ae54..f8f1609ab 100644 --- a/unilabos/registry/devices/robot_linear_motion.yaml +++ b/unilabos/registry/devices/robot_linear_motion.yaml @@ -136,9 +136,7 @@ linear_motion.grbl: poses: [] handles: {} placeholder_keys: {} - result: - error_code: error_code - error_msg: error_msg + result: {} schema: description: '' properties: @@ -355,13 +353,7 @@ linear_motion.grbl: type: object result: additionalProperties: false - properties: - error_code: - maximum: 65535 - minimum: 0 - type: integer - error_msg: - type: string + properties: {} title: NavigateThroughPoses_Result type: object required: diff --git a/unilabos/registry/registry.py b/unilabos/registry/registry.py index 377d118db..95a9b3733 100644 --- a/unilabos/registry/registry.py +++ b/unilabos/registry/registry.py @@ -62,6 +62,7 @@ from unilabos.resources.site_definition import normalize_available_sites from unilabos.ros.msgs.message_converter import ( msg_converter_manager, + ros_action_result_mapping, ros_action_to_json_schema, String, ros_message_to_json_schema, @@ -2039,8 +2040,7 @@ def _load_single_device_file( pass try: if hasattr(action_type_obj, "Result"): - res_fields = action_type_obj.Result.get_fields_and_field_types() - entry_result = {f: f for f in res_fields} + entry_result = ros_action_result_mapping(action_type_obj) except Exception: pass try: diff --git a/unilabos/ros/msgs/message_converter.py b/unilabos/ros/msgs/message_converter.py index d69c997c3..9fa512b58 100644 --- a/unilabos/ros/msgs/message_converter.py +++ b/unilabos/ros/msgs/message_converter.py @@ -974,6 +974,33 @@ def ros_message_to_json_schema(msg_class: Any, field_name: str) -> Dict[str, Any return schema +def ros_action_result_mapping(action_class: Any) -> Dict[str, str]: + """生成 action Result 字段映射,并消除跨 ROS 发行版的不稳定字段。 + + ``NavigateThroughPoses.Result`` 在 Humble 中包装 ``std_msgs/Empty``,在 + Jazzy 中则是 ``error_code/error_msg``。UniLabOS 的轨迹动作不读取这些 + 字段,因此统一声明为无结果映射,避免共享 registry 随发行版来回变化。 + """ + if action_class is NavigateThroughPoses: + return {} + return { + field_name: field_name + for field_name in action_class.Result.get_fields_and_field_types() + } + + +def ros_action_result_to_json_schema(action_class: Any) -> Dict[str, Any]: + """生成可跨受支持 ROS 发行版复用的 action Result schema。""" + if action_class is NavigateThroughPoses: + return { + "title": action_class.Result.__name__, + "type": "object", + "properties": {}, + "additionalProperties": False, + } + return ros_message_to_json_schema(action_class.Result, action_class.Result.__name__) + + def ros_action_to_json_schema( action_class: Any, description="", previous_schema: Optional[Dict[str, Any]] = None ) -> Dict[str, Any]: @@ -1011,7 +1038,7 @@ def ros_action_to_json_schema( }, "result": { # 'description': 'Action 结果 - 完成后从服务器发送到客户端', - **ros_message_to_json_schema(action_class.Result, action_class.Result.__name__) + **ros_action_result_to_json_schema(action_class) }, }, "required": ["goal"],