Files
envi-code/SourceCode/Code2026/cad_mcp_plugins/dwg_review_overview.cpp
T
company-desktop ecb8436720 feat: 移植 cad_mcp_plugins,并对齐中望平台的符号与依赖
插件工程在 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。
2026-10-29 00:16:37 +08:00

352 lines
9.8 KiB
C++

// ============================================================================
// dwg_review_overview.cpp
// 图纸审查 - 概览层工具(均为 c++_dll 后端)
// get_drawing_info : 图纸元数据(文件名/单位/布局数/各空间实体数)
// get_layers : 图层清单(开关/冻结/锁定/颜色/线型/可选实体计数)
// get_layouts : 布局清单(模型空间 + 图纸空间布局 + 视口)
// ============================================================================
#include "KTCad.h"
#include "dwg_review_common.h"
#include "mcp_plugin_api.h"
#include "dbcolor.h" // AcCmColor(图层颜色);AcDbViewport 在 dbents.h(已由 cadsdk 引入)
#include <map>
using namespace dwg_review;
namespace
{
// 图纸单位代码 -> 名称
std::string insUnitsName(int u)
{
switch (u)
{
case 0: return "Unitless";
case 1: return "Inches";
case 2: return "Feet";
case 3: return "Miles";
case 4: return "Millimeters";
case 5: return "Centimeters";
case 6: return "Meters";
case 7: return "Kilometers";
case 8: return "Microinches";
case 9: return "Mils";
case 10: return "Yards";
case 11: return "Angstroms";
case 12: return "Nanometers";
case 13: return "Microns";
case 14: return "Decimeters";
case 15: return "Decameters";
case 16: return "Hectometers";
case 17: return "Gigameters";
case 18: return "AstronomicalUnits";
case 19: return "LightYears";
case 20: return "Parsecs";
default: return "Unknown";
}
}
// 取块表记录名
std::string recordName(AcDbBlockTableRecord* pRec)
{
const ACHAR* nm = NULL;
if (pRec && pRec->getName(nm) == Acad::eOk && nm) return acharToUtf8(nm);
return std::string();
}
// 统计某空间内的实体总数
int countEntitiesIn(AcDbBlockTableRecord* pRec)
{
if (!pRec) return 0;
int n = 0;
AcDbBlockTableRecordIterator* it = nullptr;
if (pRec->newIterator(it) != Acad::eOk || !it) return 0;
for (it->start(); !it->done(); it->step()) ++n;
delete it;
return n;
}
// 取线型名
std::string linetypeName(AcDbObjectId id)
{
AcDbLinetypeTableRecord* pRec = nullptr;
if (acdbOpenObject(pRec, id, AcDb::kForRead) == Acad::eOk && pRec)
{
const ACHAR* nm = NULL;
std::string s;
if (pRec->getName(nm) == Acad::eOk && nm) s = acharToUtf8(nm);
pRec->close();
return s;
}
return std::string();
}
// 统计各图层实体数(遍历模型空间 + 所有图纸布局)
void countByLayer(AcDbDatabase* pDb, std::map<std::string, int>& out)
{
AcDbBlockTable* pBT = nullptr;
if (pDb->getBlockTable(pBT, AcDb::kForRead) != Acad::eOk || !pBT) return;
AcDbBlockTableIterator* it = nullptr;
if (pBT->newIterator(it) == Acad::eOk && it)
{
for (it->start(); !it->done(); it->step())
{
AcDbBlockTableRecord* pRec = nullptr;
if (it->getRecord(pRec, AcDb::kForRead) != Acad::eOk || !pRec) continue;
const std::string nm = recordName(pRec);
static const std::string kModelSpace = acharToUtf8(ACDB_MODEL_SPACE);
const bool isModel = (nm == kModelSpace);
if (isModel || pRec->isLayout())
{
AcDbBlockTableRecordIterator* eit = nullptr;
if (pRec->newIterator(eit) == Acad::eOk && eit)
{
for (eit->start(); !eit->done(); eit->step())
{
AcDbEntity* pEnt = nullptr;
if (eit->getEntity(pEnt, AcDb::kForRead) == Acad::eOk && pEnt)
{
out[layerOf(pEnt)]++;
pEnt->close();
}
}
delete eit;
}
}
pRec->close();
}
delete it;
}
pBT->close();
}
// 收集某布局内的视口
nlohmann::json collectViewports(AcDbBlockTableRecord* pRec)
{
nlohmann::json arr = nlohmann::json::array();
if (!pRec) return arr;
AcDbBlockTableRecordIterator* it = nullptr;
if (pRec->newIterator(it) != Acad::eOk || !it) return arr;
for (it->start(); !it->done(); it->step())
{
AcDbEntity* pEnt = nullptr;
if (it->getEntity(pEnt, AcDb::kForRead) != Acad::eOk || !pEnt) continue;
AcDbViewport* pVp = AcDbViewport::cast(pEnt);
if (pVp)
{
nlohmann::json v;
v["handle"] = handleOf(pEnt);
v["center"] = point2Json(pVp->centerPoint().x, pVp->centerPoint().y);
v["width"] = pVp->width();
v["height"] = pVp->height();
v["scale"] = pVp->customScale();
arr.push_back(v);
}
pEnt->close();
}
delete it;
return arr;
}
}
// ===========================================================================
// get_drawing_info
// ===========================================================================
class mcp_Tool_GetDrawingInfo : public mcp_Tool
{
public:
nlohmann::json Execute(const nlohmann::json&) override
{
AcDbDatabase* pDb = acdbHostApplicationServices()->workingDatabase();
if (!pDb) return makeError(kErrNoDocument, "no active document");
nlohmann::json r;
const ACHAR* fn = NULL;
pDb->getFilename(fn);
r["file_name"] = acharToUtf8(fn);
r["insunits"] = (int)pDb->insunits();
r["insunits_name"] = insUnitsName((int)pDb->insunits());
nlohmann::json counts = nlohmann::json::object();
int layoutCount = 0;
int total = 0;
AcDbBlockTable* pBT = nullptr;
if (pDb->getBlockTable(pBT, AcDb::kForRead) == Acad::eOk && pBT)
{
AcDbBlockTableRecord* pModel = nullptr;
if (pBT->getAt(ACDB_MODEL_SPACE, pModel, AcDb::kForRead) == Acad::eOk && pModel)
{
int c = countEntitiesIn(pModel);
counts["Model"] = c; total += c;
pModel->close();
}
AcDbBlockTableIterator* it = nullptr;
if (pBT->newIterator(it) == Acad::eOk && it)
{
for (it->start(); !it->done(); it->step())
{
AcDbBlockTableRecord* pRec = nullptr;
if (it->getRecord(pRec, AcDb::kForRead) != Acad::eOk || !pRec) continue;
if (pRec->isLayout())
{
++layoutCount;
int c = countEntitiesIn(pRec);
counts[recordName(pRec)] = c; total += c;
}
pRec->close();
}
delete it;
}
pBT->close();
}
r["layout_count"] = layoutCount + 1; // 含 Model
r["entity_count"] = counts;
r["entity_count_total"] = total;
return makeJsonResult(r);
}
};
EXPORT_MCP_TOOL(mcp_Tool_GetDrawingInfo, CreateGetDrawingInfoTool)
// ===========================================================================
// get_layers
// ===========================================================================
class mcp_Tool_GetLayers : public mcp_Tool
{
public:
nlohmann::json Execute(const nlohmann::json& args) override
{
AcDbDatabase* pDb = acdbHostApplicationServices()->workingDatabase();
if (!pDb) return makeError(kErrNoDocument, "no active document");
const std::string nameFilter = args.value("name_filter", std::string());
const bool includeEmpty = args.value("include_empty", false);
const bool needCount = args.value("count_entities", false);
std::map<std::string, int> layerCounts;
if (needCount) countByLayer(pDb, layerCounts);
nlohmann::json items = nlohmann::json::array();
AcDbLayerTable* pLT = nullptr;
if (pDb->getLayerTable(pLT, AcDb::kForRead) == Acad::eOk && pLT)
{
AcDbLayerTableIterator* it = nullptr;
if (pLT->newIterator(it) == Acad::eOk && it)
{
for (it->start(); !it->done(); it->step())
{
AcDbLayerTableRecord* pRec = nullptr;
if (it->getRecord(pRec, AcDb::kForRead) != Acad::eOk || !pRec) continue;
const ACHAR* nm = NULL;
pRec->getName(nm);
const std::string name = acharToUtf8(nm);
const int cnt = needCount
? (layerCounts.count(name) ? layerCounts[name] : 0) : -1;
if (!wildcardMatch(name, nameFilter) ||
(needCount && !includeEmpty && cnt == 0))
{
pRec->close();
continue;
}
nlohmann::json o;
o["name"] = name;
o["on"] = !pRec->isOff();
o["frozen"] = pRec->isFrozen();
o["locked"] = pRec->isLocked();
o["plot"] = pRec->isPlottable();
AcCmColor col = pRec->color();
o["color_index"] = (int)col.colorIndex();
o["linetype"] = linetypeName(pRec->linetypeObjectId());
if (cnt >= 0) o["entity_count"] = cnt;
items.push_back(o);
pRec->close();
}
delete it;
}
pLT->close();
}
nlohmann::json r;
r["count"] = (int)items.size();
r["items"] = items;
return makeJsonResult(r);
}
};
EXPORT_MCP_TOOL(mcp_Tool_GetLayers, CreateGetLayersTool)
// ===========================================================================
// get_layouts
// ===========================================================================
class mcp_Tool_GetLayouts : public mcp_Tool
{
public:
nlohmann::json Execute(const nlohmann::json&) override
{
AcDbDatabase* pDb = acdbHostApplicationServices()->workingDatabase();
if (!pDb) return makeError(kErrNoDocument, "no active document");
nlohmann::json items = nlohmann::json::array();
AcDbBlockTable* pBT = nullptr;
if (pDb->getBlockTable(pBT, AcDb::kForRead) == Acad::eOk && pBT)
{
// 模型空间
AcDbBlockTableRecord* pModel = nullptr;
if (pBT->getAt(ACDB_MODEL_SPACE, pModel, AcDb::kForRead) == Acad::eOk && pModel)
{
nlohmann::json o;
o["name"] = "Model";
o["is_model"] = true;
o["entity_count"] = countEntitiesIn(pModel);
o["viewports"] = nlohmann::json::array();
items.push_back(o);
pModel->close();
}
// 图纸空间布局
AcDbBlockTableIterator* it = nullptr;
if (pBT->newIterator(it) == Acad::eOk && it)
{
for (it->start(); !it->done(); it->step())
{
AcDbBlockTableRecord* pRec = nullptr;
if (it->getRecord(pRec, AcDb::kForRead) != Acad::eOk || !pRec) continue;
if (pRec->isLayout())
{
nlohmann::json o;
o["name"] = recordName(pRec);
o["is_model"] = false;
o["entity_count"] = countEntitiesIn(pRec);
o["viewports"] = collectViewports(pRec);
items.push_back(o);
}
pRec->close();
}
delete it;
}
pBT->close();
}
nlohmann::json r;
r["count"] = (int)items.size();
r["items"] = items;
return makeJsonResult(r);
}
};
EXPORT_MCP_TOOL(mcp_Tool_GetLayouts, CreateGetLayoutsTool)