solidworks-mcp-python 1.5.0__py3-none-any.whl
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.
- solidworks_mcp/__init__.py +57 -0
- solidworks_mcp/adapters/__init__.py +40 -0
- solidworks_mcp/adapters/base.py +1882 -0
- solidworks_mcp/adapters/circuit_breaker.py +2052 -0
- solidworks_mcp/adapters/com_executor.py +252 -0
- solidworks_mcp/adapters/complexity_analyzer.py +272 -0
- solidworks_mcp/adapters/connection_pool.py +1884 -0
- solidworks_mcp/adapters/factory.py +366 -0
- solidworks_mcp/adapters/intelligent_router.py +218 -0
- solidworks_mcp/adapters/mock_adapter.py +4016 -0
- solidworks_mcp/adapters/pywin32_adapter.py +2842 -0
- solidworks_mcp/adapters/solidworks/__init__.py +17 -0
- solidworks_mcp/adapters/solidworks/bitmap.py +180 -0
- solidworks_mcp/adapters/solidworks/com_utils.py +56 -0
- solidworks_mcp/adapters/solidworks/dimension_layout.py +753 -0
- solidworks_mcp/adapters/solidworks/documentation.py +1476 -0
- solidworks_mcp/adapters/solidworks/drawing_standard.py +1993 -0
- solidworks_mcp/adapters/solidworks/face_selection.py +392 -0
- solidworks_mcp/adapters/solidworks/features.py +3362 -0
- solidworks_mcp/adapters/solidworks/io.py +3649 -0
- solidworks_mcp/adapters/solidworks/rebuild_diagnostics.py +269 -0
- solidworks_mcp/adapters/solidworks/rebuild_inspector.py +146 -0
- solidworks_mcp/adapters/solidworks/selection.py +308 -0
- solidworks_mcp/adapters/solidworks/sketch.py +2545 -0
- solidworks_mcp/adapters/sw_feature_errors.py +66 -0
- solidworks_mcp/adapters/sw_type_info.py +317 -0
- solidworks_mcp/adapters/vba_adapter.py +318 -0
- solidworks_mcp/adapters/vba_macro_executor.py +215 -0
- solidworks_mcp/agents/__init__.py +68 -0
- solidworks_mcp/agents/design_knowledge/assembly_patterns.md +127 -0
- solidworks_mcp/agents/design_knowledge/hinges.md +118 -0
- solidworks_mcp/agents/design_knowledge/snap_fits.md +93 -0
- solidworks_mcp/agents/design_knowledge/solidworks_basics.md +3 -0
- solidworks_mcp/agents/design_knowledge/tolerancing.md +128 -0
- solidworks_mcp/agents/harness.py +216 -0
- solidworks_mcp/agents/history_db.py +1508 -0
- solidworks_mcp/agents/retrieval_index.py +164 -0
- solidworks_mcp/agents/schemas.py +165 -0
- solidworks_mcp/agents/smoke_test.py +285 -0
- solidworks_mcp/agents/soc_exporter.py +650 -0
- solidworks_mcp/agents/soc_pickup.py +332 -0
- solidworks_mcp/agents/soc_rewind.py +221 -0
- solidworks_mcp/agents/vector_rag.py +665 -0
- solidworks_mcp/cache/__init__.py +5 -0
- solidworks_mcp/cache/response_cache.py +164 -0
- solidworks_mcp/config.py +729 -0
- solidworks_mcp/exceptions.py +37 -0
- solidworks_mcp/security/__init__.py +68 -0
- solidworks_mcp/security/auth.py +128 -0
- solidworks_mcp/security/cors.py +27 -0
- solidworks_mcp/security/rate_limiting.py +90 -0
- solidworks_mcp/security/runtime.py +155 -0
- solidworks_mcp/server.py +777 -0
- solidworks_mcp/server_cli_fixed.py +120 -0
- solidworks_mcp/setup/__init__.py +1 -0
- solidworks_mcp/setup/cli.py +399 -0
- solidworks_mcp/setup/clients.py +157 -0
- solidworks_mcp/setup/config_file.py +230 -0
- solidworks_mcp/setup/handshake.py +202 -0
- solidworks_mcp/setup/launcher.py +102 -0
- solidworks_mcp/setup/legacy.py +100 -0
- solidworks_mcp/setup/registration.py +193 -0
- solidworks_mcp/setup/solidworks.py +49 -0
- solidworks_mcp/tools/__init__.py +64 -0
- solidworks_mcp/tools/analysis.py +723 -0
- solidworks_mcp/tools/automation.py +578 -0
- solidworks_mcp/tools/docs_discovery.py +1476 -0
- solidworks_mcp/tools/drawing.py +1015 -0
- solidworks_mcp/tools/drawing_analysis.py +456 -0
- solidworks_mcp/tools/drawing_automation.py +627 -0
- solidworks_mcp/tools/engineering_data.py +466 -0
- solidworks_mcp/tools/error_classifier.py +232 -0
- solidworks_mcp/tools/export.py +833 -0
- solidworks_mcp/tools/file_facts.py +40 -0
- solidworks_mcp/tools/file_management.py +1473 -0
- solidworks_mcp/tools/input_compat.py +17 -0
- solidworks_mcp/tools/macro_recording.py +464 -0
- solidworks_mcp/tools/modeling.py +2360 -0
- solidworks_mcp/tools/sketching.py +1422 -0
- solidworks_mcp/tools/template_management.py +367 -0
- solidworks_mcp/tools/tool_boundary.py +280 -0
- solidworks_mcp/tools/vba_generation.py +1145 -0
- solidworks_mcp/ui/__init__.py +9 -0
- solidworks_mcp/ui/local_llm.py +725 -0
- solidworks_mcp/ui/routers/__init__.py +10 -0
- solidworks_mcp/ui/routers/checkpoint.py +24 -0
- solidworks_mcp/ui/routers/docs.py +47 -0
- solidworks_mcp/ui/routers/llm.py +83 -0
- solidworks_mcp/ui/routers/local_model.py +69 -0
- solidworks_mcp/ui/routers/model.py +75 -0
- solidworks_mcp/ui/routers/preview.py +38 -0
- solidworks_mcp/ui/routers/session.py +161 -0
- solidworks_mcp/ui/routers/viewer.py +154 -0
- solidworks_mcp/ui/schemas.py +502 -0
- solidworks_mcp/ui/server.py +298 -0
- solidworks_mcp/ui/service.py +481 -0
- solidworks_mcp/ui/services/__init__.py +136 -0
- solidworks_mcp/ui/services/_utils.py +958 -0
- solidworks_mcp/ui/services/checkpoint_service.py +1110 -0
- solidworks_mcp/ui/services/docs_service.py +282 -0
- solidworks_mcp/ui/services/llm_service.py +956 -0
- solidworks_mcp/ui/services/model_service.py +459 -0
- solidworks_mcp/ui/services/parameter_repair_service.py +314 -0
- solidworks_mcp/ui/services/preview_service.py +489 -0
- solidworks_mcp/ui/services/session_service.py +1389 -0
- solidworks_mcp/utils/__init__.py +9 -0
- solidworks_mcp/utils/feature_tree_classifier.py +342 -0
- solidworks_mcp/utils/logging.py +76 -0
- solidworks_mcp/utils/validation.py +77 -0
- solidworks_mcp/version.py +8 -0
- solidworks_mcp_python-1.5.0.dist-info/METADATA +410 -0
- solidworks_mcp_python-1.5.0.dist-info/RECORD +115 -0
- solidworks_mcp_python-1.5.0.dist-info/WHEEL +4 -0
- solidworks_mcp_python-1.5.0.dist-info/entry_points.txt +3 -0
- solidworks_mcp_python-1.5.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"""Turn SolidWorks rebuild error codes into descriptions and fix hints.
|
|
2
|
+
|
|
3
|
+
Pure functions, no COM: the adapter collects ``(feature, type, code,
|
|
4
|
+
is_warning, parents)`` from ``IModelDocExtension::GetWhatsWrong`` and this
|
|
5
|
+
module explains each one so an LLM caller can repair the model rather than
|
|
6
|
+
just learning that the rebuild "didn't work".
|
|
7
|
+
|
|
8
|
+
Hints are chosen most-specific first: an exact ``swFeatureError_e`` code, then
|
|
9
|
+
the feature type (``swFeatureErrorUnknown`` - code 1 - is what SolidWorks
|
|
10
|
+
reports for many common failures, e.g. an extrude whose sketch was emptied,
|
|
11
|
+
so the type is often the only useful signal), then a parent sketch that has
|
|
12
|
+
no geometry, then a generic fallback.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from ..sw_feature_errors import SW_FEATURE_ERRORS
|
|
21
|
+
|
|
22
|
+
GENERIC_HINT = (
|
|
23
|
+
"Inspect the parents listed for this feature: re-target any missing "
|
|
24
|
+
"edge/face/sketch reference, fix the parent sketch, or suppress/delete the "
|
|
25
|
+
"feature and recreate it."
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Exact-code hints. Keys MUST exist in SW_FEATURE_ERRORS (enforced by tests).
|
|
29
|
+
_CODE_HINTS: dict[int, tuple[str, str]] = {
|
|
30
|
+
10: (
|
|
31
|
+
"The fillet could not find a closed loop of edges.",
|
|
32
|
+
"Re-select the fillet edges (e.g. select_face_edges then add_fillet "
|
|
33
|
+
"with empty edge_names) or delete and recreate the fillet.",
|
|
34
|
+
),
|
|
35
|
+
11: (
|
|
36
|
+
"A face the fillet referenced no longer exists.",
|
|
37
|
+
"Re-select the fillet's faces/edges with select_face_edges, or delete "
|
|
38
|
+
"and recreate the fillet.",
|
|
39
|
+
),
|
|
40
|
+
12: (
|
|
41
|
+
"The fillet radius is invalid.",
|
|
42
|
+
"Set a positive radius smaller than the adjacent faces allow.",
|
|
43
|
+
),
|
|
44
|
+
13: (
|
|
45
|
+
"An edge the fillet referenced no longer exists (often after an "
|
|
46
|
+
"earlier dimension or feature change).",
|
|
47
|
+
"Re-select the edges with select_face_edges (prefer whole-face "
|
|
48
|
+
"selection) and recreate the fillet.",
|
|
49
|
+
),
|
|
50
|
+
15: (
|
|
51
|
+
"The fillet radius is too small for the geometry.",
|
|
52
|
+
"Increase the fillet radius.",
|
|
53
|
+
),
|
|
54
|
+
18: (
|
|
55
|
+
"The fillet radius is too big for the adjacent faces.",
|
|
56
|
+
"Reduce the fillet radius below the thickness of the faces it joins.",
|
|
57
|
+
),
|
|
58
|
+
19: (
|
|
59
|
+
"The fillet radius is too big for the adjacent faces.",
|
|
60
|
+
"Reduce the fillet radius below the thickness of the faces it joins.",
|
|
61
|
+
),
|
|
62
|
+
30: (
|
|
63
|
+
"The extrusion would create disjoint (disconnected) bodies.",
|
|
64
|
+
"Change the extrude depth/end condition so the result touches the "
|
|
65
|
+
"existing body, or enable merge-off / multibody intentionally.",
|
|
66
|
+
),
|
|
67
|
+
31: (
|
|
68
|
+
"The extrusion's end condition found no end (e.g. Up To Surface target "
|
|
69
|
+
"missing).",
|
|
70
|
+
"Re-select the up-to reference or switch the end condition to Blind "
|
|
71
|
+
"with an explicit depth.",
|
|
72
|
+
),
|
|
73
|
+
33: (
|
|
74
|
+
"The cut profile mixes open and closed contours.",
|
|
75
|
+
"Edit the cut's sketch so it contains only closed contours.",
|
|
76
|
+
),
|
|
77
|
+
34: (
|
|
78
|
+
"The cut profile is invalid (open, overlapping or self-intersecting).",
|
|
79
|
+
"Edit the cut's sketch: close every contour and remove overlaps.",
|
|
80
|
+
),
|
|
81
|
+
36: (
|
|
82
|
+
"The boss profile mixes open and closed contours.",
|
|
83
|
+
"Edit the boss sketch so it contains only closed contours.",
|
|
84
|
+
),
|
|
85
|
+
37: (
|
|
86
|
+
"The boss profile is invalid (open, overlapping or self-intersecting).",
|
|
87
|
+
"Edit the boss sketch: close every contour and remove overlaps.",
|
|
88
|
+
),
|
|
89
|
+
43: (
|
|
90
|
+
"A mate references geometry that no longer exists (dangling).",
|
|
91
|
+
"Delete the mate and re-mate the components to existing faces/edges.",
|
|
92
|
+
),
|
|
93
|
+
46: (
|
|
94
|
+
"The assembly is over-defined by mates.",
|
|
95
|
+
"Delete or suppress the conflicting mate(s).",
|
|
96
|
+
),
|
|
97
|
+
48: ("A mate is broken.", "Delete the mate and recreate it."),
|
|
98
|
+
51: (
|
|
99
|
+
"The sketch's external reference could not be resolved.",
|
|
100
|
+
"Edit the sketch and repair or delete its dangling relations.",
|
|
101
|
+
),
|
|
102
|
+
71: (
|
|
103
|
+
"The cut does not intersect the model.",
|
|
104
|
+
"Move the cut sketch onto/inside the body, flip the cut direction, or "
|
|
105
|
+
"increase its depth.",
|
|
106
|
+
),
|
|
107
|
+
72: (
|
|
108
|
+
"The sketch contains a self-intersecting contour.",
|
|
109
|
+
"Edit the sketch and remove the crossing segments.",
|
|
110
|
+
),
|
|
111
|
+
73: (
|
|
112
|
+
"Items the feature needs are missing.",
|
|
113
|
+
"Re-select the feature's missing references or recreate the feature.",
|
|
114
|
+
),
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Feature-type hints, used when the code alone is not specific
|
|
118
|
+
# (``swFeatureErrorUnknown``) or has no curated hint. Keys are
|
|
119
|
+
# ``IFeature::GetTypeName2`` values.
|
|
120
|
+
_TYPE_HINTS: dict[str, str] = {
|
|
121
|
+
"Extrusion": (
|
|
122
|
+
"Check the boss's parent sketch: it must contain at least one closed "
|
|
123
|
+
"contour, and the depth/end condition must reach geometry."
|
|
124
|
+
),
|
|
125
|
+
"Cut": (
|
|
126
|
+
"Check the cut's parent sketch has a closed contour that overlaps the "
|
|
127
|
+
"body, and that the cut direction/depth actually removes material."
|
|
128
|
+
),
|
|
129
|
+
# "ICE" is what GetTypeName2 reports for extrudes created through the
|
|
130
|
+
# API - boss or cut (observed live on R2026x for a boss extrude).
|
|
131
|
+
"ICE": (
|
|
132
|
+
"Check the extrude's parent sketch: it must contain a closed contour, "
|
|
133
|
+
"and the depth/end condition must reach (or, for a cut, overlap) the body."
|
|
134
|
+
),
|
|
135
|
+
"Fillet": (
|
|
136
|
+
"The fillet's edges/faces probably changed. Re-select them with "
|
|
137
|
+
"select_face_edges and recreate the fillet, or reduce the radius."
|
|
138
|
+
),
|
|
139
|
+
"Chamfer": (
|
|
140
|
+
"The chamfer's edges probably changed. Re-select them with "
|
|
141
|
+
"select_face_edges and recreate the chamfer, or reduce the distance."
|
|
142
|
+
),
|
|
143
|
+
"ProfileFeature": (
|
|
144
|
+
"The sketch itself has a problem (dangling or conflicting relations, "
|
|
145
|
+
"or a reference to deleted geometry). Edit the sketch to repair it."
|
|
146
|
+
),
|
|
147
|
+
"RefPlane": (
|
|
148
|
+
"The reference plane lost the face/plane it was defined from. Redefine "
|
|
149
|
+
"it from existing geometry."
|
|
150
|
+
),
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
#: Readable names for GetTypeName2 values used in descriptions.
|
|
154
|
+
_TYPE_LABELS: dict[str, str] = {
|
|
155
|
+
"ICE": "Extrude",
|
|
156
|
+
"Extrusion": "Boss-extrude",
|
|
157
|
+
"Cut": "Cut-extrude",
|
|
158
|
+
"ProfileFeature": "Sketch",
|
|
159
|
+
"RefPlane": "Reference plane",
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
_EMPTY_SKETCH_HINT = (
|
|
163
|
+
"Parent sketch '{name}' has no geometry. Add a closed profile to it (or "
|
|
164
|
+
"delete this feature)."
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@dataclass(frozen=True)
|
|
169
|
+
class ParentInfo:
|
|
170
|
+
"""A feature the failing feature depends on.
|
|
171
|
+
|
|
172
|
+
Attributes:
|
|
173
|
+
name: The parent feature or sketch name.
|
|
174
|
+
type: Its ``GetTypeName2`` value (``""`` when unknown).
|
|
175
|
+
segment_count: For sketches, how many segments it has; ``None`` when
|
|
176
|
+
not a sketch or not readable.
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
name: str
|
|
180
|
+
type: str = ""
|
|
181
|
+
segment_count: int | None = None
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@dataclass(frozen=True)
|
|
185
|
+
class RawRebuildIssue:
|
|
186
|
+
"""One raw ``GetWhatsWrong`` entry as collected from COM."""
|
|
187
|
+
|
|
188
|
+
feature: str
|
|
189
|
+
type: str
|
|
190
|
+
code: int
|
|
191
|
+
is_warning: bool
|
|
192
|
+
parents: list[ParentInfo] = field(default_factory=list)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def code_name(code: int) -> str:
|
|
196
|
+
"""Return the ``swFeatureError_e`` member name, or ``"code <n>"``."""
|
|
197
|
+
return SW_FEATURE_ERRORS.get(code, f"code {code}")
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def explain(issue: RawRebuildIssue) -> dict[str, Any]:
|
|
201
|
+
"""Build the caller-facing diagnostic entry for one rebuild issue.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
issue: The raw entry collected from SolidWorks.
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
dict[str, Any]: ``feature``, ``type``, ``code``, ``code_name``,
|
|
208
|
+
``severity``, ``description``, ``parents`` and ``hint``.
|
|
209
|
+
"""
|
|
210
|
+
description, hint = _CODE_HINTS.get(issue.code, ("", ""))
|
|
211
|
+
empty_parent = next(
|
|
212
|
+
(
|
|
213
|
+
p
|
|
214
|
+
for p in issue.parents
|
|
215
|
+
if p.type == "ProfileFeature" and p.segment_count == 0
|
|
216
|
+
),
|
|
217
|
+
None,
|
|
218
|
+
)
|
|
219
|
+
if empty_parent is not None:
|
|
220
|
+
hint = _EMPTY_SKETCH_HINT.format(name=empty_parent.name)
|
|
221
|
+
elif not hint:
|
|
222
|
+
hint = _TYPE_HINTS.get(issue.type, GENERIC_HINT)
|
|
223
|
+
if not description:
|
|
224
|
+
description = (
|
|
225
|
+
f"{_TYPE_LABELS.get(issue.type, issue.type or 'Feature')} "
|
|
226
|
+
f"'{issue.feature}' failed to rebuild "
|
|
227
|
+
f"({code_name(issue.code)})."
|
|
228
|
+
)
|
|
229
|
+
return {
|
|
230
|
+
"feature": issue.feature,
|
|
231
|
+
"type": issue.type,
|
|
232
|
+
"code": issue.code,
|
|
233
|
+
"code_name": code_name(issue.code),
|
|
234
|
+
"severity": "warning" if issue.is_warning else "error",
|
|
235
|
+
"description": description,
|
|
236
|
+
"parents": [
|
|
237
|
+
{"name": p.name, "type": p.type}
|
|
238
|
+
| (
|
|
239
|
+
{"segment_count": p.segment_count}
|
|
240
|
+
if p.segment_count is not None
|
|
241
|
+
else {}
|
|
242
|
+
)
|
|
243
|
+
for p in issue.parents
|
|
244
|
+
],
|
|
245
|
+
"hint": hint,
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def summarize(entries: list[dict[str, Any]]) -> tuple[int, int, str]:
|
|
250
|
+
"""Count errors/warnings and build the one-line status message.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
entries: Output of :func:`explain` for every issue.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
tuple[int, int, str]: ``(error_count, warning_count, message)``.
|
|
257
|
+
"""
|
|
258
|
+
errors = [e["feature"] for e in entries if e["severity"] == "error"]
|
|
259
|
+
warnings = [e["feature"] for e in entries if e["severity"] == "warning"]
|
|
260
|
+
if errors:
|
|
261
|
+
message = (
|
|
262
|
+
f"Rebuild left {len(errors)} feature error(s): {', '.join(errors)}. "
|
|
263
|
+
"See data.errors for each cause and hint."
|
|
264
|
+
)
|
|
265
|
+
elif warnings:
|
|
266
|
+
message = f"Rebuilt with {len(warnings)} warning(s): {', '.join(warnings)}."
|
|
267
|
+
else:
|
|
268
|
+
message = "Rebuilt cleanly; no rebuild errors."
|
|
269
|
+
return len(errors), len(warnings), message
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""Collect rebuild errors/warnings from the active SolidWorks model (COM side).
|
|
2
|
+
|
|
3
|
+
Feeds :mod:`rebuild_diagnostics`, which turns each raw issue into a
|
|
4
|
+
description and fix hint. Two sources, in the order chosen from the live probe
|
|
5
|
+
(R2026x, 2026-10-13, see the #106 change's design.md):
|
|
6
|
+
|
|
7
|
+
1. ``IModelDocExtension::GetWhatsWrong`` with three byref VARIANT out-params
|
|
8
|
+
- one call returns every flagged feature, its code and warning flag.
|
|
9
|
+
2. Fallback: walk the feature tree calling
|
|
10
|
+
``IFeature::GetErrorCode2(byref IsWarning)`` on each unsuppressed feature.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from .com_utils import as_list as _as_list
|
|
18
|
+
from .com_utils import attempt as _attempt
|
|
19
|
+
from .com_utils import flag as _flag
|
|
20
|
+
from .io import _byref_bool, _byref_variant
|
|
21
|
+
from .rebuild_diagnostics import ParentInfo, RawRebuildIssue
|
|
22
|
+
|
|
23
|
+
_FEATURE_MEMBERS = (
|
|
24
|
+
"GetTypeName2",
|
|
25
|
+
"GetParents",
|
|
26
|
+
"GetNextFeature",
|
|
27
|
+
"GetFirstSubFeature",
|
|
28
|
+
"GetNextSubFeature",
|
|
29
|
+
"GetErrorCode2",
|
|
30
|
+
"IsSuppressed",
|
|
31
|
+
"GetSpecificFeature2",
|
|
32
|
+
)
|
|
33
|
+
_MAX_FEATURES = 10000 # runaway guard, same as the selector's tree walk
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _sketch_segment_count(adapter: Any, feature: Any) -> int | None:
|
|
37
|
+
"""Segment count of a sketch feature, or None when not readable."""
|
|
38
|
+
sketch = _attempt(adapter, lambda: feature.GetSpecificFeature2())
|
|
39
|
+
if sketch is None:
|
|
40
|
+
return None
|
|
41
|
+
_flag(adapter, sketch, "GetSketchSegments")
|
|
42
|
+
segments = _attempt(adapter, lambda: sketch.GetSketchSegments(), default=False)
|
|
43
|
+
if segments is False:
|
|
44
|
+
return None
|
|
45
|
+
return len(_as_list(segments))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _parents(adapter: Any, feature: Any) -> list[ParentInfo]:
|
|
49
|
+
"""Describe the features/sketches ``feature`` depends on."""
|
|
50
|
+
out = []
|
|
51
|
+
for parent in _as_list(_attempt(adapter, lambda: feature.GetParents())):
|
|
52
|
+
_flag(adapter, parent, *_FEATURE_MEMBERS)
|
|
53
|
+
name = str(_attempt(adapter, lambda p=parent: p.Name, "") or "")
|
|
54
|
+
kind = str(_attempt(adapter, lambda p=parent: p.GetTypeName2(), "") or "")
|
|
55
|
+
segments = (
|
|
56
|
+
_sketch_segment_count(adapter, parent) if kind == "ProfileFeature" else None
|
|
57
|
+
)
|
|
58
|
+
out.append(ParentInfo(name=name, type=kind, segment_count=segments))
|
|
59
|
+
return out
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _issue(adapter: Any, feature: Any, code: int, is_warning: bool) -> RawRebuildIssue:
|
|
63
|
+
_flag(adapter, feature, *_FEATURE_MEMBERS)
|
|
64
|
+
return RawRebuildIssue(
|
|
65
|
+
feature=str(_attempt(adapter, lambda: feature.Name, "") or ""),
|
|
66
|
+
type=str(_attempt(adapter, lambda: feature.GetTypeName2(), "") or ""),
|
|
67
|
+
code=int(code),
|
|
68
|
+
is_warning=bool(is_warning),
|
|
69
|
+
parents=_parents(adapter, feature),
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _from_whats_wrong(adapter: Any, model: Any) -> list[RawRebuildIssue] | None:
|
|
74
|
+
"""Source 1: one GetWhatsWrong call. None when the call is unusable."""
|
|
75
|
+
ext = _attempt(adapter, lambda: model.Extension)
|
|
76
|
+
if ext is None:
|
|
77
|
+
return None
|
|
78
|
+
_flag(adapter, ext, "GetWhatsWrongCount", "GetWhatsWrong")
|
|
79
|
+
count = _attempt(adapter, lambda: ext.GetWhatsWrongCount())
|
|
80
|
+
if isinstance(count, (int, float)) and int(count) == 0:
|
|
81
|
+
return []
|
|
82
|
+
features, codes, warnings = _byref_variant(), _byref_variant(), _byref_variant()
|
|
83
|
+
if _attempt(adapter, lambda: ext.GetWhatsWrong(features, codes, warnings)) is None:
|
|
84
|
+
return None
|
|
85
|
+
feats = _as_list(features.value)
|
|
86
|
+
errs = _as_list(codes.value)
|
|
87
|
+
warns = _as_list(warnings.value)
|
|
88
|
+
if not feats or len(errs) != len(feats):
|
|
89
|
+
return None
|
|
90
|
+
warns = (warns + [False] * len(feats))[: len(feats)]
|
|
91
|
+
return [
|
|
92
|
+
_issue(adapter, f, int(c), bool(w))
|
|
93
|
+
for f, c, w in zip(feats, errs, warns, strict=True)
|
|
94
|
+
if f is not None
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _check(adapter: Any, feature: Any, issues: list[RawRebuildIssue]) -> None:
|
|
99
|
+
"""Append ``feature``'s rebuild issue (if any) to ``issues``."""
|
|
100
|
+
if _attempt(adapter, lambda: feature.IsSuppressed(), False):
|
|
101
|
+
return
|
|
102
|
+
is_warning = _byref_bool()
|
|
103
|
+
code = _attempt(adapter, lambda: feature.GetErrorCode2(is_warning), 0)
|
|
104
|
+
if isinstance(code, (int, float)) and int(code) != 0:
|
|
105
|
+
issues.append(_issue(adapter, feature, int(code), bool(is_warning.value)))
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _from_tree_walk(adapter: Any, model: Any) -> list[RawRebuildIssue]:
|
|
109
|
+
"""Source 2: per-feature GetErrorCode2 over the unsuppressed tree.
|
|
110
|
+
|
|
111
|
+
Visits each top-level feature and its sub-features (e.g. the sketch an
|
|
112
|
+
extrude or cut absorbed), since a sketch error lives on the sketch.
|
|
113
|
+
"""
|
|
114
|
+
issues: list[RawRebuildIssue] = []
|
|
115
|
+
feature = _attempt(adapter, lambda: model.FirstFeature())
|
|
116
|
+
guard = 0
|
|
117
|
+
while feature is not None and guard < _MAX_FEATURES:
|
|
118
|
+
guard += 1
|
|
119
|
+
_flag(adapter, feature, *_FEATURE_MEMBERS)
|
|
120
|
+
current = feature
|
|
121
|
+
_check(adapter, current, issues)
|
|
122
|
+
sub = _attempt(adapter, lambda f=current: f.GetFirstSubFeature())
|
|
123
|
+
while sub is not None and guard < _MAX_FEATURES:
|
|
124
|
+
guard += 1
|
|
125
|
+
_flag(adapter, sub, *_FEATURE_MEMBERS)
|
|
126
|
+
sub_current = sub
|
|
127
|
+
_check(adapter, sub_current, issues)
|
|
128
|
+
sub = _attempt(adapter, lambda s=sub_current: s.GetNextSubFeature())
|
|
129
|
+
feature = _attempt(adapter, lambda f=current: f.GetNextFeature())
|
|
130
|
+
return issues
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def collect_rebuild_issues(adapter: Any, model: Any) -> list[RawRebuildIssue]:
|
|
134
|
+
"""Return every rebuild error/warning on ``model`` after a rebuild.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
adapter: A connected adapter (for ``_attempt``).
|
|
138
|
+
model: The active ``IModelDoc2``.
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
list[RawRebuildIssue]: Empty when the model is clean.
|
|
142
|
+
"""
|
|
143
|
+
issues = _from_whats_wrong(adapter, model)
|
|
144
|
+
if issues is None:
|
|
145
|
+
issues = _from_tree_walk(adapter, model)
|
|
146
|
+
return issues
|