ansys-pyensight-core 0.8.11__tar.gz → 0.8.12__tar.gz

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.

Files changed (37) hide show
  1. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/PKG-INFO +10 -8
  2. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/pyproject.toml +10 -8
  3. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/deep_pixel_view.html +10 -10
  4. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/libuserd.py +45 -37
  5. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/locallauncher.py +2 -0
  6. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/renderable.py +12 -3
  7. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/utils/dsg_server.py +27 -5
  8. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/utils/omniverse_cli.py +33 -24
  9. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/utils/omniverse_dsg_server.py +3 -1
  10. ansys_pyensight_core-0.8.12/src/ansys/pyensight/core/utils/omniverse_glb_server.py +625 -0
  11. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/utils/parts.py +1 -1
  12. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/utils/variables.py +1 -1
  13. ansys_pyensight_core-0.8.11/src/ansys/pyensight/core/utils/omniverse_glb_server.py +0 -279
  14. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/LICENSE +0 -0
  15. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/README.rst +0 -0
  16. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/__init__.py +0 -0
  17. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/common.py +0 -0
  18. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/dockerlauncher.py +0 -0
  19. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/enscontext.py +0 -0
  20. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/enshell_grpc.py +0 -0
  21. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/ensight_grpc.py +0 -0
  22. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/ensobj.py +0 -0
  23. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/launch_ensight.py +0 -0
  24. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/launcher.py +0 -0
  25. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/listobj.py +0 -0
  26. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/py.typed +0 -0
  27. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/session.py +0 -0
  28. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/sgeo_poll.html +0 -0
  29. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/utils/__init__.py +0 -0
  30. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/utils/adr.py +0 -0
  31. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/utils/export.py +0 -0
  32. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/utils/omniverse.py +0 -0
  33. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/utils/query.py +0 -0
  34. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/utils/readers.py +0 -0
  35. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/utils/resources/Materials/000_sky.exr +0 -0
  36. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/utils/support.py +0 -0
  37. {ansys_pyensight_core-0.8.11 → ansys_pyensight_core-0.8.12}/src/ansys/pyensight/core/utils/views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ansys-pyensight-core
3
- Version: 0.8.11
3
+ Version: 0.8.12
4
4
  Summary: A python wrapper for Ansys EnSight
5
5
  Author-email: "ANSYS, Inc." <pyansys.core@ansys.com>
6
6
  Maintainer-email: "ANSYS, Inc." <pyansys.core@ansys.com>
@@ -32,27 +32,29 @@ Requires-Dist: ipdb>=0.9.4 ; extra == "dev"
32
32
  Requires-Dist: dill>=0.3.5.1 ; extra == "dev"
33
33
  Requires-Dist: pre-commit>=3.3.3 ; extra == "dev"
34
34
  Requires-Dist: Sphinx==8.0.2 ; extra == "doc"
35
- Requires-Dist: numpydoc==1.5.0 ; extra == "doc"
36
- Requires-Dist: ansys-sphinx-theme==0.9.9 ; extra == "doc"
35
+ Requires-Dist: numpydoc==1.8.0 ; extra == "doc"
36
+ Requires-Dist: ansys-sphinx-theme==1.1.1 ; extra == "doc"
37
37
  Requires-Dist: sphinx-copybutton==0.5.2 ; extra == "doc"
38
- Requires-Dist: sphinx-gallery==0.13.0 ; extra == "doc"
38
+ Requires-Dist: sphinx-gallery==0.17.1 ; extra == "doc"
39
39
  Requires-Dist: sphinxcontrib-mermaid==0.9.2 ; extra == "doc"
40
40
  Requires-Dist: docker>=6.1.0 ; extra == "doc"
41
41
  Requires-Dist: matplotlib==3.9.1.post1 ; extra == "doc"
42
42
  Requires-Dist: requests>=2.28.2 ; extra == "doc"
43
43
  Requires-Dist: sphinxcontrib.jquery==4.1 ; extra == "doc"
44
- Requires-Dist: coverage-badge==1.1.0 ; extra == "doc"
45
- Requires-Dist: sphinxcontrib-openapi==0.8.1 ; extra == "doc"
46
- Requires-Dist: sphinxcontrib-video==0.2.0 ; extra == "doc"
44
+ Requires-Dist: sphinxcontrib-openapi==0.8.4 ; extra == "doc"
45
+ Requires-Dist: coverage-badge==1.1.2 ; extra == "doc"
46
+ Requires-Dist: sphinxcontrib-video==0.2.1 ; extra == "doc"
47
47
  Requires-Dist: usd-core>=24.8 ; extra == "doc"
48
48
  Requires-Dist: pygltflib>=1.16.2 ; extra == "doc"
49
49
  Requires-Dist: pytest==8.3.2 ; extra == "tests"
50
- Requires-Dist: pytest-cov==4.1.0 ; extra == "tests"
50
+ Requires-Dist: pytest-cov==5.0.0 ; extra == "tests"
51
51
  Requires-Dist: dill>=0.3.5.1 ; extra == "tests"
52
52
  Requires-Dist: pytest-mock==3.10.0 ; extra == "tests"
53
53
  Requires-Dist: urllib3==2.2.2 ; extra == "tests"
54
54
  Requires-Dist: requests>=2.28.2 ; extra == "tests"
55
55
  Requires-Dist: docker>=6.1.0 ; extra == "tests"
56
+ Requires-Dist: pytest-xdist==1.31.0 ; extra == "tests"
57
+ Requires-Dist: pytest-rerunfailures~=14.0 ; extra == "tests"
56
58
  Project-URL: Changelog, https://github.com/ansys/pyensight/blob/main/CHANGELOG.rst
57
59
  Project-URL: Documentation, https://ensight.docs.pyansys.com/
58
60
  Project-URL: Homepage, https://github.com/ansys/pyensight
@@ -6,7 +6,7 @@ build-backend = "flit_core.buildapi"
6
6
 
7
7
  [project]
8
8
  name = "ansys-pyensight-core"
9
- version = "0.8.11"
9
+ version = "0.8.12"
10
10
  description = "A python wrapper for Ansys EnSight"
11
11
  readme = "README.rst"
12
12
  requires-python = ">=3.9,<4"
@@ -49,27 +49,29 @@ dev = [
49
49
  ]
50
50
  tests = [
51
51
  "pytest==8.3.2",
52
- "pytest-cov==4.1.0",
52
+ "pytest-cov==5.0.0",
53
53
  "dill>=0.3.5.1",
54
54
  "pytest-mock==3.10.0",
55
55
  "urllib3==2.2.2",
56
56
  "requests>=2.28.2",
57
57
  "docker>=6.1.0",
58
+ "pytest-xdist==1.31.0",
59
+ "pytest-rerunfailures~=14.0",
58
60
  ]
59
61
  doc = [
60
62
  "Sphinx==8.0.2",
61
- "numpydoc==1.5.0",
62
- "ansys-sphinx-theme==0.9.9",
63
+ "numpydoc==1.8.0",
64
+ "ansys-sphinx-theme==1.1.1",
63
65
  "sphinx-copybutton==0.5.2",
64
- "sphinx-gallery==0.13.0",
66
+ "sphinx-gallery==0.17.1",
65
67
  "sphinxcontrib-mermaid==0.9.2",
66
68
  "docker>=6.1.0",
67
69
  "matplotlib==3.9.1.post1",
68
70
  "requests>=2.28.2",
69
71
  "sphinxcontrib.jquery==4.1",
70
- "coverage-badge==1.1.0",
71
- "sphinxcontrib-openapi==0.8.1",
72
- "sphinxcontrib-video==0.2.0",
72
+ "sphinxcontrib-openapi==0.8.4",
73
+ "coverage-badge==1.1.2",
74
+ "sphinxcontrib-video==0.2.1",
73
75
  "usd-core>=24.8",
74
76
  "pygltflib>=1.16.2",
75
77
  ]
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <link rel="stylesheet" type="text/css" href="/bootstrap.min.cssOPTIONAL_QUERY"/>
5
5
 
6
- <script src='/jquery-3.4.1.min.jsOPTIONAL_QUERY'></script>
6
+ <script src='/jqueryJQUERY_VERSION.min.jsOPTIONAL_QUERY'></script>
7
7
  <script src='/geotiff.jsOPTIONAL_QUERY'></script>
8
8
  <script src='/geotiff_nexus.jsOPTIONAL_QUERY'></script>
9
9
  <script src="/bootstrap.min.jsOPTIONAL_QUERY"></script>
@@ -76,15 +76,15 @@
76
76
  <div style="margin: 0 auto; display:flex; justify-content:center;">
77
77
  <!-- tooltip parent for img --->
78
78
  <div id="probe_display_ITEMID"
79
- data-toggle="tooltip"
80
- data-placement="bottom"
81
- data-fallbackPlacement="['top', 'right']"
82
- data-html="true"
83
- data-container="body"
84
- data-boundary="viewport"
85
- data-animation="false"
86
- data-trigger="manual"
87
- data-title='<span class="f-1r">
79
+ data-BS_PREFIXtoggle="tooltip"
80
+ data-BS_PREFIXplacement="bottom"
81
+ data-BS_PREFIXfallbackPlacement="['top', 'right']"
82
+ data-BS_PREFIXhtml="true"
83
+ data-BS_PREFIXcontainer="body"
84
+ data-BS_PREFIXboundary="viewport"
85
+ data-BS_PREFIXanimation="false"
86
+ data-BS_PREFIXtrigger="manual"
87
+ data-BS_PREFIXtitle='<span class="f-1r">
88
88
  <span>X, Y : <span id="probe_xy_ITEMID">0, 0</span></span>
89
89
  <br>
90
90
  <span id="probe_result_ITEMID"></span>
@@ -63,6 +63,22 @@ warnings.warn(
63
63
  )
64
64
 
65
65
 
66
+ def _build_enum(name: str, pb_enum: Any, flag: bool = False) -> Union[enum.IntEnum, enum.IntFlag]:
67
+ values = {}
68
+ for v in pb_enum:
69
+ values[v[0]] = v[1]
70
+ if flag:
71
+ return enum.IntFlag(name, values)
72
+ return enum.IntEnum(name, values)
73
+
74
+
75
+ ErrorCodes = _build_enum("ErrorCodes", libuserd_pb2.ErrorCodes.items())
76
+ ElementType = _build_enum("ElementType", libuserd_pb2.ElementType.items())
77
+ VariableLocation = _build_enum("VariableLocation", libuserd_pb2.VariableLocation.items())
78
+ VariableType = _build_enum("VariableType", libuserd_pb2.VariableType.items())
79
+ PartHints = _build_enum("PartHints", libuserd_pb2.PartHints.items(), flag=True)
80
+
81
+
66
82
  class LibUserdError(Exception):
67
83
  """
68
84
  This class is an exception object raised from the libuserd
@@ -179,9 +195,9 @@ class Variable(object):
179
195
  The unit label of this variable, "Pa" for example.
180
196
  unitDims : str
181
197
  The dimensions of this variable, "L/S" for distance per second.
182
- location : "LibUserd.LocationType"
198
+ location : "VariableLocation"
183
199
  The location of this variable.
184
- type : "LibUserd.VariableType"
200
+ type : "VariableType"
185
201
  The type of this variable.
186
202
  timeVarying : bool
187
203
  True if the variable is time-varying.
@@ -200,8 +216,8 @@ class Variable(object):
200
216
  self.name = pb.name
201
217
  self.unitLabel = pb.unitLabel
202
218
  self.unitDims = pb.unitDims
203
- self.location = self._userd.VariableLocation(pb.varLocation)
204
- self.type = self._userd.VariableType(pb.type)
219
+ self.location = VariableLocation(pb.varLocation) # type: ignore
220
+ self.type = VariableType(pb.type) # type: ignore
205
221
  self.timeVarying = pb.timeVarying
206
222
  self.isComplex = pb.isComplex
207
223
  self.interleaveFlag = pb.interleaveFlag
@@ -240,7 +256,7 @@ class Part(object):
240
256
  reader_id : int
241
257
  The id of the Reader this part is associated with.
242
258
  hints : int
243
- See: `LibUserd.PartHints`.
259
+ See: `PartHints`.
244
260
  reader_api_version : float
245
261
  The API version number of the USERD reader this part was read with.
246
262
  metadata : Dict[str, str]
@@ -315,7 +331,7 @@ class Part(object):
315
331
  >>> part = reader.parts()[0]
316
332
  >>> elements = part.elements()
317
333
  >>> for etype, count in elements.items():
318
- ... print(libuserd_instance.ElementType(etype).name, count)
334
+ ... print(libuserd.ElementType(etype).name, count)
319
335
 
320
336
  """
321
337
  self._userd.connect_check()
@@ -350,14 +366,14 @@ class Part(object):
350
366
  --------
351
367
 
352
368
  >>> part = reader.parts()[0]
353
- >>> conn = part.element_conn(libuserd_instance.ElementType.HEX08)
354
- >>> nodes_per_elem = libuserd_instance.nodes_per_element(libuserd_instance.ElementType.HEX08)
369
+ >>> conn = part.element_conn(libuserd.ElementType.HEX08)
370
+ >>> nodes_per_elem = libuserd_instance.nodes_per_element(libuserd.ElementType.HEX08)
355
371
  >>> conn.shape = (len(conn)//nodes_per_elem, nodes_per_elem)
356
372
  >>> for element in conn:
357
373
  ... print(element)
358
374
 
359
375
  """
360
- if elem_type >= self._userd.ElementType.NSIDED:
376
+ if elem_type >= ElementType.NSIDED: # type: ignore
361
377
  raise RuntimeError(f"Element type {elem_type} is not valid for this call")
362
378
  pb = libuserd_pb2.Part_element_connRequest()
363
379
  pb.part_id = self.id
@@ -375,7 +391,7 @@ class Part(object):
375
391
  error = self._userd.libuserd_exception(e)
376
392
  # if we get an "UNKNOWN" error, then return an empty array
377
393
  if isinstance(error, LibUserdError):
378
- if error.code == self._userd.ErrorCodes.UNKNOWN: # type: ignore
394
+ if error.code == ErrorCodes.UNKNOWN: # type: ignore
379
395
  return numpy.empty(0, dtype=numpy.uint32)
380
396
  raise error
381
397
  return conn
@@ -1147,27 +1163,12 @@ class LibUserd(object):
1147
1163
  raise RuntimeError(f"Unable to start the gRPC server ({str(self.server_pathname)})")
1148
1164
 
1149
1165
  def _build_enums(self) -> None:
1150
- """Build the enums values."""
1151
- values = {}
1152
- for v in libuserd_pb2.ErrorCodes.items():
1153
- values[v[0]] = v[1]
1154
- self.ErrorCodes = enum.IntEnum("ErrorCodes", values) # type: ignore
1155
- values = {}
1156
- for v in libuserd_pb2.ElementType.items():
1157
- values[v[0]] = v[1]
1158
- self.ElementType = enum.IntEnum("ElementType", values) # type: ignore
1159
- values = {}
1160
- for v in libuserd_pb2.VariableLocation.items():
1161
- values[v[0]] = v[1]
1162
- self.VariableLocation = enum.IntEnum("VariableLocation", values) # type: ignore
1163
- values = {}
1164
- for v in libuserd_pb2.VariableType.items():
1165
- values[v[0]] = v[1]
1166
- self.VariableType = enum.IntEnum("VariableType", values) # type: ignore
1167
- values = {}
1168
- for v in libuserd_pb2.PartHints.items():
1169
- values[v[0]] = v[1]
1170
- self.PartHints = enum.IntEnum("PartHints", values) # type: ignore
1166
+ # retained for backward compatibility
1167
+ self.ErrorCodes = ErrorCodes
1168
+ self.ElementType = ElementType
1169
+ self.VariableLocation = VariableLocation
1170
+ self.VariableType = VariableType
1171
+ self.PartHints = PartHints
1171
1172
 
1172
1173
  def _pull_docker_image(self) -> None:
1173
1174
  """Pull the docker image if not available"""
@@ -1329,6 +1330,13 @@ class LibUserd(object):
1329
1330
  # ways, we'll add that one too, just in case.
1330
1331
  dirs_to_check.append(os.path.join(env_inst, "CEI"))
1331
1332
 
1333
+ try:
1334
+ import enve
1335
+
1336
+ dirs_to_check.append(enve.home())
1337
+ except ModuleNotFoundError:
1338
+ pass
1339
+
1332
1340
  if "CEI_HOME" in os.environ:
1333
1341
  env_inst = os.environ["CEI_HOME"]
1334
1342
  dirs_to_check.append(env_inst)
@@ -1577,7 +1585,7 @@ class LibUserd(object):
1577
1585
  Parameters
1578
1586
  ----------
1579
1587
  element_type
1580
- The element type: LibUserd.ElementType enum value
1588
+ The element type: ElementType enum value
1581
1589
 
1582
1590
  Returns
1583
1591
  -------
@@ -1602,7 +1610,7 @@ class LibUserd(object):
1602
1610
  Parameters
1603
1611
  ----------
1604
1612
  element_type
1605
- The element type: LibUserd.ElementType enum value
1613
+ The element type: ElementType enum value
1606
1614
 
1607
1615
  Returns
1608
1616
  -------
@@ -1625,7 +1633,7 @@ class LibUserd(object):
1625
1633
  Parameters
1626
1634
  ----------
1627
1635
  element_type
1628
- The element type: LibUserd.ElementType enum value
1636
+ The element type: ElementType enum value
1629
1637
 
1630
1638
  Returns
1631
1639
  -------
@@ -1648,7 +1656,7 @@ class LibUserd(object):
1648
1656
  Parameters
1649
1657
  ----------
1650
1658
  element_type
1651
- The element type: LibUserd.ElementType enum value
1659
+ The element type: ElementType enum value
1652
1660
 
1653
1661
  Returns
1654
1662
  -------
@@ -1671,7 +1679,7 @@ class LibUserd(object):
1671
1679
  Parameters
1672
1680
  ----------
1673
1681
  element_type
1674
- The element type: LibUserd.ElementType enum value
1682
+ The element type: ElementType enum value
1675
1683
 
1676
1684
  Returns
1677
1685
  -------
@@ -1693,7 +1701,7 @@ class LibUserd(object):
1693
1701
  Part.element_conn() method. This function returns the number of those elements
1694
1702
  and may be useful in common element type handling code.
1695
1703
 
1696
- Note: The value is effectively int(LibUserd.ElementType.NSIDED).
1704
+ Note: The value is effectively int(ElementType.NSIDED).
1697
1705
 
1698
1706
  Returns
1699
1707
  -------
@@ -314,6 +314,8 @@ class LocalLauncher(Launcher):
314
314
  return
315
315
  except PermissionError:
316
316
  pass
317
+ except FileNotFoundError:
318
+ pass
317
319
  except Exception:
318
320
  raise
319
321
  raise RuntimeError(f"Unable to remove {self.session_directory} in {maximum_wait_secs}s")
@@ -389,10 +389,17 @@ class RenderableDeepPixel(Renderable):
389
389
  for script in ["geotiff.js", "geotiff_nexus.js", "bootstrap.min.js"]:
390
390
  name = base_name + f"'{script}')"
391
391
  cmd += f'shutil.copy({name}, r"""{self._session.launcher.session_directory}""")\n'
392
+ name = "os.path.join(enve.home(), f'nexus{ceiversion.nexus_suffix}', 'django', "
393
+ name += "'website', 'static', 'website', 'content', 'bootstrap.min.css')"
394
+ cmd += f'shutil.copy({name}, r"""{self._session.launcher.session_directory}""")\n'
395
+ self._session.cmd(cmd, do_eval=False)
396
+ # With Bootstrap 5 (2025 R1), class names have '-bs-' in them, e.g. 'data-bs-toggle' vs 'data-toggle'
397
+ bs_prefix = "bs-"
398
+ jquery_version = ""
392
399
  if int(self._session._cei_suffix) < 251:
393
- jquery = "jquery-3.4.1.min.js"
394
- else:
395
- jquery = "jquery.min.js"
400
+ bs_prefix = ""
401
+ jquery_version = "-3.4.1"
402
+ jquery = f"jquery{jquery_version}.min.js"
396
403
  cmd = "import shutil, enve, ceiversion, os.path\n"
397
404
  name = base_name + f"'{jquery}')"
398
405
  cmd += "try:"
@@ -409,6 +416,8 @@ class RenderableDeepPixel(Renderable):
409
416
  html = html.replace("TIFF_URL", tiff_url)
410
417
  html = html.replace("ITEMID", self._guid)
411
418
  html = html.replace("OPTIONAL_QUERY", optional_query)
419
+ html = html.replace("JQUERY_VERSION", jquery_version)
420
+ html = html.replace("BS_PREFIX", bs_prefix)
412
421
  # refresh the remote HTML
413
422
  self._save_remote_html_page(html)
414
423
  super().update()
@@ -101,6 +101,12 @@ class Part(object):
101
101
  self.tcoords[
102
102
  cmd.chunk_offset : cmd.chunk_offset + len(cmd.flt_array)
103
103
  ] = cmd.flt_array
104
+
105
+ # Add the variable hash to the Part's hash, to pick up palette changes
106
+ var_cmd = self.session.variables.get(cmd.variable_id, None)
107
+ if var_cmd is not None:
108
+ self.hash.update(var_cmd.hash.encode("utf-8"))
109
+
104
110
  if self.cmd.node_size_variableid == cmd.variable_id: # type: ignore
105
111
  # Receive the node size var values
106
112
  if self.node_sizes.size != cmd.total_array_size:
@@ -618,6 +624,15 @@ class DSGSession(object):
618
624
  """
619
625
  logging.warning(s)
620
626
 
627
+ @staticmethod
628
+ def error(s: str) -> None:
629
+ """Issue an error to the logging system
630
+
631
+ The logging message is mapped to "error" and cannot be blocked via verbosity
632
+ checks.
633
+ """
634
+ logging.error(s)
635
+
621
636
  def start(self) -> int:
622
637
  """Start a gRPC connection to an EnSight instance
623
638
 
@@ -741,6 +756,13 @@ class DSGSession(object):
741
756
  except queue.Empty:
742
757
  return None
743
758
 
759
+ def _reset(self):
760
+ self._variables = {}
761
+ self._groups = {}
762
+ self._part = Part(self)
763
+ self._scene_bounds = None
764
+ self._mesh_block_count = 0 # reset when a new group shows up
765
+
744
766
  def handle_one_update(self) -> None:
745
767
  """Monitor the DSG stream and handle a single update operation
746
768
 
@@ -759,11 +781,7 @@ class DSGSession(object):
759
781
  cmd = self._get_next_message()
760
782
 
761
783
  # Start anew
762
- self._variables = {}
763
- self._groups = {}
764
- self._part = Part(self)
765
- self._scene_bounds = None
766
- self._mesh_block_count = 0 # reset when a new group shows up
784
+ self._reset()
767
785
  self._callback_handler.begin_update()
768
786
 
769
787
  # Update our status
@@ -839,7 +857,11 @@ class DSGSession(object):
839
857
  try:
840
858
  self._callback_handler.finalize_part(self.part)
841
859
  except Exception as e:
860
+ import traceback
861
+
842
862
  self.warn(f"Error encountered while finalizing part geometry: {str(e)}")
863
+ traceback_str = "".join(traceback.format_tb(e.__traceback__))
864
+ logging.debug(f"Traceback: {traceback_str}")
843
865
  self._mesh_block_count += 1
844
866
 
845
867
  def _handle_part(self, part_cmd: Any) -> None:
@@ -276,17 +276,16 @@ class OmniverseGeometryServer(object):
276
276
  update_handler = ov_dsg_server.OmniverseUpdateHandler(omni_link)
277
277
 
278
278
  # Link it to the GLB file monitoring service
279
- glb_link = ov_glb_server.GLBSession(
280
- verbose=1,
281
- handler=update_handler,
282
- )
279
+ glb_link = ov_glb_server.GLBSession(verbose=1, handler=update_handler, vrmode=self.vrmode)
283
280
  if single_file_upload:
284
281
  start_time = time.time()
285
282
  logging.info(f"Uploading file: {the_dir}.")
286
283
  try:
284
+ glb_link.start_uploads([0.0, 0.0])
287
285
  glb_link.upload_file(the_dir)
286
+ glb_link.end_uploads()
288
287
  except Exception as error:
289
- logging.warning(f"Error uploading file: {the_dir}: {error}")
288
+ logging.error(f"Unable to upload file: {the_dir}: {error}")
290
289
  logging.info(f"Uploaded in {(time.time() - start_time):.2f}")
291
290
  else:
292
291
  logging.info(f"Starting file monitoring for {the_dir}.")
@@ -297,7 +296,7 @@ class OmniverseGeometryServer(object):
297
296
  while not os.path.exists(stop_file):
298
297
  loop_time = time.time()
299
298
  files_to_remove = []
300
- for filename in glob.glob(os.path.join(the_dir, "*", "*.upload")):
299
+ for filename in glob.glob(os.path.join(the_dir, "*.upload")):
301
300
  # reset to the launch URI/directory
302
301
  omni_link.destination = orig_destination
303
302
  # Keep track of the files and time values
@@ -317,7 +316,7 @@ class OmniverseGeometryServer(object):
317
316
  with open(filename, "r") as fp:
318
317
  glb_info = json.load(fp)
319
318
  except Exception:
320
- logging.warning(f"Error reading file: {filename}")
319
+ logging.error(f"Unable to read file: {filename}")
321
320
  continue
322
321
  # if specified, set the URI/directory target
323
322
  omni_link.destination = glb_info.get("destination", orig_destination)
@@ -326,28 +325,35 @@ class OmniverseGeometryServer(object):
326
325
  files_to_remove.extend(the_files)
327
326
  # Times not used for now, but parse them anyway
328
327
  the_times = glb_info.get("times", [0.0] * len(the_files))
328
+ file_timestamps.extend(the_times)
329
329
  # Validate a few things
330
330
  if len(the_files) != len(the_times):
331
- logging.warning(
331
+ logging.error(
332
332
  f"Number of times and files are not the same in: {filename}"
333
333
  )
334
334
  continue
335
- if len(set(the_times)) != 1:
336
- logging.warning("Time values not currently supported.")
337
- if len(the_files) > 1:
338
- logging.warning("Multiple glb files not currently fully supported.")
339
335
  files_to_process.extend(the_files)
340
- # Upload the files
341
- for glb_file in files_to_process:
342
- start_time = time.time()
343
- logging.info(
344
- f"Uploading file: {glb_file} to {omni_link.destination}."
345
- )
346
- try:
347
- glb_link.upload_file(glb_file)
348
- except Exception as error:
349
- logging.warning(f"Error uploading file: {glb_file}: {error}")
350
- logging.info(f"Uploaded in {(time.time() - start_time):%.2f}")
336
+ # manage time
337
+ timeline = sorted(set(file_timestamps))
338
+ if len(timeline) != 1:
339
+ logging.warning("Time values not currently supported.")
340
+ if len(files_to_process) > 1:
341
+ logging.warning("Multiple glb files not currently fully supported.")
342
+ # Upload the files
343
+ glb_link.start_uploads([timeline[0], timeline[-1]])
344
+ for glb_file, timestamp in zip(files_to_process, file_timestamps):
345
+ start_time = time.time()
346
+ logging.info(f"Uploading file: {glb_file} to {omni_link.destination}.")
347
+ try:
348
+ time_idx = timeline.index(timestamp) + 1
349
+ if time_idx == len(timeline):
350
+ time_idx -= 1
351
+ limits = [timestamp, timeline[time_idx]]
352
+ glb_link.upload_file(glb_file, timeline=limits)
353
+ except Exception as error:
354
+ logging.error(f"Unable to upload file: {glb_file}: {error}")
355
+ logging.info(f"Uploaded in {(time.time() - start_time):.2f}s")
356
+ glb_link.end_uploads()
351
357
  for filename in files_to_remove:
352
358
  try:
353
359
  # Only delete the file if it is in the_dir_path
@@ -361,7 +367,10 @@ class OmniverseGeometryServer(object):
361
367
  except Exception as error:
362
368
  logging.error(f"Error encountered while monitoring: {error}")
363
369
  logging.info("Stopping file monitoring.")
364
- os.remove(stop_file)
370
+ try:
371
+ os.remove(stop_file)
372
+ except IOError:
373
+ logging.error("Unable to remove 'shutdown' file.")
365
374
 
366
375
  omni_link.shutdown()
367
376
 
@@ -401,12 +401,14 @@ class OmniverseWrapper(object):
401
401
  self, stage, mesh, root_name, diffuse=[1.0, 1.0, 1.0, 1.0], variable=None
402
402
  ):
403
403
  # https://graphics.pixar.com/usd/release/spec_usdpreviewsurface.html
404
+ # Use ior==1.0 to be more like EnSight - rays of light do not bend when passing through transparent objs
404
405
  material = UsdShade.Material.Define(stage, root_name + "/Material")
405
406
  pbrShader = UsdShade.Shader.Define(stage, root_name + "/Material/PBRShader")
406
407
  pbrShader.CreateIdAttr("UsdPreviewSurface")
407
408
  pbrShader.CreateInput("roughness", Sdf.ValueTypeNames.Float).Set(1.0)
408
409
  pbrShader.CreateInput("metallic", Sdf.ValueTypeNames.Float).Set(0.0)
409
410
  pbrShader.CreateInput("opacity", Sdf.ValueTypeNames.Float).Set(diffuse[3])
411
+ pbrShader.CreateInput("ior", Sdf.ValueTypeNames.Float).Set(1.0)
410
412
  pbrShader.CreateInput("useSpecularWorkflow", Sdf.ValueTypeNames.Int).Set(1)
411
413
  if variable:
412
414
  stReader = UsdShade.Shader.Define(stage, root_name + "/Material/stReader")
@@ -416,7 +418,7 @@ class OmniverseWrapper(object):
416
418
  )
417
419
  diffuseTextureSampler.CreateIdAttr("UsdUVTexture")
418
420
  name = self.clean_name(variable.name)
419
- filename = self._destinationPath + f"/Parts/Textures/palette_{name}.png"
421
+ filename = f"./Textures/palette_{name}.png"
420
422
  diffuseTextureSampler.CreateInput("file", Sdf.ValueTypeNames.Asset).Set(filename)
421
423
  diffuseTextureSampler.CreateInput("st", Sdf.ValueTypeNames.Float2).ConnectToSource(
422
424
  stReader.ConnectableAPI(), "result"