第 4 项(按 sys/*.mnu 扩工具)的第一步:从菜单命令里筛出一类参数清晰的实体先做通。 - 新增 add_aec_climb_stair / update_aec_climb_stair(AecDbClimbStair,对应菜单命令 PL_ZXPT 直爬梯、PL_YHZT 圆护爬梯)。建模序列照抄 pool3dcmdCmd.cpp:5742 的命令实现: SetSection(踏管直径) -> SetBaseCurve(底->顶) -> SetClimbStairData(参数体), 再写专业数据 2dStyle / 朝向(KEY_MAJBR)。工具直接收两个端点,不需要复现 "放置点+朝向"那套偏移算法。 参数:start/end、width、cage_diameter、height、bottom_elevation、type(0/1)、 bar_diameter、step_height、step_spacing、wall_distance、facing、material、layer。 - query_aec_entities / get_aec_entity_detail 支持 climb_stair(读 GetClimbStairData)。 - mcp_config.json 增加这两个工具(40 -> 42),query 的 type 枚举补 climb_stair。 实测(AutoCAD 2020 / R230 Debug): - tools/list = 42;add_aec_climb_stair 建出 handle 3A0(宽 450 / 护笼 700 / 高 3000); - get_aec_entity_detail 读回 type=0/width=450/cage=700/height=3000/bar=20/step=300; - update 改 width=600、step_height=250 后读回一致;query type=climb_stair 命中 1 条。
1142 lines
39 KiB
C++
1142 lines
39 KiB
C++
// ============================================================================
|
||
// 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 "AecDbClimbStair.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 <typename T>
|
||
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<std::string>();
|
||
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<AecDbEwPipe>(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<std::string>(), "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<AecDbXWall>(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<std::string>(), "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<AecDbXSlab>(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<std::string>(), "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<AecDbColumn>(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<std::string>(), "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<AecDbBeam>(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<std::string>(), "beam");
|
||
}
|
||
};
|
||
EXPORT_MCP_TOOL(ToolUpdateBeam, UpdateAecBeamTool)
|
||
|
||
// ===========================================================================
|
||
// 爬梯(AecDbClimbStair)
|
||
// 对应菜单命令 PL_ZXPT 直爬梯 / PL_YHZT 圆护爬梯(pool3dcmdCmd.cpp:5742)。
|
||
// 建模序列照抄命令实现:setLayer -> SetSection(踏管直径) -> SetBaseCurve(底->顶)
|
||
// -> SetClimbStairData(参数体) -> 专业数据 2dStyle/朝向。
|
||
// ===========================================================================
|
||
namespace
|
||
{
|
||
// 爬梯参数体:没传的字段用"现有值",新建时用命令对话框里的缺省量级
|
||
bool ApplyClimbStair(AecDbClimbStair* 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_climb_stair 需要 start(梯段底部点) 与 end(顶部点)";
|
||
return false;
|
||
}
|
||
|
||
ClimbStair data;
|
||
::memset(&data, 0, sizeof(ClimbStair));
|
||
p->GetClimbStairData(data);
|
||
|
||
if (bCreate)
|
||
{
|
||
data.nType = hasKey(a, "type") ? intOr(a, "type", 0) : 0; // 0 圆护直梯 1 圆护凸爬
|
||
data.dSegSp = 300.0;
|
||
data.dSteepH = 300.0;
|
||
data.dSpWall = 180.0;
|
||
data.dD = 700.0;
|
||
data.dW = 450.0;
|
||
data.dd = 20.0;
|
||
data.dt = 2.5;
|
||
}
|
||
|
||
if (hasKey(a, "type")) data.nType = intOr(a, "type", data.nType);
|
||
if (hasKey(a, "cage_diameter")) data.dD = numOr(a, "cage_diameter", data.dD);
|
||
if (hasKey(a, "width")) data.dW = numOr(a, "width", data.dW);
|
||
if (hasKey(a, "height")) data.dH = numOr(a, "height", data.dH);
|
||
if (hasKey(a, "bottom_elevation")) data.dBomEl = numOr(a, "bottom_elevation", data.dBomEl);
|
||
if (hasKey(a, "step_height")) data.dSteepH = numOr(a, "step_height", data.dSteepH);
|
||
if (hasKey(a, "step_spacing")) data.dSegSp = numOr(a, "step_spacing", data.dSegSp);
|
||
if (hasKey(a, "wall_distance")) data.dSpWall = numOr(a, "wall_distance", data.dSpWall);
|
||
if (hasKey(a, "bar_diameter")) data.dd = numOr(a, "bar_diameter", data.dd);
|
||
if (hasKey(a, "bar_thickness")) data.dt = numOr(a, "bar_thickness", data.dt);
|
||
if (hasKey(a, "material"))
|
||
{
|
||
CString sMat(wstrOr(a, "material", L"").c_str());
|
||
_tcsncpy_s(data.m_sMaterial, 256, sMat, _TRUNCATE);
|
||
}
|
||
|
||
if (data.dW <= 0.0 || data.dD <= 0.0)
|
||
{
|
||
err = L"爬梯需要 width 与 cage_diameter,且都 > 0";
|
||
return false;
|
||
}
|
||
|
||
// 梯段基线:命令里是按"放置点+朝向"算出来的,工具直接收两个端点(更直接)
|
||
if (bHasS && bHasE)
|
||
{
|
||
if (ptS.distanceTo(ptE) < 1e-9)
|
||
{
|
||
err = L"start 与 end 重合,梯段长度为 0";
|
||
return false;
|
||
}
|
||
if (data.dH <= 1e-9)
|
||
data.dH = ptE.z - ptS.z;
|
||
if (data.dH <= 1e-9)
|
||
data.dH = 3000.0;
|
||
|
||
p->SetSection(data.dd, 0.0);
|
||
p->SetBaseCurve(ptS, ptE);
|
||
}
|
||
|
||
p->SetClimbStairData(data);
|
||
|
||
// 朝向:命令里存的是"人面对的方向",缺省给 +Y
|
||
AcGeVector3d vtFace(0.0, 1.0, 0.0);
|
||
{
|
||
nlohmann::json jFace;
|
||
if (a.is_object() && a.contains("facing"))
|
||
{
|
||
AcGePoint3d ptFace;
|
||
if (readPoint(a, "facing", ptFace))
|
||
vtFace = ptFace.asVector();
|
||
}
|
||
vtFace.normalize();
|
||
}
|
||
p->GetSpecialtyData()->SetAt(KEY_2DSTYLE, 0);
|
||
p->GetSpecialtyData()->SetAt(KEY_MAJBR, vtFace);
|
||
|
||
return true;
|
||
}
|
||
}
|
||
|
||
class ToolAddClimbStair : public mcp_Tool
|
||
{
|
||
public:
|
||
std::string private_name() const override { return "add_aec_climb_stair"; }
|
||
|
||
std::string private_description() const override
|
||
{
|
||
return "新建爬梯(AecDbClimbStair,对应菜单命令 直爬梯/圆护爬梯)。"
|
||
"给梯段底部点与顶部点,以及梯宽、护笼直径、总高;其余按对话框缺省。尺寸用图纸单位。";
|
||
}
|
||
|
||
nlohmann::json private_input_schema() const override
|
||
{
|
||
return nlohmann::json::parse(R"({
|
||
"type": "object",
|
||
"properties": {
|
||
"start": { "type": "array", "items": { "type": "number" }, "description": "梯段底部点 [x,y] 或 [x,y,z]" },
|
||
"end": { "type": "array", "items": { "type": "number" }, "description": "梯段顶部点" },
|
||
"width": { "type": "number", "description": "梯宽,图纸单位(缺省 450)" },
|
||
"cage_diameter": { "type": "number", "description": "护笼总直径(缺省 700)" },
|
||
"height": { "type": "number", "description": "总高,不给则用 end.z - start.z" },
|
||
"bottom_elevation": { "type": "number", "description": "底标高,缺省 0" },
|
||
"type": { "type": "integer", "description": "0=圆护直梯(缺省)1=圆护凸爬" },
|
||
"bar_diameter": { "type": "number", "description": "踏管直径(缺省 20)" },
|
||
"step_height": { "type": "number", "description": "踏步高(缺省 300)" },
|
||
"step_spacing": { "type": "number", "description": "踏棍间距(缺省 300)" },
|
||
"wall_distance": { "type": "number", "description": "距墙距离(缺省 180)" },
|
||
"facing": { "type": "array", "items": { "type": "number" }, "description": "人面对的方向 [x,y],缺省 +Y" },
|
||
"material": { "type": "string", "description": "材质" },
|
||
"layer": { "type": "string", "description": "图层,缺省 LAY_CSTAIR" }
|
||
},
|
||
"required": [ "start", "end" ]
|
||
})");
|
||
}
|
||
|
||
nlohmann::json Execute(const nlohmann::json& args) override
|
||
{
|
||
if (workingDb() == NULL) return errJson(kErrNoDocument, L"没有活动图纸");
|
||
DocLock lock;
|
||
if (!lock.isLocked()) return errJson(kErrInternal, L"拿不到图纸写锁(有别的命令在跑?)");
|
||
|
||
AecDbClimbStair* pStair = new AecDbClimbStair();
|
||
std::wstring err;
|
||
if (!ApplyClimbStair(pStair, args, true, err))
|
||
{
|
||
delete pStair;
|
||
return errJson(kErrBadParam, err);
|
||
}
|
||
|
||
// 命令里固定用 LAY_CSTAIR 图层;工具允许覆盖
|
||
if (!hasKey(args, "layer"))
|
||
{
|
||
AcDbObjectId idLay = layerIdByName(L"LAY_CSTAIR");
|
||
if (!idLay.isNull())
|
||
pStair->setLayer(idLay);
|
||
}
|
||
|
||
return finishCreate(pStair, args, "climb_stair");
|
||
}
|
||
};
|
||
EXPORT_MCP_TOOL(ToolAddClimbStair, AddAecClimbStairTool)
|
||
|
||
class ToolUpdateClimbStair : public mcp_Tool
|
||
{
|
||
public:
|
||
std::string private_name() const override { return "update_aec_climb_stair"; }
|
||
|
||
std::string private_description() const override
|
||
{
|
||
return "修改已有爬梯:只传要改的字段(端点、梯宽、护笼直径、总高、踏步、材质…)。";
|
||
}
|
||
|
||
nlohmann::json private_input_schema() const override
|
||
{
|
||
return nlohmann::json::parse(R"({
|
||
"type": "object",
|
||
"properties": {
|
||
"handle": { "type": "string", "description": "爬梯句柄" },
|
||
"start": { "type": "array", "items": { "type": "number" }, "description": "新起点(可选)" },
|
||
"end": { "type": "array", "items": { "type": "number" }, "description": "新终点(可选)" },
|
||
"width": { "type": "number" }, "cage_diameter": { "type": "number" },
|
||
"height": { "type": "number" }, "bottom_elevation": { "type": "number" },
|
||
"type": { "type": "integer" }, "bar_diameter": { "type": "number" },
|
||
"step_height": { "type": "number" }, "step_spacing": { "type": "number" },
|
||
"wall_distance": { "type": "number" }, "facing": { "type": "array", "items": { "type": "number" } },
|
||
"material": { "type": "string" }
|
||
},
|
||
"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;
|
||
AecDbClimbStair* pStair = openTyped<AecDbClimbStair>(args, L"AecDbClimbStair", err);
|
||
if (pStair == NULL) return err;
|
||
|
||
std::wstring msg;
|
||
if (!ApplyClimbStair(pStair, args, false, msg))
|
||
{
|
||
pStair->close();
|
||
return errJson(kErrBadParam, msg);
|
||
}
|
||
return finishUpdate(pStair, args["handle"].get<std::string>(), "climb_stair");
|
||
}
|
||
};
|
||
EXPORT_MCP_TOOL(ToolUpdateClimbStair, UpdateAecClimbStairTool)
|