From 1d1c1367df1a8592c979fedb37e3ad1a36973451 Mon Sep 17 00:00:00 2001 From: Xuwznln <18435084+Xuwznln@users.noreply.github.com> Date: Thu, 9 Apr 2026 14:15:38 +0800 Subject: [PATCH] scale multi exec thread up to 48 --- .cursor/skills/create-device-skill/SKILL.md | 4 +++- unilabos/app/web/client.py | 6 ++++-- unilabos/ros/main_slave_run.py | 5 +++-- unilabos/workflow/common.py | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.cursor/skills/create-device-skill/SKILL.md b/.cursor/skills/create-device-skill/SKILL.md index 7361a09e..6ce54450 100644 --- a/.cursor/skills/create-device-skill/SKILL.md +++ b/.cursor/skills/create-device-skill/SKILL.md @@ -203,6 +203,8 @@ API 模板结构: # - #10 查询任务状态 GET /lab/mcp/task/{task_uuid} # - #11 运行工作流单节点 POST /lab/mcp/run/workflow/action # - #12 获取资源树 GET /lab/material/download/{lab_uuid} +# - #13 获取工作流模板详情 GET /lab/workflow/template/detail/{workflow_uuid} +# 返回 workflow 完整结构:data.nodes[] 含每个节点的 uuid、name、param、device_name、handles ## Placeholder Slot 填写规则 - unilabos_resources → ResourceSlot → {"id":"/path/name","name":"name","uuid":"xxx"} @@ -219,7 +221,7 @@ API 模板结构: ### Step 5 — 验证 检查文件完整性: -- [ ] `SKILL.md` 包含 API endpoint(#1 获取 lab_uuid、#2-#7 工作流/节点/边、#8-#11 运行/查询、#12 资源树) +- [ ] `SKILL.md` 包含 API endpoint(#1 获取 lab_uuid、#2-#7 工作流/节点/边、#8-#11 运行/查询、#12 资源树、#13 工作流模板详情) - [ ] `SKILL.md` 包含 Placeholder Slot 填写规则(ResourceSlot / DeviceSlot / NodeSlot / ClassSlot + create_resource 特例)和本设备的 Slot 字段表 - [ ] `action-index.md` 列出所有 action 并有描述 - [ ] `actions/` 目录中每个 action 有对应 JSON 文件 diff --git a/unilabos/app/web/client.py b/unilabos/app/web/client.py index b1cc67eb..1dd056ae 100644 --- a/unilabos/app/web/client.py +++ b/unilabos/app/web/client.py @@ -80,19 +80,20 @@ class HTTPClient: f.write(json.dumps(payload, indent=4)) # 从序列化数据中提取所有节点的UUID(保存旧UUID) old_uuids = {n.res_content.uuid: n for n in resources.all_nodes} + nodes_info = [x for xs in resources.dump() for x in xs] if not self.initialized or first_add: self.initialized = True info(f"首次添加资源,当前远程地址: {self.remote_addr}") response = requests.post( f"{self.remote_addr}/edge/material", - json={"nodes": [x for xs in resources.dump() for x in xs], "mount_uuid": mount_uuid}, + json={"nodes": nodes_info, "mount_uuid": mount_uuid}, headers={"Authorization": f"Lab {self.auth}"}, timeout=60, ) else: response = requests.put( f"{self.remote_addr}/edge/material", - json={"nodes": [x for xs in resources.dump() for x in xs], "mount_uuid": mount_uuid}, + json={"nodes": nodes_info, "mount_uuid": mount_uuid}, headers={"Authorization": f"Lab {self.auth}"}, timeout=10, ) @@ -111,6 +112,7 @@ class HTTPClient: uuid_mapping[i["uuid"]] = i["cloud_uuid"] else: logger.error(f"添加物料失败: {response.text}") + logger.trace(f"添加物料失败: {nodes_info}") for u, n in old_uuids.items(): if u in uuid_mapping: n.res_content.uuid = uuid_mapping[u] diff --git a/unilabos/ros/main_slave_run.py b/unilabos/ros/main_slave_run.py index c24f9e8e..7dca43e8 100644 --- a/unilabos/ros/main_slave_run.py +++ b/unilabos/ros/main_slave_run.py @@ -1,4 +1,5 @@ import json +import os # from nt import device_encoding import threading @@ -61,7 +62,7 @@ def main( rclpy.init(args=rclpy_init_args) else: logger.info("[ROS] rclpy already initialized, reusing context") - executor = rclpy.__executor = MultiThreadedExecutor() + executor = rclpy.__executor = MultiThreadedExecutor(num_threads=max(os.cpu_count() * 4, 48)) # 创建主机节点 host_node = HostNode( "host_node", @@ -122,7 +123,7 @@ def slave( rclpy.init(args=rclpy_init_args) executor = rclpy.__executor if not executor: - executor = rclpy.__executor = MultiThreadedExecutor() + executor = rclpy.__executor = MultiThreadedExecutor(num_threads=max(os.cpu_count() * 4, 48)) # 1.5 启动 executor 线程 thread = threading.Thread(target=executor.spin, daemon=True, name="slave_executor_thread") diff --git a/unilabos/workflow/common.py b/unilabos/workflow/common.py index 3e2fec92..e0efad56 100644 --- a/unilabos/workflow/common.py +++ b/unilabos/workflow/common.py @@ -346,7 +346,7 @@ def refactor_data( "template_name": template_name, "resource_name": resource_name, "description": step.get("description", step.get("purpose", f"{operation} operation")), - "lab_node_type": "Device", + "lab_node_type": "ILab", "param": step.get("parameters", step.get("action_args", {})), "footer": f"{template_name}-{resource_name}", }