ansys-pyensight-core 0.7.6__py3-none-any.whl → 0.7.8__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.
Potentially problematic release.
This version of ansys-pyensight-core might be problematic. Click here for more details.
- ansys/pyensight/core/ensight_grpc.py +430 -411
- ansys/pyensight/core/renderable.py +42 -21
- ansys/pyensight/core/session.py +5 -2
- ansys/pyensight/core/utils/omniverse.py +239 -0
- ansys/pyensight/core/utils/omniverse_dsg_server.py +1354 -0
- ansys/pyensight/core/utils/resources/Materials/000_sky.exr +0 -0
- ansys/pyensight/core/utils/resources/Materials/Fieldstone/Fieldstone_BaseColor.png +0 -0
- ansys/pyensight/core/utils/resources/Materials/Fieldstone/Fieldstone_N.png +0 -0
- ansys/pyensight/core/utils/resources/Materials/Fieldstone/Fieldstone_ORM.png +0 -0
- ansys/pyensight/core/utils/resources/Materials/Fieldstone.mdl +54 -0
- ansys/pyensight/core/utils/variables.py +119 -107
- {ansys_pyensight_core-0.7.6.dist-info → ansys_pyensight_core-0.7.8.dist-info}/METADATA +4 -2
- {ansys_pyensight_core-0.7.6.dist-info → ansys_pyensight_core-0.7.8.dist-info}/RECORD +15 -8
- {ansys_pyensight_core-0.7.6.dist-info → ansys_pyensight_core-0.7.8.dist-info}/LICENSE +0 -0
- {ansys_pyensight_core-0.7.6.dist-info → ansys_pyensight_core-0.7.8.dist-info}/WHEEL +0 -0
|
@@ -540,6 +540,20 @@ class RenderableVNC(Renderable):
|
|
|
540
540
|
self._rendertype = "remote"
|
|
541
541
|
self.update()
|
|
542
542
|
|
|
543
|
+
def _update_2023R2_or_less(self):
|
|
544
|
+
"""Update the remote rendering widget and display it for
|
|
545
|
+
backend EnSight of version earlier than 2024R1
|
|
546
|
+
"""
|
|
547
|
+
query_params = {
|
|
548
|
+
"autoconnect": "true",
|
|
549
|
+
"host": self._session.html_hostname,
|
|
550
|
+
"port": self._session.ws_port,
|
|
551
|
+
}
|
|
552
|
+
url = f"{self._http_protocol}://{self._session.html_hostname}:{self._session.html_port}"
|
|
553
|
+
url += "/ansys/nexus/novnc/vnc_envision.html"
|
|
554
|
+
url += self._get_query_parameters_str(query_params)
|
|
555
|
+
self._url = url
|
|
556
|
+
|
|
543
557
|
def update(self):
|
|
544
558
|
"""Update the remote rendering widget and display it.
|
|
545
559
|
|
|
@@ -549,29 +563,36 @@ class RenderableVNC(Renderable):
|
|
|
549
563
|
"""
|
|
550
564
|
optional_query = self._get_query_parameters_str()
|
|
551
565
|
version = _get_ansysnexus_version(self._session._cei_suffix)
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
attributes += ' active="true"'
|
|
565
|
-
attributes += (
|
|
566
|
-
" renderer_options='"
|
|
567
|
-
+ f'{{ "ws":"{ws_uri}", "http":"{rest_uri}", "security_token":"{self._session.secret_key}", "connect_to_running_ens":true {query_args} }}'
|
|
568
|
-
+ "'"
|
|
569
|
-
)
|
|
566
|
+
if int(self._session._cei_suffix) < 241:
|
|
567
|
+
self._update_2023R2_or_less()
|
|
568
|
+
else:
|
|
569
|
+
html = (
|
|
570
|
+
f"<script src='/ansys{version}/nexus/viewer-loader.js{optional_query}'></script>\n"
|
|
571
|
+
)
|
|
572
|
+
rest_uri = (
|
|
573
|
+
f"{self._http_protocol}://{self._session.html_hostname}:{self._session.html_port}"
|
|
574
|
+
)
|
|
575
|
+
ws_uri = (
|
|
576
|
+
f"{self._http_protocol}://{self._session.html_hostname}:{self._session.ws_port}"
|
|
577
|
+
)
|
|
570
578
|
|
|
571
|
-
|
|
579
|
+
query_args = ""
|
|
580
|
+
if self._using_proxy and optional_query:
|
|
581
|
+
query_args = f', "extra_query_args":"{optional_query[1:]}"'
|
|
582
|
+
|
|
583
|
+
attributes = ' renderer="envnc"'
|
|
584
|
+
attributes += ' ui="simple"'
|
|
585
|
+
attributes += ' active="true"'
|
|
586
|
+
attributes += (
|
|
587
|
+
" renderer_options='"
|
|
588
|
+
+ f'{{ "ws":"{ws_uri}", "http":"{rest_uri}", "security_token":"{self._session.secret_key}", "connect_to_running_ens":true {query_args} }}'
|
|
589
|
+
+ "'"
|
|
590
|
+
)
|
|
572
591
|
|
|
573
|
-
|
|
574
|
-
|
|
592
|
+
html += f"<ansys-nexus-viewer {attributes}></ansys-nexus-viewer>\n"
|
|
593
|
+
|
|
594
|
+
# refresh the remote HTML
|
|
595
|
+
self._save_remote_html_page(html)
|
|
575
596
|
super().update()
|
|
576
597
|
|
|
577
598
|
|
ansys/pyensight/core/session.py
CHANGED
|
@@ -1058,8 +1058,11 @@ class Session:
|
|
|
1058
1058
|
if _utils_dir not in sys.path:
|
|
1059
1059
|
sys.path.insert(0, _utils_dir)
|
|
1060
1060
|
onlyfiles = [f for f in listdir(_utils_dir) if os.path.isfile(os.path.join(_utils_dir, f))]
|
|
1061
|
-
for
|
|
1062
|
-
|
|
1061
|
+
for _basename in onlyfiles:
|
|
1062
|
+
# skip over any files with the "_server" in their names
|
|
1063
|
+
if "_server" in _basename:
|
|
1064
|
+
continue
|
|
1065
|
+
_filename = os.path.join(_utils_dir, _basename)
|
|
1063
1066
|
try:
|
|
1064
1067
|
# get the module and class names
|
|
1065
1068
|
_name = os.path.splitext(os.path.basename(_filename))[0]
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
from types import ModuleType
|
|
5
|
+
from typing import TYPE_CHECKING, List, Optional, Union
|
|
6
|
+
|
|
7
|
+
import psutil
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
try:
|
|
11
|
+
import ensight
|
|
12
|
+
except ImportError:
|
|
13
|
+
from ansys.api.pyensight import ensight_api
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Omniverse:
|
|
17
|
+
"""Provides the ``ensight.utils.omniverse`` interface.
|
|
18
|
+
|
|
19
|
+
The omniverse class methods provide an interface between an EnSight session
|
|
20
|
+
and an Omniverse instance. See :ref:`omniverse_info` for additional details.
|
|
21
|
+
|
|
22
|
+
Note
|
|
23
|
+
----
|
|
24
|
+
This interface is only available when using pyensight (they do not work with
|
|
25
|
+
the ensight Python interpreter) and the module must be used in an interpreter
|
|
26
|
+
that includes the Omniverse Python modules (e.g. omni and pxr). Only a single
|
|
27
|
+
Omniverse connection can be established within a single pyensight session.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
interface:
|
|
32
|
+
Entity that provides the ``ensight`` namespace. In the case of
|
|
33
|
+
EnSight Python, the ``ensight`` module is passed. In the case
|
|
34
|
+
of PyEnSight, ``Session.ensight`` is passed.
|
|
35
|
+
|
|
36
|
+
Example
|
|
37
|
+
-------
|
|
38
|
+
::
|
|
39
|
+
from ansys.pyensight.core import LocalLauncher
|
|
40
|
+
session = LocalLauncher().start()
|
|
41
|
+
ov = session.ensight.utils.omniverse
|
|
42
|
+
ov.create_connection()
|
|
43
|
+
ov.update()
|
|
44
|
+
ov.close_connection()
|
|
45
|
+
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, interface: Union["ensight_api.ensight", "ensight"]):
|
|
49
|
+
self._ensight = interface
|
|
50
|
+
self._server_pid: Optional[int] = None
|
|
51
|
+
self._interpreter: List[str] = []
|
|
52
|
+
|
|
53
|
+
def _check_modules(self) -> None:
|
|
54
|
+
"""Verify that the Python interpreter is correct
|
|
55
|
+
|
|
56
|
+
Check for omni module. If not present, raise an exception.
|
|
57
|
+
If pxr is there as well, then we can just use sys.executable.
|
|
58
|
+
If not, check to see if 'kit.bat' or 'kit.sh' can be found and
|
|
59
|
+
arrange to use those instead.
|
|
60
|
+
|
|
61
|
+
Raises
|
|
62
|
+
------
|
|
63
|
+
RuntimeError if the necessary modules are missing.
|
|
64
|
+
|
|
65
|
+
"""
|
|
66
|
+
# One time check for this
|
|
67
|
+
if len(self._interpreter):
|
|
68
|
+
return
|
|
69
|
+
try:
|
|
70
|
+
# Note: the EnSight embedded interpreter will not have these
|
|
71
|
+
import omni.client # noqa: F401
|
|
72
|
+
except ImportError:
|
|
73
|
+
raise RuntimeError("The module requires the omni module to be installed.") from None
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
# if we can import pxr, then we can just use sys.executable
|
|
77
|
+
from pxr import Gf, Sdf, Usd, UsdGeom, UsdLux, UsdShade # noqa: F401
|
|
78
|
+
|
|
79
|
+
if os.path.basename(sys.executable).startswith("kit"):
|
|
80
|
+
# we are running inside of an Omniverse app like Create, use the 'kit' script
|
|
81
|
+
raise ImportError("Internal retry")
|
|
82
|
+
|
|
83
|
+
self._interpreter = [sys.executable]
|
|
84
|
+
return
|
|
85
|
+
except ImportError:
|
|
86
|
+
# Can we find 'kit.bat' or 'kit.sh' (we may be running in it)?
|
|
87
|
+
# Interesting cases: something/kit/python/python.exe,
|
|
88
|
+
# something/kit/kit.exe. All mapped to something/kit.{bat,sh} if found.
|
|
89
|
+
ov_dir = os.path.dirname(sys.executable)
|
|
90
|
+
for _ in range(3):
|
|
91
|
+
for name in ("kit.bat", "kit.sh"):
|
|
92
|
+
exe_name = os.path.join(ov_dir, name)
|
|
93
|
+
if os.path.exists(exe_name):
|
|
94
|
+
self._interpreter = [
|
|
95
|
+
exe_name,
|
|
96
|
+
"--enable",
|
|
97
|
+
"omni.client",
|
|
98
|
+
"--enable",
|
|
99
|
+
"omni.usd",
|
|
100
|
+
"--exec",
|
|
101
|
+
]
|
|
102
|
+
return
|
|
103
|
+
ov_dir = os.path.dirname(ov_dir)
|
|
104
|
+
raise RuntimeError("Unable to detect a copy of the Omniverse kit executable.") from None
|
|
105
|
+
|
|
106
|
+
def _is_running_omniverse(self) -> bool:
|
|
107
|
+
"""Check that an Omniverse connection is active
|
|
108
|
+
Returns
|
|
109
|
+
-------
|
|
110
|
+
True if the connection is active, False otherwise.
|
|
111
|
+
"""
|
|
112
|
+
if self._server_pid is None:
|
|
113
|
+
return False
|
|
114
|
+
if psutil.pid_exists(self._server_pid):
|
|
115
|
+
return True
|
|
116
|
+
self._server_pid = None
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
def create_connection(
|
|
120
|
+
self,
|
|
121
|
+
omniverse_path: str,
|
|
122
|
+
include_camera: bool = False,
|
|
123
|
+
normalize_geometry: bool = False,
|
|
124
|
+
live: bool = True,
|
|
125
|
+
debug_filename: str = "",
|
|
126
|
+
) -> None:
|
|
127
|
+
"""Ensure that an EnSight dsg -> omniverse server is running
|
|
128
|
+
|
|
129
|
+
Connect the current EnSight session to an Omniverse server.
|
|
130
|
+
This is done by launching a new service that makes a dynamic scene graph
|
|
131
|
+
connection to the EnSight session and pushes updates to the Omniverse server.
|
|
132
|
+
The initial EnSight scene will be pushed after the connection is established.
|
|
133
|
+
|
|
134
|
+
Parameters
|
|
135
|
+
----------
|
|
136
|
+
omniverse_path : str
|
|
137
|
+
The URI to the Omniverse server. It will look like this:
|
|
138
|
+
"omniverse://localhost/Users/test"
|
|
139
|
+
include_camera : bool
|
|
140
|
+
If True, apply the EnSight camera to the Omniverse scene. This option
|
|
141
|
+
should be used if the target viewer is in AR/VR mode. Defaults to False.
|
|
142
|
+
normalize_geometry : bool
|
|
143
|
+
Omniverse units are in meters. If the source dataset is not in the correct
|
|
144
|
+
unit system or is just too large/small, this option will remap the geometry
|
|
145
|
+
to a unit cube. Defaults to False.
|
|
146
|
+
live : bool
|
|
147
|
+
If True, one can call 'update()' to send updated geometry to Omniverse.
|
|
148
|
+
If False, the Omniverse connection will push a single update and then
|
|
149
|
+
disconnect. Defaults to True.
|
|
150
|
+
debug_filename : str
|
|
151
|
+
If the name of a file is provided, it will be used to save logging information on
|
|
152
|
+
the connection between EnSight and Omniverse.
|
|
153
|
+
|
|
154
|
+
"""
|
|
155
|
+
if not isinstance(self._ensight, ModuleType):
|
|
156
|
+
self._ensight._session.ensight_version_check("2023 R2")
|
|
157
|
+
self._check_modules()
|
|
158
|
+
if self._is_running_omniverse():
|
|
159
|
+
raise RuntimeError("An Omniverse server connection is already active.")
|
|
160
|
+
# Make sure the internal ui module is loaded
|
|
161
|
+
self._ensight._session.cmd("import enspyqtgui_int", do_eval=False)
|
|
162
|
+
# Get the gRPC connection details and use them to launch the service
|
|
163
|
+
port = self._ensight._session.grpc.port()
|
|
164
|
+
hostname = self._ensight._session.grpc.host
|
|
165
|
+
token = self._ensight._session.grpc.security_token
|
|
166
|
+
script_name = "omniverse_dsg_server.py"
|
|
167
|
+
working_dir = os.path.dirname(__file__)
|
|
168
|
+
cmd = [
|
|
169
|
+
script_name,
|
|
170
|
+
"--host",
|
|
171
|
+
hostname,
|
|
172
|
+
"--port",
|
|
173
|
+
str(port),
|
|
174
|
+
"--path",
|
|
175
|
+
omniverse_path,
|
|
176
|
+
]
|
|
177
|
+
if live:
|
|
178
|
+
cmd.extend(["--live"])
|
|
179
|
+
if include_camera:
|
|
180
|
+
cmd.extend(["--vrmode"])
|
|
181
|
+
if token:
|
|
182
|
+
cmd.extend(["--security", token])
|
|
183
|
+
# if temporal:
|
|
184
|
+
# cmd.extend(["--animation"])
|
|
185
|
+
# else:
|
|
186
|
+
# cmd.extend(["--no-animation"])
|
|
187
|
+
if debug_filename:
|
|
188
|
+
cmd.extend(["--log_file", debug_filename])
|
|
189
|
+
cmd.extend(["--verbose", "1"])
|
|
190
|
+
if normalize_geometry:
|
|
191
|
+
cmd.extend(["--normalize_geometry"])
|
|
192
|
+
# if using kit.bat, convert args into a string, otherwise, just use them
|
|
193
|
+
cmdline = []
|
|
194
|
+
cmdline.extend(self._interpreter)
|
|
195
|
+
if len(self._interpreter) > 1:
|
|
196
|
+
cmd = [" ".join(cmd)]
|
|
197
|
+
cmdline.extend(cmd)
|
|
198
|
+
env_vars = os.environ.copy()
|
|
199
|
+
process = subprocess.Popen(cmdline, close_fds=True, env=env_vars, cwd=working_dir)
|
|
200
|
+
self._server_pid = process.pid
|
|
201
|
+
|
|
202
|
+
def close_connection(self) -> None:
|
|
203
|
+
"""Shut down the open EnSight dsg -> omniverse server
|
|
204
|
+
|
|
205
|
+
Break the connection between the EnSight instance and Omniverse.
|
|
206
|
+
|
|
207
|
+
"""
|
|
208
|
+
self._check_modules()
|
|
209
|
+
if not self._is_running_omniverse():
|
|
210
|
+
return
|
|
211
|
+
proc = psutil.Process(self._server_pid)
|
|
212
|
+
for child in proc.children(recursive=True):
|
|
213
|
+
if psutil.pid_exists(child.pid):
|
|
214
|
+
# This can be a race condition, so it is ok if the child is dead already
|
|
215
|
+
try:
|
|
216
|
+
child.kill()
|
|
217
|
+
except psutil.NoSuchProcess:
|
|
218
|
+
pass
|
|
219
|
+
# Same issue, this process might already be shutting down, so NoSuchProcess is ok.
|
|
220
|
+
try:
|
|
221
|
+
proc.kill()
|
|
222
|
+
except psutil.NoSuchProcess:
|
|
223
|
+
pass
|
|
224
|
+
self._server_pid = None
|
|
225
|
+
|
|
226
|
+
def update(self) -> None:
|
|
227
|
+
"""Update the geometry in Omniverse
|
|
228
|
+
|
|
229
|
+
Push the current EnSight scene to the current Omniverse connection.
|
|
230
|
+
|
|
231
|
+
"""
|
|
232
|
+
if not isinstance(self._ensight, ModuleType):
|
|
233
|
+
self._ensight._session.ensight_version_check("2023 R2")
|
|
234
|
+
self._check_modules()
|
|
235
|
+
if not self._is_running_omniverse():
|
|
236
|
+
raise RuntimeError("No Omniverse server connection is currently active.")
|
|
237
|
+
update_cmd = "dynamicscenegraph://localhost/client/update"
|
|
238
|
+
cmd = f'enspyqtgui_int.dynamic_scene_graph_command("{update_cmd}")'
|
|
239
|
+
self._ensight._session.cmd(cmd, do_eval=False)
|