@atlisp/mcp 1.8.15 → 1.8.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/handlers/3d-handlers.js +128 -0
- package/dist/handlers/batch-handlers.js +260 -0
- package/dist/handlers/block-handlers.js +214 -0
- package/dist/handlers/cad-handlers.js +180 -0
- package/dist/handlers/constraint-handlers.js +130 -0
- package/dist/handlers/dim-hatch-handlers.js +340 -0
- package/dist/handlers/draw-handlers.js +175 -0
- package/dist/handlers/edit-handlers.js +179 -0
- package/dist/handlers/entity-handlers.js +196 -0
- package/dist/handlers/external-handlers.js +218 -0
- package/dist/handlers/file-handlers.js +172 -0
- package/dist/handlers/function-handlers.js +158 -0
- package/dist/handlers/layer-handlers.js +181 -0
- package/dist/handlers/package-handlers.js +89 -0
- package/dist/handlers/parametric-handlers.js +272 -0
- package/dist/handlers/pdf-annotation-handlers.js +117 -0
- package/dist/handlers/prompt-handlers.js +9 -0
- package/dist/handlers/purge-handlers.js +137 -0
- package/dist/handlers/query-print-handlers.js +662 -0
- package/dist/handlers/resource-cache.js +63 -0
- package/dist/handlers/resource-defs.js +79 -0
- package/dist/handlers/resource-handlers.js +131 -0
- package/dist/handlers/resource-readers.js +1045 -0
- package/dist/handlers/sheetset-handlers.js +430 -0
- package/dist/handlers/spatial-handlers.js +174 -0
- package/dist/handlers/style-handlers.js +162 -0
- package/dist/handlers/text-handlers.js +198 -0
- package/dist/handlers/ucs-layout-handlers.js +271 -0
- package/dist/handlers/viewport-handlers.js +150 -0
- package/dist/handlers/xdata-handlers.js +148 -0
- package/dist/handlers/xref-handlers.js +334 -0
- package/dist/lisp-security.js +7 -2
- package/package.json +2 -2
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { cad } from '../cad.js';
|
|
2
|
+
import { escapeLispString } from '../handler-utils.js';
|
|
3
|
+
|
|
4
|
+
export async function createLayer(name, color = 7, linetype = 'Continuous') {
|
|
5
|
+
if (!cad.connected) { await cad.connect(); }
|
|
6
|
+
if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
|
|
7
|
+
|
|
8
|
+
const code = `(progn
|
|
9
|
+
(vl-load-com)
|
|
10
|
+
(if (not (tblsearch "LAYER" "${escapeLispString(name)}"))
|
|
11
|
+
(progn
|
|
12
|
+
(entmake
|
|
13
|
+
(list
|
|
14
|
+
(cons 0 "LAYER")
|
|
15
|
+
(cons 70 0)
|
|
16
|
+
(cons 2 "${escapeLispString(name)}")
|
|
17
|
+
(cons 62 ${color})
|
|
18
|
+
(cons 6 "${escapeLispString(linetype)}")
|
|
19
|
+
)
|
|
20
|
+
)
|
|
21
|
+
"CREATED"
|
|
22
|
+
)
|
|
23
|
+
"EXISTS"
|
|
24
|
+
)
|
|
25
|
+
)`;
|
|
26
|
+
try {
|
|
27
|
+
const result = await cad.sendCommandWithResult(code);
|
|
28
|
+
if (result === 'CREATED' || result === '"CREATED"') {
|
|
29
|
+
return { content: [{ type: 'text', text: `已创建图层: ${name}` }] };
|
|
30
|
+
}
|
|
31
|
+
if (result === 'EXISTS' || result === '"EXISTS"') {
|
|
32
|
+
return { content: [{ type: 'text', text: `图层已存在: ${name}` }] };
|
|
33
|
+
}
|
|
34
|
+
return { content: [{ type: 'text', text: `创建失败: ${result}` }], isError: true };
|
|
35
|
+
} catch (e) {
|
|
36
|
+
return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function deleteLayer(name) {
|
|
41
|
+
if (!cad.connected) { await cad.connect(); }
|
|
42
|
+
if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
|
|
43
|
+
|
|
44
|
+
const code = `(progn
|
|
45
|
+
(vl-load-com)
|
|
46
|
+
(cond
|
|
47
|
+
((eq (getvar "CLAYER") "${escapeLispString(name)}")
|
|
48
|
+
"CURRENT")
|
|
49
|
+
((tblsearch "LAYER" "${escapeLispString(name)}")
|
|
50
|
+
(command "._LAYER" "D" "${escapeLispString(name)}" "")
|
|
51
|
+
(if (not (tblsearch "LAYER" "${escapeLispString(name)}"))
|
|
52
|
+
"DELETED"
|
|
53
|
+
"FAILED"))
|
|
54
|
+
(t "NOTFOUND")
|
|
55
|
+
)
|
|
56
|
+
)`;
|
|
57
|
+
try {
|
|
58
|
+
const result = await cad.sendCommandWithResult(code);
|
|
59
|
+
if (result === 'DELETED' || result === '"DELETED"') {
|
|
60
|
+
return { content: [{ type: 'text', text: `已删除图层: ${name}` }] };
|
|
61
|
+
}
|
|
62
|
+
if (result === 'CURRENT' || result === '"CURRENT"') {
|
|
63
|
+
return { content: [{ type: 'text', text: `无法删除当前图层: ${name}` }], isError: true };
|
|
64
|
+
}
|
|
65
|
+
if (result === 'NOTFOUND' || result === '"NOTFOUND"') {
|
|
66
|
+
return { content: [{ type: 'text', text: `图层不存在: ${name}` }], isError: true };
|
|
67
|
+
}
|
|
68
|
+
return { content: [{ type: 'text', text: `删除失败: ${result}` }], isError: true };
|
|
69
|
+
} catch (e) {
|
|
70
|
+
return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function setLayerProperty(name, property, value) {
|
|
75
|
+
if (!cad.connected) { await cad.connect(); }
|
|
76
|
+
if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
|
|
77
|
+
|
|
78
|
+
let propCode = '';
|
|
79
|
+
switch (property) {
|
|
80
|
+
case 'color':
|
|
81
|
+
propCode = `(command "._LAYER" "P" "${escapeLispString(name)}" "C" ${isNaN(Number(value)) ? 7 : Number(value)} "")`;
|
|
82
|
+
break;
|
|
83
|
+
case 'linetype':
|
|
84
|
+
propCode = `(command "._LAYER" "P" "${escapeLispString(name)}" "L" "${escapeLispString(value)}" "")`;
|
|
85
|
+
break;
|
|
86
|
+
case 'freeze':
|
|
87
|
+
propCode = `(command "._LAYER" "F" "${escapeLispString(name)}" "")`;
|
|
88
|
+
break;
|
|
89
|
+
case 'thaw':
|
|
90
|
+
propCode = `(command "._LAYER" "T" "${escapeLispString(name)}" "")`;
|
|
91
|
+
break;
|
|
92
|
+
case 'lock':
|
|
93
|
+
propCode = `(command "._LAYER" "LO" "${escapeLispString(name)}" "")`;
|
|
94
|
+
break;
|
|
95
|
+
case 'unlock':
|
|
96
|
+
propCode = `(command "._LAYER" "U" "${escapeLispString(name)}" "")`;
|
|
97
|
+
break;
|
|
98
|
+
default:
|
|
99
|
+
return { content: [{ type: 'text', text: `不支持的属性: ${property}` }], isError: true };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const code = `(progn
|
|
103
|
+
(vl-load-com)
|
|
104
|
+
(if (tblsearch "LAYER" "${escapeLispString(name)}")
|
|
105
|
+
(progn
|
|
106
|
+
${propCode}
|
|
107
|
+
"OK"
|
|
108
|
+
)
|
|
109
|
+
"NOTFOUND"
|
|
110
|
+
)
|
|
111
|
+
)`;
|
|
112
|
+
try {
|
|
113
|
+
const result = await cad.sendCommandWithResult(code);
|
|
114
|
+
if (result === 'OK' || result === '"OK"') {
|
|
115
|
+
return { content: [{ type: 'text', text: `已设置图层 ${name} 的 ${property} 为 ${value}` }] };
|
|
116
|
+
}
|
|
117
|
+
return { content: [{ type: 'text', text: `操作失败: ${result}` }], isError: true };
|
|
118
|
+
} catch (e) {
|
|
119
|
+
return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export async function setCurrentLayer(name) {
|
|
124
|
+
if (!cad.connected) { await cad.connect(); }
|
|
125
|
+
if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
|
|
126
|
+
|
|
127
|
+
const code = `(progn
|
|
128
|
+
(vl-load-com)
|
|
129
|
+
(if (tblsearch "LAYER" "${escapeLispString(name)}")
|
|
130
|
+
(progn
|
|
131
|
+
(setvar "CLAYER" "${escapeLispString(name)}")
|
|
132
|
+
"OK"
|
|
133
|
+
)
|
|
134
|
+
"NOTFOUND"
|
|
135
|
+
)
|
|
136
|
+
)`;
|
|
137
|
+
try {
|
|
138
|
+
const result = await cad.sendCommandWithResult(code);
|
|
139
|
+
if (result === 'OK' || result === '"OK"') {
|
|
140
|
+
return { content: [{ type: 'text', text: `已设置当前图层: ${name}` }] };
|
|
141
|
+
}
|
|
142
|
+
return { content: [{ type: 'text', text: `设置失败: ${result}` }], isError: true };
|
|
143
|
+
} catch (e) {
|
|
144
|
+
return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
export async function layerIsolate(name) {
|
|
148
|
+
if (!cad.connected) { await cad.connect(); }
|
|
149
|
+
if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
|
|
150
|
+
try { await cad.sendCommand(`(command "_.LAYISO" "${escapeLispString(name)}" "")\n`); return { content: [{ type: 'text', text: `已隔离图层: ${name}` }] }; } catch (e) { return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true }; }
|
|
151
|
+
}
|
|
152
|
+
export async function layerUnisolate() {
|
|
153
|
+
if (!cad.connected) { await cad.connect(); }
|
|
154
|
+
if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
|
|
155
|
+
try { await cad.sendCommand('(command "_.LAYUNISO")\n'); return { content: [{ type: 'text', text: '已取消图层隔离' }] }; } catch (e) { return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true }; }
|
|
156
|
+
}
|
|
157
|
+
export async function layerFreezeAll() {
|
|
158
|
+
if (!cad.connected) { await cad.connect(); }
|
|
159
|
+
if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
|
|
160
|
+
try { await cad.sendCommand('(command "_.LAYER" "F" "*" "")\n'); return { content: [{ type: 'text', text: '已冻结所有图层' }] }; } catch (e) { return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true }; }
|
|
161
|
+
}
|
|
162
|
+
export async function layerThawAll() {
|
|
163
|
+
if (!cad.connected) { await cad.connect(); }
|
|
164
|
+
if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
|
|
165
|
+
try { await cad.sendCommand('(command "_.LAYER" "T" "*" "")\n'); return { content: [{ type: 'text', text: '已解冻所有图层' }] }; } catch (e) { return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true }; }
|
|
166
|
+
}
|
|
167
|
+
export async function layerLockAll() {
|
|
168
|
+
if (!cad.connected) { await cad.connect(); }
|
|
169
|
+
if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
|
|
170
|
+
try { await cad.sendCommand('(command "_.LAYER" "LO" "*" "")\n'); return { content: [{ type: 'text', text: '已锁定所有图层' }] }; } catch (e) { return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true }; }
|
|
171
|
+
}
|
|
172
|
+
export async function layerUnlockAll() {
|
|
173
|
+
if (!cad.connected) { await cad.connect(); }
|
|
174
|
+
if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
|
|
175
|
+
try { await cad.sendCommand('(command "_.LAYER" "U" "*" "")\n'); return { content: [{ type: 'text', text: '已解锁所有图层' }] }; } catch (e) { return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true }; }
|
|
176
|
+
}
|
|
177
|
+
export async function layerMerge(sourceName, targetName) {
|
|
178
|
+
if (!cad.connected) { await cad.connect(); }
|
|
179
|
+
if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
|
|
180
|
+
try { await cad.sendCommand(`(command "_.LAYMRG" "${escapeLispString(sourceName)}" "" "${escapeLispString(targetName)}" "Y")\n`); return { content: [{ type: 'text', text: `已将图层 ${sourceName} 合并到 ${targetName}` }] }; } catch (e) { return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true }; }
|
|
181
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { log, error as logError } from '../logger.js';
|
|
2
|
+
const PACKAGES_LIST_URL = process.env.PACKAGES_LIST_URL || 'http://s3.atlisp.cn/packages-list?fmt=json';
|
|
3
|
+
import { cad } from '../cad.js';
|
|
4
|
+
import { MOCK_PACKAGES } from '../constants.js';
|
|
5
|
+
let cachedPackages = null;
|
|
6
|
+
let cachedAt = null;
|
|
7
|
+
const CACHE_TTL = 10 * 60 * 1000;
|
|
8
|
+
|
|
9
|
+
async function fetchPackages() {
|
|
10
|
+
const now = Date.now();
|
|
11
|
+
if (cachedPackages && cachedAt && (now - cachedAt) < CACHE_TTL) {
|
|
12
|
+
return cachedPackages;
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
const response = await fetch(PACKAGES_LIST_URL, {
|
|
16
|
+
headers: { 'User-Agent': 'atlisp-mcp' },
|
|
17
|
+
signal: AbortSignal.timeout(10000),
|
|
18
|
+
});
|
|
19
|
+
if (response.ok) {
|
|
20
|
+
const data = await response.json();
|
|
21
|
+
cachedPackages = data;
|
|
22
|
+
cachedAt = now;
|
|
23
|
+
return data;
|
|
24
|
+
}
|
|
25
|
+
} catch (e) { logError(`Package fetch error: ${e.message}`); }
|
|
26
|
+
return cachedPackages || MOCK_PACKAGES;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function flattenPackages(data) {
|
|
30
|
+
if (Array.isArray(data)) return data;
|
|
31
|
+
if (data && Array.isArray(data.packages)) return data.packages;
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function listPackages() {
|
|
36
|
+
if (!cad.connected) await cad.connect();
|
|
37
|
+
if (!cad.connected) {
|
|
38
|
+
const data = await fetchPackages();
|
|
39
|
+
const items = flattenPackages(data);
|
|
40
|
+
const text = items.length
|
|
41
|
+
? items.map(p => `${p.name} v${p.version || ''} - ${p.description || ''}`).join('\n')
|
|
42
|
+
: '无可用包';
|
|
43
|
+
return { content: [{ type: 'text', text }] };
|
|
44
|
+
}
|
|
45
|
+
const result = await cad.sendCommandWithResult('(@::package-list-in-local)');
|
|
46
|
+
if (result && result !== 'nil') return { content: [{ type: 'text', text: result }] };
|
|
47
|
+
const data = await fetchPackages();
|
|
48
|
+
const items = flattenPackages(data);
|
|
49
|
+
const text = items.length
|
|
50
|
+
? `从注册表获取的 @lisp 包:\n${items.map(p => `${p.name} v${p.version || ''} - ${p.description || ''}`).join('\n')}`
|
|
51
|
+
: MOCK_PACKAGES.map(p => `${p.name} v${p.version} - ${p.description}`).join('\n');
|
|
52
|
+
return { content: [{ type: 'text', text: `已安装的 @lisp 包:\n${text}` }] };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function searchPackages(query) {
|
|
56
|
+
const data = await fetchPackages();
|
|
57
|
+
if (!data) {
|
|
58
|
+
return { content: [{ type: 'text', text: '无法获取包列表' }], isError: true };
|
|
59
|
+
}
|
|
60
|
+
const items = flattenPackages(data);
|
|
61
|
+
const results = [];
|
|
62
|
+
if (query) {
|
|
63
|
+
const q = query.toLowerCase();
|
|
64
|
+
for (const p of items) {
|
|
65
|
+
const name = (p.name || '').toLowerCase();
|
|
66
|
+
const desc = (p.description || '').toLowerCase();
|
|
67
|
+
if (name.includes(q) || desc.includes(q)) results.push(p);
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
for (const p of items) results.push(p);
|
|
71
|
+
}
|
|
72
|
+
if (results.length === 0) {
|
|
73
|
+
return { content: [{ type: 'text', text: `未找到包含 "${query}" 的包` }] };
|
|
74
|
+
}
|
|
75
|
+
return { content: [{ type: 'text', text: `搜索结果 (${results.length}):\n${results.map(p => `${p.name} v${p.version || ''} - ${p.description || ''}`).join('\n')}` }] };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function installPackage(packageName) {
|
|
79
|
+
const data = await fetchPackages();
|
|
80
|
+
const items = flattenPackages(data);
|
|
81
|
+
const pkg = items.find(p => p.name === packageName);
|
|
82
|
+
if (!pkg) {
|
|
83
|
+
return { content: [{ type: 'text', text: `错误: 包 "${packageName}" 不存在` }], isError: true };
|
|
84
|
+
}
|
|
85
|
+
if (cad.connected) {
|
|
86
|
+
await cad.sendCommand(`(@::load-module '${packageName})\n`);
|
|
87
|
+
}
|
|
88
|
+
return { content: [{ type: 'text', text: `包 "${packageName}" 安装命令已发送!` }] };
|
|
89
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { cad } from '../cad.js';
|
|
2
|
+
import { mcpSuccess, mcpError, escapeLispString } from '../handler-utils.js';
|
|
3
|
+
|
|
4
|
+
function esc(s) {
|
|
5
|
+
return escapeLispString(String(s || ''));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function getDynamicBlockProperties(args) {
|
|
9
|
+
try {
|
|
10
|
+
if (!cad.connected) await cad.connect();
|
|
11
|
+
if (!cad.connected) return mcpError('未连接 CAD');
|
|
12
|
+
const handle = esc(args.blockHandle);
|
|
13
|
+
const code = `(progn
|
|
14
|
+
(vl-load-com)
|
|
15
|
+
(if (setq ent (handent "${handle}"))
|
|
16
|
+
(progn
|
|
17
|
+
(setq vobj (vlax-ename->vla-object ent))
|
|
18
|
+
(setq dynProps (vl-catch-all-apply 'vlax-invoke (list vobj 'GetDynamicBlockProperties)))
|
|
19
|
+
(if (vl-catch-all-error-p dynProps)
|
|
20
|
+
"NotDynamicBlock"
|
|
21
|
+
(progn
|
|
22
|
+
(setq result nil)
|
|
23
|
+
(foreach p dynProps
|
|
24
|
+
(setq allowed (vl-catch-all-apply 'vlax-invoke (list p 'GetAllowedValues)))
|
|
25
|
+
(if (vl-catch-all-error-p allowed) (setq allowed nil))
|
|
26
|
+
(setq result (cons (list
|
|
27
|
+
(cons "propertyName" (vla-get-propertyname p))
|
|
28
|
+
(cons "value" (vl-princ-to-string (vla-get-value p)))
|
|
29
|
+
(cons "typeCode" (vla-get-propertytype p))
|
|
30
|
+
(cons "allowedValues" (if allowed (vl-princ-to-string allowed) nil))
|
|
31
|
+
) result))
|
|
32
|
+
)
|
|
33
|
+
(vl-princ-to-string (reverse result))
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
"EntityNotFound"
|
|
38
|
+
)
|
|
39
|
+
)`;
|
|
40
|
+
const result = await cad.sendCommandWithResult(code);
|
|
41
|
+
return mcpSuccess(result);
|
|
42
|
+
} catch (e) {
|
|
43
|
+
return mcpError(`错误: ${e.message}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function setDynamicBlockProperty(args) {
|
|
48
|
+
try {
|
|
49
|
+
if (!cad.connected) await cad.connect();
|
|
50
|
+
if (!cad.connected) return mcpError('未连接 CAD');
|
|
51
|
+
const handle = esc(args.blockHandle);
|
|
52
|
+
const propName = esc(args.propertyName);
|
|
53
|
+
const value = esc(args.value);
|
|
54
|
+
const code = `(progn
|
|
55
|
+
(vl-load-com)
|
|
56
|
+
(if (setq ent (handent "${handle}"))
|
|
57
|
+
(progn
|
|
58
|
+
(setq vobj (vlax-ename->vla-object ent))
|
|
59
|
+
(setq dynProps (vl-catch-all-apply 'vlax-invoke (list vobj 'GetDynamicBlockProperties)))
|
|
60
|
+
(if (vl-catch-all-error-p dynProps)
|
|
61
|
+
"NotDynamicBlock"
|
|
62
|
+
(progn
|
|
63
|
+
(setq found nil)
|
|
64
|
+
(foreach p dynProps
|
|
65
|
+
(if (= (strcase (vla-get-propertyname p)) (strcase "${propName}"))
|
|
66
|
+
(progn
|
|
67
|
+
(setq numVal (distof "${value}" 2))
|
|
68
|
+
(if numVal
|
|
69
|
+
(vla-put-value p numVal)
|
|
70
|
+
(vla-put-value p "${value}")
|
|
71
|
+
)
|
|
72
|
+
(setq found T)
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
)
|
|
76
|
+
(vla-update vobj)
|
|
77
|
+
(if found "ok" "PropertyNotFound")
|
|
78
|
+
)
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
"EntityNotFound"
|
|
82
|
+
)
|
|
83
|
+
)`;
|
|
84
|
+
const result = await cad.sendCommandWithResult(code);
|
|
85
|
+
if (result === 'ok') return mcpSuccess(`属性 ${args.propertyName} 已设置为 ${args.value}`);
|
|
86
|
+
return mcpError(`设置失败: ${result}`);
|
|
87
|
+
} catch (e) {
|
|
88
|
+
return mcpError(`错误: ${e.message}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function getBlockParameters(args) {
|
|
93
|
+
try {
|
|
94
|
+
if (!cad.connected) await cad.connect();
|
|
95
|
+
if (!cad.connected) return mcpError('未连接 CAD');
|
|
96
|
+
const handle = esc(args.blockHandle);
|
|
97
|
+
const code = `(progn
|
|
98
|
+
(vl-load-com)
|
|
99
|
+
(if (setq ent (handent "${handle}"))
|
|
100
|
+
(progn
|
|
101
|
+
(setq vobj (vlax-ename->vla-object ent))
|
|
102
|
+
(setq dynProps (vl-catch-all-apply 'vlax-invoke (list vobj 'GetDynamicBlockProperties)))
|
|
103
|
+
(if (vl-catch-all-error-p dynProps)
|
|
104
|
+
"NotDynamicBlock"
|
|
105
|
+
(progn
|
|
106
|
+
(setq result nil)
|
|
107
|
+
(foreach p dynProps
|
|
108
|
+
(setq tc (vla-get-propertytype p))
|
|
109
|
+
(if (>= tc 3)
|
|
110
|
+
(progn
|
|
111
|
+
(setq allowed (vl-catch-all-apply 'vlax-invoke (list p 'GetAllowedValues)))
|
|
112
|
+
(if (vl-catch-all-error-p allowed) (setq allowed nil))
|
|
113
|
+
(setq result (cons (list
|
|
114
|
+
(cons "parameterName" (vla-get-propertyname p))
|
|
115
|
+
(cons "value" (vl-princ-to-string (vla-get-value p)))
|
|
116
|
+
(cons "typeCode" tc)
|
|
117
|
+
(cons "allowedValues" (if allowed (vl-princ-to-string allowed) nil))
|
|
118
|
+
) result))
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
(vl-princ-to-string (reverse result))
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
"EntityNotFound"
|
|
127
|
+
)
|
|
128
|
+
)`;
|
|
129
|
+
const result = await cad.sendCommandWithResult(code);
|
|
130
|
+
return mcpSuccess(result);
|
|
131
|
+
} catch (e) {
|
|
132
|
+
return mcpError(`错误: ${e.message}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function setBlockParameter(args) {
|
|
137
|
+
try {
|
|
138
|
+
if (!cad.connected) await cad.connect();
|
|
139
|
+
if (!cad.connected) return mcpError('未连接 CAD');
|
|
140
|
+
const handle = esc(args.blockHandle);
|
|
141
|
+
const paramName = esc(args.parameterName);
|
|
142
|
+
const value = esc(args.value);
|
|
143
|
+
const code = `(progn
|
|
144
|
+
(vl-load-com)
|
|
145
|
+
(if (setq ent (handent "${handle}"))
|
|
146
|
+
(progn
|
|
147
|
+
(setq vobj (vlax-ename->vla-object ent))
|
|
148
|
+
(setq dynProps (vl-catch-all-apply 'vlax-invoke (list vobj 'GetDynamicBlockProperties)))
|
|
149
|
+
(if (vl-catch-all-error-p dynProps)
|
|
150
|
+
"NotDynamicBlock"
|
|
151
|
+
(progn
|
|
152
|
+
(setq found nil)
|
|
153
|
+
(foreach p dynProps
|
|
154
|
+
(if (and (>= (vla-get-propertytype p) 3)
|
|
155
|
+
(= (strcase (vla-get-propertyname p)) (strcase "${paramName}")))
|
|
156
|
+
(progn
|
|
157
|
+
(setq numVal (distof "${value}" 2))
|
|
158
|
+
(if numVal
|
|
159
|
+
(vla-put-value p numVal)
|
|
160
|
+
(vla-put-value p "${value}")
|
|
161
|
+
)
|
|
162
|
+
(setq found T)
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
(vla-update vobj)
|
|
167
|
+
(if found "ok" "ParameterNotFound")
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
"EntityNotFound"
|
|
172
|
+
)
|
|
173
|
+
)`;
|
|
174
|
+
const result = await cad.sendCommandWithResult(code);
|
|
175
|
+
if (result === 'ok') return mcpSuccess(`参数 ${args.parameterName} 已设置为 ${args.value}`);
|
|
176
|
+
return mcpError(`设置失败: ${result}`);
|
|
177
|
+
} catch (e) {
|
|
178
|
+
return mcpError(`错误: ${e.message}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export async function listVisibilityStates(args) {
|
|
183
|
+
try {
|
|
184
|
+
if (!cad.connected) await cad.connect();
|
|
185
|
+
if (!cad.connected) return mcpError('未连接 CAD');
|
|
186
|
+
const handle = esc(args.blockHandle);
|
|
187
|
+
const code = `(progn
|
|
188
|
+
(vl-load-com)
|
|
189
|
+
(if (setq ent (handent "${handle}"))
|
|
190
|
+
(progn
|
|
191
|
+
(setq vobj (vlax-ename->vla-object ent))
|
|
192
|
+
(setq dynProps (vl-catch-all-apply 'vlax-invoke (list vobj 'GetDynamicBlockProperties)))
|
|
193
|
+
(if (vl-catch-all-error-p dynProps)
|
|
194
|
+
"NotDynamicBlock"
|
|
195
|
+
(progn
|
|
196
|
+
(setq visProp nil)
|
|
197
|
+
(foreach p dynProps
|
|
198
|
+
(if (= (strcase (vla-get-propertyname p)) "VISIBILITY")
|
|
199
|
+
(setq visProp p)
|
|
200
|
+
)
|
|
201
|
+
)
|
|
202
|
+
(if visProp
|
|
203
|
+
(progn
|
|
204
|
+
(setq allowed (vl-catch-all-apply 'vlax-invoke (list visProp 'GetAllowedValues)))
|
|
205
|
+
(if (vl-catch-all-error-p allowed) (setq allowed nil))
|
|
206
|
+
(setq current (vl-princ-to-string (vla-get-value visProp)))
|
|
207
|
+
(setq states nil)
|
|
208
|
+
(foreach s allowed
|
|
209
|
+
(setq states (cons (list
|
|
210
|
+
(cons "name" s)
|
|
211
|
+
(cons "isActive" (= (strcase (vl-princ-to-string s)) (strcase current)))
|
|
212
|
+
) states))
|
|
213
|
+
)
|
|
214
|
+
(vl-princ-to-string (reverse states))
|
|
215
|
+
)
|
|
216
|
+
"NoVisibilityProperty"
|
|
217
|
+
)
|
|
218
|
+
)
|
|
219
|
+
)
|
|
220
|
+
)
|
|
221
|
+
"EntityNotFound"
|
|
222
|
+
)
|
|
223
|
+
)`;
|
|
224
|
+
const result = await cad.sendCommandWithResult(code);
|
|
225
|
+
return mcpSuccess(result);
|
|
226
|
+
} catch (e) {
|
|
227
|
+
return mcpError(`错误: ${e.message}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export async function setVisibilityState(args) {
|
|
232
|
+
try {
|
|
233
|
+
if (!cad.connected) await cad.connect();
|
|
234
|
+
if (!cad.connected) return mcpError('未连接 CAD');
|
|
235
|
+
const handle = esc(args.blockHandle);
|
|
236
|
+
const stateName = esc(args.stateName);
|
|
237
|
+
const code = `(progn
|
|
238
|
+
(vl-load-com)
|
|
239
|
+
(if (setq ent (handent "${handle}"))
|
|
240
|
+
(progn
|
|
241
|
+
(setq vobj (vlax-ename->vla-object ent))
|
|
242
|
+
(setq dynProps (vl-catch-all-apply 'vlax-invoke (list vobj 'GetDynamicBlockProperties)))
|
|
243
|
+
(if (vl-catch-all-error-p dynProps)
|
|
244
|
+
"NotDynamicBlock"
|
|
245
|
+
(progn
|
|
246
|
+
(setq visProp nil)
|
|
247
|
+
(foreach p dynProps
|
|
248
|
+
(if (= (strcase (vla-get-propertyname p)) "VISIBILITY")
|
|
249
|
+
(setq visProp p)
|
|
250
|
+
)
|
|
251
|
+
)
|
|
252
|
+
(if visProp
|
|
253
|
+
(progn
|
|
254
|
+
(vla-put-value visProp "${stateName}")
|
|
255
|
+
(vla-update vobj)
|
|
256
|
+
"ok"
|
|
257
|
+
)
|
|
258
|
+
"NoVisibilityProperty"
|
|
259
|
+
)
|
|
260
|
+
)
|
|
261
|
+
)
|
|
262
|
+
)
|
|
263
|
+
"EntityNotFound"
|
|
264
|
+
)
|
|
265
|
+
)`;
|
|
266
|
+
const result = await cad.sendCommandWithResult(code);
|
|
267
|
+
if (result === 'ok') return mcpSuccess(`可见性状态已切换为: ${args.stateName}`);
|
|
268
|
+
return mcpError(`设置失败: ${result}`);
|
|
269
|
+
} catch (e) {
|
|
270
|
+
return mcpError(`错误: ${e.message}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { cad } from '../cad.js';
|
|
2
|
+
import { log } from '../logger.js';
|
|
3
|
+
import { mcpSuccess, mcpError, ensureCadConnected, escapeLispString } from '../handler-utils.js';
|
|
4
|
+
|
|
5
|
+
export async function attachPdf(point, pdfPath, scale = 1, rotation = 0) {
|
|
6
|
+
try { await ensureCadConnected(); const pt = Array.isArray(point) ? point.join(' ') : point; await cad.sendCommand(`(command "_.pdfattach" "${escapeLispString(pdfPath)}" "${escapeLispString(pt)}" "${scale}" "${rotation}")`); return mcpSuccess(JSON.stringify({ pdfPath, point: pt, scale, rotation })); } catch (e) { return mcpError(`错误: ${e.message}`); }
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function listAttachedPdfs() {
|
|
10
|
+
try {
|
|
11
|
+
await ensureCadConnected();
|
|
12
|
+
const result = await cad.sendCommandWithResult(`(progn
|
|
13
|
+
(setq i -1 pdfs nil ss (ssget "X" (list (cons 0 "PDFREFERENCE"))))
|
|
14
|
+
(if ss (repeat (sslength ss) (setq ent (ssname ss (setq i (1+ i))) pdfs (cons (cdr (assoc -1 (entget ent))) pdfs))))
|
|
15
|
+
(princ (vl-prin1-to-string (reverse pdfs)))
|
|
16
|
+
)`);
|
|
17
|
+
let handles = [];
|
|
18
|
+
if (result && result !== 'nil') { try { handles = JSON.parse(result); } catch { handles = []; } }
|
|
19
|
+
return mcpSuccess(JSON.stringify({ handles, count: handles.length }));
|
|
20
|
+
} catch (e) { return mcpError(`错误: ${e.message}`); }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function reloadPdf(pdfHandle) {
|
|
24
|
+
try { await ensureCadConnected(); const result = await cad.sendCommandWithResult(`(progn (command "_.pdfreload" (handent "${escapeLispString(pdfHandle)}")) (princ "reloaded"))`); return mcpSuccess(JSON.stringify({ handle: pdfHandle, reloaded: result?.includes('reloaded') })); } catch (e) { return mcpError(`错误: ${e.message}`); }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function detachPdf(pdfHandle) {
|
|
28
|
+
try { await ensureCadConnected(); await cad.sendCommand(`(entdel (handent "${escapeLispString(pdfHandle)}"))`); return mcpSuccess(JSON.stringify({ handle: pdfHandle, detached: true })); } catch (e) { return mcpError(`错误: ${e.message}`); }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function getPdfInfo(pdfHandle) {
|
|
32
|
+
try {
|
|
33
|
+
await ensureCadConnected();
|
|
34
|
+
const result = await cad.sendCommandWithResult(`(progn
|
|
35
|
+
(setq ent (handent "${escapeLispString(pdfHandle)}"))
|
|
36
|
+
(if ent (princ (vl-prin1-to-string (list (cons "path" (cdr (assoc 1 (entget ent)))) (cons "name" (cdr (assoc 2 (entget ent))))))) (princ "nil"))
|
|
37
|
+
)`);
|
|
38
|
+
let info = {};
|
|
39
|
+
if (result && result !== 'nil') { try { info = JSON.parse(result); } catch { info = {}; } }
|
|
40
|
+
return mcpSuccess(JSON.stringify({ handle: pdfHandle, info }));
|
|
41
|
+
} catch (e) { return mcpError(`错误: ${e.message}`); }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function createDgnUnderlay(dgnPath, point, scale = 1) {
|
|
45
|
+
try { await ensureCadConnected(); const pt = Array.isArray(point) ? point.join(' ') : point; await cad.sendCommand(`(command "_.dgnattach" "${escapeLispString(dgnPath)}" "${escapeLispString(pt)}" "${scale}")`); return mcpSuccess(JSON.stringify({ dgnPath, point: pt, scale })); } catch (e) { return mcpError(`错误: ${e.message}`); }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function createDwfUnderlay(dwfPath, point, scale = 1) {
|
|
49
|
+
try { await ensureCadConnected(); const pt = Array.isArray(point) ? point.join(' ') : point; await cad.sendCommand(`(command "_.dwfattach" "${escapeLispString(dwfPath)}" "${escapeLispString(pt)}" "${scale}")`); return mcpSuccess(JSON.stringify({ dwfPath, point: pt, scale })); } catch (e) { return mcpError(`错误: ${e.message}`); }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function importDxf(dxfPath, explode = false) {
|
|
53
|
+
try { await ensureCadConnected(); await cad.sendCommand(`(command "_.dxfin" "${escapeLispString(dxfPath)}" ${explode ? '"explode"' : ''})`); return mcpSuccess(JSON.stringify({ dxfPath, explode })); } catch (e) { return mcpError(`错误: ${e.message}`); }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function importDgn(sourceFile, targetFile = '') {
|
|
57
|
+
try { await ensureCadConnected(); await cad.sendCommand(`(command "_.dgnin" "${escapeLispString(sourceFile)}" "${escapeLispString(targetFile)}")`); return mcpSuccess(JSON.stringify({ sourceFile, targetFile })); } catch (e) { return mcpError(`错误: ${e.message}`); }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function exportDgn(filePath) {
|
|
61
|
+
try { await ensureCadConnected(); await cad.sendCommand(`(command "_.dgnout" "${escapeLispString(filePath)}")`); return mcpSuccess(JSON.stringify({ filePath })); } catch (e) { return mcpError(`错误: ${e.message}`); }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function createHyperlink(handle, url, description = '') {
|
|
65
|
+
try {
|
|
66
|
+
await ensureCadConnected();
|
|
67
|
+
const result = await cad.sendCommandWithResult(`(progn
|
|
68
|
+
(vl-load-com)
|
|
69
|
+
(setq ent (handent "${escapeLispString(handle)}"))
|
|
70
|
+
(if ent (progn
|
|
71
|
+
(vla-put-hyperlinks (vlax-ename->vla-object ent) (vlax-add (vla-get-hyperlinks (vlax-ename->vla-object ent)) "${escapeLispString(url)}" "${escapeLispString(description)}"))
|
|
72
|
+
(princ "added")) (princ "nil"))
|
|
73
|
+
)`);
|
|
74
|
+
return mcpSuccess(JSON.stringify({ handle, url, description, added: result?.includes('added') }));
|
|
75
|
+
} catch (e) { return mcpError(`错误: ${e.message}`); }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function removeHyperlink(handle) {
|
|
79
|
+
try {
|
|
80
|
+
await ensureCadConnected();
|
|
81
|
+
const result = await cad.sendCommandWithResult(`(progn
|
|
82
|
+
(vl-load-com)
|
|
83
|
+
(setq ent (handent "${escapeLispString(handle)}"))
|
|
84
|
+
(if ent (progn (vla-clear (vla-get-hyperlinks (vlax-ename->vla-object ent))) (princ "removed")) (princ "nil"))
|
|
85
|
+
)`);
|
|
86
|
+
return mcpSuccess(JSON.stringify({ handle, removed: result?.includes('removed') }));
|
|
87
|
+
} catch (e) { return mcpError(`错误: ${e.message}`); }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function batchAttachPdfs(pdfFiles, basePoint, spacing = 0, direction = 'x') {
|
|
91
|
+
try {
|
|
92
|
+
await ensureCadConnected();
|
|
93
|
+
const results = [];
|
|
94
|
+
const pt = Array.isArray(basePoint) ? basePoint.join(' ') : basePoint;
|
|
95
|
+
for (let i = 0; i < pdfFiles.length; i++) {
|
|
96
|
+
try {
|
|
97
|
+
const offsetX = direction === 'x' ? 0 : spacing * i;
|
|
98
|
+
const offsetY = direction === 'y' ? 0 : spacing * i;
|
|
99
|
+
await cad.sendCommand(`(command "_.pdfattach" "${escapeLispString(pdfFiles[i])}" (getvar "viewctr") "1" "0")`);
|
|
100
|
+
if (spacing > 0 && i > 0) { await cad.sendCommand(`(command "_.move" "l" "" (getvar "viewctr") (list ${offsetX} ${offsetY} 0))`); }
|
|
101
|
+
results.push({ file: pdfFiles[i], success: true });
|
|
102
|
+
} catch (e) { results.push({ file: pdfFiles[i], error: e.message, success: false }); }
|
|
103
|
+
}
|
|
104
|
+
return mcpSuccess(JSON.stringify(results));
|
|
105
|
+
} catch (e) { return mcpError(`错误: ${e.message}`); }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function savePdfLayout(layoutName, outputPath, plotDevice = 'DWG to PDF.pc3', paperSize = 'A4') {
|
|
109
|
+
try {
|
|
110
|
+
await ensureCadConnected();
|
|
111
|
+
const result = await cad.sendCommandWithResult(`(progn
|
|
112
|
+
(command "_.plot" "y" "${escapeLispString(layoutName)}" "${escapeLispString(plotDevice)}" "${escapeLispString(paperSize)}" "Landscape" "n" "n" "n" "n" "n" "${escapeLispString(outputPath)}")
|
|
113
|
+
(princ "saved")
|
|
114
|
+
)`);
|
|
115
|
+
return mcpSuccess(JSON.stringify({ layoutName, outputPath, saved: result?.includes('saved') || result?.includes('配置') }));
|
|
116
|
+
} catch (e) { return mcpError(`错误: ${e.message}`); }
|
|
117
|
+
}
|