diff --git a/src/AutoAgent/AutoAgent.cpp b/src/AutoAgent/AutoAgent.cpp index e162389..a284ba7 100644 --- a/src/AutoAgent/AutoAgent.cpp +++ b/src/AutoAgent/AutoAgent.cpp @@ -1,6 +1,7 @@ #include "StdAfx.h" #include "resource.h" #include "cadsdk.h" +#include "mcp.h" HINSTANCE _hdllInstance = NULL ; @@ -49,8 +50,9 @@ extern "C" AcRx::AppRetCode acrxEntryPoint(AcRx::AppMsgCode msg, void* pkt) void CR_SCGXCmdFnc(); void InitApplication() { - acrxBuildClassHierarchy(); + mcp::close_mcp(); + acrxBuildClassHierarchy(); AddCommand(_T("SHCRACKTOOLS"), _T("CR_SCGX"), _T("CR_SCGX"), ACRX_CMD_TRANSPARENT | ACRX_CMD_USEPICKSET, CR_SCGXCmdFnc); } @@ -58,6 +60,7 @@ void InitApplication() void UnloadApplication() { acedRegCmds->removeGroup(_T("SHCRACKTOOLS")); + mcp::close_mcp(); } void AddCommand(const TCHAR* cmdGroup, const TCHAR* cmdInt, const TCHAR* cmdLoc, diff --git a/src/AutoAgent/cmakelists.txt b/src/AutoAgent/cmakelists.txt index 139886e..e016af6 100644 --- a/src/AutoAgent/cmakelists.txt +++ b/src/AutoAgent/cmakelists.txt @@ -10,6 +10,7 @@ set(SOURCES "command.cpp" "stdafx.cpp" "AutoAgent.rc" + "mcp.cpp" ) add_def_file_name(SOURCES "${PROJECT_NAME}") diff --git a/src/AutoAgent/mcp.cpp b/src/AutoAgent/mcp.cpp new file mode 100644 index 0000000..6952232 --- /dev/null +++ b/src/AutoAgent/mcp.cpp @@ -0,0 +1,147 @@ +#include "StdAfx.h" +#include "cadsdk.h" +#include +#include +#include +#include +#include + +#pragma comment(lib, "ws2_32.lib") + +static std::thread g_serverThread; +static std::atomic g_bRunning(false); +static SOCKET g_listenSocket = INVALID_SOCKET; + +// 用于跨线程传递数据的结构体 +struct TaskData +{ + std::string rawJson; +}; + +// 真正执行绘图的回调函数 (此函数由 AutoCAD 主线程调度运行) +static void MainThreadCallback(void *pData) +{ + TaskData *pTask = static_cast(pData); + if (pTask == nullptr) + { + return; + } + + try + { + // 1. 解析 JSON 指令 + auto j = nlohmann::json::parse(pTask->rawJson); + if (j.contains("cmd") && j["cmd"] == "draw_circle") + { + double x = j.value("x", 0.0); + double y = j.value("y", 0.0); + double r = j.value("r", 100.0); + + // 2. 锁定当前活动文档 (跨线程操作后的绝对必要步骤) + AcApDocument *pDoc = acDocManager->curDocument(); + if (pDoc) + { + acDocManager->lockDocument(pDoc, AcAp::kWrite, nullptr, nullptr, false); + + // 3. 常规 ObjectARX 绘图逻辑 + AcDbDatabase *pDb = pDoc->database(); + AcDbBlockTable *pBlockTable = nullptr; + pDb->getBlockTable(pBlockTable, AcDb::kForRead); + + AcDbBlockTableRecord *pBTR = nullptr; + pBlockTable->getAt(ACDB_MODEL_SPACE, pBTR, AcDb::kForWrite); + pBlockTable->close(); + + AcGePoint3d center(x, y, 0.0); + AcGeVector3d normal(0.0, 0.0, 1.0); + AcDbCircle *pCircle = new AcDbCircle(center, normal, r); + + AcDbObjectId circleId; + pBTR->appendAcDbEntity(circleId, pCircle); + + pBTR->close(); + pCircle->close(); + + acDocManager->unlockDocument(pDoc); + acutPrintf(_T("\n[MCP Server] 成功在坐标(%.2f, %.2f)绘制了半径为%.2f的圆\n"), x, y, r); + } + } + } + catch (...) + { + acutPrintf(_T("\n[MCP Server] JSON 解析或执行异常!\n")); + } + + // 4. 清理由 Socket 线程在堆上分配的内存 + delete pTask; +} + +// TCP 监听后台线程 +static void TcpServerLoop() +{ + WSADATA wsaData; + WSAStartup(MAKEWORD(2, 2), &wsaData); + + g_listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + sockaddr_in serverAddr = { 0 }; + serverAddr.sin_family = AF_INET; + inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr); + serverAddr.sin_port = htons(8080); + + bind(g_listenSocket, (sockaddr *)&serverAddr, sizeof(serverAddr)); + listen(g_listenSocket, SOMAXCONN); + + while (g_bRunning) + { + // 使用 select 模型实现带超时的轮询,确保插件卸载时线程能正常退出 + fd_set readSet; + FD_ZERO(&readSet); + FD_SET(g_listenSocket, &readSet); + + timeval timeout = { 1, 0 }; // 1秒超时机制 + int ret = select(0, &readSet, nullptr, nullptr, &timeout); + + if (ret > 0 && FD_ISSET(g_listenSocket, &readSet)) + { + SOCKET clientSocket = accept(g_listenSocket, nullptr, nullptr); + if (clientSocket != INVALID_SOCKET) + { + char recvBuf[2048] = { 0 }; + int bytesReceived = recv(clientSocket, recvBuf, sizeof(recvBuf) - 1, 0); + if (bytesReceived > 0) + { + // 将数据拷贝到堆内存,准备派发给主线程 + TaskData *pTask = new TaskData; + pTask->rawJson = recvBuf; + + // 核心:委托 AutoCAD 主线程在下一个消息循环中执行回调 + acDocManager->executeInApplicationContext(MainThreadCallback, pTask); + } + closesocket(clientSocket); + } + } + } + + closesocket(g_listenSocket); + WSACleanup(); +} + +namespace mcp +{ + bool init_mcp() + { + g_bRunning = true; + g_serverThread = std::thread(TcpServerLoop); + acutPrintf(_T("\n[MCP Server] 后台 TCP 监听线程已启动 (端口: 8080)...\n")); + } + + bool close_mcp() + { + g_bRunning = false; + if (g_serverThread.joinable()) + { + g_serverThread.join(); + } + acutPrintf(_T("\n[MCP Server] 插件已卸载,线程已关闭。\n")); + } +} diff --git a/src/AutoAgent/mcp.h b/src/AutoAgent/mcp.h new file mode 100644 index 0000000..5f7c3d8 --- /dev/null +++ b/src/AutoAgent/mcp.h @@ -0,0 +1,9 @@ +#pragma once + +namespace mcp +{ + bool init_mcp(); + bool close_mcp(); +} + +