company-desktop/company-desktop-computer/feat-cad-mcp-frame AGit
main
移植已完成(R230 ARX + Z2026 ZRX 均编译通过并部署),本文记录:
把这条线的结论收成一个文档,避免散落在多个 PR 的分析里。 内容: - 优化前后数字:开图墙钟 113 s -> 93 s,截面恢复 22.4 s -> 9.4 s, 开图期回写 1140 -> 0,临时文件 6773 -> 1733 - 剩下时间的归属(优化后 81,046 采样): base_ViewportDraw 之下 74.6%,Parasolid 67.5%, 且 Parasolid 的 93.4% 发生在我们的视口绘制里;我们的 arx 自耗时 <1% - 结论:剩余成本是"构件数量 x 单构件建模成本", 既不是 CAD 自己在拖时间(绘制之外只剩 6.6%),也不是我们的代码慢(<1%)。 一次开图要建 1335 个 AcDbX3dSolid + 1248 个 AecDbX3dPart,计数器验证每个只建一次。 - 还能动的方向按性价比排序:减少要建的几何(LOD)、几何跨会话持久化(Phase 3)、 用基本体替代扫掠、截面再优化(已判定不做,收益 <0.5%) - 诊断方法与踩过的坑:采样算不出次数必须用计数器;加密构建下函数级符号化不可靠 (GuardBar::Draw3d 报 27% 实为误判);VSDiagnostics 本机会卡死、wpr -stop 有 COM bug, 改用 xperf on/stop/merge;RVA 必须用 PDB 解析再下结论;计数器别写进同一份报告文件 验证:纯文档,数据来自 tools/run_acad_perf.ps1 + tools/perf_diagsession.py 的实测。
源工程在 d:\new-start\CadProject\src\cad_mcp_frame(vcpkg 独立工程), 本次按 code2026 的 preset/CMake 体系移植到 SourceCode/Code2026/cad_mcp_frame。 移植改动: - 源码 + .def + .rc + res/ + cad-mcp-frame.json 拷入 cad_mcp_frame/; 插件接口头 mcp_plugin_api.h 放到 cad_mcp_frame/inc/ - cadsdk.h -> KTCad.h(两者除尾部 4 个 include 与 using namespace MemOper 外完全一致) - nlohmann/json 单头放 SourceCode/dep_inc/nlohmann/json.hpp, ghc::filesystem 单头放 SourceCode/dep_inc/ghc/filesystem.hpp; target_link_objectarx 已把 DEP_INC_ROOT 加进 include 路径,故无需额外配置 - 新增 cad_mcp_frame/cmakelists.txt:按 PipeHangerCmd 的写法 (add_def_file_name / target_set_name_and_suffix .arx / target_link_objectarx / target_include_directories inc / set_debug_post_build) - 补 SUB_ARX 宏:原工程由 CadProject 的 cmake 注入,code2026 只注入 ARX; 在模块自己的 CMake 里补,不动共享 functions.cmake(中望给 0) - 顶层挂载 add_custom_module_2(cad_mcp_frame CadMcpFrame "SH/Other"), 并加条件:nlohmann/json 与 ghc::filesystem 都需要 C++11+, 而 R180/R190 是 C++98,故只在 (IS_ZWCAD OR CAD_SDK_VERSION >= 20) 时编译 - 中望适配两处:_zrx.def 要导出 zcrxEntryPoint/zcrxGetApiVersion; 代码里显式声明的 acrxGetApiVersion() 不会被 SDK 宏改名, 改为 #ifdef ZWCAD 取 zcrxGetApiVersion() 验证: - R230 Debug 编译通过,cad_mcp_frame.arx 818 KB,自动部署到 c:\ShangHuaSoft\Envi3D-2026-M\R230\ - Z2026 Debug 编译通过,cad_mcp_frame.zrx 818 KB,自动部署到 ...\Z2026\ - R190/R180 按条件跳过(C++98 无法编译 nlohmann/json) - 所有 .cpp/.h 均为 UTF-8 with BOM;cmakelists.txt 保持 ASCII 无 BOM 未做:没有在 CAD 里加载实测;配置文件仍是 cad-mcp-frame.json, 而代码里找的是 mcp_config.json,加载前需要统一(详见 cad_mcp_frame-review.md); 插件工程 cad_mcp_plugins 未移植(18 个工具里 16 个依赖它)。
移植已完成(R230 ARX + Z2026 ZRX 均编译通过并部署),本文记录: - 移植做了哪些改动(cadsdk.h->KTCad.h、两个单头依赖、SUB_ARX、中望 .def 与版本号函数) - C++11+ 限制:R180/R190 是 C++98,无法编译 nlohmann/json,故按 preset 条件跳过 - 架构:后台线程收包 -> PostMessage -> 主线程执行,工具调用全在主线程,避开了跨线程访问数据库 - 问题清单(按严重度): P0 收包只 recv 一次、缓冲固定 2048:大报文截断、TCP 分包即失败,且没有长度前缀 P0 bytesReceived<=0 时 socket 泄漏 P1 socket/bind/listen 返回值全未检查,端口占用时静默失败但 g_bRunning 仍为 true P1 无鉴权:本机任意进程都能调用会改图的工具 P1 端口 8080 硬编码;代码找 mcp_config.json 而仓库里是 cad-mcp-frame.json P2 卸载时挂起的 PostMessage 任务不释放;LISP 工具是异步 fire-and-forget 无结果回传; 加载期弹 AfxMessageBox 会卡住 CAD 启动;g_hMsgWnd 无同步; .def 的 LIBRARY 名与实际输出不一致;/Zc:forScope 语义与 code2026 全局设置相反 - 移植后仍需处理:统一配置文件名、移植 cad_mcp_plugins、带上 lisp 目录、在 CAD 里实测
P0 收包协议(大请求被截断 + 分包即失败) 原来只 recv 一次、缓冲固定 2048 字节。改为: - IsJsonComplete():按 {} 与 [] 配平判断报文完整性(跳过字符串内与转义字符) - RecvOneRequest():select + 循环 recv 追加,单次等 5s、整请求 30s、上限 4MB - SendAll():send 循环到发完(tools/list 应答可能超一个包) 不改客户端协议:客户端仍是 sendall 后等响应断开。 P0 socket 泄漏 recv<=0 的分支原来什么都不做。现在收包失败/超时回一个 JSON-RPC 错误帧并 closesocket; accept 失败直接 continue(原代码对 INVALID_SOCKET 也调 closesocket)。 P1 监听失败不再静默 socket/WSASocket/bind/listen/CreateWindowEx/std::thread 全部检查返回值, 监听 socket 改在主线程创建,失败时 init_mcp_server() 返回 false 并打印 WSAGetLastError, 端口被占用时能看到 "Fatal: bind 127.0.0.1:8080 failed, err=10048"。 P1 端口与配置文件名可配 - server.port 可配(默认 8080) - 配置先找 mcp_config.json,再找 cad-mcp-frame.json(此前代码只找前者, 而仓库里带的是后者,导致加载即自关) - 启动日志打印实际端口与 token 是否启用 P1 可选 token 鉴权 配置写了 server.token 就要求请求根级带 token,不匹配返回 -32001 且不执行工具; 没配 token 保持原行为,不破坏现有客户端。 顺带 P2:close_mcp_server() 先排空挂起的 WM_MCP_EXECUTE_TASK 并 delete TaskData (原来会漏 socket + 内存),再销毁窗口。 验证:R230 与 Z2026 Debug 均编译通过(818->826 KB,已部署)。 未做:没有在 CAD 里实际跑 tools/list 与 tools/call 验证协议改动。
b14ed8c5b8
b9c4ce56f9
插件工程在 CadProject/src/cad_mcp_plugins(8 个 cpp/h),本次一并整合进 code2026。 移植改动: - 源码拷到 SourceCode/Code2026/cad_mcp_plugins/;cadsdk.h -> KTCad.h - 输出是普通 DLL(.dll),由 cad_mcp_frame 运行时 LoadLibrary,不注册 ARX 命令 - 新增 cad_mcp_plugins/cmakelists.txt:按 code2026 惯例 (target_set_name_and_suffix ".dll" / target_link_objectarx / 预编译头 / SUB_ARX) - mcp_plugin_api.h 从 cad_mcp_frame/inc 上移到 SourceCode/Code2026/inc: frame 与 plugins 共用,且在 ShsGlobalIncludes 的 include 路径里两边都可见; frame 的 CMake 去掉已删除的本地 inc 目录 - 顶层在同一个 C++11+ 条件里挂载 add_custom_module_2(cad_mcp_plugins CadMcpPlugins) - 中望适配:源码里的 #pragma comment(lib,"rxapi.lib") 在中望下没有这个库(叫 ZwRx.lib), 该 pragma 与导出名都按 #ifdef ZWCAD 分开(中望导出 zcrxGetApiVersion); frame 里 GetProcAddress 查插件版本号的函数名同样按平台分 验证: - R230:cad_mcp_frame.arx 826 KB + cad_mcp_plugins.dll 866 KB,已自动部署 - Z2026:cad_mcp_frame.zrx 825 KB + cad_mcp_plugins.dll 864 KB,已自动部署 - 配置里 16 个 c++_dll 工具的工厂函数(CreateXxxTool)全部出现在 dll 导出表里 - 所有 .cpp/.h 为 UTF-8 with BOM 未做:没有在 CAD 里实跑。另外配置文件本身没有随构建部署(set_debug_post_build 只拷产物), 需要把 cad-mcp-frame.json(或改名 mcp_config.json)与 LISP 目录放到部署目录, 否则 frame 起来后 LoadAndRegister 失败会直接自关。详见 cad_mcp_frame-review.md。
端到端实测(/b 脚本按 AecRxBase -> CoreCommand -> cad_mcp_frame 顺序加载) 暴露两个真问题: 1) 版本校验拿到垃圾值,15/16 个工具被误踢 原实现 GetProcAddress(hMod,"acrxGetApiVersion") 转函数指针调用, 和基座自己的 acrxGetApiVersion() 比数值。实测两边返回值是乱的 (1507328 / 2052390912 / 0 交替,其中 1507328=0x170000 才是正确的 23.0), 结果只有第 3 个工具加载成功,其余全被判 SDK 版本不匹配。 改为插件导出数据符号 g_mcpPluginApiVersion=(ARX<<16)|SUB_ARX (mcp_plugin_api.h 新增 EXPORT_MCP_PLUGIN_VERSION(),每个插件 DLL 调用一次), 基座 GetProcAddress 读内存比对,不涉及调用约定;旧插件没有该符号时只告警按兼容处理。 2) 加载期 AfxMessageBox 会卡住主线程 mcp_ConfigParser 里的几处弹窗全部换成 McpLog: 同时写 %TEMP%\mcp_plugin_load.log 和命令行,并补上每个工具的 [try]/[skip] 明细(dll 是否存在、版本、create/destroy 指针)。 这也是"看起来只加载 3 个工具"的原因——弹窗把主线程挡住了。 实测结果(R230,自动化烟测): 端口 127.0.0.1:8080 6 s 内监听 tools/list 返回 18 个工具(2 lisp_inline + 16 c++_dll) 5 KB 大请求 -32601,证明整包被读完(旧实现会截断) tools/call get_layers 真正执行插件,返回 {"count":1,"items":[{"name":"0",...}]} 加载日志 16 个 c++_dll 全部 [try] 成功,0 个 [skip] 另外把配置文件加入构建部署:copyarx.exe 能拷任意文件,照 set_debug_post_build 的写法 再加一条 POST_BUILD,把 cad-mcp-frame.json 也拷到部署目录(ZWCAD 走 zw_output_path.json)。 不部署的话 frame 起来后 LoadAndRegister 找不到配置会直接自关。 验证:R230 与 Z2026 的 frame + plugins 均编译通过并部署;R230 已做上述端到端实测。
No dependencies set.
The note is not visible to the blocked user.
移植已完成(R230 ARX + Z2026 ZRX 均编译通过并部署),本文记录:
P0 收包只 recv 一次、缓冲固定 2048:大报文截断、TCP 分包即失败,且没有长度前缀
P0 bytesReceived<=0 时 socket 泄漏
P1 socket/bind/listen 返回值全未检查,端口占用时静默失败但 g_bRunning 仍为 true
P1 无鉴权:本机任意进程都能调用会改图的工具
P1 端口 8080 硬编码;代码找 mcp_config.json 而仓库里是 cad-mcp-frame.json
P2 卸载时挂起的 PostMessage 任务不释放;LISP 工具是异步 fire-and-forget 无结果回传;
加载期弹 AfxMessageBox 会卡住 CAD 启动;g_hMsgWnd 无同步;
.def 的 LIBRARY 名与实际输出不一致;/Zc:forScope 语义与 code2026 全局设置相反
移植已完成(R230 ARX + Z2026 ZRX 均编译通过并部署),本文记录: - 移植做了哪些改动(cadsdk.h->KTCad.h、两个单头依赖、SUB_ARX、中望 .def 与版本号函数) - C++11+ 限制:R180/R190 是 C++98,无法编译 nlohmann/json,故按 preset 条件跳过 - 架构:后台线程收包 -> PostMessage -> 主线程执行,工具调用全在主线程,避开了跨线程访问数据库 - 问题清单(按严重度): P0 收包只 recv 一次、缓冲固定 2048:大报文截断、TCP 分包即失败,且没有长度前缀 P0 bytesReceived<=0 时 socket 泄漏 P1 socket/bind/listen 返回值全未检查,端口占用时静默失败但 g_bRunning 仍为 true P1 无鉴权:本机任意进程都能调用会改图的工具 P1 端口 8080 硬编码;代码找 mcp_config.json 而仓库里是 cad-mcp-frame.json P2 卸载时挂起的 PostMessage 任务不释放;LISP 工具是异步 fire-and-forget 无结果回传; 加载期弹 AfxMessageBox 会卡住 CAD 启动;g_hMsgWnd 无同步; .def 的 LIBRARY 名与实际输出不一致;/Zc:forScope 语义与 code2026 全局设置相反 - 移植后仍需处理:统一配置文件名、移植 cad_mcp_plugins、带上 lisp 目录、在 CAD 里实测P0 收包协议(大请求被截断 + 分包即失败) 原来只 recv 一次、缓冲固定 2048 字节。改为: - IsJsonComplete():按 {} 与 [] 配平判断报文完整性(跳过字符串内与转义字符) - RecvOneRequest():select + 循环 recv 追加,单次等 5s、整请求 30s、上限 4MB - SendAll():send 循环到发完(tools/list 应答可能超一个包) 不改客户端协议:客户端仍是 sendall 后等响应断开。 P0 socket 泄漏 recv<=0 的分支原来什么都不做。现在收包失败/超时回一个 JSON-RPC 错误帧并 closesocket; accept 失败直接 continue(原代码对 INVALID_SOCKET 也调 closesocket)。 P1 监听失败不再静默 socket/WSASocket/bind/listen/CreateWindowEx/std::thread 全部检查返回值, 监听 socket 改在主线程创建,失败时 init_mcp_server() 返回 false 并打印 WSAGetLastError, 端口被占用时能看到 "Fatal: bind 127.0.0.1:8080 failed, err=10048"。 P1 端口与配置文件名可配 - server.port 可配(默认 8080) - 配置先找 mcp_config.json,再找 cad-mcp-frame.json(此前代码只找前者, 而仓库里带的是后者,导致加载即自关) - 启动日志打印实际端口与 token 是否启用 P1 可选 token 鉴权 配置写了 server.token 就要求请求根级带 token,不匹配返回 -32001 且不执行工具; 没配 token 保持原行为,不破坏现有客户端。 顺带 P2:close_mcp_server() 先排空挂起的 WM_MCP_EXECUTE_TASK 并 delete TaskData (原来会漏 socket + 内存),再销毁窗口。 验证:R230 与 Z2026 Debug 均编译通过(818->826 KB,已部署)。 未做:没有在 CAD 里实际跑 tools/list 与 tools/call 验证协议改动。b14ed8c5b8tob9c4ce56f9端到端实测(/b 脚本按 AecRxBase -> CoreCommand -> cad_mcp_frame 顺序加载) 暴露两个真问题: 1) 版本校验拿到垃圾值,15/16 个工具被误踢 原实现 GetProcAddress(hMod,"acrxGetApiVersion") 转函数指针调用, 和基座自己的 acrxGetApiVersion() 比数值。实测两边返回值是乱的 (1507328 / 2052390912 / 0 交替,其中 1507328=0x170000 才是正确的 23.0), 结果只有第 3 个工具加载成功,其余全被判 SDK 版本不匹配。 改为插件导出数据符号 g_mcpPluginApiVersion=(ARX<<16)|SUB_ARX (mcp_plugin_api.h 新增 EXPORT_MCP_PLUGIN_VERSION(),每个插件 DLL 调用一次), 基座 GetProcAddress 读内存比对,不涉及调用约定;旧插件没有该符号时只告警按兼容处理。 2) 加载期 AfxMessageBox 会卡住主线程 mcp_ConfigParser 里的几处弹窗全部换成 McpLog: 同时写 %TEMP%\mcp_plugin_load.log 和命令行,并补上每个工具的 [try]/[skip] 明细(dll 是否存在、版本、create/destroy 指针)。 这也是"看起来只加载 3 个工具"的原因——弹窗把主线程挡住了。 实测结果(R230,自动化烟测): 端口 127.0.0.1:8080 6 s 内监听 tools/list 返回 18 个工具(2 lisp_inline + 16 c++_dll) 5 KB 大请求 -32601,证明整包被读完(旧实现会截断) tools/call get_layers 真正执行插件,返回 {"count":1,"items":[{"name":"0",...}]} 加载日志 16 个 c++_dll 全部 [try] 成功,0 个 [skip] 另外把配置文件加入构建部署:copyarx.exe 能拷任意文件,照 set_debug_post_build 的写法 再加一条 POST_BUILD,把 cad-mcp-frame.json 也拷到部署目录(ZWCAD 走 zw_output_path.json)。 不部署的话 frame 起来后 LoadAndRegister 找不到配置会直接自关。 验证:R230 与 Z2026 的 frame + plugins 均编译通过并部署;R230 已做上述端到端实测。