ansys-pyensight-core 0.11.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.
- ansys/pyensight/core/__init__.py +41 -0
- ansys/pyensight/core/common.py +341 -0
- ansys/pyensight/core/deep_pixel_view.html +98 -0
- ansys/pyensight/core/dockerlauncher.py +1124 -0
- ansys/pyensight/core/dvs.py +872 -0
- ansys/pyensight/core/enscontext.py +345 -0
- ansys/pyensight/core/enshell_grpc.py +641 -0
- ansys/pyensight/core/ensight_grpc.py +874 -0
- ansys/pyensight/core/ensobj.py +515 -0
- ansys/pyensight/core/launch_ensight.py +296 -0
- ansys/pyensight/core/launcher.py +388 -0
- ansys/pyensight/core/libuserd.py +2110 -0
- ansys/pyensight/core/listobj.py +280 -0
- ansys/pyensight/core/locallauncher.py +579 -0
- ansys/pyensight/core/py.typed +0 -0
- ansys/pyensight/core/renderable.py +880 -0
- ansys/pyensight/core/session.py +1923 -0
- ansys/pyensight/core/sgeo_poll.html +24 -0
- ansys/pyensight/core/utils/__init__.py +21 -0
- ansys/pyensight/core/utils/adr.py +111 -0
- ansys/pyensight/core/utils/dsg_server.py +1220 -0
- ansys/pyensight/core/utils/export.py +606 -0
- ansys/pyensight/core/utils/omniverse.py +769 -0
- ansys/pyensight/core/utils/omniverse_cli.py +614 -0
- ansys/pyensight/core/utils/omniverse_dsg_server.py +1196 -0
- ansys/pyensight/core/utils/omniverse_glb_server.py +848 -0
- ansys/pyensight/core/utils/parts.py +1221 -0
- ansys/pyensight/core/utils/query.py +487 -0
- ansys/pyensight/core/utils/readers.py +300 -0
- ansys/pyensight/core/utils/resources/Materials/000_sky.exr +0 -0
- ansys/pyensight/core/utils/support.py +128 -0
- ansys/pyensight/core/utils/variables.py +2019 -0
- ansys/pyensight/core/utils/views.py +674 -0
- ansys_pyensight_core-0.11.0.dist-info/METADATA +309 -0
- ansys_pyensight_core-0.11.0.dist-info/RECORD +37 -0
- ansys_pyensight_core-0.11.0.dist-info/WHEEL +4 -0
- ansys_pyensight_core-0.11.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
# Copyright (C) 2022 - 2026 ANSYS, Inc. and/or its affiliates.
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
#
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
# furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
# copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
# SOFTWARE.
|
|
22
|
+
|
|
23
|
+
"""Local Launcher module.
|
|
24
|
+
|
|
25
|
+
The Local Launcher module provides PyEnSight with the ability to launch an
|
|
26
|
+
EnSight :class:`Session<ansys.pyensight.core.Session>` instance using a
|
|
27
|
+
local Ansys installation.
|
|
28
|
+
|
|
29
|
+
Examples:
|
|
30
|
+
>>> from ansys.pyensight.core import LocalLauncher
|
|
31
|
+
>>> session = LocalLauncher().start()
|
|
32
|
+
"""
|
|
33
|
+
import glob
|
|
34
|
+
import logging
|
|
35
|
+
import os.path
|
|
36
|
+
import platform
|
|
37
|
+
import re
|
|
38
|
+
import shutil
|
|
39
|
+
import subprocess
|
|
40
|
+
import tempfile
|
|
41
|
+
import time
|
|
42
|
+
from typing import Optional
|
|
43
|
+
import uuid
|
|
44
|
+
import warnings
|
|
45
|
+
|
|
46
|
+
import ansys.pyensight.core as pyensight
|
|
47
|
+
from ansys.pyensight.core.common import GRPC_WARNING_MESSAGE, find_unused_ports, grpc_version_check
|
|
48
|
+
from ansys.pyensight.core.launcher import Launcher
|
|
49
|
+
import ansys.pyensight.core.session
|
|
50
|
+
import psutil
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class LocalLauncher(Launcher):
|
|
54
|
+
"""Creates a ``Session`` instance by launching a local copy of EnSight.
|
|
55
|
+
|
|
56
|
+
This class allows you to launch locally a copy of EnSight that supports the
|
|
57
|
+
gRPC interface. It creates and binds a :class:`Session<ansys.pyensight.core.Session>`
|
|
58
|
+
instance to the created gRPC session and returns that instance.
|
|
59
|
+
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
ansys_installation : str, optional
|
|
63
|
+
Path to the local Ansys installation, including the version
|
|
64
|
+
directory. The default is ``None``, in which case common locations
|
|
65
|
+
are scanned to detect the latest local Ansys installation. The
|
|
66
|
+
``PYENSIGHT_ANSYS_INSTALLATION`` environmental variable is checked first.
|
|
67
|
+
application : str, optional
|
|
68
|
+
App to launch. The default is ``ensight``, but ``envision`` is
|
|
69
|
+
also an option.
|
|
70
|
+
batch : bool, optional
|
|
71
|
+
Whether to run EnSight (or EnVision) in batch mode. The default
|
|
72
|
+
is ``True``, in which case the full GUI is not presented.
|
|
73
|
+
grpc_use_tcp_sockets :
|
|
74
|
+
If using gRPC, and if True, then allow TCP Socket based connections
|
|
75
|
+
instead of only local connections.
|
|
76
|
+
grpc_allow_network_connections :
|
|
77
|
+
If using gRPC and using TCP Socket based connections, listen on all networks.
|
|
78
|
+
grpc_disable_tls :
|
|
79
|
+
If using gRPC and using TCP Socket based connections, disable TLS.
|
|
80
|
+
grpc_uds_pathname :
|
|
81
|
+
If using gRPC and using Unix Domain Socket based connections, explicitly
|
|
82
|
+
set the pathname to the shared UDS file instead of using the default.
|
|
83
|
+
timeout : float, optional
|
|
84
|
+
Number of seconds to try a gRPC connection before giving up.
|
|
85
|
+
This parameter is defined on the parent ``Launcher`` class,
|
|
86
|
+
where the default is ``120``.
|
|
87
|
+
use_egl : bool, optional
|
|
88
|
+
Whether to use EGL hardware for accelerated graphics. The platform
|
|
89
|
+
must be able to support this hardware. This parameter is defined on
|
|
90
|
+
the parent ``Launcher`` class, where the default is ``False``.
|
|
91
|
+
use_sos : int, optional
|
|
92
|
+
Number of EnSight servers to use for SOS (Server of Server) mode.
|
|
93
|
+
This parameter is defined on the parent ``Launcher`` class, where
|
|
94
|
+
the default is ``None``, in which case SOS mode is not used.
|
|
95
|
+
additional_command_line_options: list, optional
|
|
96
|
+
Additional command line options to be used to launch EnSight.
|
|
97
|
+
|
|
98
|
+
Examples
|
|
99
|
+
--------
|
|
100
|
+
>>> from ansys.pyensight.core import LocalLauncher
|
|
101
|
+
>>> # Create one EnSight session
|
|
102
|
+
>>> session1 = LocalLauncher(ansys_installation='/ansys_inc/v232').start()
|
|
103
|
+
>>> # Create a second session (a new LocalLauncher instance is required)
|
|
104
|
+
>>> session2 = LocalLauncher(ansys_installation='/ansys_inc/v232').start()
|
|
105
|
+
|
|
106
|
+
WARNING:
|
|
107
|
+
Overriding the default values for these options: grpc_use_tcp_sockets, grpc_allow_network_connections,
|
|
108
|
+
and grpc_disable_tls
|
|
109
|
+
can possibly permit control of this computer and any data which resides on it.
|
|
110
|
+
Modification of this configuration is not recommended. Please see the
|
|
111
|
+
documentation for your installed product for additional information.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
def __init__(
|
|
115
|
+
self,
|
|
116
|
+
ansys_installation: Optional[str] = None,
|
|
117
|
+
application: Optional[str] = "ensight",
|
|
118
|
+
batch: bool = True,
|
|
119
|
+
grpc_use_tcp_sockets: Optional[bool] = False,
|
|
120
|
+
grpc_allow_network_connections: Optional[bool] = False,
|
|
121
|
+
grpc_disable_tls: Optional[bool] = False,
|
|
122
|
+
grpc_uds_pathname: Optional[str] = None,
|
|
123
|
+
**kwargs,
|
|
124
|
+
) -> None:
|
|
125
|
+
super().__init__(**kwargs)
|
|
126
|
+
|
|
127
|
+
# get the user selected installation directory
|
|
128
|
+
self._install_path: str = self.get_cei_install_directory(ansys_installation)
|
|
129
|
+
# Will this be ensight or envision
|
|
130
|
+
self._application = application
|
|
131
|
+
# EnSight session secret key
|
|
132
|
+
self._secret_key: str = ""
|
|
133
|
+
# temporary directory served by websocketserver
|
|
134
|
+
self._session_directory = None
|
|
135
|
+
# launched process ids
|
|
136
|
+
self._ensight_pid = None
|
|
137
|
+
self._websocketserver_pid = None
|
|
138
|
+
self._webui_pid = None
|
|
139
|
+
# and ports
|
|
140
|
+
self._ports = None
|
|
141
|
+
# Are we running the instance in batch
|
|
142
|
+
self._batch = batch
|
|
143
|
+
self._grpc_use_tcp_sockets = grpc_use_tcp_sockets
|
|
144
|
+
self._grpc_allow_network_connections = grpc_allow_network_connections
|
|
145
|
+
self._grpc_disable_tls = grpc_disable_tls
|
|
146
|
+
self._grpc_uds_pathname = grpc_uds_pathname
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def application(self):
|
|
150
|
+
"""Type of app to launch. Options are ``ensight`` and ``envision``."""
|
|
151
|
+
return self._application
|
|
152
|
+
|
|
153
|
+
def launch_webui(self, version, popen_common):
|
|
154
|
+
if os.environ.get("PYENSIGHT_FLUIDSONE_PATH"):
|
|
155
|
+
fluids_one_path = os.environ["PYENSIGHT_FLUIDSONE_PATH"]
|
|
156
|
+
else:
|
|
157
|
+
awp_path = os.path.dirname(self._install_path)
|
|
158
|
+
platf = "winx64" if self._is_windows() else "linx64"
|
|
159
|
+
fluids_one_path = os.path.join(awp_path, "FluidsOne", "server", platf, "fluids_one")
|
|
160
|
+
if self._is_windows():
|
|
161
|
+
fluids_one_path += ".exe"
|
|
162
|
+
cmd = [fluids_one_path, "--main-run-mode", "post"]
|
|
163
|
+
path_to_webui = self._install_path
|
|
164
|
+
# Dev environment
|
|
165
|
+
path_to_webui_internal = os.path.join(
|
|
166
|
+
path_to_webui, f"nexus{version}", f"ansys{version}", "ensight", "WebUI", "web", "ui"
|
|
167
|
+
)
|
|
168
|
+
# Ansys environment
|
|
169
|
+
path_to_webui_ansys = os.path.join(os.path.dirname(path_to_webui), "FluidsOne", "web", "ui")
|
|
170
|
+
path_to_webui = path_to_webui_internal
|
|
171
|
+
if os.path.exists(path_to_webui_ansys):
|
|
172
|
+
path_to_webui = path_to_webui_ansys
|
|
173
|
+
cmd += ["--server-listen-port", str(self._ports[5])]
|
|
174
|
+
cmd += ["--server-web-roots", path_to_webui]
|
|
175
|
+
cmd += ["--ensight-grpc-port", str(self._ports[0])]
|
|
176
|
+
cmd += ["--ensight-html-port", str(self._ports[2])]
|
|
177
|
+
cmd += ["--ensight-ws-port", str(self._ports[3])]
|
|
178
|
+
cmd += ["--ensight-session-directory", self._session_directory]
|
|
179
|
+
cmd += ["--ensight-secret-key", self._secret_key]
|
|
180
|
+
cmd += ["--main-show-gui", "'False'"]
|
|
181
|
+
if "PYENSIGHT_DEBUG" in os.environ:
|
|
182
|
+
try:
|
|
183
|
+
if int(os.environ["PYENSIGHT_DEBUG"]) > 0:
|
|
184
|
+
del popen_common["stdout"]
|
|
185
|
+
del popen_common["stderr"]
|
|
186
|
+
except (ValueError, KeyError):
|
|
187
|
+
pass
|
|
188
|
+
popen_common["env"].update(
|
|
189
|
+
{
|
|
190
|
+
"SIMBA_WEBSERVER_TOKEN": self._secret_key,
|
|
191
|
+
"FLUENT_WEBSERVER_TOKEN": self._secret_key,
|
|
192
|
+
}
|
|
193
|
+
)
|
|
194
|
+
self._webui_pid = subprocess.Popen(cmd, **popen_common).pid
|
|
195
|
+
|
|
196
|
+
def _grpc_version_check(self):
|
|
197
|
+
"""Check if the gRPC security options apply to the EnSight install."""
|
|
198
|
+
buildinfo = os.path.join(self._install_path, "BUILDINFO.txt")
|
|
199
|
+
if not os.path.exists(buildinfo):
|
|
200
|
+
if not os.path.exists(
|
|
201
|
+
os.path.join(os.path.dirname(self._install_path), "licensingclient")
|
|
202
|
+
):
|
|
203
|
+
# Dev installation. Assume the gRPC security options are available
|
|
204
|
+
return True
|
|
205
|
+
raise RuntimeError("Couldn't find BUILDINFO file, cannot check installation.")
|
|
206
|
+
with open(buildinfo, "r") as buildinfo_file:
|
|
207
|
+
text = buildinfo_file.read()
|
|
208
|
+
internal_version, ensight_full_version = self._get_versionfrom_buildinfo(text)
|
|
209
|
+
return grpc_version_check(internal_version, ensight_full_version)
|
|
210
|
+
|
|
211
|
+
def start(self) -> "pyensight.Session":
|
|
212
|
+
"""Start an EnSight session using the local EnSight installation.
|
|
213
|
+
|
|
214
|
+
This method launches a copy of EnSight locally that supports the
|
|
215
|
+
gRPC interface. It creates and binds a ``Session`` instance to the
|
|
216
|
+
created gRPC session and returns that session.
|
|
217
|
+
|
|
218
|
+
Returns
|
|
219
|
+
-------
|
|
220
|
+
obj
|
|
221
|
+
PyEnSight ``Session`` object instance.
|
|
222
|
+
|
|
223
|
+
Raises
|
|
224
|
+
------
|
|
225
|
+
RuntimeError:
|
|
226
|
+
If the necessary number of ports could not be allocated.
|
|
227
|
+
"""
|
|
228
|
+
self._has_grpc_changes = self._grpc_version_check()
|
|
229
|
+
if not self._has_grpc_changes:
|
|
230
|
+
warnings.warn(GRPC_WARNING_MESSAGE)
|
|
231
|
+
tmp_session = super().start()
|
|
232
|
+
if tmp_session:
|
|
233
|
+
return tmp_session
|
|
234
|
+
if self._ports is None:
|
|
235
|
+
# session directory and UUID
|
|
236
|
+
self._secret_key = str(uuid.uuid1())
|
|
237
|
+
self.session_directory = tempfile.mkdtemp(prefix="pyensight_")
|
|
238
|
+
if (
|
|
239
|
+
not self._grpc_uds_pathname
|
|
240
|
+
and not self._grpc_use_tcp_sockets
|
|
241
|
+
and not self._is_windows()
|
|
242
|
+
):
|
|
243
|
+
self._grpc_uds_pathname = os.path.join(self.session_directory, "pyensight")
|
|
244
|
+
|
|
245
|
+
# gRPC port, VNC port, websocketserver ws, websocketserver html
|
|
246
|
+
to_avoid = self._find_ports_used_by_other_pyensight_and_ensight()
|
|
247
|
+
num_ports = 5
|
|
248
|
+
if self._launch_webui: # port 6
|
|
249
|
+
num_ports += 1
|
|
250
|
+
if self._vtk_ws_port: # port 6 or 7 depending on launch_webui
|
|
251
|
+
num_ports += 1
|
|
252
|
+
self._ports = find_unused_ports(num_ports, avoid=to_avoid)
|
|
253
|
+
if self._ports is None:
|
|
254
|
+
raise RuntimeError("Unable to allocate local ports for EnSight session")
|
|
255
|
+
is_windows = self._is_windows()
|
|
256
|
+
|
|
257
|
+
# Launch EnSight
|
|
258
|
+
# create the environmental variables
|
|
259
|
+
local_env = os.environ.copy()
|
|
260
|
+
if not local_env.get("ENSIGHT_GRPC_DISABLE_SECURITY_TOKEN"):
|
|
261
|
+
local_env["ENSIGHT_SECURITY_TOKEN"] = self._secret_key
|
|
262
|
+
local_env["WEBSOCKETSERVER_SECURITY_TOKEN"] = self._secret_key
|
|
263
|
+
local_env["ENSIGHT_SESSION_TEMPDIR"] = self.session_directory
|
|
264
|
+
# If for some reason, the ENSIGHT_ANSYS_LAUNCH is set previously,
|
|
265
|
+
# honor that value, otherwise set it to "pyensight". This allows
|
|
266
|
+
# for an environmental setup to set the value to something else
|
|
267
|
+
# (e.g. their "app").
|
|
268
|
+
if "ENSIGHT_ANSYS_LAUNCH" not in local_env:
|
|
269
|
+
local_env["ENSIGHT_ANSYS_LAUNCH"] = "pyensight"
|
|
270
|
+
|
|
271
|
+
# build the EnSight command
|
|
272
|
+
exe = os.path.join(self._install_path, "bin", self.application)
|
|
273
|
+
cmd = [exe]
|
|
274
|
+
if self._batch:
|
|
275
|
+
cmd.append("-batch")
|
|
276
|
+
else:
|
|
277
|
+
cmd.append("-no_start_screen")
|
|
278
|
+
cmd.extend(["-grpc_server", str(self._ports[0])])
|
|
279
|
+
if self._has_grpc_changes:
|
|
280
|
+
if self._grpc_use_tcp_sockets:
|
|
281
|
+
cmd.append("-grpc_use_tcp_sockets")
|
|
282
|
+
if self._grpc_allow_network_connections:
|
|
283
|
+
cmd.append("-grpc_allow_network_connections")
|
|
284
|
+
if self._grpc_disable_tls:
|
|
285
|
+
cmd.append("-grpc_disable_tls")
|
|
286
|
+
if self._grpc_uds_pathname:
|
|
287
|
+
cmd.append("-grpc_uds_pathname")
|
|
288
|
+
cmd.append(self._grpc_uds_pathname)
|
|
289
|
+
vnc_url = f"vnc://%%3Frfb_port={self._ports[1]}%%26use_auth=0"
|
|
290
|
+
cmd.extend(["-vnc", vnc_url])
|
|
291
|
+
cmd.extend(["-ports", str(self._ports[4])])
|
|
292
|
+
if self._additional_command_line_options:
|
|
293
|
+
cmd.extend(self._additional_command_line_options)
|
|
294
|
+
|
|
295
|
+
use_egl = self._use_egl()
|
|
296
|
+
|
|
297
|
+
# to aid in debugging, PYENSIGHT_DEBUG can be set to a non-zero integer
|
|
298
|
+
popen_common = dict(
|
|
299
|
+
stdout=subprocess.DEVNULL,
|
|
300
|
+
stderr=subprocess.DEVNULL,
|
|
301
|
+
cwd=self.session_directory,
|
|
302
|
+
env=local_env,
|
|
303
|
+
)
|
|
304
|
+
if "PYENSIGHT_DEBUG" in os.environ:
|
|
305
|
+
try:
|
|
306
|
+
if int(os.environ["PYENSIGHT_DEBUG"]) > 0:
|
|
307
|
+
del popen_common["stdout"]
|
|
308
|
+
del popen_common["stderr"]
|
|
309
|
+
except ValueError:
|
|
310
|
+
pass
|
|
311
|
+
|
|
312
|
+
if is_windows:
|
|
313
|
+
cmd[0] += ".bat"
|
|
314
|
+
if use_egl:
|
|
315
|
+
cmd.append("-egl")
|
|
316
|
+
if self._use_sos:
|
|
317
|
+
cmd.append("-sos")
|
|
318
|
+
if not self._use_mpi:
|
|
319
|
+
cmd.append("-nservers")
|
|
320
|
+
cmd.append(str(int(self._use_sos)))
|
|
321
|
+
else:
|
|
322
|
+
cmd.append(f"--np={int(self._use_sos)+1}")
|
|
323
|
+
cmd.append(f"--mpi={self._use_mpi}")
|
|
324
|
+
cmd.append(f"--ic={self._interconnect}")
|
|
325
|
+
hosts = ",".join(self._server_hosts)
|
|
326
|
+
cmd.append(f"--cnf={hosts}")
|
|
327
|
+
if self._liben_rest:
|
|
328
|
+
cmd.extend(["-rest_server", str(self._ports[2])])
|
|
329
|
+
|
|
330
|
+
# cmd.append("-minimize_console")
|
|
331
|
+
logging.debug(f"Starting EnSight with : {cmd}\n")
|
|
332
|
+
self._ensight_pid = subprocess.Popen(cmd, **popen_common).pid
|
|
333
|
+
|
|
334
|
+
# Launch websocketserver
|
|
335
|
+
|
|
336
|
+
# find websocketserver script
|
|
337
|
+
found_scripts = glob.glob(
|
|
338
|
+
os.path.join(self._install_path, "nexus*", "nexus_launcher", "websocketserver.py")
|
|
339
|
+
)
|
|
340
|
+
if not found_scripts:
|
|
341
|
+
raise RuntimeError("Unable to find websocketserver script")
|
|
342
|
+
# If more than one nexus directory is found, find the one that corresponds
|
|
343
|
+
# to the version that should be used. Otherwise, just take the first one found.
|
|
344
|
+
# This is likely to only happen for developer installations or build areas.
|
|
345
|
+
idx = 0
|
|
346
|
+
try:
|
|
347
|
+
found_scripts_len = len(found_scripts)
|
|
348
|
+
if found_scripts_len > 1:
|
|
349
|
+
version_str = str(pyensight.__ansys_version__)
|
|
350
|
+
for i in range(found_scripts_len):
|
|
351
|
+
if version_str in found_scripts[i]:
|
|
352
|
+
idx = i
|
|
353
|
+
break
|
|
354
|
+
except Exception:
|
|
355
|
+
pass
|
|
356
|
+
websocket_script = found_scripts[idx]
|
|
357
|
+
version = re.findall(r"nexus(\d+)", websocket_script)[0]
|
|
358
|
+
# build the commandline
|
|
359
|
+
if not self._liben_rest:
|
|
360
|
+
cmd = [os.path.join(self._install_path, "bin", "cpython"), websocket_script]
|
|
361
|
+
if is_windows:
|
|
362
|
+
cmd[0] += ".bat"
|
|
363
|
+
cmd.extend(["--http_directory", self.session_directory])
|
|
364
|
+
# http port
|
|
365
|
+
cmd.extend(["--http_port", str(self._ports[2])])
|
|
366
|
+
# vnc port
|
|
367
|
+
cmd.extend(["--client_port", str(self._ports[1])])
|
|
368
|
+
if self._enable_rest_api:
|
|
369
|
+
# grpc port
|
|
370
|
+
cmd.extend(["--grpc_port", str(self._ports[0])])
|
|
371
|
+
if self._has_grpc_changes:
|
|
372
|
+
if self._grpc_use_tcp_sockets:
|
|
373
|
+
cmd.append("--grpc_use_tcp_sockets")
|
|
374
|
+
if self._grpc_allow_network_connections:
|
|
375
|
+
cmd.append("--grpc_allow_network_connections")
|
|
376
|
+
if self._grpc_disable_tls:
|
|
377
|
+
cmd.append("--grpc_disable_tls")
|
|
378
|
+
if self._grpc_uds_pathname:
|
|
379
|
+
cmd.append("--grpc_uds_pathname")
|
|
380
|
+
cmd.append(self._grpc_uds_pathname)
|
|
381
|
+
# EnVision sessions
|
|
382
|
+
cmd.extend(["--local_session", "envision", "5"])
|
|
383
|
+
if int(version) > 252 and self._rest_ws_separate_loops:
|
|
384
|
+
cmd.append("--separate_loops")
|
|
385
|
+
cmd.extend(["--security_token", self._secret_key])
|
|
386
|
+
# websocket port
|
|
387
|
+
if int(version) > 252 and self._do_not_start_ws:
|
|
388
|
+
cmd.append("-1")
|
|
389
|
+
else:
|
|
390
|
+
cmd.append(str(self._ports[3]))
|
|
391
|
+
logging.debug(f"Starting WSS: {cmd}\n")
|
|
392
|
+
if is_windows:
|
|
393
|
+
startupinfo = subprocess.STARTUPINFO()
|
|
394
|
+
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
|
395
|
+
popen_common["startupinfo"] = startupinfo
|
|
396
|
+
self._websocketserver_pid = subprocess.Popen(cmd, **popen_common).pid
|
|
397
|
+
|
|
398
|
+
# build the session instance
|
|
399
|
+
logging.debug(
|
|
400
|
+
f"Creating session with ports for grpc:{self._ports[0]}\n"
|
|
401
|
+
+ f"html:{self._ports[2]} ws:{self._ports[3]}\n"
|
|
402
|
+
+ f"key:{self._secret_key}\n"
|
|
403
|
+
)
|
|
404
|
+
use_sos = False
|
|
405
|
+
if self._use_sos:
|
|
406
|
+
use_sos = True
|
|
407
|
+
|
|
408
|
+
# need to use Session like this for mock testing this class
|
|
409
|
+
session = ansys.pyensight.core.session.Session(
|
|
410
|
+
host="127.0.0.1",
|
|
411
|
+
grpc_port=self._ports[0],
|
|
412
|
+
grpc_use_tcp_sockets=self._grpc_use_tcp_sockets,
|
|
413
|
+
grpc_allow_network_connections=self._grpc_allow_network_connections,
|
|
414
|
+
grpc_disable_tls=self._grpc_disable_tls,
|
|
415
|
+
grpc_uds_pathname=self._grpc_uds_pathname,
|
|
416
|
+
html_port=self._ports[2],
|
|
417
|
+
ws_port=self._ports[3],
|
|
418
|
+
install_path=self._install_path,
|
|
419
|
+
secret_key=self._secret_key,
|
|
420
|
+
timeout=self._timeout,
|
|
421
|
+
sos=use_sos,
|
|
422
|
+
rest_api=self._enable_rest_api,
|
|
423
|
+
webui_port=self._ports[5] if self._launch_webui else None,
|
|
424
|
+
disable_grpc_options=not self._has_grpc_changes,
|
|
425
|
+
)
|
|
426
|
+
session.launcher = self
|
|
427
|
+
self._sessions.append(session)
|
|
428
|
+
if self._launch_webui:
|
|
429
|
+
self.launch_webui(version, popen_common)
|
|
430
|
+
return session
|
|
431
|
+
|
|
432
|
+
@staticmethod
|
|
433
|
+
def _kill_process_unix(pid):
|
|
434
|
+
external_kill = ["kill", "-9", str(pid)]
|
|
435
|
+
process = psutil.Popen(external_kill, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
|
|
436
|
+
process.wait()
|
|
437
|
+
|
|
438
|
+
@staticmethod
|
|
439
|
+
def _kill_process_windows(pid):
|
|
440
|
+
external_kill = ["taskkill", "/F", "/PID", str(pid)]
|
|
441
|
+
process = psutil.Popen(external_kill, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
|
|
442
|
+
process.wait()
|
|
443
|
+
|
|
444
|
+
def _kill_process_by_pid(self, pid):
|
|
445
|
+
if self._is_windows():
|
|
446
|
+
self._kill_process_windows(pid)
|
|
447
|
+
else:
|
|
448
|
+
self._kill_process_unix(pid)
|
|
449
|
+
|
|
450
|
+
def _kill_process_tree(self, pid):
|
|
451
|
+
try:
|
|
452
|
+
parent = psutil.Process(pid)
|
|
453
|
+
for child in parent.children(recursive=True):
|
|
454
|
+
try:
|
|
455
|
+
self._kill_process_by_pid(child.pid)
|
|
456
|
+
child.kill()
|
|
457
|
+
except (psutil.AccessDenied, psutil.ZombieProcess, OSError, psutil.NoSuchProcess):
|
|
458
|
+
continue
|
|
459
|
+
self._kill_process_by_pid(parent.pid)
|
|
460
|
+
parent.kill()
|
|
461
|
+
except (psutil.AccessDenied, psutil.ZombieProcess, OSError, psutil.NoSuchProcess):
|
|
462
|
+
pass
|
|
463
|
+
|
|
464
|
+
def stop(self) -> None:
|
|
465
|
+
"""Release any additional resources allocated during launching."""
|
|
466
|
+
maximum_wait_secs = 120.0
|
|
467
|
+
start_time = time.time()
|
|
468
|
+
while (time.time() - start_time) < maximum_wait_secs:
|
|
469
|
+
try:
|
|
470
|
+
shutil.rmtree(self.session_directory)
|
|
471
|
+
self._ports = None
|
|
472
|
+
super().stop()
|
|
473
|
+
return
|
|
474
|
+
except PermissionError:
|
|
475
|
+
pass
|
|
476
|
+
except FileNotFoundError:
|
|
477
|
+
pass
|
|
478
|
+
except Exception:
|
|
479
|
+
raise
|
|
480
|
+
raise RuntimeError(f"Unable to remove {self.session_directory} in {maximum_wait_secs}s")
|
|
481
|
+
|
|
482
|
+
def close(self, session):
|
|
483
|
+
"""Shut down the launched EnSight session.
|
|
484
|
+
|
|
485
|
+
This method closes all associated sessions and then stops the
|
|
486
|
+
launched EnSight instance.
|
|
487
|
+
|
|
488
|
+
Parameters
|
|
489
|
+
----------
|
|
490
|
+
session : ``pyensight.Session``
|
|
491
|
+
Session to close.
|
|
492
|
+
|
|
493
|
+
Raises
|
|
494
|
+
------
|
|
495
|
+
RuntimeError
|
|
496
|
+
If the session was not launched by this launcher.
|
|
497
|
+
|
|
498
|
+
"""
|
|
499
|
+
if self._websocketserver_pid:
|
|
500
|
+
self._kill_process_tree(self._websocketserver_pid)
|
|
501
|
+
return super().close(session)
|
|
502
|
+
|
|
503
|
+
@staticmethod
|
|
504
|
+
def get_cei_install_directory(ansys_installation: Optional[str]) -> str:
|
|
505
|
+
"""Get the Ansys distribution CEI directory to use.
|
|
506
|
+
|
|
507
|
+
Parameters
|
|
508
|
+
----------
|
|
509
|
+
ansys_installation : str, optional
|
|
510
|
+
Path to the local Ansys installation, including the version
|
|
511
|
+
directory. The default is ``None``, in which case common locations
|
|
512
|
+
are scanned to detect the latest local Ansys installation. The
|
|
513
|
+
``PYENSIGHT_ANSYS_INSTALLATION`` environmental variable is checked first.
|
|
514
|
+
|
|
515
|
+
Returns
|
|
516
|
+
-------
|
|
517
|
+
str
|
|
518
|
+
Validated installation directory, which contains ``bin/ensight``.
|
|
519
|
+
|
|
520
|
+
Raises
|
|
521
|
+
------
|
|
522
|
+
RuntimeError:
|
|
523
|
+
If the installation directory does not point to a
|
|
524
|
+
valid EnSight installation.
|
|
525
|
+
"""
|
|
526
|
+
dirs_to_check = []
|
|
527
|
+
if ansys_installation:
|
|
528
|
+
# User passed directory
|
|
529
|
+
dirs_to_check.append(os.path.join(ansys_installation, "CEI"))
|
|
530
|
+
dirs_to_check.append(ansys_installation)
|
|
531
|
+
else:
|
|
532
|
+
# Environmental variable
|
|
533
|
+
if "PYENSIGHT_ANSYS_INSTALLATION" in os.environ:
|
|
534
|
+
env_inst = os.environ["PYENSIGHT_ANSYS_INSTALLATION"]
|
|
535
|
+
dirs_to_check.append(env_inst)
|
|
536
|
+
# Note: PYENSIGHT_ANSYS_INSTALLATION is designed for devel builds
|
|
537
|
+
# where there is no CEI directory, but for folks using it in other
|
|
538
|
+
# ways, we'll add that one too, just in case.
|
|
539
|
+
dirs_to_check.append(os.path.join(env_inst, "CEI"))
|
|
540
|
+
# 'enve' home directory (running in local distro)
|
|
541
|
+
try:
|
|
542
|
+
import enve
|
|
543
|
+
|
|
544
|
+
dirs_to_check.append(enve.home())
|
|
545
|
+
except ModuleNotFoundError:
|
|
546
|
+
pass
|
|
547
|
+
# Look for Ansys install using target version number
|
|
548
|
+
version = pyensight.__ansys_version__
|
|
549
|
+
if f"AWP_ROOT{version}" in os.environ:
|
|
550
|
+
dirs_to_check.append(os.path.join(os.environ[f"AWP_ROOT{version}"], "CEI"))
|
|
551
|
+
# Common, default install locations
|
|
552
|
+
install_dir = f"/ansys_inc/v{version}/CEI"
|
|
553
|
+
if platform.system().startswith("Wind"):
|
|
554
|
+
install_dir = rf"C:\Program Files\ANSYS Inc\v{version}\CEI"
|
|
555
|
+
dirs_to_check.append(install_dir)
|
|
556
|
+
|
|
557
|
+
for install_dir in dirs_to_check:
|
|
558
|
+
launch_file = os.path.join(install_dir, "bin", "ensight")
|
|
559
|
+
if os.path.exists(launch_file):
|
|
560
|
+
return install_dir
|
|
561
|
+
|
|
562
|
+
raise RuntimeError(f"Unable to detect an EnSight installation in: {dirs_to_check}")
|
|
563
|
+
|
|
564
|
+
def _is_system_egl_capable(self) -> bool:
|
|
565
|
+
"""Check if the system supports the EGL launch.
|
|
566
|
+
|
|
567
|
+
Returns
|
|
568
|
+
-------
|
|
569
|
+
bool
|
|
570
|
+
``True`` if the system supports the EGL launch, ``False`` otherwise.
|
|
571
|
+
"""
|
|
572
|
+
if self._is_windows():
|
|
573
|
+
return False
|
|
574
|
+
egl_test_path = os.path.join(self._install_path, "bin", "cei_egltest")
|
|
575
|
+
egl_proc = subprocess.Popen([egl_test_path], stdout=subprocess.PIPE)
|
|
576
|
+
_, _ = egl_proc.communicate()
|
|
577
|
+
if egl_proc.returncode == 0:
|
|
578
|
+
return True
|
|
579
|
+
return False
|
|
File without changes
|