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,308 @@
|
|
|
1
|
+
"""Feature selection mixin for PyWin32 SolidWorks operations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from collections.abc import Callable
|
|
9
|
+
|
|
10
|
+
from .. import sw_type_info as _sw_type_info
|
|
11
|
+
from ..base import AdapterResult, AdapterResultStatus
|
|
12
|
+
from .com_utils import selection_count, strip_op_prefix
|
|
13
|
+
from .face_selection import FaceEdgeSelector, describe_edge, validate_face_query
|
|
14
|
+
|
|
15
|
+
# swSelectType_e values actually observed on this SW install for the entity
|
|
16
|
+
# kinds this method cares about (EDGE=1, FACE=2 confirmed live 2026-09-18 by
|
|
17
|
+
# selecting each kind and checking which methods resolve on the returned
|
|
18
|
+
# object -- IEdge.GetStartVertex for EDGE, IFace2.GetBox for FACE. VERTEX=3
|
|
19
|
+
# is the documented swSelectType_e value but has not been live-verified the
|
|
20
|
+
# same way; treat it as a good guess, not a fact, if it ever misbehaves.
|
|
21
|
+
_SELECTED_ENTITY_TYPE_NAMES = {1: "EDGE", 2: "FACE", 3: "VERTEX"}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SolidWorksSelectionMixin:
|
|
25
|
+
"""Expose feature-selection and feature-list methods through a mixin."""
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
# Mixin protocol: declare attributes that must exist on the parent class
|
|
29
|
+
currentModel: Any
|
|
30
|
+
swApp: Any
|
|
31
|
+
_feature_selector: Any
|
|
32
|
+
|
|
33
|
+
def _handle_com_operation(
|
|
34
|
+
self, operation_name: str, operation: Callable[..., Any], *args: Any
|
|
35
|
+
) -> AdapterResult[Any]: ...
|
|
36
|
+
|
|
37
|
+
def _attempt(
|
|
38
|
+
self, operation: Callable[[], Any], *, default: Any = None
|
|
39
|
+
) -> Any: ...
|
|
40
|
+
|
|
41
|
+
async def list_features(
|
|
42
|
+
self, include_suppressed: bool = False, max_assembly_depth: int = 2
|
|
43
|
+
) -> AdapterResult[list[dict[str, Any]]]:
|
|
44
|
+
sw_app = getattr(self, "swApp", None)
|
|
45
|
+
active_model = getattr(sw_app, "ActiveDoc", None) if sw_app else None
|
|
46
|
+
if active_model is not None:
|
|
47
|
+
self.currentModel = active_model
|
|
48
|
+
if not self.currentModel:
|
|
49
|
+
return AdapterResult(
|
|
50
|
+
status=AdapterResultStatus.ERROR,
|
|
51
|
+
error="No active model",
|
|
52
|
+
)
|
|
53
|
+
return self._handle_com_operation(
|
|
54
|
+
"list_features",
|
|
55
|
+
self._feature_selector.list_features,
|
|
56
|
+
include_suppressed,
|
|
57
|
+
max_assembly_depth,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def _normalize_feature_name(raw_name: str | None) -> str:
|
|
62
|
+
return str(raw_name or "").strip().strip('"').casefold()
|
|
63
|
+
|
|
64
|
+
def _build_feature_candidate_names(
|
|
65
|
+
self, feature_name: str, target_doc: Any
|
|
66
|
+
) -> list[str]:
|
|
67
|
+
return cast(
|
|
68
|
+
list[str],
|
|
69
|
+
self._feature_selector.build_feature_candidate_names(
|
|
70
|
+
feature_name, target_doc
|
|
71
|
+
),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def _try_select_by_extension(
|
|
75
|
+
self,
|
|
76
|
+
target_doc: Any,
|
|
77
|
+
candidate_names: list[str],
|
|
78
|
+
feature_name: str,
|
|
79
|
+
) -> dict[str, Any] | None:
|
|
80
|
+
return cast(
|
|
81
|
+
"dict[str, Any] | None",
|
|
82
|
+
self._feature_selector.try_select_by_extension(
|
|
83
|
+
target_doc, candidate_names, feature_name
|
|
84
|
+
),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def _try_select_by_component(
|
|
88
|
+
self,
|
|
89
|
+
target_doc: Any,
|
|
90
|
+
candidate_names: list[str],
|
|
91
|
+
feature_name: str,
|
|
92
|
+
) -> dict[str, Any] | None:
|
|
93
|
+
return cast(
|
|
94
|
+
"dict[str, Any] | None",
|
|
95
|
+
self._feature_selector.try_select_by_component(
|
|
96
|
+
target_doc, candidate_names, feature_name
|
|
97
|
+
),
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def _try_select_by_feature_tree(
|
|
101
|
+
self,
|
|
102
|
+
target_doc: Any,
|
|
103
|
+
feature_name: str,
|
|
104
|
+
candidate_names: list[str],
|
|
105
|
+
) -> dict[str, Any] | None:
|
|
106
|
+
return cast(
|
|
107
|
+
"dict[str, Any] | None",
|
|
108
|
+
self._feature_selector.try_select_by_feature_tree(
|
|
109
|
+
target_doc, feature_name, candidate_names
|
|
110
|
+
),
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
async def select_feature(
|
|
114
|
+
self, feature_name: str, append: bool = False
|
|
115
|
+
) -> AdapterResult[dict[str, Any]]:
|
|
116
|
+
"""Select a named feature, sketch, plane or component.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
feature_name: Feature name, optionally ``@document`` qualified.
|
|
120
|
+
append: Add to the current selection instead of replacing it.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
AdapterResult[dict[str, Any]]: The selector payload plus
|
|
124
|
+
``selection_count``. ERROR - never SUCCESS with
|
|
125
|
+
``selected: False`` - when no strategy could select the name.
|
|
126
|
+
"""
|
|
127
|
+
if not self.currentModel:
|
|
128
|
+
return AdapterResult(
|
|
129
|
+
status=AdapterResultStatus.ERROR, error="No active model"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
def _select() -> dict[str, Any]:
|
|
133
|
+
payload = self._feature_selector.select_feature(feature_name, append)
|
|
134
|
+
if not payload or not payload.get("selected"):
|
|
135
|
+
raise LookupError(
|
|
136
|
+
f"Feature not found or could not be selected: {feature_name}"
|
|
137
|
+
)
|
|
138
|
+
payload["selection_count"] = selection_count(self, self.currentModel)
|
|
139
|
+
return payload
|
|
140
|
+
|
|
141
|
+
return self._run_clean("select_feature", _select)
|
|
142
|
+
|
|
143
|
+
async def select_face_edges(
|
|
144
|
+
self,
|
|
145
|
+
point: list[float] | None = None,
|
|
146
|
+
side: str | None = None,
|
|
147
|
+
plane: str | None = None,
|
|
148
|
+
edge_type: str = "all",
|
|
149
|
+
append: bool = False,
|
|
150
|
+
) -> AdapterResult[dict[str, Any]]:
|
|
151
|
+
"""Multi-select every edge bounding one face of the active part.
|
|
152
|
+
|
|
153
|
+
See ``SolidWorksAdapter.select_face_edges`` for the contract; the face
|
|
154
|
+
resolution and selection live in ``face_selection.FaceEdgeSelector``.
|
|
155
|
+
"""
|
|
156
|
+
problem = validate_face_query(point, side, plane, edge_type)
|
|
157
|
+
if problem:
|
|
158
|
+
return AdapterResult(status=AdapterResultStatus.ERROR, error=problem)
|
|
159
|
+
if not self.currentModel:
|
|
160
|
+
return AdapterResult(
|
|
161
|
+
status=AdapterResultStatus.ERROR, error="No active model"
|
|
162
|
+
)
|
|
163
|
+
from .io import _doc_type
|
|
164
|
+
|
|
165
|
+
doc_type = _doc_type(self)
|
|
166
|
+
if doc_type is not None and doc_type != 1:
|
|
167
|
+
return AdapterResult(
|
|
168
|
+
status=AdapterResultStatus.ERROR,
|
|
169
|
+
error=(
|
|
170
|
+
"select_face_edges requires an active part document "
|
|
171
|
+
f"(active document type is {doc_type})."
|
|
172
|
+
),
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
def _select_edges() -> dict[str, Any]:
|
|
176
|
+
selector = FaceEdgeSelector(self)
|
|
177
|
+
faces = selector.resolve(point, side, plane)
|
|
178
|
+
edges = selector.edges(faces, edge_type)
|
|
179
|
+
if not edges:
|
|
180
|
+
kind = "" if edge_type == "all" else f"{edge_type} "
|
|
181
|
+
raise LookupError(
|
|
182
|
+
f"The selected face has no {kind}edges "
|
|
183
|
+
f"(face centre {faces[0].describe()['center_mm']} mm)."
|
|
184
|
+
)
|
|
185
|
+
count = selector.select(edges, append)
|
|
186
|
+
described = [f.describe() for f in faces]
|
|
187
|
+
return {
|
|
188
|
+
"face": described[0],
|
|
189
|
+
"faces": described,
|
|
190
|
+
"edge_type": edge_type,
|
|
191
|
+
"edges": [info for _edge, info in edges],
|
|
192
|
+
"selected_edges": len(edges),
|
|
193
|
+
"selection_count": count,
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return self._run_clean("select_face_edges", _select_edges)
|
|
197
|
+
|
|
198
|
+
def _run_clean(
|
|
199
|
+
self, operation_name: str, operation: Callable[[], Any]
|
|
200
|
+
) -> AdapterResult[dict[str, Any]]:
|
|
201
|
+
"""Run ``operation`` via ``_handle_com_operation`` with a clean error.
|
|
202
|
+
|
|
203
|
+
Strips the generic ``"Error in <op>: "`` prefix so expected failures
|
|
204
|
+
(not found, nothing to select) read as plain sentences for the LLM.
|
|
205
|
+
"""
|
|
206
|
+
result = self._handle_com_operation(operation_name, operation)
|
|
207
|
+
if not result.is_success:
|
|
208
|
+
result.error = strip_op_prefix(result.error, operation_name)
|
|
209
|
+
return cast("AdapterResult[dict[str, Any]]", result)
|
|
210
|
+
|
|
211
|
+
async def get_selected_entities(self) -> AdapterResult[list[dict[str, Any]]]:
|
|
212
|
+
"""Report the faces/edges/vertices currently highlighted in the live
|
|
213
|
+
SolidWorks UI, so an agent can see exactly what the user clicked on
|
|
214
|
+
instead of guessing from a screenshot (see com-api-pitfalls.md #21 for
|
|
215
|
+
why a screenshot alone is not reliable proof of document/view state).
|
|
216
|
+
|
|
217
|
+
Refreshes ``currentModel`` from ``swApp.ActiveDoc`` first, the same
|
|
218
|
+
way ``list_features`` does, since the user's selection lives on
|
|
219
|
+
whatever document is actually active in the UI right now -- which
|
|
220
|
+
may not be the document this adapter last created or touched.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
AdapterResult[list[dict[str, Any]]]: One entry per selected
|
|
224
|
+
entity, each with at least ``index`` and ``type``
|
|
225
|
+
(``"FACE"``/``"EDGE"``/``"VERTEX"``/``"UNKNOWN(<n>)"``). Faces
|
|
226
|
+
additionally carry ``bounding_box_mm``, ``center_mm``,
|
|
227
|
+
``area_mm2`` and ``normal`` (when planar). Edges carry
|
|
228
|
+
``start_mm``/``end_mm`` and a straight-line ``length_mm``
|
|
229
|
+
(the true length of a curved edge will be longer). Vertices
|
|
230
|
+
carry ``point_mm``. Any field that fails to resolve is simply
|
|
231
|
+
omitted rather than failing the whole call -- a partial
|
|
232
|
+
description is more useful than none.
|
|
233
|
+
"""
|
|
234
|
+
sw_app = getattr(self, "swApp", None)
|
|
235
|
+
active_model = getattr(sw_app, "ActiveDoc", None) if sw_app else None
|
|
236
|
+
if active_model is not None:
|
|
237
|
+
self.currentModel = active_model
|
|
238
|
+
if not self.currentModel:
|
|
239
|
+
return AdapterResult(
|
|
240
|
+
status=AdapterResultStatus.ERROR, error="No active model"
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
def _describe() -> list[dict[str, Any]]:
|
|
244
|
+
model = self.currentModel
|
|
245
|
+
sel_mgr = model.SelectionManager
|
|
246
|
+
self._attempt(
|
|
247
|
+
lambda: _sw_type_info.flag_methods(sel_mgr, "ISelectionMgr"),
|
|
248
|
+
default=0,
|
|
249
|
+
)
|
|
250
|
+
count = (
|
|
251
|
+
self._attempt(lambda: sel_mgr.GetSelectedObjectCount2(-1), default=0)
|
|
252
|
+
or 0
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
results: list[dict[str, Any]] = []
|
|
256
|
+
for i in range(1, int(count) + 1):
|
|
257
|
+
raw_type = self._attempt(
|
|
258
|
+
lambda i=i: sel_mgr.GetSelectedObjectType3(i, -1), default=None
|
|
259
|
+
)
|
|
260
|
+
obj = self._attempt(
|
|
261
|
+
lambda i=i: sel_mgr.GetSelectedObject6(i, -1), default=None
|
|
262
|
+
)
|
|
263
|
+
if obj is None:
|
|
264
|
+
continue
|
|
265
|
+
self._attempt(
|
|
266
|
+
lambda o=obj: _sw_type_info.flag_methods(
|
|
267
|
+
o, "IFace2", "IEdge", "IVertex"
|
|
268
|
+
),
|
|
269
|
+
default=0,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
type_name = _SELECTED_ENTITY_TYPE_NAMES.get(
|
|
273
|
+
int(raw_type) if isinstance(raw_type, (int, float)) else -1,
|
|
274
|
+
f"UNKNOWN({raw_type})",
|
|
275
|
+
)
|
|
276
|
+
entry: dict[str, Any] = {"index": i, "type": type_name}
|
|
277
|
+
|
|
278
|
+
if type_name == "FACE":
|
|
279
|
+
box = self._attempt(lambda o=obj: o.GetBox(), default=None)
|
|
280
|
+
if box and len(box) == 6:
|
|
281
|
+
entry["bounding_box_mm"] = {
|
|
282
|
+
"min": [c * 1000 for c in box[0:3]],
|
|
283
|
+
"max": [c * 1000 for c in box[3:6]],
|
|
284
|
+
}
|
|
285
|
+
entry["center_mm"] = [
|
|
286
|
+
(box[j] + box[j + 3]) / 2 * 1000 for j in range(3)
|
|
287
|
+
]
|
|
288
|
+
area = self._attempt(lambda o=obj: o.GetArea(), default=None)
|
|
289
|
+
if area is not None:
|
|
290
|
+
entry["area_mm2"] = area * 1e6
|
|
291
|
+
normal = self._attempt(lambda o=obj: o.Normal, default=None)
|
|
292
|
+
if normal:
|
|
293
|
+
entry["normal"] = [round(float(n), 6) for n in normal]
|
|
294
|
+
|
|
295
|
+
elif type_name == "EDGE":
|
|
296
|
+
# Shared with select_face_edges: start/end/length plus
|
|
297
|
+
# curve kind and, for circles, centre/axis/radius.
|
|
298
|
+
entry.update(describe_edge(self, obj))
|
|
299
|
+
|
|
300
|
+
elif type_name == "VERTEX":
|
|
301
|
+
point = self._attempt(lambda o=obj: o.GetPoint(), default=None)
|
|
302
|
+
if point:
|
|
303
|
+
entry["point_mm"] = [c * 1000 for c in point]
|
|
304
|
+
|
|
305
|
+
results.append(entry)
|
|
306
|
+
return results
|
|
307
|
+
|
|
308
|
+
return self._handle_com_operation("get_selected_entities", _describe)
|