// ============================================================================ // aec_mcp_entity_tools.cpp // cad_mcp_aec 的"建/改"工具:管道、墙、板、柱、梁 // // 参数约定: // * 坐标与建筑尺寸(墙厚/墙高/板厚/柱高/梁宽…)用**图纸单位**; // * 管道的公称直径 / 外径 / 壁厚用 **mm**,内部按 AS_MUNIT 转米 // (与 BZGDDlg、AecPipeLineTool 的既有写法一致)。 // // 属性 key 用的是 EarThwormDef.h 里的 PIPE_DN / PIPE_OD / PIPE_THICK 等 // (AecDbCurveBase.h 里包含的就是它;注意 inc/AecWPipe.h 里有同名但值不同的宏, // 本文件不要包含 AecWPipe.h,否则写进去的 key 会变)。 // ============================================================================ #include "StdAfx.h" #include "KTCad.h" #include "aec_mcp_common.h" #include "mcp_plugin_api.h" #include "AecDbEwPipe.h" #include "AecDbWall.h" #include "AecDbSlab.h" #include "AecDbColumn.h" #include "AecDbBeam.h" #include "AecDbObjXDataMap.h" #include "KTSetting.h" using namespace aec_mcp; namespace { // --------------------------------------------------------------------------- // 参数应用(建/改共用) // --------------------------------------------------------------------------- // 管道:至少要有起终点与公称直径。其余参数缺省时保留原值(改的时候)。 bool applyPipe(AecDbEwPipe* p, const nlohmann::json& a, bool bCreate, std::wstring& err) { AcGePoint3d ptS, ptE; bool bHasS = readPoint(a, "start", ptS); bool bHasE = readPoint(a, "end", ptE); if (bCreate && (!bHasS || !bHasE)) { err = L"add_aec_pipe 需要 start / end({\"x\",\"y\",\"z\"} 或 [x,y,z])"; return false; } // 改的时候只给一个端点:另一个沿用实体现有端点,别把管拉回原点 if (!bCreate && (bHasS != bHasE)) { const AcDbCurve* pCrv = AcDbCurve::cast(p); AcGePoint3d ptCurS, ptCurE; if (pCrv != NULL && pCrv->getStartPoint(ptCurS) == Acad::eOk && pCrv->getEndPoint(ptCurE) == Acad::eOk) { if (!bHasS) { ptS = ptCurS; bHasS = true; } if (!bHasE) { ptE = ptCurE; bHasE = true; } } } if (bHasS && bHasE) { if (ptS.distanceTo(ptE) < 1e-9) { err = L"start 与 end 重合,管道长度为 0"; return false; } p->SetBaseCurve(ptS, ptE); } AecDbObjXDataMap* pX = p->GetSpecialtyData(); if (pX == NULL) { err = L"取专业数据失败(GetSpecialtyData 返回空)"; return false; } // 现有的截面参数(内部存的是米),下面按"传了就用新的、没传就保持"合并后统一重写: // SetSection() 会把 PIPE_THICK/PIPE_OD 之类的派生字段清掉,所以必须先取再补。 double dDnOld = 0.0, dOdOld = 0.0, dThOld = 0.0; pX->GetAt(PIPE_DN, dDnOld); pX->GetAt(PIPE_OD, dOdOld); pX->GetAt(PIPE_THICK, dThOld); const double dDn = hasKey(a, "dn_mm") ? numOr(a, "dn_mm", 0.0) : dDnOld * 1000.0; const double dOd = hasKey(a, "od_mm") ? numOr(a, "od_mm", 0.0) : dOdOld * 1000.0; const double dTh = hasKey(a, "wall_thickness_mm") ? numOr(a, "wall_thickness_mm", 0.0) : dThOld * 1000.0; if (bCreate || hasKey(a, "dn_mm")) { if (dDn <= 0.0) { err = L"add_aec_pipe 需要 dn_mm(公称直径,mm)"; return false; } // 与 BZGDDlg 一致:截面圆用 DN(米制) p->SetSection(AS_MUNIT(dDn), 0.0); } pX->SetAt(KEY_2DSTYLE, 1); // 统一重写三项:SetSection 之后无论传没传都要落到专业数据里 if (dDn > 0.0) pX->SetAt(PIPE_DN, AS_MUNIT(dDn)); if (dOd > 0.0) pX->SetAt(PIPE_OD, AS_MUNIT(dOd)); pX->SetAt(PIPE_THICK, AS_MUNIT(dTh)); if (hasKey(a, "system")) pX->SetAt(PIPE_SYS, wstrOr(a, "system", L"").c_str()); if (hasKey(a, "material")) pX->SetAt(PIPE_MATERIAL, wstrOr(a, "material", L"").c_str()); // 颜色:与 cad_mcp_plugins 的既有工具统一用 color(ACI,256=ByLayer), // 同时兼容旧写法 color_index if (hasKey(a, "color")) pX->SetAt(_T("COLOR"), intOr(a, "color", 256)); else if (hasKey(a, "color_index")) pX->SetAt(_T("COLOR"), intOr(a, "color_index", 256)); return true; } // 墙:起点-终点 + 厚/高/底标高(+ 对齐、材质) bool applyWall(AecDbXWall* p, const nlohmann::json& a, bool bCreate, std::wstring& err) { AcGePoint3d ptS, ptE; const bool bHasS = readPoint(a, "start", ptS); const bool bHasE = readPoint(a, "end", ptE); if (bCreate && (!bHasS || !bHasE)) { err = L"add_aec_wall 需要 start / end"; return false; } if (bHasS && bHasE) { if (ptS.distanceTo(ptE) < 1e-9) { err = L"start 与 end 重合,墙长为 0"; return false; } p->SetBaseCurve(ptS, ptE); } WallData data; ::memset(&data, 0, sizeof(WallData)); p->GetWallData(data); const double dThick = numOr(a, "thickness", bCreate ? 0.0 : data.dThick); const double dH = numOr(a, "height", bCreate ? 0.0 : data.dH); const double dElev = numOr(a, "bottom_elevation", bCreate ? 0.0 : data.dBomEl); if (dThick <= 0.0 || dH <= 0.0) { err = L"墙需要 thickness(厚)与 height(高),且都 > 0"; return false; } EAlign eAlign = data.kAlign; if (hasKey(a, "align")) { const std::wstring s = wstrOr(a, "align", L"center"); if (s == L"left") eAlign = kLeft; else if (s == L"right") eAlign = kRight; else eAlign = kMid; } p->SetWallData(dThick, dH, dElev, eAlign); if (hasKey(a, "material")) p->GetSpecialtyData()->SetAt(KEY_MATERIAL, wstrOr(a, "material", L"").c_str()); return true; } // 板:闭合点串 + 板厚 + 板底标高 bool applySlab(AecDbXSlab* p, const nlohmann::json& a, bool bCreate, std::wstring& err) { AcGePoint2dArray arPts; const bool bHasPts = readPoint2dArray(a, "points", arPts); if (bCreate && !bHasPts) { err = L"add_aec_slab 需要 points:[[x,y],...](至少 3 个点,可不闭合)"; return false; } if (bHasPts && arPts.length() < 3) { err = L"points 至少要有 3 个点"; return false; } // SetRegionSlab() 内部用 CreatePolygonRegionOnly() 连线,而后者只连 // [0, n-2] 段、不自动闭合(闭合点只写进了 KEY_POINTS 字符串)。 // 所以这里必须自己把首点补到末尾,否则 region 建不出来、直接返回 false。 if (bHasPts && arPts.first().distanceTo(arPts.last()) > 1e-9) arPts.append(arPts.first()); const double dThick = numOr(a, "thickness", 0.0); const double dElev = numOr(a, "bottom_elevation", 0.0); if (bCreate && dThick <= 0.0) { err = L"add_aec_slab 需要 thickness(板厚)> 0"; return false; } if (bHasPts) { // 中心点缺省取各点平均 AcGePoint3d ptCen(0.0, 0.0, 0.0); if (!readPoint(a, "center", ptCen)) { for (int i = 0; i < arPts.length(); ++i) { ptCen.x += arPts.at(i).x; ptCen.y += arPts.at(i).y; } ptCen.x /= (double)arPts.length(); ptCen.y /= (double)arPts.length(); ptCen.z = 0.0; } if (!p->SetRegionSlab(arPts, dThick, dElev, ptCen)) { err = L"SetRegionSlab 失败(点串可能自交或退化)"; return false; } } else if (!bCreate) { // 只改板厚/标高:SetRegionSlab 的数组重载必须重新给轮廓, // 所以这里把存盘时记在 KEY_POINTS 里的轮廓串取出来,走字符串重载。 const AecDbObjXDataMap* pX = p->GetSpecialtyData(AcDb::kForRead); CString sPts; AcGePoint3d ptOrg(0.0, 0.0, 0.0); if (pX != NULL) { pX->GetAt(KEY_POINTS, sPts); pX->GetAt(KEY_ORGPT, ptOrg); } if (sPts.IsEmpty()) { err = L"这块板没有记录轮廓(KEY_POINTS 为空),无法在不给 points 的情况下改尺寸"; return false; } double dThickNow = dThick; p->GetAt(KEY_BOMTHICK, dThickNow); const double dThNew = hasKey(a, "thickness") ? dThick : (dThickNow > 0.0 ? dThickNow : dThick); const double dElevNew = hasKey(a, "bottom_elevation") ? dElev : ptOrg.z; AcGePoint3d ptCen = ptOrg; readPoint(a, "center", ptCen); if (!p->SetRegionSlab((LPCTSTR)sPts, dThNew, dElevNew, ptCen)) { err = L"按已有轮廓重建板失败(轮廓串可能已损坏)"; return false; } } if (hasKey(a, "material")) p->GetSpecialtyData()->SetAt(KEY_MATERIAL, wstrOr(a, "material", L"").c_str()); return true; } // 柱:位置 + 高 + 截面(长 x 宽)+ 旋转 bool applyColumn(AecDbColumn* p, const nlohmann::json& a, bool bCreate, std::wstring& err) { AcGePoint3d ptPos; const bool bHasPos = readPoint(a, "position", ptPos); ColumnParam data; ::memset(&data, 0, sizeof(ColumnParam)); p->GetColumnData(data); const bool bAny = bHasPos || hasKey(a, "height") || hasKey(a, "length") || hasKey(a, "width") || hasKey(a, "rotation_deg"); if (bCreate && !bHasPos) { err = L"add_aec_column 需要 position(柱底中心点)"; return false; } if (!bAny) return true; // 只改了材质之类的场景 const AcGePoint3d ptBase = bHasPos ? ptPos : p->GetPosition(); const double dH = numOr(a, "height", bCreate ? 0.0 : p->GetHeight()); const AcGePoint2d rc = p->GetRect(); const double dL = numOr(a, "length", rc.x); const double dW = numOr(a, "width", rc.y); const double dRot = hasKey(a, "rotation_deg") ? numOr(a, "rotation_deg", 0.0) * PI / 180.0 : p->GetRotation(); if (dH <= 0.0 || dL <= 0.0 || dW <= 0.0) { err = L"柱需要 height / length / width,且都 > 0"; return false; } p->SetColumnData(ptBase, dL, dW, dH, dRot); if (hasKey(a, "material")) p->GetSpecialtyData()->SetAt(KEY_MATERIAL, wstrOr(a, "material", L"").c_str()); return true; } // 梁:起点-终点 + 截面宽 x 高 bool applyBeam(AecDbBeam* p, const nlohmann::json& a, bool bCreate, std::wstring& err) { AcGePoint3d ptS, ptE; const bool bHasS = readPoint(a, "start", ptS); const bool bHasE = readPoint(a, "end", ptE); if (bCreate && (!bHasS || !bHasE)) { err = L"add_aec_beam 需要 start / end"; return false; } if (bHasS && bHasE) { if (ptS.distanceTo(ptE) < 1e-9) { err = L"start 与 end 重合,梁长为 0"; return false; } p->SetBaseCurve(ptS, ptE); } ColumnParam data; ::memset(&data, 0, sizeof(ColumnParam)); p->GetBeamData(data); // GetBeamData 把截面尺寸放在 m_dSizes[0](宽)/ m_dSizes[1](高) const double dW = numOr(a, "width", bCreate ? 0.0 : data.m_dSizes[0]); const double dH = numOr(a, "height", bCreate ? 0.0 : data.m_dSizes[1]); if (dW <= 0.0 || dH <= 0.0) { err = L"梁需要 width(宽)与 height(高),且都 > 0"; return false; } p->SetBeamData(dW, dH); if (hasKey(a, "material")) p->GetSpecialtyData()->SetAt(KEY_MATERIAL, wstrOr(a, "material", L"").c_str()); return true; } // --------------------------------------------------------------------------- // 共用收尾:设图层、加入空间、回包 // --------------------------------------------------------------------------- nlohmann::json finishCreate(AcDbEntity* pEnt, const nlohmann::json& a, const char* pszType) { if (hasKey(a, "layer")) { const std::wstring sLayer = wstrOr(a, "layer", L""); if (!sLayer.empty()) { AcDbObjectId idLay = layerIdByName(sLayer); if (!idLay.isNull()) pEnt->setLayer(idLay); } } // space:与 cad_mcp_plugins 的 add_* 工具一致,缺省模型空间 std::wstring sSpace; if (hasKey(a, "space")) sSpace = wstrOr(a, "space", L""); AcDbObjectId idNew; Acad::ErrorStatus es = addToSpace(pEnt, &idNew, sSpace); // 句柄要**入图之后**才有值:append 之前 pEnt 还没 handle,取出来是 "0" const std::string sHandle = (es == Acad::eOk) ? handleOf(pEnt) : std::string(); pEnt->close(); if (es != Acad::eOk) { delete pEnt; return errJson(kErrInternal, std::wstring(L"加入模型空间失败,err=") + std::to_wstring((int)es)); } nlohmann::json body; body["type"] = pszType; body["handle"] = sHandle; return okJson(body); } // 按 handle 打开并确认类型,失败时直接给出可返回的错误 template T* openTyped(const nlohmann::json& a, const wchar_t* pszExpect, nlohmann::json& errOut) { if (!hasKey(a, "handle")) { errOut = errJson(kErrBadParam, L"缺少 handle(实体句柄,十六进制字符串)"); return NULL; } const std::string sHandle = a["handle"].get(); AcDbEntity* pEnt = openByHandle(sHandle, AcDb::kForWrite); if (pEnt == NULL) { errOut = errJson(kErrNotFound, std::wstring(L"按 handle 打不开实体:") + utf8ToWstr(sHandle)); return NULL; } T* pTyped = T::cast(pEnt); if (pTyped == NULL) { const std::string sCls = classNameOf(pEnt); pEnt->close(); errOut = errJson(kErrWrongType, std::wstring(L"handle ") + utf8ToWstr(sHandle) + L" 的实体是 " + utf8ToWstr(sCls) + L",不是 " + pszExpect); return NULL; } return pTyped; } nlohmann::json finishUpdate(AcDbObject* pObj, const std::string& sHandle, const char* pszType) { touchEntity(AcDbEntity::cast(pObj)); const std::string sH = sHandle.empty() ? handleOf(pObj) : sHandle; pObj->close(); nlohmann::json body; body["type"] = pszType; body["handle"] = sH; body["updated"] = true; return okJson(body); } } // namespace // =========================================================================== // 管道 // =========================================================================== class ToolCreatePipe : public mcp_Tool { public: std::string private_name() const override { return "add_aec_pipe"; } std::string private_description() const override { return "在当前图纸新建一根 Envi3D 管道(AecDbEwPipe)。" "坐标用图纸单位,dn_mm/od_mm/wall_thickness_mm 用毫米。" "返回新实体的 handle,后续可用 update_aec_pipe 修改。"; } nlohmann::json private_input_schema() const override { return nlohmann::json::parse(R"({ "type": "object", "properties": { "start": { "type": "object", "description": "起点 {\"x\",\"y\",\"z\"},图纸单位;也可用 [x,y,z]" }, "end": { "type": "object", "description": "终点,格式同 start" }, "dn_mm": { "type": "number", "description": "公称直径 DN,毫米" }, "od_mm": { "type": "number", "description": "外径,毫米(缺省不改)" }, "wall_thickness_mm": { "type": "number", "description": "壁厚,毫米(缺省不改)" }, "system": { "type": "string", "description": "管道系统名,如 给水/排水" }, "material": { "type": "string", "description": "材质名" }, "color": { "type": "integer", "description": "ACI 颜色号,256=ByLayer" }, "layer": { "type": "string", "description": "图层名,留空用当前图层" } }, "required": [ "start", "end", "dn_mm" ] })"); } nlohmann::json Execute(const nlohmann::json& args) override { if (workingDb() == NULL) return errJson(kErrNoDocument, L"没有活动图纸"); DocLock lock; if (!lock.isLocked()) return errJson(kErrInternal, L"拿不到图纸写锁(有别的命令在跑?)"); AecDbEwPipe* pPipe = new AecDbEwPipe(); std::wstring err; if (!applyPipe(pPipe, args, true, err)) { delete pPipe; return errJson(kErrBadParam, err); } return finishCreate(pPipe, args, "pipe"); } }; EXPORT_MCP_TOOL(ToolCreatePipe, CreateAecPipeTool) class ToolUpdatePipe : public mcp_Tool { public: std::string private_name() const override { return "update_aec_pipe"; } std::string private_description() const override { return "修改已存在的 Envi3D 管道(AecDbEwPipe)。只传要改的字段,未传的保持原值。" "改起终点会重算几何;改 dn_mm 会重建截面。"; } nlohmann::json private_input_schema() const override { return nlohmann::json::parse(R"({ "type": "object", "properties": { "handle": { "type": "string", "description": "要修改的管道句柄(十六进制)" }, "start": { "type": "object", "description": "新起点(可选)" }, "end": { "type": "object", "description": "新终点(可选)" }, "dn_mm": { "type": "number", "description": "新公称直径,毫米(可选)" }, "od_mm": { "type": "number", "description": "新外径,毫米(可选)" }, "wall_thickness_mm": { "type": "number", "description": "新壁厚,毫米(可选)" }, "system": { "type": "string", "description": "管道系统名(可选)" }, "material": { "type": "string", "description": "材质(可选)" }, "color": { "type": "integer", "description": "ACI 颜色号,256=ByLayer(可选)" } }, "required": [ "handle" ] })"); } nlohmann::json Execute(const nlohmann::json& args) override { if (workingDb() == NULL) return errJson(kErrNoDocument, L"没有活动图纸"); DocLock lock; if (!lock.isLocked()) return errJson(kErrInternal, L"拿不到图纸写锁(有别的命令在跑?)"); nlohmann::json err; AecDbEwPipe* pPipe = openTyped(args, L"AecDbEwPipe", err); if (pPipe == NULL) return err; std::wstring msg; if (!applyPipe(pPipe, args, false, msg)) { pPipe->close(); return errJson(kErrBadParam, msg); } return finishUpdate(pPipe, args["handle"].get(), "pipe"); } }; EXPORT_MCP_TOOL(ToolUpdatePipe, UpdateAecPipeTool) // =========================================================================== // 墙 // =========================================================================== class ToolCreateWall : public mcp_Tool { public: std::string private_name() const override { return "add_aec_wall"; } std::string private_description() const override { return "新建 Envi3D 墙体(AecDbXWall)。沿 start→end 建墙,尺寸用图纸单位," "bottom_elevation 是墙底标高;align 决定墙厚在基线哪一侧。返回 handle。"; } nlohmann::json private_input_schema() const override { return nlohmann::json::parse(R"({ "type": "object", "properties": { "start": { "type": "object", "description": "墙基线起点 {x,y,z}" }, "end": { "type": "object", "description": "墙基线终点 {x,y,z}" }, "thickness": { "type": "number", "description": "墙厚,图纸单位" }, "height": { "type": "number", "description": "墙高,图纸单位" }, "bottom_elevation": { "type": "number", "description": "墙底标高,图纸单位,缺省 0" }, "align": { "type": "string", "enum": [ "left", "center", "right" ], "description": "对齐方式,缺省 center" }, "material": { "type": "string", "description": "材质" }, "layer": { "type": "string", "description": "图层名" } }, "required": [ "start", "end", "thickness", "height" ] })"); } nlohmann::json Execute(const nlohmann::json& args) override { if (workingDb() == NULL) return errJson(kErrNoDocument, L"没有活动图纸"); DocLock lock; if (!lock.isLocked()) return errJson(kErrInternal, L"拿不到图纸写锁(有别的命令在跑?)"); AecDbXWall* pWall = new AecDbXWall(); std::wstring err; if (!applyWall(pWall, args, true, err)) { delete pWall; return errJson(kErrBadParam, err); } return finishCreate(pWall, args, "wall"); } }; EXPORT_MCP_TOOL(ToolCreateWall, CreateAecWallTool) class ToolUpdateWall : public mcp_Tool { public: std::string private_name() const override { return "update_aec_wall"; } std::string private_description() const override { return "修改已存在的 Envi3D 墙体(AecDbXWall)。只传要改的字段。"; } nlohmann::json private_input_schema() const override { return nlohmann::json::parse(R"({ "type": "object", "properties": { "handle": { "type": "string", "description": "墙体句柄" }, "start": { "type": "object", "description": "新起点(可选)" }, "end": { "type": "object", "description": "新终点(可选)" }, "thickness": { "type": "number", "description": "新墙厚(可选)" }, "height": { "type": "number", "description": "新墙高(可选)" }, "bottom_elevation": { "type": "number", "description": "新墙底标高(可选)" }, "align": { "type": "string", "enum": [ "left", "center", "right" ], "description": "对齐(可选)" }, "material": { "type": "string", "description": "材质(可选)" } }, "required": [ "handle" ] })"); } nlohmann::json Execute(const nlohmann::json& args) override { if (workingDb() == NULL) return errJson(kErrNoDocument, L"没有活动图纸"); DocLock lock; if (!lock.isLocked()) return errJson(kErrInternal, L"拿不到图纸写锁(有别的命令在跑?)"); nlohmann::json err; AecDbXWall* pWall = openTyped(args, L"AecDbXWall", err); if (pWall == NULL) return err; std::wstring msg; if (!applyWall(pWall, args, false, msg)) { pWall->close(); return errJson(kErrBadParam, msg); } return finishUpdate(pWall, args["handle"].get(), "wall"); } }; EXPORT_MCP_TOOL(ToolUpdateWall, UpdateAecWallTool) // =========================================================================== // 板 // =========================================================================== class ToolCreateSlab : public mcp_Tool { public: std::string private_name() const override { return "add_aec_slab"; } std::string private_description() const override { return "新建 Envi3D 板/底板(AecDbXSlab)。points 是水平轮廓点串(可不闭合)," "thickness 是板厚,bottom_elevation 是板底标高,均用图纸单位。返回 handle。"; } nlohmann::json private_input_schema() const override { return nlohmann::json::parse(R"({ "type": "object", "properties": { "points": { "type": "array", "description": "轮廓点 [[x,y],...],至少 3 个", "items": { "type": "array", "items": { "type": "number" } } }, "thickness": { "type": "number", "description": "板厚,图纸单位" }, "bottom_elevation": { "type": "number", "description": "板底标高,缺省 0" }, "center": { "type": "object", "description": "板位置参考点 {x,y,z},缺省取点串平均值" }, "material": { "type": "string", "description": "材质" }, "layer": { "type": "string", "description": "图层名" } }, "required": [ "points", "thickness" ] })"); } nlohmann::json Execute(const nlohmann::json& args) override { if (workingDb() == NULL) return errJson(kErrNoDocument, L"没有活动图纸"); DocLock lock; if (!lock.isLocked()) return errJson(kErrInternal, L"拿不到图纸写锁(有别的命令在跑?)"); AecDbXSlab* pSlab = new AecDbXSlab(); std::wstring err; if (!applySlab(pSlab, args, true, err)) { delete pSlab; return errJson(kErrBadParam, err); } return finishCreate(pSlab, args, "slab"); } }; EXPORT_MCP_TOOL(ToolCreateSlab, CreateAecSlabTool) class ToolUpdateSlab : public mcp_Tool { public: std::string private_name() const override { return "update_aec_slab"; } std::string private_description() const override { return "修改已存在的 Envi3D 板(AecDbXSlab)。传 points 会重做轮廓," "否则只改板厚/标高/材质。"; } nlohmann::json private_input_schema() const override { return nlohmann::json::parse(R"({ "type": "object", "properties": { "handle": { "type": "string", "description": "板句柄" }, "points": { "type": "array", "description": "新轮廓点(可选)", "items": { "type": "array", "items": { "type": "number" } } }, "thickness": { "type": "number", "description": "新板厚(可选)" }, "bottom_elevation": { "type": "number", "description": "新板底标高(可选)" }, "center": { "type": "object", "description": "参考点(可选)" }, "material": { "type": "string", "description": "材质(可选)" } }, "required": [ "handle" ] })"); } nlohmann::json Execute(const nlohmann::json& args) override { if (workingDb() == NULL) return errJson(kErrNoDocument, L"没有活动图纸"); DocLock lock; if (!lock.isLocked()) return errJson(kErrInternal, L"拿不到图纸写锁(有别的命令在跑?)"); nlohmann::json err; AecDbXSlab* pSlab = openTyped(args, L"AecDbXSlab", err); if (pSlab == NULL) return err; std::wstring msg; if (!applySlab(pSlab, args, false, msg)) { pSlab->close(); return errJson(kErrBadParam, msg); } return finishUpdate(pSlab, args["handle"].get(), "slab"); } }; EXPORT_MCP_TOOL(ToolUpdateSlab, UpdateAecSlabTool) // =========================================================================== // 柱 // =========================================================================== class ToolCreateColumn : public mcp_Tool { public: std::string private_name() const override { return "add_aec_column"; } std::string private_description() const override { return "新建矩形柱(AecDbColumn)。position 是柱底中心点,height 是柱高," "length/width 是截面两个方向尺寸,rotation_deg 是绕 Z 轴转角(度)。尺寸用图纸单位。"; } nlohmann::json private_input_schema() const override { return nlohmann::json::parse(R"({ "type": "object", "properties": { "position": { "type": "object", "description": "柱底中心点 {x,y,z}" }, "height": { "type": "number", "description": "柱高,图纸单位" }, "length": { "type": "number", "description": "截面长(X 向),图纸单位" }, "width": { "type": "number", "description": "截面宽(Y 向),图纸单位" }, "rotation_deg": { "type": "number", "description": "绕 Z 轴旋转角度(度),缺省 0" }, "material": { "type": "string", "description": "材质" }, "layer": { "type": "string", "description": "图层名" } }, "required": [ "position", "height", "length", "width" ] })"); } nlohmann::json Execute(const nlohmann::json& args) override { if (workingDb() == NULL) return errJson(kErrNoDocument, L"没有活动图纸"); DocLock lock; if (!lock.isLocked()) return errJson(kErrInternal, L"拿不到图纸写锁(有别的命令在跑?)"); AecDbColumn* pCol = new AecDbColumn(); std::wstring err; if (!applyColumn(pCol, args, true, err)) { delete pCol; return errJson(kErrBadParam, err); } return finishCreate(pCol, args, "column"); } }; EXPORT_MCP_TOOL(ToolCreateColumn, CreateAecColumnTool) class ToolUpdateColumn : public mcp_Tool { public: std::string private_name() const override { return "update_aec_column"; } std::string private_description() const override { return "修改已存在的矩形柱(AecDbColumn)。只传要改的字段。"; } nlohmann::json private_input_schema() const override { return nlohmann::json::parse(R"({ "type": "object", "properties": { "handle": { "type": "string", "description": "柱句柄" }, "position": { "type": "object", "description": "新柱底中心点(可选)" }, "height": { "type": "number", "description": "新柱高(可选)" }, "length": { "type": "number", "description": "新截面长(可选)" }, "width": { "type": "number", "description": "新截面宽(可选)" }, "rotation_deg": { "type": "number", "description": "新旋转角度(可选)" }, "material": { "type": "string", "description": "材质(可选)" } }, "required": [ "handle" ] })"); } nlohmann::json Execute(const nlohmann::json& args) override { if (workingDb() == NULL) return errJson(kErrNoDocument, L"没有活动图纸"); DocLock lock; if (!lock.isLocked()) return errJson(kErrInternal, L"拿不到图纸写锁(有别的命令在跑?)"); nlohmann::json err; AecDbColumn* pCol = openTyped(args, L"AecDbColumn", err); if (pCol == NULL) return err; std::wstring msg; if (!applyColumn(pCol, args, false, msg)) { pCol->close(); return errJson(kErrBadParam, msg); } return finishUpdate(pCol, args["handle"].get(), "column"); } }; EXPORT_MCP_TOOL(ToolUpdateColumn, UpdateAecColumnTool) // =========================================================================== // 梁 // =========================================================================== class ToolCreateBeam : public mcp_Tool { public: std::string private_name() const override { return "add_aec_beam"; } std::string private_description() const override { return "新建梁(AecDbBeam)。沿 start→end 建梁,width/height 是截面尺寸,用图纸单位。"; } nlohmann::json private_input_schema() const override { return nlohmann::json::parse(R"({ "type": "object", "properties": { "start": { "type": "object", "description": "梁起点 {x,y,z}" }, "end": { "type": "object", "description": "梁终点 {x,y,z}" }, "width": { "type": "number", "description": "梁宽,图纸单位" }, "height": { "type": "number", "description": "梁高,图纸单位" }, "material": { "type": "string", "description": "材质" }, "layer": { "type": "string", "description": "图层名" } }, "required": [ "start", "end", "width", "height" ] })"); } nlohmann::json Execute(const nlohmann::json& args) override { if (workingDb() == NULL) return errJson(kErrNoDocument, L"没有活动图纸"); DocLock lock; if (!lock.isLocked()) return errJson(kErrInternal, L"拿不到图纸写锁(有别的命令在跑?)"); AecDbBeam* pBeam = new AecDbBeam(); std::wstring err; if (!applyBeam(pBeam, args, true, err)) { delete pBeam; return errJson(kErrBadParam, err); } return finishCreate(pBeam, args, "beam"); } }; EXPORT_MCP_TOOL(ToolCreateBeam, CreateAecBeamTool) class ToolUpdateBeam : public mcp_Tool { public: std::string private_name() const override { return "update_aec_beam"; } std::string private_description() const override { return "修改已存在的梁(AecDbBeam)。只传要改的字段。"; } nlohmann::json private_input_schema() const override { return nlohmann::json::parse(R"({ "type": "object", "properties": { "handle": { "type": "string", "description": "梁句柄" }, "start": { "type": "object", "description": "新起点(可选)" }, "end": { "type": "object", "description": "新终点(可选)" }, "width": { "type": "number", "description": "新梁宽(可选)" }, "height": { "type": "number", "description": "新梁高(可选)" }, "material": { "type": "string", "description": "材质(可选)" } }, "required": [ "handle" ] })"); } nlohmann::json Execute(const nlohmann::json& args) override { if (workingDb() == NULL) return errJson(kErrNoDocument, L"没有活动图纸"); DocLock lock; if (!lock.isLocked()) return errJson(kErrInternal, L"拿不到图纸写锁(有别的命令在跑?)"); nlohmann::json err; AecDbBeam* pBeam = openTyped(args, L"AecDbBeam", err); if (pBeam == NULL) return err; std::wstring msg; if (!applyBeam(pBeam, args, false, msg)) { pBeam->close(); return errJson(kErrBadParam, msg); } return finishUpdate(pBeam, args["handle"].get(), "beam"); } }; EXPORT_MCP_TOOL(ToolUpdateBeam, UpdateAecBeamTool)