ansys-pyensight-core 0.7.7__py3-none-any.whl → 0.7.9__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/dockerlauncher.py +52 -44
- ansys/pyensight/core/enscontext.py +17 -15
- ansys/pyensight/core/enshell_grpc.py +8 -8
- ansys/pyensight/core/ensight_grpc.py +37 -35
- ansys/pyensight/core/ensobj.py +4 -4
- ansys/pyensight/core/launch_ensight.py +2 -2
- ansys/pyensight/core/launcher.py +4 -4
- ansys/pyensight/core/listobj.py +18 -16
- ansys/pyensight/core/renderable.py +47 -25
- ansys/pyensight/core/session.py +33 -29
- ansys/pyensight/core/utils/export.py +9 -9
- ansys/pyensight/core/utils/omniverse.py +59 -24
- ansys/pyensight/core/utils/omniverse_dsg_server.py +43 -5
- ansys/pyensight/core/utils/parts.py +50 -36
- ansys/pyensight/core/utils/query.py +41 -35
- ansys/pyensight/core/utils/variables.py +256 -225
- ansys/pyensight/core/utils/views.py +14 -14
- {ansys_pyensight_core-0.7.7.dist-info → ansys_pyensight_core-0.7.9.dist-info}/METADATA +2 -2
- ansys_pyensight_core-0.7.9.dist-info/RECORD +34 -0
- ansys_pyensight_core-0.7.7.dist-info/RECORD +0 -34
- {ansys_pyensight_core-0.7.7.dist-info → ansys_pyensight_core-0.7.9.dist-info}/LICENSE +0 -0
- {ansys_pyensight_core-0.7.7.dist-info → ansys_pyensight_core-0.7.9.dist-info}/WHEEL +0 -0
ansys/pyensight/core/listobj.py
CHANGED
|
@@ -107,12 +107,14 @@ class ensobjlist(List[T]): # noqa: N801
|
|
|
107
107
|
value_list = [value]
|
|
108
108
|
out_list: ensobjlist[Any] = ensobjlist(session=self._session)
|
|
109
109
|
for item in self:
|
|
110
|
-
if isinstance(item, ENSOBJ):
|
|
110
|
+
if isinstance(item, ENSOBJ): # pragma: no cover
|
|
111
111
|
try:
|
|
112
112
|
item_value = item.getattr(attr)
|
|
113
113
|
for check_value in value_list:
|
|
114
114
|
if wildcard == 2:
|
|
115
|
-
if fnmatch.fnmatch(
|
|
115
|
+
if fnmatch.fnmatch(
|
|
116
|
+
str(item_value), str(check_value)
|
|
117
|
+
): # pragma: no cover
|
|
116
118
|
out_list.append(item)
|
|
117
119
|
break
|
|
118
120
|
elif wildcard > 0:
|
|
@@ -123,14 +125,14 @@ class ensobjlist(List[T]): # noqa: N801
|
|
|
123
125
|
if item_value == check_value:
|
|
124
126
|
out_list.append(item)
|
|
125
127
|
break
|
|
126
|
-
except RuntimeError:
|
|
127
|
-
pass
|
|
128
|
+
except RuntimeError: # pragma: no cover
|
|
129
|
+
pass # pragma: no cover
|
|
128
130
|
if group:
|
|
129
131
|
# This is a bit of a hack, but the find() method generates a local list of
|
|
130
132
|
# proxy objects. We want to put that in a group. We do that by running
|
|
131
133
|
# a script in EnSight that creates an empty group and then adds those
|
|
132
134
|
# children to the group. The output becomes the remote referenced ENS_GROUP.
|
|
133
|
-
if self._session is not None:
|
|
135
|
+
if self._session is not None: # pragma: no cover
|
|
134
136
|
ens_group_cmd = "ensight.objs.core.VPORTS.find('__unknown__', group=1)"
|
|
135
137
|
ens_group = self._session.cmd(ens_group_cmd)
|
|
136
138
|
ens_group.addchild(out_list)
|
|
@@ -163,14 +165,14 @@ class ensobjlist(List[T]): # noqa: N801
|
|
|
163
165
|
"""
|
|
164
166
|
session = None
|
|
165
167
|
objid_list = [x.__OBJID__ for x in self if isinstance(x, ENSOBJ)]
|
|
166
|
-
for item in self:
|
|
167
|
-
if hasattr(item, "_session"):
|
|
168
|
+
for item in self: # pragma: no cover
|
|
169
|
+
if hasattr(item, "_session"): # pragma: no cover
|
|
168
170
|
session = item._session
|
|
169
171
|
break
|
|
170
|
-
if session:
|
|
172
|
+
if session: # pragma: no cover
|
|
171
173
|
msg = f"ensight.objs.ensobjlist(ensight.objs.wrap_id(x) for x in {objid_list}).set_attr({attr.__repr__()}, {value.__repr__()})"
|
|
172
174
|
return session.cmd(msg)
|
|
173
|
-
return 0
|
|
175
|
+
return 0 # pragma: no cover
|
|
174
176
|
|
|
175
177
|
def get_attr(self, attr: Any, default: Optional[Any] = None):
|
|
176
178
|
"""Query a specific attribute for all ENSOBJ objects in the list
|
|
@@ -199,20 +201,20 @@ class ensobjlist(List[T]): # noqa: N801
|
|
|
199
201
|
"""
|
|
200
202
|
session = None
|
|
201
203
|
objid_list = [x.__OBJID__ for x in self if isinstance(x, ENSOBJ)]
|
|
202
|
-
for item in self:
|
|
203
|
-
if hasattr(item, "_session"):
|
|
204
|
+
for item in self: # pragma: no cover
|
|
205
|
+
if hasattr(item, "_session"): # pragma: no cover
|
|
204
206
|
session = item._session
|
|
205
207
|
break
|
|
206
208
|
value = None
|
|
207
|
-
if session:
|
|
208
|
-
if default:
|
|
209
|
-
msg = f"ensight.objs.ensobjlist(ensight.objs.wrap_id(x) for x in {objid_list}).get_attr({attr.__repr__()}, {default.__repr__()})"
|
|
209
|
+
if session: # pragma: no cover
|
|
210
|
+
if default: # pragma: no cover
|
|
211
|
+
msg = f"ensight.objs.ensobjlist(ensight.objs.wrap_id(x) for x in {objid_list}).get_attr({attr.__repr__()}, {default.__repr__()})" # pragma: no cover
|
|
210
212
|
else:
|
|
211
213
|
msg = f"ensight.objs.ensobjlist(ensight.objs.wrap_id(x) for x in {objid_list}).get_attr({attr.__repr__()})"
|
|
212
214
|
value = session.cmd(msg)
|
|
213
|
-
if value:
|
|
215
|
+
if value: # pragma: no cover
|
|
214
216
|
return value
|
|
215
|
-
return [default] * len(objid_list)
|
|
217
|
+
return [default] * len(objid_list) # pragma: no cover
|
|
216
218
|
|
|
217
219
|
@overload
|
|
218
220
|
def __getitem__(self, index: SupportsIndex) -> T:
|
|
@@ -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,37 @@ 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
|
-
|
|
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) < 242: # pragma: no cover
|
|
567
|
+
version = ""
|
|
568
|
+
self._update_2023R2_or_less() # pragma: no cover
|
|
569
|
+
else:
|
|
570
|
+
html = (
|
|
571
|
+
f"<script src='/ansys{version}/nexus/viewer-loader.js{optional_query}'></script>\n"
|
|
572
|
+
)
|
|
573
|
+
rest_uri = (
|
|
574
|
+
f"{self._http_protocol}://{self._session.html_hostname}:{self._session.html_port}"
|
|
575
|
+
)
|
|
576
|
+
ws_uri = (
|
|
577
|
+
f"{self._http_protocol}://{self._session.html_hostname}:{self._session.ws_port}"
|
|
578
|
+
)
|
|
570
579
|
|
|
571
|
-
|
|
580
|
+
query_args = ""
|
|
581
|
+
if self._using_proxy and optional_query: # pragma: no cover
|
|
582
|
+
query_args = f', "extra_query_args":"{optional_query[1:]}"' # pragma: no cover
|
|
583
|
+
|
|
584
|
+
attributes = ' renderer="envnc"'
|
|
585
|
+
attributes += ' ui="simple"'
|
|
586
|
+
attributes += ' active="true"'
|
|
587
|
+
attributes += (
|
|
588
|
+
" renderer_options='"
|
|
589
|
+
+ f'{{ "ws":"{ws_uri}", "http":"{rest_uri}", "security_token":"{self._session.secret_key}", "connect_to_running_ens":true {query_args} }}'
|
|
590
|
+
+ "'"
|
|
591
|
+
)
|
|
572
592
|
|
|
573
|
-
|
|
574
|
-
|
|
593
|
+
html += f"<ansys-nexus-viewer {attributes}></ansys-nexus-viewer>\n"
|
|
594
|
+
|
|
595
|
+
# refresh the remote HTML
|
|
596
|
+
self._save_remote_html_page(html)
|
|
575
597
|
super().update()
|
|
576
598
|
|
|
577
599
|
|
|
@@ -672,11 +694,11 @@ class RenderableEVSN(Renderable):
|
|
|
672
694
|
f'"ws":"{self._http_protocol}://{self._session.html_hostname}:{self._session.ws_port}"'
|
|
673
695
|
)
|
|
674
696
|
secrets = f'"security_token":"{self._session.secret_key}"'
|
|
675
|
-
if not self._using_proxy or optional_query == "":
|
|
697
|
+
if not self._using_proxy or optional_query == "": # pragma: no cover
|
|
676
698
|
attributes += f" renderer_options='{{ {http_uri}, {ws_uri}, {secrets} }}'"
|
|
677
|
-
else:
|
|
678
|
-
query_args = f'"extra_query_args":"{optional_query[1:]}"'
|
|
679
|
-
attributes += f" renderer_options='{{ {http_uri}, {ws_uri}, {secrets}, {query_args} }}'"
|
|
699
|
+
else: # pragma: no cover
|
|
700
|
+
query_args = f'"extra_query_args":"{optional_query[1:]}"' # pragma: no cover
|
|
701
|
+
attributes += f" renderer_options='{{ {http_uri}, {ws_uri}, {secrets}, {query_args} }}'" # pragma: no cover
|
|
680
702
|
html += f"<ansys-nexus-viewer {attributes}></ansys-nexus-viewer>\n"
|
|
681
703
|
# refresh the remote HTML
|
|
682
704
|
self._save_remote_html_page(html)
|
ansys/pyensight/core/session.py
CHANGED
|
@@ -172,7 +172,7 @@ class Session:
|
|
|
172
172
|
# are we in a jupyter notebook?
|
|
173
173
|
try:
|
|
174
174
|
_ = get_ipython() # type: ignore
|
|
175
|
-
self._jupyter_notebook = True
|
|
175
|
+
self._jupyter_notebook = True # pragma: no cover
|
|
176
176
|
except NameError:
|
|
177
177
|
self._jupyter_notebook = False
|
|
178
178
|
|
|
@@ -279,7 +279,7 @@ class Session:
|
|
|
279
279
|
If true, actually try to communicate with EnSight. By default false.
|
|
280
280
|
"""
|
|
281
281
|
time_start = time.time()
|
|
282
|
-
while time.time() - time_start < self._timeout:
|
|
282
|
+
while time.time() - time_start < self._timeout: # pragma: no cover
|
|
283
283
|
if self._grpc.is_connected():
|
|
284
284
|
try:
|
|
285
285
|
if validate:
|
|
@@ -287,10 +287,10 @@ class Session:
|
|
|
287
287
|
self._cei_suffix = self.cmd("ensight.version('suffix')")
|
|
288
288
|
self._check_rest_connection()
|
|
289
289
|
return
|
|
290
|
-
except OSError:
|
|
291
|
-
pass
|
|
290
|
+
except OSError: # pragma: no cover
|
|
291
|
+
pass # pragma: no cover
|
|
292
292
|
self._grpc.connect(timeout=self._timeout)
|
|
293
|
-
raise RuntimeError("Unable to establish a gRPC connection to EnSight.")
|
|
293
|
+
raise RuntimeError("Unable to establish a gRPC connection to EnSight.") # pragma: no cover
|
|
294
294
|
|
|
295
295
|
def _check_rest_connection(self) -> None:
|
|
296
296
|
"""Validate the REST API connection works
|
|
@@ -532,7 +532,7 @@ class Session:
|
|
|
532
532
|
|
|
533
533
|
out = []
|
|
534
534
|
dirlen = 0
|
|
535
|
-
if localdir:
|
|
535
|
+
if localdir: # pragma: no cover
|
|
536
536
|
# we use dirlen + 1 here to remove the '/' inserted by os.path.join()
|
|
537
537
|
dirlen = len(localdir) + 1
|
|
538
538
|
for item in filelist:
|
|
@@ -547,7 +547,7 @@ class Session:
|
|
|
547
547
|
out.append((fullname[dirlen:], os.stat(fullname).st_size))
|
|
548
548
|
except Exception:
|
|
549
549
|
pass
|
|
550
|
-
if progress:
|
|
550
|
+
if progress: # pragma: no cover
|
|
551
551
|
try:
|
|
552
552
|
from tqdm.auto import tqdm
|
|
553
553
|
except ImportError:
|
|
@@ -568,7 +568,9 @@ class Session:
|
|
|
568
568
|
data = fp.read(chunk_size)
|
|
569
569
|
if data == b"":
|
|
570
570
|
break
|
|
571
|
-
self.cmd(
|
|
571
|
+
self.cmd(
|
|
572
|
+
f"copy_write_function__(r'{name}', {data!r})", do_eval=False
|
|
573
|
+
) # pragma: no cover
|
|
572
574
|
return out
|
|
573
575
|
|
|
574
576
|
def copy_from_session(
|
|
@@ -713,23 +715,23 @@ class Session:
|
|
|
713
715
|
|
|
714
716
|
"""
|
|
715
717
|
dirname = os.path.dirname(filename)
|
|
716
|
-
if not dirname:
|
|
717
|
-
dirname = "."
|
|
718
|
+
if not dirname: # pragma: no cover
|
|
719
|
+
dirname = "." # pragma: no cover
|
|
718
720
|
if dirname not in sys.path:
|
|
719
721
|
sys.path.append(dirname)
|
|
720
722
|
module_name, _ = os.path.splitext(os.path.basename(filename))
|
|
721
723
|
# get the module reference
|
|
722
724
|
spec = importlib.util.find_spec(module_name)
|
|
723
|
-
if spec:
|
|
725
|
+
if spec: # pragma: no cover
|
|
724
726
|
module = importlib.util.module_from_spec(spec)
|
|
725
727
|
# insert an ensight interface into the module
|
|
726
728
|
if self.ensight:
|
|
727
729
|
module.ensight = self.ensight # type: ignore
|
|
728
730
|
# load (run) the module
|
|
729
|
-
if spec.loader:
|
|
731
|
+
if spec.loader: # pragma: no cover
|
|
730
732
|
spec.loader.exec_module(module)
|
|
731
733
|
return module
|
|
732
|
-
return None
|
|
734
|
+
return None # pragma: no cover
|
|
733
735
|
|
|
734
736
|
def exec(self, function: Callable, *args, remote: bool = False, **kwargs) -> Any:
|
|
735
737
|
"""Run a function containing EnSight API calls locally or in the EnSight interpreter.
|
|
@@ -812,7 +814,9 @@ class Session:
|
|
|
812
814
|
# Create a bound object that allows for direct encoding of the args/kwargs params
|
|
813
815
|
# The new function would be bound_function(ensight) where the args are captured
|
|
814
816
|
# in the lambda.
|
|
815
|
-
bound_function = lambda ens: function(
|
|
817
|
+
bound_function = lambda ens: function( # noqa: E731 # pragma: no cover
|
|
818
|
+
ens, *args, **kwargs
|
|
819
|
+
)
|
|
816
820
|
# Serialize the bound function
|
|
817
821
|
serialized_function = dill.dumps(bound_function, recurse=True)
|
|
818
822
|
self.cmd("import dill", do_eval=False)
|
|
@@ -1022,8 +1026,8 @@ class Session:
|
|
|
1022
1026
|
|
|
1023
1027
|
"""
|
|
1024
1028
|
obj_str = ""
|
|
1025
|
-
if object_id:
|
|
1026
|
-
obj_str = f", id={object_id}"
|
|
1029
|
+
if object_id: # pragma: no cover
|
|
1030
|
+
obj_str = f", id={object_id}" # pragma: no cover
|
|
1027
1031
|
cmd = f"ensight.objs.release_id('{self.name}'{obj_str})"
|
|
1028
1032
|
_ = self.cmd(cmd, do_eval=False)
|
|
1029
1033
|
|
|
@@ -1073,9 +1077,9 @@ class Session:
|
|
|
1073
1077
|
spec = importlib.util.spec_from_file_location(
|
|
1074
1078
|
f"ansys.pyensight.core.utils.{_name}", _filename
|
|
1075
1079
|
)
|
|
1076
|
-
if spec:
|
|
1080
|
+
if spec: # pragma: no cover
|
|
1077
1081
|
_module = importlib.util.module_from_spec(spec)
|
|
1078
|
-
if spec.loader:
|
|
1082
|
+
if spec.loader: # pragma: no cover
|
|
1079
1083
|
spec.loader.exec_module(_module)
|
|
1080
1084
|
# get the class from the module (query.py filename -> Query() object)
|
|
1081
1085
|
_the_class = getattr(_module, _cap_name)
|
|
@@ -1329,8 +1333,8 @@ class Session:
|
|
|
1329
1333
|
|
|
1330
1334
|
"""
|
|
1331
1335
|
base_uri = "https://s3.amazonaws.com/www3.ensight.com/PyEnSight/ExampleData"
|
|
1332
|
-
if root is not None:
|
|
1333
|
-
base_uri = root
|
|
1336
|
+
if root is not None: # pragma: no cover
|
|
1337
|
+
base_uri = root # pragma: no cover
|
|
1334
1338
|
pathname = self.download_pyansys_example(example_name, root=base_uri)
|
|
1335
1339
|
script = f'outpath = r"""{pathname}"""\n'
|
|
1336
1340
|
if uncompress:
|
|
@@ -1600,8 +1604,8 @@ class Session:
|
|
|
1600
1604
|
if tail == -1:
|
|
1601
1605
|
tail_len = 12
|
|
1602
1606
|
tail = s.find(", cached:yes", offset)
|
|
1603
|
-
if tail == -1:
|
|
1604
|
-
break
|
|
1607
|
+
if tail == -1: # pragma: no cover
|
|
1608
|
+
break # pragma: no cover
|
|
1605
1609
|
# just this object substring
|
|
1606
1610
|
tmp = s[start + 7 : tail]
|
|
1607
1611
|
# Subtype (PartType:, AnnotType:, ToolType:)
|
|
@@ -1634,7 +1638,7 @@ class Session:
|
|
|
1634
1638
|
if (classname_lookup is not None) and (subtype in classname_lookup):
|
|
1635
1639
|
classname = classname_lookup[subtype]
|
|
1636
1640
|
subclass_info = f",attr_id={attr_id}, attr_value={subtype}"
|
|
1637
|
-
elif classname_lookup is not None:
|
|
1641
|
+
elif classname_lookup is not None: # pragma: no cover
|
|
1638
1642
|
# if a "subclass" case and no subclass attrid value, ask for it...
|
|
1639
1643
|
remote_name = self.remote_obj(objid)
|
|
1640
1644
|
cmd = f"{remote_name}.getattr({attr_id})"
|
|
@@ -1645,8 +1649,8 @@ class Session:
|
|
|
1645
1649
|
if owned_flag:
|
|
1646
1650
|
subclass_info += ",owned=True"
|
|
1647
1651
|
replace_text = f"session.ensight.objs.{classname}(session, {objid}{subclass_info})"
|
|
1648
|
-
if replace_text is None:
|
|
1649
|
-
break
|
|
1652
|
+
if replace_text is None: # pragma: no cover
|
|
1653
|
+
break # pragma: no cover
|
|
1650
1654
|
offset = start + len(replace_text)
|
|
1651
1655
|
s = prefix + replace_text + suffix
|
|
1652
1656
|
s = s.strip()
|
|
@@ -1752,7 +1756,7 @@ class Session:
|
|
|
1752
1756
|
ens_version = int(self.ensight.version("suffix"))
|
|
1753
1757
|
# handle various input formats
|
|
1754
1758
|
target = version
|
|
1755
|
-
if isinstance(target, str):
|
|
1759
|
+
if isinstance(target, str): # pragma: no cover
|
|
1756
1760
|
# could be 'year RX' or the suffix as a string
|
|
1757
1761
|
if "R" in target:
|
|
1758
1762
|
tmp = [int(x) for x in target.split("R")]
|
|
@@ -1762,14 +1766,14 @@ class Session:
|
|
|
1762
1766
|
# check validity
|
|
1763
1767
|
valid = ens_version == target
|
|
1764
1768
|
at_least = ""
|
|
1765
|
-
if not strict:
|
|
1769
|
+
if not strict: # pragma: no cover
|
|
1766
1770
|
at_least = "at least "
|
|
1767
1771
|
valid = ens_version >= target
|
|
1768
1772
|
if (not valid) and exception:
|
|
1769
1773
|
ens_version = self.ensight.version("version-full")
|
|
1770
1774
|
base_msg = f" ({at_least}'{version}' required, '{ens_version}' current)"
|
|
1771
|
-
if message:
|
|
1772
|
-
message += base_msg
|
|
1775
|
+
if message: # pragma: no cover
|
|
1776
|
+
message += base_msg # pragma: no cover
|
|
1773
1777
|
else:
|
|
1774
1778
|
message = f"A newer version of EnSight is required to use this API:{base_msg}"
|
|
1775
1779
|
raise InvalidEnSightVersion(message)
|
|
@@ -53,10 +53,10 @@ class Export:
|
|
|
53
53
|
return # pragma: no cover
|
|
54
54
|
try:
|
|
55
55
|
_ = self._ensight._session.cmd("dir(ensight.utils.export)")
|
|
56
|
-
except RuntimeError:
|
|
57
|
-
import ansys.pyensight.core
|
|
56
|
+
except RuntimeError: # pragma: no cover
|
|
57
|
+
import ansys.pyensight.core # pragma: no cover
|
|
58
58
|
|
|
59
|
-
raise RuntimeError(
|
|
59
|
+
raise RuntimeError( # pragma: no cover
|
|
60
60
|
f"Remote EnSight session must have PyEnsight version \
|
|
61
61
|
{ansys.pyensight.core.DEFAULT_ANSYS_VERSION} or higher installed to use this API."
|
|
62
62
|
)
|
|
@@ -344,8 +344,8 @@ class Export:
|
|
|
344
344
|
else:
|
|
345
345
|
num_frames = frames
|
|
346
346
|
|
|
347
|
-
if num_frames < 1:
|
|
348
|
-
raise RuntimeError(
|
|
347
|
+
if num_frames < 1: # pragma: no cover
|
|
348
|
+
raise RuntimeError( # pragma: no cover
|
|
349
349
|
"No frames selected. Perhaps a static dataset SOLUTIONTIME request \
|
|
350
350
|
or no FLIPBOOK/KEYFRAME defined."
|
|
351
351
|
)
|
|
@@ -478,7 +478,7 @@ class Export:
|
|
|
478
478
|
GEOM_EXPORT_STL: ".stl",
|
|
479
479
|
}
|
|
480
480
|
|
|
481
|
-
def _geometry_remote(
|
|
481
|
+
def _geometry_remote( # pragma: no cover
|
|
482
482
|
self, format: str, starting_timestep: int, frames: int, delta_timestep: int
|
|
483
483
|
) -> List[bytes]:
|
|
484
484
|
"""EnSight-side implementation.
|
|
@@ -570,7 +570,7 @@ class Export:
|
|
|
570
570
|
self._ensight._session.ensight_version_check("2024 R2")
|
|
571
571
|
cmd = f"ensight.utils.export._geometry_remote('{format}', {starting_timestep}, {frames}, {delta_timestep})"
|
|
572
572
|
raw_data_list = self._ensight._session.cmd(cmd)
|
|
573
|
-
if raw_data_list:
|
|
573
|
+
if raw_data_list: # pragma: no cover
|
|
574
574
|
if len(raw_data_list) == 1:
|
|
575
575
|
with open(filename, "wb") as fp:
|
|
576
576
|
fp.write(raw_data_list[0])
|
|
@@ -580,5 +580,5 @@ class Export:
|
|
|
580
580
|
_filename = f"{filename_base}{str(idx).zfill(3)}{extension}"
|
|
581
581
|
with open(_filename, "wb") as fp:
|
|
582
582
|
fp.write(raw_data)
|
|
583
|
-
else:
|
|
584
|
-
raise IOError("Export was not successful")
|
|
583
|
+
else: # pragma: no cover
|
|
584
|
+
raise IOError("Export was not successful") # pragma: no cover
|
|
@@ -2,7 +2,7 @@ import os
|
|
|
2
2
|
import subprocess
|
|
3
3
|
import sys
|
|
4
4
|
from types import ModuleType
|
|
5
|
-
from typing import TYPE_CHECKING, Optional, Union
|
|
5
|
+
from typing import TYPE_CHECKING, List, Optional, Union
|
|
6
6
|
|
|
7
7
|
import psutil
|
|
8
8
|
|
|
@@ -17,7 +17,7 @@ class Omniverse:
|
|
|
17
17
|
"""Provides the ``ensight.utils.omniverse`` interface.
|
|
18
18
|
|
|
19
19
|
The omniverse class methods provide an interface between an EnSight session
|
|
20
|
-
and an Omniverse instance.
|
|
20
|
+
and an Omniverse instance. See :ref:`omniverse_info` for additional details.
|
|
21
21
|
|
|
22
22
|
Note
|
|
23
23
|
----
|
|
@@ -48,26 +48,60 @@ class Omniverse:
|
|
|
48
48
|
def __init__(self, interface: Union["ensight_api.ensight", "ensight"]):
|
|
49
49
|
self._ensight = interface
|
|
50
50
|
self._server_pid: Optional[int] = None
|
|
51
|
+
self._interpreter: List[str] = []
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
def _check_modules() -> None:
|
|
53
|
+
def _check_modules(self) -> None:
|
|
54
54
|
"""Verify that the Python interpreter is correct
|
|
55
55
|
|
|
56
|
-
Check for omni
|
|
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.
|
|
57
60
|
|
|
58
61
|
Raises
|
|
59
62
|
------
|
|
60
63
|
RuntimeError if the necessary modules are missing.
|
|
61
64
|
|
|
62
65
|
"""
|
|
66
|
+
# One time check for this
|
|
67
|
+
if len(self._interpreter):
|
|
68
|
+
return
|
|
63
69
|
try:
|
|
64
70
|
# Note: the EnSight embedded interpreter will not have these
|
|
65
|
-
import omni # noqa: F401
|
|
66
|
-
|
|
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
|
|
67
85
|
except ImportError:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
|
71
105
|
|
|
72
106
|
def _is_running_omniverse(self) -> bool:
|
|
73
107
|
"""Check that an Omniverse connection is active
|
|
@@ -88,7 +122,6 @@ class Omniverse:
|
|
|
88
122
|
include_camera: bool = False,
|
|
89
123
|
normalize_geometry: bool = False,
|
|
90
124
|
live: bool = True,
|
|
91
|
-
temporal: bool = False,
|
|
92
125
|
debug_filename: str = "",
|
|
93
126
|
) -> None:
|
|
94
127
|
"""Ensure that an EnSight dsg -> omniverse server is running
|
|
@@ -103,21 +136,18 @@ class Omniverse:
|
|
|
103
136
|
omniverse_path : str
|
|
104
137
|
The URI to the Omniverse server. It will look like this:
|
|
105
138
|
"omniverse://localhost/Users/test"
|
|
106
|
-
include_camera: bool
|
|
139
|
+
include_camera : bool
|
|
107
140
|
If True, apply the EnSight camera to the Omniverse scene. This option
|
|
108
141
|
should be used if the target viewer is in AR/VR mode. Defaults to False.
|
|
109
|
-
normalize_geometry: bool
|
|
142
|
+
normalize_geometry : bool
|
|
110
143
|
Omniverse units are in meters. If the source dataset is not in the correct
|
|
111
144
|
unit system or is just too large/small, this option will remap the geometry
|
|
112
145
|
to a unit cube. Defaults to False.
|
|
113
|
-
live: bool
|
|
146
|
+
live : bool
|
|
114
147
|
If True, one can call 'update()' to send updated geometry to Omniverse.
|
|
115
148
|
If False, the Omniverse connection will push a single update and then
|
|
116
149
|
disconnect. Defaults to True.
|
|
117
|
-
|
|
118
|
-
If True, all EnSight timesteps will be pushed to Omniverse. Defaults to False, only
|
|
119
|
-
the current timestep is pushed.
|
|
120
|
-
debug_filename: str
|
|
150
|
+
debug_filename : str
|
|
121
151
|
If the name of a file is provided, it will be used to save logging information on
|
|
122
152
|
the connection between EnSight and Omniverse.
|
|
123
153
|
|
|
@@ -136,7 +166,6 @@ class Omniverse:
|
|
|
136
166
|
script_name = "omniverse_dsg_server.py"
|
|
137
167
|
working_dir = os.path.dirname(__file__)
|
|
138
168
|
cmd = [
|
|
139
|
-
sys.executable,
|
|
140
169
|
script_name,
|
|
141
170
|
"--host",
|
|
142
171
|
hostname,
|
|
@@ -151,17 +180,23 @@ class Omniverse:
|
|
|
151
180
|
cmd.extend(["--vrmode"])
|
|
152
181
|
if token:
|
|
153
182
|
cmd.extend(["--security", token])
|
|
154
|
-
if temporal:
|
|
155
|
-
|
|
156
|
-
else:
|
|
157
|
-
|
|
183
|
+
# if temporal:
|
|
184
|
+
# cmd.extend(["--animation"])
|
|
185
|
+
# else:
|
|
186
|
+
# cmd.extend(["--no-animation"])
|
|
158
187
|
if debug_filename:
|
|
159
188
|
cmd.extend(["--log_file", debug_filename])
|
|
160
189
|
cmd.extend(["--verbose", "1"])
|
|
161
190
|
if normalize_geometry:
|
|
162
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)
|
|
163
198
|
env_vars = os.environ.copy()
|
|
164
|
-
process = subprocess.Popen(
|
|
199
|
+
process = subprocess.Popen(cmdline, close_fds=True, env=env_vars, cwd=working_dir)
|
|
165
200
|
self._server_pid = process.pid
|
|
166
201
|
|
|
167
202
|
def close_connection(self) -> None:
|
|
@@ -63,6 +63,8 @@ class OmniverseWrapper:
|
|
|
63
63
|
path: str = "omniverse://localhost/Users/test",
|
|
64
64
|
verbose: int = 0,
|
|
65
65
|
):
|
|
66
|
+
self._cleaned_index = 0
|
|
67
|
+
self._cleaned_names: dict = {}
|
|
66
68
|
self._connectionStatusSubscription = None
|
|
67
69
|
self._stage = None
|
|
68
70
|
self._destinationPath = path
|
|
@@ -299,17 +301,52 @@ class OmniverseWrapper:
|
|
|
299
301
|
self.save_stage()
|
|
300
302
|
return boxPrim
|
|
301
303
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
304
|
+
def clear_cleaned_names(self) -> None:
|
|
305
|
+
"""Clear the list of cleaned names"""
|
|
306
|
+
self._cleaned_names = {}
|
|
307
|
+
self._cleaned_index = 0
|
|
308
|
+
|
|
309
|
+
def clean_name(self, name: str, id_name: Any = None) -> str:
|
|
310
|
+
"""Generate a vais USD name
|
|
311
|
+
|
|
312
|
+
From a base (EnSight) varname, partname, etc. and the DSG id, generate
|
|
313
|
+
a unique, valid USD name. Save the names so that if the same name
|
|
314
|
+
comes in again, the previously computed name is returned and if the
|
|
315
|
+
manipulation results in a conflict, the name can be made unique.
|
|
316
|
+
|
|
317
|
+
Parameters
|
|
318
|
+
----------
|
|
319
|
+
name:
|
|
320
|
+
The name to generate a USD name for.
|
|
321
|
+
|
|
322
|
+
id_name:
|
|
323
|
+
The DSG id associated with the DSG name, if any.
|
|
324
|
+
|
|
325
|
+
Returns
|
|
326
|
+
-------
|
|
327
|
+
A unique USD name.
|
|
328
|
+
"""
|
|
329
|
+
# return any previously generated name
|
|
330
|
+
if (name, id_name) in self._cleaned_names:
|
|
331
|
+
return self._cleaned_names[(name, id_name)]
|
|
332
|
+
# replace invalid characters
|
|
333
|
+
name = name.replace("+", "_").replace("-", "_")
|
|
305
334
|
name = name.replace(".", "_").replace(":", "_")
|
|
306
335
|
name = name.replace("[", "_").replace("]", "_")
|
|
307
336
|
name = name.replace("(", "_").replace(")", "_")
|
|
308
337
|
name = name.replace("<", "_").replace(">", "_")
|
|
309
338
|
name = name.replace("/", "_").replace("=", "_")
|
|
310
|
-
name = name.replace(",", "_")
|
|
311
|
-
|
|
339
|
+
name = name.replace(",", "_").replace(" ", "_")
|
|
340
|
+
name = name.replace("\\", "_")
|
|
341
|
+
if id_name is not None:
|
|
312
342
|
name = name + "_" + str(id_name)
|
|
343
|
+
if name in self._cleaned_names.values():
|
|
344
|
+
# Make the name unique
|
|
345
|
+
while f"{name}_{self._cleaned_index}" in self._cleaned_names.values():
|
|
346
|
+
self._cleaned_index += 1
|
|
347
|
+
name = f"{name}_{self._cleaned_index}"
|
|
348
|
+
# store off the cleaned name
|
|
349
|
+
self._cleaned_names[(name, id_name)] = name
|
|
313
350
|
return name
|
|
314
351
|
|
|
315
352
|
@staticmethod
|
|
@@ -1011,6 +1048,7 @@ class DSGOmniverseLink(object):
|
|
|
1011
1048
|
self._part = Part(self)
|
|
1012
1049
|
self._scene_bounds = None
|
|
1013
1050
|
self._mesh_block_count = 0 # reset when a new group shows up
|
|
1051
|
+
self._omni.clear_cleaned_names()
|
|
1014
1052
|
|
|
1015
1053
|
# handle the various commands until UPDATE_SCENE_END
|
|
1016
1054
|
cmd = self.get_next_message()
|