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.

@@ -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(str(item_value), str(check_value)):
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
- html = f"<script src='/ansys{version}/nexus/viewer-loader.js{optional_query}'></script>\n"
553
- rest_uri = (
554
- f"{self._http_protocol}://{self._session.html_hostname}:{self._session.html_port}"
555
- )
556
- ws_uri = f"{self._http_protocol}://{self._session.html_hostname}:{self._session.ws_port}"
557
-
558
- query_args = ""
559
- if self._using_proxy and optional_query:
560
- query_args = f', "extra_query_args":"{optional_query[1:]}"'
561
-
562
- attributes = ' renderer="envnc"'
563
- attributes += ' ui="simple"'
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) < 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
- html += f"<ansys-nexus-viewer {attributes}></ansys-nexus-viewer>\n"
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
- # refresh the remote HTML
574
- self._save_remote_html_page(html)
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)
@@ -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(f"copy_write_function__(r'{name}', {data!r})", do_eval=False)
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(ens, *args, **kwargs) # noqa: E731
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
- @staticmethod
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 and pxr modules. If not present, raise an exception.
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
- import pxr # noqa: F401
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
- raise RuntimeError(
69
- "The module requires the omni and pxr modules to be installed."
70
- ) from None
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
- temporal: bool
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
- cmd.extend(["--animation"])
156
- else:
157
- cmd.extend(["--no-animation"])
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(cmd, close_fds=True, env=env_vars, cwd=working_dir)
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
- @staticmethod
303
- def clean_name(name: str, id_name: Any = None) -> str:
304
- name = name.replace(" ", "_").replace("-", "_")
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
- if id is not None:
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()