dsh-engineering-ui 0.0.0-stage → 1.5.0

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 (122) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +772 -3
  3. package/engineering/plugins/dsh-engineering-ui/cordis.patch.yml +5 -0
  4. package/engineering/plugins/dsh-engineering-ui/icon.svg +14 -0
  5. package/engineering/plugins/dsh-engineering-ui/lib/client.js +8259 -0
  6. package/engineering/plugins/dsh-engineering-ui/lib/defense-sign.js +1552 -0
  7. package/engineering/plugins/dsh-engineering-ui/lib/index.js +5633 -0
  8. package/engineering/plugins/dsh-engineering-ui/lib/job-wait-guard.js +236 -0
  9. package/engineering/plugins/dsh-engineering-ui/lib/mem-guard.js +331 -0
  10. package/engineering/plugins/dsh-engineering-ui/lib/webots.js +114 -0
  11. package/engineering/plugins/dsh-engineering-ui/locale/en.json +6 -0
  12. package/engineering/plugins/dsh-engineering-ui/locale/zh.json +6 -0
  13. package/engineering/plugins/dsh-engineering-ui/preset-engineering.patch.yml +601 -0
  14. package/engineering/skills/assembly-orchestration/SKILL.md +651 -0
  15. package/engineering/skills/cad-workflow/SKILL.md +132 -0
  16. package/engineering/skills/mode-selection/SKILL.md +660 -0
  17. package/engineering/skills/physics-in-loop/SKILL.md +238 -0
  18. package/engineering/skills/physics-in-loop/references/feadocs.md +142 -0
  19. package/engineering/skills/solidworks-bridge/SKILL.md +445 -0
  20. package/engineering/skills/sw-design/SKILL.md +112 -0
  21. package/engineering/skills/sw-design/references/autocad-workflow.md +81 -0
  22. package/engineering/skills/sw-design/references/gbt-drafting.md +71 -0
  23. package/engineering/skills/sw-to-cad/SKILL.md +121 -0
  24. package/engineering/skills/webots-simulation/SKILL.md +58 -0
  25. package/engineering/tools/LICENSE.Auto-SolidWorks +21 -0
  26. package/engineering/tools/_plugin_paths.py +93 -0
  27. package/engineering/tools/_root.py +330 -0
  28. package/engineering/tools/_store.py +446 -0
  29. package/engineering/tools/ac_bridge.py +459 -0
  30. package/engineering/tools/ac_rules.json +21 -0
  31. package/engineering/tools/ac_validate.py +699 -0
  32. package/engineering/tools/ask_user.py +887 -0
  33. package/engineering/tools/build_arm1.py +46 -0
  34. package/engineering/tools/build_assembly.py +94 -0
  35. package/engineering/tools/build_base.py +57 -0
  36. package/engineering/tools/build_housing.py +70 -0
  37. package/engineering/tools/build_structural.py +43 -0
  38. package/engineering/tools/build_transmission.py +69 -0
  39. package/engineering/tools/cadx.lsp +128 -0
  40. package/engineering/tools/choice_contract.py +61 -0
  41. package/engineering/tools/conn_state.py +1671 -0
  42. package/engineering/tools/defense_gate.py +3374 -0
  43. package/engineering/tools/drawing_input.py +314 -0
  44. package/engineering/tools/env_check.py +467 -0
  45. package/engineering/tools/examples/build_axis.py +142 -0
  46. package/engineering/tools/examples/build_cup.py +56 -0
  47. package/engineering/tools/examples/build_plate.py +58 -0
  48. package/engineering/tools/examples/build_sphere.py +30 -0
  49. package/engineering/tools/examples/build_transition_duct.py +381 -0
  50. package/engineering/tools/gate_integrity.py +401 -0
  51. package/engineering/tools/mode2_flow.py +3589 -0
  52. package/engineering/tools/mode_checker.py +77 -0
  53. package/engineering/tools/mode_gate.py +8013 -0
  54. package/engineering/tools/part_registry.py +4266 -0
  55. package/engineering/tools/physics/__init__.py +50 -0
  56. package/engineering/tools/physics/_compat.py +12 -0
  57. package/engineering/tools/physics/design_state.py +142 -0
  58. package/engineering/tools/physics/domain_validator.py +751 -0
  59. package/engineering/tools/physics/fatigue.py +489 -0
  60. package/engineering/tools/physics/fea_solver.py +1163 -0
  61. package/engineering/tools/physics/feapy_solver.py +314 -0
  62. package/engineering/tools/physics/gb_materials.json +1228 -0
  63. package/engineering/tools/physics/geometry_gate.py +337 -0
  64. package/engineering/tools/physics/load_case.py +951 -0
  65. package/engineering/tools/physics/material_db.py +874 -0
  66. package/engineering/tools/physics/mesh_adapter.py +321 -0
  67. package/engineering/tools/physics/refine_rules.py +272 -0
  68. package/engineering/tools/physics/rules/gb_base_rules.json +377 -0
  69. package/engineering/tools/physics/rules/gb_drafting_rules.json +57 -0
  70. package/engineering/tools/physics/rules/gb_electrical_rules.json +25 -0
  71. package/engineering/tools/physics/rules/gb_fasteners_rules.json +73 -0
  72. package/engineering/tools/physics/rules/gb_housing_rules.json +41 -0
  73. package/engineering/tools/physics/rules/gb_manufacturing_rules.json +25 -0
  74. package/engineering/tools/physics/rules/gb_material_verify_rules.json +72 -0
  75. package/engineering/tools/physics/rules/gb_materials_rules.json +377 -0
  76. package/engineering/tools/physics/rules/gb_safety_rules.json +89 -0
  77. package/engineering/tools/physics/rules/gb_springs_rules.json +41 -0
  78. package/engineering/tools/physics/rules/gb_standards_registry.json +1646 -0
  79. package/engineering/tools/physics/rules/gb_structural_rules.json +201 -0
  80. package/engineering/tools/physics/rules/gb_surface_rules.json +25 -0
  81. package/engineering/tools/physics/rules/gb_transmission_rules.json +233 -0
  82. package/engineering/tools/physics/rules/housing_rules.json +48 -0
  83. package/engineering/tools/physics/rules/manifest.json +54 -0
  84. package/engineering/tools/physics/rules/mold_rules.json +48 -0
  85. package/engineering/tools/physics/rules/structural_rules.json +56 -0
  86. package/engineering/tools/physics/rules/transmission_rules.json +48 -0
  87. package/engineering/tools/physics/simulation_report.py +829 -0
  88. package/engineering/tools/physics_bridge.py +2685 -0
  89. package/engineering/tools/profile-manifest.ps1 +72 -0
  90. package/engineering/tools/run_regression.py +109 -0
  91. package/engineering/tools/settings.py +419 -0
  92. package/engineering/tools/solidworks-modeling.md +519 -0
  93. package/engineering/tools/sw_bridge.py +8215 -0
  94. package/engineering/tools/sw_guard.py +1066 -0
  95. package/engineering/tools/sw_surface.py +87 -0
  96. package/engineering/tools/swapi.py +14766 -0
  97. package/engineering/tools/verify_sync.py +218 -0
  98. package/engineering/tools/visual_check.py +2589 -0
  99. package/engineering/tools/webots_env.py +121 -0
  100. package/engineering/tools/webots_mcp/__init__.py +3 -0
  101. package/engineering/tools/webots_mcp/__main__.py +3 -0
  102. package/engineering/tools/webots_mcp/assembly.py +391 -0
  103. package/engineering/tools/webots_mcp/bootstrap.py +55 -0
  104. package/engineering/tools/webots_mcp/controller_worker.py +124 -0
  105. package/engineering/tools/webots_mcp/evidence.py +288 -0
  106. package/engineering/tools/webots_mcp/fake_worker.py +69 -0
  107. package/engineering/tools/webots_mcp/fault_resources.py +121 -0
  108. package/engineering/tools/webots_mcp/host.py +45 -0
  109. package/engineering/tools/webots_mcp/ledger.py +73 -0
  110. package/engineering/tools/webots_mcp/owned_job.py +70 -0
  111. package/engineering/tools/webots_mcp/physical_gate.py +187 -0
  112. package/engineering/tools/webots_mcp/physics.py +152 -0
  113. package/engineering/tools/webots_mcp/pipeline.py +108 -0
  114. package/engineering/tools/webots_mcp/recipes.py +129 -0
  115. package/engineering/tools/webots_mcp/repair.py +132 -0
  116. package/engineering/tools/webots_mcp/repair_trial.py +375 -0
  117. package/engineering/tools/webots_mcp/runtime.py +311 -0
  118. package/engineering/tools/webots_mcp/server.py +217 -0
  119. package/engineering/tools/webots_mcp/sw_export.py +509 -0
  120. package/engineering/tools/webots_mcp/transport.py +156 -0
  121. package/engineering/tools/workflow_gate.py +6134 -0
  122. package/package.json +64 -4
@@ -0,0 +1,459 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ ac_bridge.py — AutoCAD COM 桥接(DSH 工程模式通用版)
4
+ ======================================================
5
+ 从 CADX 提取的 AutoCAD COM 桥接模块,适配 DSH 工程模式使用。
6
+
7
+ 功能:
8
+ - 连接运行中的 AutoCAD(COM 自动化)
9
+ - 导出 DXF 文件(供 AI 读取验证)
10
+ - 读取图纸实体(图层/线条/圆/文字/标注)
11
+ - 高亮验证问题(在 AutoCAD 中标记)
12
+
13
+ 依赖:
14
+ - pywin32 (win32com, pythoncom)
15
+ - 与 sw_bridge.py 使用相同的连接模式(dynamic dispatch)
16
+
17
+ 用法:
18
+ import ac_bridge
19
+ ac = ac_bridge.connect() # 连接 AutoCAD
20
+ dxf_path = ac.export_dxf() # 导出 DXF
21
+ entities = ac.get_entities() # 读取所有实体
22
+ issues = ac.validate() # 运行几何验证
23
+
24
+ 【重要约束】
25
+ - COM 操作使用 dynamic dispatch(不依赖类型库注册)
26
+ - 坐标单位默认为 mm(与 swapi 一致)
27
+ - AutoCAD busy 时使用 ESC 取消挂起命令后重试
28
+ """
29
+ import os
30
+ import sys
31
+ import time
32
+ import json
33
+ import subprocess
34
+
35
+ import pythoncom
36
+ import win32com.client
37
+
38
+ # Fix encoding on Windows terminals
39
+ if sys.platform == 'win32':
40
+ try:
41
+ sys.stdout.reconfigure(encoding='utf-8')
42
+ sys.stderr.reconfigure(encoding='utf-8')
43
+ except Exception:
44
+ pass
45
+
46
+
47
+ # ==================== 常量 ====================
48
+ _VALIDATION_LAYER = "DSH_CADX_VALIDATION"
49
+ _COLOR_CRITICAL = 1 # 红色
50
+ _COLOR_WARNING = 4 # 黄色
51
+ _COLOR_INFO = 3 # 绿色
52
+
53
+
54
+ # ==================== 连接 ====================
55
+
56
+ def _acad_process_running():
57
+ """进程层探测 AutoCAD 是否已在运行(【连接自检修复】)。
58
+
59
+ 为什么需要它:`Dispatch('AutoCAD.Application')` 在 AutoCAD 已安装但
60
+ 未运行时【会触发 COM 激活 → 尝试启动 AutoCAD】,可能挂起数分钟,
61
+ 甚至在安装/许可异常时把 CAD 弄崩(实测事故)。
62
+ 先做一次纯进程探测(tasklist,与 sw_bridge 的进程探测手段一致),
63
+ 没有 acad.exe 就一个 COM 调用都不发 ——
64
+ 保证本模块【永不启动、也永不搞崩】用户的 AutoCAD。
65
+
66
+ Returns: True/False;探测手段不可用时返回 True(保守放行,
67
+ 让 COM 层自己去报错,避免因探测失败而误判"CAD 没开")。
68
+ """
69
+ try:
70
+ out = subprocess.run(
71
+ ["tasklist", "/FI", "IMAGENAME eq acad.exe", "/FO", "CSV", "/NH"],
72
+ capture_output=True, text=True, timeout=15)
73
+ _txt = (out.stdout or "")
74
+ if "no tasks are running" in _txt.lower() or "没有运行的任务" in _txt:
75
+ return False
76
+ # tasklist 命中时输出 CSV 行,其中含 acad.exe
77
+ return "acad.exe" in _txt.lower()
78
+ except Exception:
79
+ return True
80
+
81
+
82
+ def connect(timeout=30):
83
+ """连接运行中的 AutoCAD 实例。
84
+
85
+ 返回 AutoCADBridge 对象;失败返回 None。
86
+ """
87
+ pythoncom.CoInitialize()
88
+ bridge = AutoCADBridge()
89
+ if bridge._do_connect(timeout=timeout):
90
+ return bridge
91
+ return None
92
+
93
+
94
+ class AutoCADBridge:
95
+ """AutoCAD COM 桥接。所有 COM 操作在调用线程上执行(单线程模式)。
96
+
97
+ 与 sw_bridge.py / swapi.py 保持一致:使用 dynamic dispatch,
98
+ 不依赖类型库注册。
99
+ """
100
+
101
+ def __init__(self):
102
+ self.acad = None
103
+ self.doc = None
104
+ self._connected = False
105
+ self._drawing_name = None
106
+
107
+ def _do_connect(self, timeout=30):
108
+ """尝试连接 AutoCAD(带超时和重试)。
109
+
110
+ ── 【连接自检修复·安全前置检查】 AutoCAD 未运行 → 直接失败,不激活 COM ──
111
+ 原缺陷(实测事故):直接 `Dispatch('AutoCAD.Application')` 在
112
+ "AutoCAD 已安装但【未运行】"的机器上会触发 COM 激活 ——
113
+ Windows 会尝试【启动 AutoCAD】(含许可检查/启动画面)。
114
+ 后果:
115
+ · 命令挂起数分钟(远超 30s 超时,因为激活本身阻塞在循环里);
116
+ · 若 AutoCAD 安装已损坏/许可异常,这次激活会把 CAD 【弄崩】;
117
+ · 用户并未要求启动 CAD,却被动收获一个崩掉的 CAD 进程。
118
+ 修复:Dispatch 之前【先进程层探测】。没有 acad.exe 就直接返回 False,
119
+ 一个 COM 调用都不发 —— 保证本模块【永远不会】启动或搞崩 AutoCAD
120
+ (这也与文档承诺一致:只挂接已运行实例,不自行启动)。
121
+ """
122
+ # ── 安全前置检查:AutoCAD 没在跑就立刻放弃(绝不激活 COM)────────
123
+ if not _acad_process_running():
124
+ print("[ac_bridge] AutoCAD 未在运行 —— 不尝试激活 COM"
125
+ "(避免启动/搞崩 AutoCAD)。请手动启动 AutoCAD 后重试。")
126
+ self.doc = None
127
+ self.acad = None
128
+ return False
129
+
130
+ deadline = time.time() + timeout
131
+ esc_sent = False
132
+ # 连续【非忙】失败计数:AutoCAD 在跑但 COM 始终连不上时快速失败,
133
+ # 不再把 30s 超时白白耗光(原来每轮 sleep 1s,最多空转 30 次)。
134
+ hard_fail = 0
135
+ while time.time() < deadline:
136
+ try:
137
+ pythoncom.CoInitialize()
138
+ self.acad = win32com.client.dynamic.Dispatch('AutoCAD.Application')
139
+ self.acad.Visible = True
140
+ self.doc = self.acad.ActiveDocument
141
+ # 触发一次属性读取以验证连接
142
+ _ = self.doc.Name
143
+ self._drawing_name = self.doc.Name
144
+ self._connected = True
145
+ print(f"[ac_bridge] Connected to AutoCAD — document: {self._drawing_name}")
146
+ return True
147
+ except pythoncom.com_error as e:
148
+ hr = getattr(e, 'hresult', None)
149
+ # RPC_E_CALL_REJECTED (-2147418111) = AutoCAD busy
150
+ if hr == -2147418111 and not esc_sent:
151
+ print("[ac_bridge] AutoCAD busy, sending ESC to cancel pending command...")
152
+ self._cancel_pending_command()
153
+ esc_sent = True
154
+ time.sleep(1.0)
155
+ elif hr == -2147418111:
156
+ delay = 0.5 * (2 ** min(int((deadline - time.time()) * 2), 4))
157
+ print(f"[ac_bridge] AutoCAD still busy, retrying in {delay:.1f}s")
158
+ time.sleep(delay)
159
+ pythoncom.PumpWaitingMessages()
160
+ else:
161
+ # 非"忙"类错误(服务器运行失败/被拒/许可等)→ 快速失败。
162
+ hard_fail += 1
163
+ print(f"[ac_bridge] COM error (attempt {hard_fail}): {e}")
164
+ self.doc = None
165
+ self.acad = None
166
+ if hard_fail >= 2:
167
+ print("[ac_bridge] 连续 COM 失败,放弃重试"
168
+ "(AutoCAD 可能正在启动或状态异常)。")
169
+ return False
170
+ time.sleep(1.0)
171
+ except Exception as e:
172
+ hard_fail += 1
173
+ print(f"[ac_bridge] Connection failed (attempt {hard_fail}): {e}")
174
+ self.doc = None
175
+ self.acad = None
176
+ if hard_fail >= 2:
177
+ return False
178
+ time.sleep(1.0)
179
+ return False
180
+
181
+ @property
182
+ def connected(self):
183
+ return self._connected
184
+
185
+ @property
186
+ def drawing_name(self):
187
+ return self._drawing_name or ""
188
+
189
+ @property
190
+ def model_space(self):
191
+ if not self.doc:
192
+ raise RuntimeError("No active AutoCAD document")
193
+ return self.doc.ModelSpace
194
+
195
+ # ------------------------------------------------------------------
196
+ # 辅助方法
197
+ # ------------------------------------------------------------------
198
+
199
+ def _cancel_pending_command(self):
200
+ """发送 ESC 取消 AutoCAD 挂起的命令。"""
201
+ try:
202
+ hwnd = self.acad.HWND
203
+ import ctypes
204
+ from ctypes import wintypes
205
+ user32 = ctypes.windll.user32
206
+ user32.SetForegroundWindow(hwnd)
207
+ time.sleep(0.3)
208
+ shell = win32com.client.dynamic.Dispatch("WScript.Shell")
209
+ for _ in range(5):
210
+ shell.SendKeys("{ESC}")
211
+ time.sleep(0.15)
212
+ time.sleep(1.0)
213
+ except Exception as e:
214
+ print(f"[ac_bridge] _cancel_pending_command: {e}")
215
+
216
+ def _point(self, x, y, z=0.0):
217
+ """创建 COM 兼容的 3D 点数组。"""
218
+ return win32com.client.VARIANT(
219
+ pythoncom.VT_ARRAY | pythoncom.VT_R8, (float(x), float(y), float(z))
220
+ )
221
+
222
+ # ------------------------------------------------------------------
223
+ # DXF 导出
224
+ # ------------------------------------------------------------------
225
+
226
+ def export_dxf(self, output_path=None):
227
+ """将当前图纸导出为 DXF 文件。
228
+
229
+ Args:
230
+ output_path: 输出路径(可选),默认保存到临时目录
231
+ Returns:
232
+ DXF 文件路径
233
+
234
+ Bug-27 修复: AutoCAD 2020 SaveAs 参数兼容性处理。
235
+ - AutoCAD 2020+: doc.SaveAs(filename, file_format)
236
+ - 旧版本: doc.SaveAs(filename)
237
+ 自动尝试两种格式,失败时抛出明确错误信息。
238
+ """
239
+ if not self.doc:
240
+ raise RuntimeError("No active AutoCAD document")
241
+ if output_path is None:
242
+ base = os.path.splitext(self.doc.Name)[0]
243
+ out_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..", "output")
244
+ os.makedirs(out_dir, exist_ok=True)
245
+ output_path = os.path.join(out_dir, f"{base}_export.dxf")
246
+ abs_path = os.path.abspath(output_path)
247
+
248
+ # Bug-27: 尝试多种 SaveAs 调用方式以兼容不同 AutoCAD 版本
249
+ saved = False
250
+ errors = []
251
+ for try_name, try_args in [
252
+ ("SaveAs(filename, format)", (abs_path, 18)), # AutoCAD 2018+
253
+ ("SaveAs(filename)", (abs_path,)), # 兼容旧版本
254
+ ("SaveAs2(format, filename)", (18, abs_path)), # 部分版本反序
255
+ ]:
256
+ try:
257
+ getattr(self.doc, "SaveAs")(*try_args)
258
+ if os.path.exists(abs_path):
259
+ saved = True
260
+ break
261
+ errors.append(f"{try_name}: file not created")
262
+ except Exception as e:
263
+ errors.append(f"{try_name}: {e}")
264
+
265
+ if not saved:
266
+ raise RuntimeError(
267
+ f"DXF 导出失败({abs_path}),所有方法均失败:\n" +
268
+ "\n".join(f" - {e}" for e in errors) +
269
+ "\n请确认: 1) AutoCAD 已安装并打开图纸 2) 输出路径有写入权限"
270
+ )
271
+ return abs_path
272
+
273
+ # ------------------------------------------------------------------
274
+ # 实体读取
275
+ # ------------------------------------------------------------------
276
+
277
+ def get_entities(self):
278
+ """读取 ModelSpace 中所有实体,返回列表。
279
+
280
+ 每个实体包含:type, layer, handle, 几何数据(坐标/半径/面积等)。
281
+ """
282
+ if not self.doc:
283
+ raise RuntimeError("No active AutoCAD document")
284
+ entities = []
285
+ ms = self.model_space
286
+ for i in range(ms.Count):
287
+ ent = ms.Item(i)
288
+ info = {
289
+ "index": i,
290
+ "type": ent.EntityName,
291
+ "layer": ent.Layer,
292
+ "handle": ent.Handle,
293
+ "color": ent.Color,
294
+ }
295
+ # LINE
296
+ try:
297
+ info["start"] = (ent.StartPoint[0], ent.StartPoint[1])
298
+ info["end"] = (ent.EndPoint[0], ent.EndPoint[1])
299
+ info["length"] = ent.Length
300
+ except Exception:
301
+ pass
302
+ # CIRCLE / ARC
303
+ try:
304
+ info["center"] = (ent.Center[0], ent.Center[1])
305
+ info["radius"] = ent.Radius
306
+ except Exception:
307
+ pass
308
+ # LWPOLYLINE / POLYLINE
309
+ try:
310
+ coords = list(ent.Coordinates)
311
+ points = [(coords[j], coords[j + 1]) for j in range(0, len(coords), 2)]
312
+ info["points"] = points
313
+ info["closed"] = bool(ent.Closed)
314
+ info["area"] = ent.Area
315
+ except Exception:
316
+ pass
317
+ # TEXT / MTEXT
318
+ try:
319
+ info["text"] = ent.TextString
320
+ except Exception:
321
+ pass
322
+ # DIMENSION
323
+ try:
324
+ info["measurement"] = ent.Measurement
325
+ except Exception:
326
+ pass
327
+ entities.append(info)
328
+ return entities
329
+
330
+ def get_entities_on_layer(self, layer_name):
331
+ """获取指定图层上的所有实体。"""
332
+ return [e for e in self.get_entities() if e.get("layer") == layer_name]
333
+
334
+ def get_all_layers(self):
335
+ """返回所有图层名称列表。"""
336
+ if not self.doc:
337
+ raise RuntimeError("No active AutoCAD document")
338
+ return [self.doc.Layers.Item(i).Name for i in range(self.doc.Layers.Count)]
339
+
340
+ # ------------------------------------------------------------------
341
+ # 图层管理
342
+ # ------------------------------------------------------------------
343
+
344
+ def ensure_layer(self, name, color=7):
345
+ """确保图层存在(不存在则创建),并设置颜色。"""
346
+ if not self.doc:
347
+ raise RuntimeError("No active AutoCAD document")
348
+ try:
349
+ self.doc.Layers.Add(name)
350
+ except pythoncom.com_error:
351
+ pass # 已存在
352
+ layer = self.doc.Layers.Item(name)
353
+ layer.Color = color
354
+
355
+ # ------------------------------------------------------------------
356
+ # 问题高亮
357
+ # ------------------------------------------------------------------
358
+
359
+ def highlight_issue(self, x, y, message, severity="CRITICAL"):
360
+ """在指定坐标处绘制标记(圆圈 + 文字标注)。
361
+
362
+ Args:
363
+ x, y: 坐标(mm)
364
+ message: 问题描述
365
+ severity: CRITICAL / WARNING / INFO
366
+ """
367
+ color_map = {
368
+ "CRITICAL": _COLOR_CRITICAL,
369
+ "WARNING": _COLOR_WARNING,
370
+ "INFO": _COLOR_INFO,
371
+ }
372
+ color = color_map.get(severity, _COLOR_CRITICAL)
373
+ self.ensure_layer(_VALIDATION_LAYER, color)
374
+ try:
375
+ center = self._point(x, y)
376
+ circle = self.model_space.AddCircle(center, 500.0)
377
+ circle.Layer = _VALIDATION_LAYER
378
+ circle.Color = color
379
+ # 文字标注(略高于标记)
380
+ text_pt = self._point(x, y + 600)
381
+ mtext = self.model_space.AddMText(text_pt, 5000.0, f"[{severity}] {message}")
382
+ mtext.Layer = _VALIDATION_LAYER
383
+ mtext.Color = color
384
+ mtext.Height = 200.0
385
+ except Exception as e:
386
+ print(f"[ac_bridge] highlight_issue failed: {e}")
387
+
388
+ def clear_validation_layer(self):
389
+ """清除验证图层上的所有标记。"""
390
+ try:
391
+ self.send_command(f'-LAYDEL\nN\n{_VALIDATION_LAYER}\n\nY\n')
392
+ time.sleep(0.5)
393
+ self.ensure_layer(_VALIDATION_LAYER, _COLOR_CRITICAL)
394
+ except Exception as e:
395
+ print(f"[ac_bridge] clear_validation_layer: {e}")
396
+
397
+ # ------------------------------------------------------------------
398
+ # 绘图命令(供设计生成使用)
399
+ # ------------------------------------------------------------------
400
+
401
+ def send_command(self, command):
402
+ """向 AutoCAD 发送命令字符串。"""
403
+ if not self.doc:
404
+ raise RuntimeError("No active AutoCAD document")
405
+ self.doc.SendCommand(command + "\n")
406
+
407
+ def draw_line(self, x1, y1, x2, y2, layer="0"):
408
+ """画直线。"""
409
+ p1 = self._point(x1, y1)
410
+ p2 = self._point(x2, y2)
411
+ line = self.model_space.AddLine(p1, p2)
412
+ line.Layer = layer
413
+ return line
414
+
415
+ def draw_rectangle(self, x, y, width, height, layer="0"):
416
+ """画矩形(闭合多段线)。"""
417
+ points = win32com.client.VARIANT(
418
+ pythoncom.VT_ARRAY | pythoncom.VT_R8,
419
+ [x, y, x + width, y, x + width, y + height, x, y + height],
420
+ )
421
+ pline = self.model_space.AddLightWeightPolyline(points)
422
+ pline.Closed = True
423
+ pline.Layer = layer
424
+ return pline
425
+
426
+ def zoom_extents(self):
427
+ """缩放至全部实体。"""
428
+ self.send_command("ZOOM\nE\n")
429
+
430
+ def regen(self):
431
+ """重生成图纸。"""
432
+ self.send_command("REGEN\n")
433
+
434
+ # ------------------------------------------------------------------
435
+ # 状态查询
436
+ # ------------------------------------------------------------------
437
+
438
+ def status(self):
439
+ """返回连接状态摘要。"""
440
+ return {
441
+ "connected": self._connected,
442
+ "drawing": self._drawing_name,
443
+ "layer_count": len(self.get_all_layers()) if self._connected else 0,
444
+ "entity_count": len(self.get_entities()) if self._connected else 0,
445
+ }
446
+
447
+ def close(self):
448
+ """断开连接(不清闭 AutoCAD 本身)。"""
449
+ self._connected = False
450
+ self._drawing_name = None
451
+ self.doc = None
452
+ self.acad = None
453
+
454
+
455
+ # ==================== 便捷函数 ====================
456
+
457
+ def get_ac():
458
+ """获取 AutoCAD 桥接实例(连接失败时返回 None)。"""
459
+ return connect()
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "机械制图验证规则(DSH 工程模式)",
3
+ "description": "适用于 GB/T 机械制图的几何/结构验证规则,从 CADX residential/general 适配而来。",
4
+ "min_room_area_sqm": 6.0,
5
+ "min_wall_thickness_mm": 100,
6
+ "max_wall_thickness_mm": 500,
7
+ "min_door_width_mm": 700,
8
+ "max_door_width_mm": 2000,
9
+ "min_window_area_sqm": 0.5,
10
+ "ventilation_pct_of_floor": 10,
11
+ "max_stair_riser_mm": 160,
12
+ "min_stair_tread_mm": 250,
13
+ "min_ceiling_height_m": 2.7,
14
+ "required_layers": ["OUTLINE", "THIN", "CENTER", "HIDDEN", "DIM", "TEXT", "HATCH"],
15
+ "layer_name_pattern": "^[A-Z][A-Z0-9_-]*$",
16
+ "min_room_width_m": 2.4,
17
+ "min_room_length_m": 3.0,
18
+ "max_room_aspect_ratio": 3.0,
19
+ "unit_scale": 1.0,
20
+ "gap_threshold_mm": 10.0
21
+ }