Files
envi-code/SourceCode/Code2026/cad_mcp_frame/mcp_tool.cpp
T
company-desktop 0d02d33f1d feat: 把 cad_mcp_frame 整合进 Code2026 的 CMake 体系
源工程在 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 个依赖它)。
2026-10-28 23:20:07 +08:00

118 lines
3.4 KiB
C++

#include "KTCad.h"
#include "mcp_tool.h"
#include "mcp_utils.h"
namespace
{
bool ReplaceTemplateArgs(const std::string &_tpl, const nlohmann::json &args, std::wstring &outScript)
{
if (_tpl.empty()) return false;
std::string tpl(_tpl);
for (auto it = args.begin(); it != args.end(); ++it)
{
std::string placeholder = "{" + it.key() + "}";
std::string valueStr;
if (it.value().is_string())
{
valueStr = it.value().get<std::string>();
}
else if (it.value().is_boolean())
{
valueStr = it.value().get<bool>() ? "T" : "nil"; // 适配 AutoLISP 的布尔逻辑
}
else
{
valueStr = it.value().dump(); // 数字类型直接转字符串
}
size_t pos = 0;
while ((pos = tpl.find(placeholder, pos)) != std::string::npos)
{
tpl.replace(pos, placeholder.length(), valueStr);
pos += valueStr.length();
}
}
outScript = mcp_utils::utf8ToWString(tpl);
return true;
}
}
#pragma region LispInline
nlohmann::json mcp_Tool_LispInline::Execute(const nlohmann::json &args)
{
std::string tpl = m_config["backend"].value("script_template", "");
std::wstring finalScript;
if (!ReplaceTemplateArgs(tpl, args, finalScript))
return mcp_Tool_Utility::make_error(-32603, L"Script template replacement failed.");
finalScript += _T(" "); // 尾部加空格,等同于在 CAD 命令行敲回车
// 异步推送到 AutoCAD 命令行执行
acDocManager->sendStringToExecute(acDocManager->curDocument(), finalScript.c_str());
return mcp_Tool_Utility::make_text_result(L"LISP inline command issued for execution (asynchronous)");
}
#pragma endregion LispInline
#pragma region LispFile
nlohmann::json mcp_Tool_LispFile::Execute(const nlohmann::json &args)
{
fs::path filePath
= mcp_utils::relative2absolute(m_config["backend"].value("file_name", "")
, fs::path(m_lispDir));
if (!fs::exists(filePath))
{
return mcp_Tool_Utility::make_error(-32603, L"LISP file does not exist: " + mcp_utils::utf8ToWString(filePath.string()));
}
std::string callTpl = m_config["backend"].value("call_template", "");
// 组装前置加载命令,末尾的 nil 是为了防止文件不存在时 LISP 引擎报错弹窗
std::wstring loadCmd = std::wstring(_T("(load \"")) + filePath.generic_wstring() + _T("\" nil) ");
std::wstring executeCmd;
if (!ReplaceTemplateArgs(callTpl, args, executeCmd))
{
return mcp_Tool_Utility::make_error(-32603, L"Script template replacement failed.");
}
executeCmd += _T(" ");
std::wstring finalScript = loadCmd + executeCmd;
acDocManager->sendStringToExecute(acDocManager->curDocument(), finalScript.c_str());
return mcp_Tool_Utility::make_text_result(L"LISP external file loaded and issued for execution (asynchronous)");
}
#pragma endregion LispFile
#pragma region DllProxy
// RAII:基座销毁工具时,自动通知 DLL 释放内存,并卸载模块
mcp_Tool_DllProxy::~mcp_Tool_DllProxy()
{
if (m_pRealTool && m_destroyFunc)
{
m_destroyFunc(m_pRealTool);
}
if (m_hModule)
{
FreeLibrary(m_hModule);
}
}
nlohmann::json mcp_Tool_DllProxy::Execute(const nlohmann::json &args)
{
if (m_pRealTool)
{
std::string argStr = args.dump();
//return m_pRealTool->Execute(args); // 完美转发
const char* retStr = m_pRealTool->ExecuteAbiSafe(argStr.c_str());
return nlohmann::json::parse(retStr);
}
return mcp_Tool_Utility::make_error(-32603, L"Underlying DLL tool instance is invalid");
}
#pragma endregion DllProxy