@atlisp/mcp 1.8.16 → 1.8.18

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.
Files changed (34) hide show
  1. package/dist/atlisp-mcp.js +1 -10
  2. package/dist/handlers/3d-handlers.js +128 -0
  3. package/dist/handlers/batch-handlers.js +260 -0
  4. package/dist/handlers/block-handlers.js +214 -0
  5. package/dist/handlers/cad-handlers.js +180 -0
  6. package/dist/handlers/constraint-handlers.js +130 -0
  7. package/dist/handlers/dim-hatch-handlers.js +340 -0
  8. package/dist/handlers/draw-handlers.js +175 -0
  9. package/dist/handlers/edit-handlers.js +179 -0
  10. package/dist/handlers/entity-handlers.js +196 -0
  11. package/dist/handlers/external-handlers.js +218 -0
  12. package/dist/handlers/file-handlers.js +172 -0
  13. package/dist/handlers/function-handlers.js +158 -0
  14. package/dist/handlers/layer-handlers.js +181 -0
  15. package/dist/handlers/package-handlers.js +89 -0
  16. package/dist/handlers/parametric-handlers.js +272 -0
  17. package/dist/handlers/pdf-annotation-handlers.js +117 -0
  18. package/dist/handlers/prompt-handlers.js +9 -0
  19. package/dist/handlers/purge-handlers.js +137 -0
  20. package/dist/handlers/query-print-handlers.js +662 -0
  21. package/dist/handlers/resource-cache.js +63 -0
  22. package/dist/handlers/resource-defs.js +79 -0
  23. package/dist/handlers/resource-handlers.js +131 -0
  24. package/dist/handlers/resource-readers.js +1045 -0
  25. package/dist/handlers/sheetset-handlers.js +430 -0
  26. package/dist/handlers/spatial-handlers.js +174 -0
  27. package/dist/handlers/style-handlers.js +162 -0
  28. package/dist/handlers/text-handlers.js +198 -0
  29. package/dist/handlers/ucs-layout-handlers.js +271 -0
  30. package/dist/handlers/viewport-handlers.js +150 -0
  31. package/dist/handlers/xdata-handlers.js +148 -0
  32. package/dist/handlers/xref-handlers.js +334 -0
  33. package/dist/lisp-security.js +7 -2
  34. package/package.json +2 -2
@@ -0,0 +1,662 @@
1
+ import { cad } from '../cad.js';
2
+ import { escapeLispString } from '../handler-utils.js';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import os from 'os';
6
+
7
+ function toSafePoint(pt) {
8
+ const parts = (Array.isArray(pt) ? pt : String(pt).split(/[,\s]+/)).map(Number);
9
+ return parts.filter(n => !isNaN(n)).join(' ');
10
+ }
11
+
12
+ export async function plotToPdf(outputPath, layout = 'Model', style = {}) {
13
+ if (!cad.connected) { await cad.connect(); }
14
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
15
+
16
+ const paperSize = style.paperSize || 'A4';
17
+ const code = `(progn
18
+ (vl-load-com)
19
+ (setq doc (vla-get-activedocument (vlax-get-acad-object)))
20
+ (vlax-for layout (vla-get-layouts doc)
21
+ (if (or (eq (vla-get-name layout) "${escapeLispString(layout)}") (eq "${escapeLispString(layout)}" "Model"))
22
+ (progn
23
+ (vla-put-active layout)
24
+ (vla-plottofile doc "${escapeLispString(outputPath)}" "${escapeLispString(paperSize)}")
25
+ (setq result "EXPORTED")
26
+ )
27
+ )
28
+ )
29
+ (if result result "FAILED")
30
+ )`;
31
+ try {
32
+ const result = await cad.sendCommandWithResult(code);
33
+ if (result === 'EXPORTED' || result === '"EXPORTED"') {
34
+ return { content: [{ type: 'text', text: `已打印到 PDF: ${outputPath}` }] };
35
+ }
36
+ return { content: [{ type: 'text', text: `打印失败: ${result}` }], isError: true };
37
+ } catch (e) {
38
+ return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
39
+ }
40
+ }
41
+
42
+ export async function plotToPrinter(printerName, layout = 'Model') {
43
+ if (!cad.connected) { await cad.connect(); }
44
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
45
+
46
+ const code = `(progn
47
+ (vl-load-com)
48
+ (setq doc (vla-get-activedocument (vlax-get-acad-object)))
49
+ (vlax-for layout (vla-get-layouts doc)
50
+ (if (or (eq (vla-get-name layout) "${escapeLispString(layout)}") (eq "${escapeLispString(layout)}" "Model"))
51
+ (progn
52
+ (vla-put-active layout)
53
+ (vla-plottofile doc "${escapeLispString(printerName)}")
54
+ (setq result "PRINTED")
55
+ )
56
+ )
57
+ )
58
+ (if result result "FAILED")
59
+ )`;
60
+ try {
61
+ const result = await cad.sendCommandWithResult(code);
62
+ return { content: [{ type: 'text', text: result === 'PRINTED' ? '已发送到打印机' : '打印失败' }] };
63
+ } catch (e) {
64
+ return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
65
+ }
66
+ }
67
+
68
+ export async function createGroup(name, handles) {
69
+ if (!cad.connected) { await cad.connect(); }
70
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
71
+
72
+ if (!Array.isArray(handles) || handles.length === 0) {
73
+ return { content: [{ type: 'text', text: '句柄列表为空' }], isError: true };
74
+ }
75
+
76
+ const handlesCode = handles.map(h => `(ssadd (handent "${escapeLispString(h)}") ss)`).join('\n');
77
+ const code = `(progn
78
+ (vl-load-com)
79
+ (setq ss (ssadd))
80
+ ${handlesCode}
81
+ (command "._GROUP" "C" "${escapeLispString(name)}" "" ss "")
82
+ "CREATED"
83
+ )`;
84
+ try {
85
+ const result = await cad.sendCommandWithResult(code);
86
+ if (result === 'CREATED' || result === '"CREATED"') {
87
+ return { content: [{ type: 'text', text: `已创建组: ${name}` }] };
88
+ }
89
+ return { content: [{ type: 'text', text: `创建失败: ${result}` }], isError: true };
90
+ } catch (e) {
91
+ return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
92
+ }
93
+ }
94
+
95
+ export async function deleteGroup(name) {
96
+ if (!cad.connected) { await cad.connect(); }
97
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
98
+
99
+ const code = `(progn
100
+ (vl-load-com)
101
+ (setq @dict (dictsearch (namedobjdict) "ACAD_GROUP"))
102
+ (if (and @dict (dictsearch (cdr (assoc -1 @dict)) "${escapeLispString(name)}"))
103
+ (progn
104
+ (command "._GROUP" "D" "${escapeLispString(name)}")
105
+ "DELETED"
106
+ )
107
+ "NOTFOUND"
108
+ )
109
+ )`;
110
+ try {
111
+ const result = await cad.sendCommandWithResult(code);
112
+ if (result === 'DELETED' || result === '"DELETED"') {
113
+ return { content: [{ type: 'text', text: `已删除组: ${name}` }] };
114
+ }
115
+ return { content: [{ type: 'text', text: result === 'NOTFOUND' ? `组不存在: ${name}` : '删除失败' }], isError: true };
116
+ } catch (e) {
117
+ return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
118
+ }
119
+ }
120
+
121
+ export async function listGroups() {
122
+ if (!cad.connected) { await cad.connect(); }
123
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
124
+
125
+ const code = `(progn
126
+ (vl-load-com)
127
+ (setq @groups nil)
128
+ (setq @dict (dictsearch (namedobjdict) "ACAD_GROUP"))
129
+ (if @dict
130
+ (setq @group-dict (cdr (assoc -1 @dict)))
131
+ (setq @group-dict nil))
132
+ (if @group-dict
133
+ (progn
134
+ (setq @entry (dictsearch @group-dict "*"))
135
+ (while @entry
136
+ (setq @groups (cons (cdr (assoc 3 @entry)) @groups))
137
+ (setq @entry (dictsearch @group-dict @entry))
138
+ )
139
+ (vl-prin1-to-string (reverse @groups))
140
+ )
141
+ "nil"
142
+ )
143
+ )`;
144
+ try {
145
+ const result = await cad.sendCommandWithResult(code);
146
+ return { content: [{ type: 'text', text: result || '无组' }] };
147
+ } catch (e) {
148
+ return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
149
+ }
150
+ }
151
+
152
+ export async function getGroupEntities(groupName) {
153
+ if (!cad.connected) { await cad.connect(); }
154
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
155
+
156
+ const code = `(progn
157
+ (vl-load-com)
158
+ (setq @dict (dictsearch (namedobjdict) "ACAD_GROUP"))
159
+ (if (and @dict (dictsearch (cdr (assoc -1 @dict)) "${escapeLispString(groupName)}"))
160
+ (progn
161
+ (command "._SELECT" "G" "${escapeLispString(groupName)}" "")
162
+ (if (and (getvar 'ssmode) (ssget-first))
163
+ (progn
164
+ (setq ss (ssget "+" (list (cons 410 (getvar 'ctab)))))
165
+ (if ss
166
+ (progn
167
+ (setq cnt (sslength ss) handles nil i 0)
168
+ (while (< i cnt)
169
+ (setq handles (cons (cdr (assoc 5 (entget (ssname ss i)))) handles))
170
+ (setq i (1+ i)))
171
+ (reverse handles)
172
+ )
173
+ nil
174
+ )
175
+ (sssetfirst nil)
176
+ )
177
+ nil
178
+ )
179
+ )
180
+ nil
181
+ )
182
+ )`;
183
+ try {
184
+ const result = await cad.sendCommandWithResult(code);
185
+ return { content: [{ type: 'text', text: result || '无实体' }] };
186
+ } catch (e) {
187
+ return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
188
+ }
189
+ }
190
+
191
+ export async function measureDistance(p1, p2) {
192
+ if (!cad.connected) { await cad.connect(); }
193
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
194
+
195
+ const code = `(progn
196
+ (vl-load-com)
197
+ (setq pt1 (list ${toSafePoint(p1)}) pt2 (list ${toSafePoint(p2)}))
198
+ (setq dist (distance pt1 pt2))
199
+ (setq ang (angle pt1 pt2))
200
+ (list
201
+ (strcat "Distance: " (rtos dist 2 4))
202
+ (strcat "Angle: " (rtos (* 180 (/ ang pi)) 2 2) "°")
203
+ (strcat "DX: " (rtos (- (car pt2) (car pt1)) 2 4))
204
+ (strcat "DY: " (rtos (- (cadr pt2) (cadr pt1)) 2 4))
205
+ )
206
+ )`;
207
+ try {
208
+ const result = await cad.sendCommandWithResult(code);
209
+ return { content: [{ type: 'text', text: result || '测量失败' }] };
210
+ } catch (e) {
211
+ return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
212
+ }
213
+ }
214
+
215
+ export async function measureArea(points) {
216
+ if (!cad.connected) { await cad.connect(); }
217
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
218
+
219
+ const pts = Array.isArray(points) ? points : points.split(/[\s,]+/).filter(Boolean);
220
+ if (pts.length < 3) {
221
+ return { content: [{ type: 'text', text: '至少需要 3 个点' }], isError: true };
222
+ }
223
+
224
+ const coords = pts.map((p, i) => {
225
+ const parts = String(p).split(',').map(Number);
226
+ return parts.length >= 2 && !isNaN(parts[0]) && !isNaN(parts[1])
227
+ ? `(list ${parts[0]} ${parts[1]})`
228
+ : '(list 0 0)';
229
+ }).join(' ');
230
+
231
+ const code = `(progn
232
+ (vl-load-com)
233
+ (setq pts (list ${coords}))
234
+ (if (>= (length pts) 3)
235
+ (progn
236
+ (command "._PLINE")
237
+ (foreach p pts (command p))
238
+ (command "")
239
+ (command "._AREA" "O" "L")
240
+ (list
241
+ (strcat "Area: " (rtos (getvar 'area) 2 4) " sq units")
242
+ (strcat "Perimeter: " (rtos (getvar 'perimeter) 2 4))
243
+ )
244
+ )
245
+ "NEED_3_POINTS"
246
+ )
247
+ )`;
248
+ try {
249
+ const result = await cad.sendCommandWithResult(code);
250
+ return { content: [{ type: 'text', text: result || '测量失败' }] };
251
+ } catch (e) {
252
+ return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
253
+ }
254
+ }
255
+
256
+ export async function publishLayouts(args) {
257
+ if (!cad.connected) { await cad.connect(); }
258
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
259
+
260
+ const layouts = Array.isArray(args.layouts) ? args.layouts : [args.layouts || 'Model'];
261
+ const outputDir = args.outputDir || '.';
262
+ const namingRule = args.namingRule || '{layout}';
263
+ const format = args.format || 'PDF';
264
+ const merge = args.merge !== false;
265
+
266
+ let results = [];
267
+ for (const layout of layouts) {
268
+ try {
269
+ const safeName = String(layout).replace(/[^a-zA-Z0-9_\-\u4e00-\u9fff]/g, '_');
270
+ const outputPath = namingRule.replace('{layout}', safeName).replace('{format}', format.toLowerCase());
271
+ const fullPath = outputDir.endsWith('/') || outputDir.endsWith('\\')
272
+ ? `${outputDir}${outputPath}.${format.toLowerCase()}`
273
+ : `${outputDir}/${outputPath}.${format.toLowerCase()}`;
274
+
275
+ const result = await cad.sendCommandWithResult(`(progn
276
+ (vl-load-com)
277
+ (setq doc (vla-get-activedocument (vlax-get-acad-object)))
278
+ (setq found nil)
279
+ (vlax-for l (vla-get-layouts doc)
280
+ (if (= (vla-get-name l) "${escapeLispString(layout)}")
281
+ (progn
282
+ (vla-put-active l)
283
+ (vla-plottofile doc "${escapeLispString(fullPath)}" "A4")
284
+ (setq found "ok")
285
+ )
286
+ )
287
+ )
288
+ (if found (princ found) (princ "not found"))
289
+ )`);
290
+ results.push({ layout, outputPath: fullPath, success: result === 'ok' });
291
+ } catch (e) {
292
+ results.push({ layout, outputPath: '', success: false, error: e.message });
293
+ }
294
+ }
295
+
296
+ const successCount = results.filter(r => r.success).length;
297
+ const msg = `布局批量发布完成: 成功 ${successCount}/${results.length}`;
298
+ return {
299
+ content: [{
300
+ type: 'text',
301
+ text: JSON.stringify({ summary: msg, results })
302
+ }],
303
+ isError: successCount < results.length
304
+ };
305
+ }
306
+
307
+ export async function batchPlotWizard(args) {
308
+ if (!cad.connected) { await cad.connect(); }
309
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
310
+
311
+ const layouts = Array.isArray(args.layouts) ? args.layouts : [args.layouts || 'Model'];
312
+ const outputDir = args.outputDir || '.';
313
+ const format = (args.format || 'PDF').toUpperCase();
314
+ const namingRule = args.namingRule || '{layout}';
315
+ const pageSetup = args.pageSetup || '';
316
+
317
+ const dateStr = new Date().toISOString().slice(0, 10).replace(/-/g, '');
318
+ const results = [];
319
+
320
+ for (const layout of layouts) {
321
+ try {
322
+ const safeName = String(layout).replace(/[^a-zA-Z0-9_\-\u4e00-\u9fff]/g, '_');
323
+ const outputName = namingRule
324
+ .replace('{layout}', safeName)
325
+ .replace('{date}', dateStr)
326
+ .replace('{format}', format.toLowerCase());
327
+ const fullPath = outputDir.endsWith('/') || outputDir.endsWith('\\')
328
+ ? `${outputDir}${outputName}.${format.toLowerCase()}`
329
+ : `${outputDir}/${outputName}.${format.toLowerCase()}`;
330
+
331
+ const preview = args.preview === true;
332
+ if (preview) {
333
+ results.push({ layout, outputPath: fullPath, status: 'PREVIEW' });
334
+ continue;
335
+ }
336
+
337
+ const code = `(progn
338
+ (vl-load-com)
339
+ (setq doc (vla-get-activedocument (vlax-get-acad-object)))
340
+ (setq found nil)
341
+ (vlax-for l (vla-get-layouts doc)
342
+ (if (= (vla-get-name l) "${escapeLispString(layout)}")
343
+ (progn
344
+ (vla-put-active l)
345
+ ${pageSetup ? `(vla-put-activelayout doc "${escapeLispString(pageSetup)}")` : ''}
346
+ (vla-plottofile doc "${escapeLispString(fullPath)}" "A4")
347
+ (setq found "ok")
348
+ )
349
+ )
350
+ )
351
+ (if found found "not found")
352
+ )`;
353
+ const result = await cad.sendCommandWithResult(code);
354
+ results.push({ layout, outputPath: fullPath, status: result === 'ok' ? 'EXPORTED' : 'FAILED', raw: result });
355
+ } catch (e) {
356
+ results.push({ layout, outputPath: '', status: 'ERROR', error: e.message });
357
+ }
358
+ }
359
+
360
+ const successCount = results.filter(r => r.status === 'EXPORTED').length;
361
+ return {
362
+ content: [{
363
+ type: 'text',
364
+ text: JSON.stringify({
365
+ summary: `批量打印完成: 成功 ${successCount}/${results.length}`,
366
+ total: results.length,
367
+ success: successCount,
368
+ results
369
+ })
370
+ }],
371
+ isError: successCount < results.length
372
+ };
373
+ }
374
+
375
+ export async function plotDeviceManagement(args) {
376
+ if (!cad.connected) { await cad.connect(); }
377
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
378
+
379
+ const action = (args.action || 'list').toLowerCase();
380
+
381
+ try {
382
+ if (action === 'list') {
383
+ const code = `(progn
384
+ (vl-load-com)
385
+ (setq cfg (vla-get-plotconfigurations (vla-get-activedocument (vlax-get-acad-object))))
386
+ (setq devices nil i 0)
387
+ (vlax-for pc cfg
388
+ (setq devices (cons
389
+ (list
390
+ "name" (vla-get-name pc)
391
+ "description" (vla-get-description pc)
392
+ "paperSizes" (vlax-get pc 'CanonicalMediaNames)
393
+ "isValid" (vla-get-valid pc)
394
+ )
395
+ devices
396
+ ))
397
+ )
398
+ (reverse devices)
399
+ )`;
400
+ const result = await cad.sendCommandWithResult(code);
401
+ return { content: [{ type: 'text', text: result || '无打印设备' }] };
402
+ }
403
+
404
+ if (action === 'get') {
405
+ const name = args.name || '';
406
+ if (!name) return { content: [{ type: 'text', text: '需要 name 参数' }], isError: true };
407
+ const code = `(progn
408
+ (vl-load-com)
409
+ (setq cfg (vla-get-plotconfigurations (vla-get-activedocument (vlax-get-acad-object))))
410
+ (setq found nil)
411
+ (vlax-for pc cfg
412
+ (if (= (vla-get-name pc) "${escapeLispString(name)}")
413
+ (setq found (list
414
+ "name" (vla-get-name pc)
415
+ "description" (vla-get-description pc)
416
+ "paperSizes" (vlax-get pc 'CanonicalMediaNames)
417
+ "isValid" (vla-get-valid pc)
418
+ "locDeviceName" (vlax-get pc 'LocDeviceName)
419
+ ))
420
+ )
421
+ )
422
+ (if found found (strcat "NOTFOUND:" "${escapeLispString(name)}"))
423
+ )`;
424
+ const result = await cad.sendCommandWithResult(code);
425
+ return { content: [{ type: 'text', text: result }] };
426
+ }
427
+
428
+ if (action === 'set') {
429
+ const name = args.name || '';
430
+ if (!name) return { content: [{ type: 'text', text: '需要 name 参数' }], isError: true };
431
+ const code = `(progn
432
+ (vl-load-com)
433
+ (setq doc (vla-get-activedocument (vlax-get-acad-object)))
434
+ (vla-put-activeplotconfig doc "${escapeLispString(name)}")
435
+ (if (= (vla-get-activeplotconfig doc) "${escapeLispString(name)}") "SET" "FAILED")
436
+ )`;
437
+ const result = await cad.sendCommandWithResult(code);
438
+ return { content: [{ type: 'text', text: result === 'SET' ? `打印设备已设为: ${name}` : `设置失败: ${result || name}` }] };
439
+ }
440
+
441
+ if (action === 'test') {
442
+ const name = args.name;
443
+ const code = `(progn
444
+ (vl-load-com)
445
+ (setq doc (vla-get-activedocument (vlax-get-acad-object)))
446
+ ${name ? `(vla-put-activeplotconfig doc "${escapeLispString(name)}")` : ''}
447
+ (setq cfg (vla-get-activeplotconfig doc))
448
+ (list
449
+ "name" cfg
450
+ "canPlot" (if (vl-catch-all-error-p (vl-catch-all-apply 'vla-plottofile (list doc (strcat (vl-filename-mktemp) ".pdf") "A4"))) "FAIL" "OK")
451
+ )
452
+ )`;
453
+ const result = await cad.sendCommandWithResult(code);
454
+ return { content: [{ type: 'text', text: result || '测试失败' }] };
455
+ }
456
+
457
+ return { content: [{ type: 'text', text: `未知 action: ${action}` }], isError: true };
458
+ } catch (e) {
459
+ return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
460
+ }
461
+ }
462
+
463
+ export async function plotLog(args) {
464
+ const homedir = os.homedir ? os.homedir() : (process.env.HOME || process.env.USERPROFILE || '');
465
+ const logPath = path.join(homedir, '.atlisp', 'plot-log.json');
466
+
467
+ let logs = [];
468
+ try {
469
+ if (fs.existsSync(logPath)) {
470
+ const raw = fs.readFileSync(logPath, 'utf-8');
471
+ logs = JSON.parse(raw);
472
+ if (!Array.isArray(logs)) logs = [];
473
+ }
474
+ } catch {
475
+ logs = [];
476
+ }
477
+
478
+ const from = args.fromDate ? new Date(args.fromDate).getTime() : 0;
479
+ const to = args.toDate ? new Date(args.toDate).getTime() : Date.now() + 86400000;
480
+ const status = args.status ? args.status.toLowerCase() : '';
481
+
482
+ const filtered = logs.filter(entry => {
483
+ const t = new Date(entry.timestamp || entry.date || 0).getTime();
484
+ if (t < from || t > to) return false;
485
+ if (status && (entry.status || '').toLowerCase() !== status) return false;
486
+ return true;
487
+ });
488
+
489
+ return {
490
+ content: [{
491
+ type: 'text',
492
+ text: JSON.stringify({
493
+ total: filtered.length,
494
+ fromDate: args.fromDate || null,
495
+ toDate: args.toDate || null,
496
+ statusFilter: args.status || null,
497
+ entries: filtered
498
+ })
499
+ }]
500
+ };
501
+ }
502
+
503
+ export async function plotFromFileList(args) {
504
+ if (!cad.connected) { await cad.connect(); }
505
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
506
+
507
+ let fileList = [];
508
+ if (Array.isArray(args.fileList)) {
509
+ fileList = args.fileList;
510
+ } else if (args.textFile) {
511
+ try {
512
+ const content = fs.readFileSync(args.textFile, 'utf-8');
513
+ const lines = content.split(/\r?\n/).filter(Boolean);
514
+ fileList = lines.map(line => {
515
+ const parts = line.split('|').map(s => s.trim());
516
+ return { dwgPath: parts[0], layout: parts[1] || 'Model', outputPath: parts[2] || '' };
517
+ });
518
+ } catch (e) {
519
+ return { content: [{ type: 'text', text: `读取文件失败: ${e.message}` }], isError: true };
520
+ }
521
+ } else {
522
+ return { content: [{ type: 'text', text: '需要 fileList 或 textFile' }], isError: true };
523
+ }
524
+
525
+ if (fileList.length === 0) {
526
+ return { content: [{ type: 'text', text: '文件列表为空' }], isError: true };
527
+ }
528
+
529
+ const results = [];
530
+
531
+ for (const item of fileList) {
532
+ try {
533
+ const dwgPath = item.dwgPath;
534
+ const layout = item.layout || 'Model';
535
+ let outputPath = item.outputPath;
536
+
537
+ if (!dwgPath) {
538
+ results.push({ dwgPath: '', layout, outputPath: '', status: 'ERROR', error: '缺少 dwgPath' });
539
+ continue;
540
+ }
541
+
542
+ if (!outputPath) {
543
+ const ext = '.pdf';
544
+ outputPath = dwgPath.replace(/\.dwg$/i, '') + `_${layout}${ext}`;
545
+ }
546
+
547
+ let openResult;
548
+ if (dwgPath) {
549
+ openResult = await cad.sendCommandWithResult(`(progn
550
+ (vl-load-com)
551
+ (setq doc (vla-open (vla-get-documents (vlax-get-acad-object)) "${escapeLispString(dwgPath)}"))
552
+ (if doc "OPENED" "FAILED")
553
+ )`);
554
+ if (openResult !== 'OPENED') {
555
+ results.push({ dwgPath, layout, outputPath, status: 'ERROR', error: `打开文件失败: ${openResult || ''}` });
556
+ continue;
557
+ }
558
+ }
559
+
560
+ const code = `(progn
561
+ (vl-load-com)
562
+ (setq doc (vla-get-activedocument (vlax-get-acad-object)))
563
+ (setq found nil)
564
+ (vlax-for l (vla-get-layouts doc)
565
+ (if (= (vla-get-name l) "${escapeLispString(layout)}")
566
+ (progn
567
+ (vla-put-active l)
568
+ (vla-plottofile doc "${escapeLispString(outputPath)}" "A4")
569
+ (setq found "ok")
570
+ )
571
+ )
572
+ )
573
+ (if found found "not found")
574
+ )`;
575
+ const result = await cad.sendCommandWithResult(code);
576
+ results.push({ dwgPath, layout, outputPath, status: result === 'ok' ? 'EXPORTED' : 'FAILED', raw: result });
577
+ } catch (e) {
578
+ results.push({ dwgPath: item.dwgPath || '', layout: item.layout || 'Model', outputPath: item.outputPath || '', status: 'ERROR', error: e.message });
579
+ }
580
+ }
581
+
582
+ const successCount = results.filter(r => r.status === 'EXPORTED').length;
583
+ return {
584
+ content: [{
585
+ type: 'text',
586
+ text: JSON.stringify({
587
+ summary: `文件列表批量打印完成: 成功 ${successCount}/${results.length}`,
588
+ total: results.length,
589
+ success: successCount,
590
+ results
591
+ })
592
+ }],
593
+ isError: successCount < results.length
594
+ };
595
+ }
596
+
597
+ export async function getEntityInfo(handle) {
598
+ if (!cad.connected) { await cad.connect(); }
599
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
600
+
601
+ const code = `(progn
602
+ (vl-load-com)
603
+ (if (setq ent (handent "${escapeLispString(handle)}"))
604
+ (progn
605
+ (setq ed (entget ent) typ (cdr (assoc 0 ed)))
606
+ (list
607
+ "type" typ
608
+ "handle" (cdr (assoc 5 ed))
609
+ "layer" (cdr (assoc 8 ed))
610
+ "color" (if (assoc 62 ed) (cdr (assoc 62 ed)) 256)
611
+ "linetype" (if (assoc 6 ed) (cdr (assoc 6 ed)) "ByLayer")
612
+ "layerFrozen" (if (assoc 70 ed) (/= (logand (cdr (assoc 70 ed)) 1) 0) nil)
613
+ "layerLocked" (if (assoc 70 ed) (/= (logand (cdr (assoc 70 ed)) 4) 0) nil)
614
+ )
615
+ )
616
+ nil
617
+ )
618
+ )`;
619
+ try {
620
+ const result = await cad.sendCommandWithResult(code);
621
+ if (!result || result === 'nil') {
622
+ return { content: [{ type: 'text', text: `未找到实体: ${handle}` }], isError: true };
623
+ }
624
+ return { content: [{ type: 'text', text: result }] };
625
+ } catch (e) {
626
+ return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true };
627
+ }
628
+ }
629
+
630
+ export async function createPageSetup(name, printer = 'DWG To PDF.pc3', paperSize = 'A4', orientation = 'Landscape', scale = '1:1') {
631
+ if (!cad.connected) { await cad.connect(); }
632
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
633
+ try {
634
+ await cad.sendCommand(`(command "_.-PAGESETUP" "${escapeLispString(name)}" "" "" "${escapeLispString(printer)}" "${escapeLispString(paperSize)}" "${orientation}" "N" "${scale}" "1=1" "" "N" "" "" "")\n`);
635
+ return { content: [{ type: 'text', text: `已创建页面设置: ${name}` }] };
636
+ } catch (e) { return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true }; }
637
+ }
638
+ export async function listPageSetups() {
639
+ if (!cad.connected) { await cad.connect(); }
640
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
641
+ try {
642
+ const result = await cad.sendCommandWithResult('(progn (setq setups nil) (vlax-for ps (vla-get-plotconfigurations (vla-get-activedocument (vlax-get-acad-object))) (setq setups (cons (vla-get-name ps) setups))) (reverse setups))');
643
+ return { content: [{ type: 'text', text: result || '[]' }] };
644
+ } catch (e) { return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true }; }
645
+ }
646
+ export async function setPageSetup(layout, pageSetupName) {
647
+ if (!cad.connected) { await cad.connect(); }
648
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
649
+ try {
650
+ await cad.sendCommand(`(command "_.-PAGESETUP" "${escapeLispString(pageSetupName)}" "" "${escapeLispString(layout)}")\n`);
651
+ return { content: [{ type: 'text', text: `已为布局 ${layout} 设置页面设置: ${pageSetupName}` }] };
652
+ } catch (e) { return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true }; }
653
+ }
654
+ export async function importPageSetup(dwgPath, pageSetupName = null) {
655
+ if (!cad.connected) { await cad.connect(); }
656
+ if (!cad.connected) return { content: [{ type: 'text', text: '未连接 CAD' }], isError: true };
657
+ try {
658
+ const namePart = pageSetupName ? ` "${escapeLispString(pageSetupName)}"` : ' "*"';
659
+ await cad.sendCommand(`(command "_.-PSETUPIN" "${escapeLispString(dwgPath)}" ${namePart})\n`);
660
+ return { content: [{ type: 'text', text: `已导入页面设置: ${dwgPath}` }] };
661
+ } catch (e) { return { content: [{ type: 'text', text: `错误: ${e.message}` }], isError: true }; }
662
+ }