ansys-pyensight-core 0.8.4__py3-none-any.whl → 0.8.6__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/exts/ansys.geometry.service/ansys/geometry/service/__init__.py +1 -0
- ansys/pyensight/core/exts/ansys.geometry.service/ansys/geometry/service/extension.py +407 -0
- ansys/pyensight/core/exts/ansys.geometry.service/config/extension.toml +59 -0
- ansys/pyensight/core/exts/ansys.geometry.service/data/icon.png +0 -0
- ansys/pyensight/core/exts/ansys.geometry.service/data/preview.png +0 -0
- ansys/pyensight/core/exts/ansys.geometry.service/docs/CHANGELOG.md +8 -0
- ansys/pyensight/core/exts/ansys.geometry.service/docs/README.md +13 -0
- ansys/pyensight/core/exts/ansys.geometry.service/docs/index.rst +18 -0
- ansys/pyensight/core/exts/ansys.geometry.serviceui/ansys/geometry/serviceui/__init__.py +1 -0
- ansys/pyensight/core/exts/ansys.geometry.serviceui/ansys/geometry/serviceui/extension.py +193 -0
- ansys/pyensight/core/exts/ansys.geometry.serviceui/config/extension.toml +49 -0
- ansys/pyensight/core/exts/ansys.geometry.serviceui/data/icon.png +0 -0
- ansys/pyensight/core/exts/ansys.geometry.serviceui/data/preview.png +0 -0
- ansys/pyensight/core/exts/ansys.geometry.serviceui/docs/CHANGELOG.md +8 -0
- ansys/pyensight/core/exts/ansys.geometry.serviceui/docs/README.md +13 -0
- ansys/pyensight/core/exts/ansys.geometry.serviceui/docs/index.rst +18 -0
- ansys/pyensight/core/launcher.py +36 -1
- ansys/pyensight/core/locallauncher.py +3 -1
- ansys/pyensight/core/utils/dsg_server.py +36 -11
- ansys/pyensight/core/utils/omniverse.py +151 -95
- ansys/pyensight/core/utils/omniverse_dsg_server.py +91 -37
- {ansys_pyensight_core-0.8.4.dist-info → ansys_pyensight_core-0.8.6.dist-info}/METADATA +1 -1
- ansys_pyensight_core-0.8.6.dist-info/RECORD +52 -0
- ansys_pyensight_core-0.8.4.dist-info/RECORD +0 -36
- {ansys_pyensight_core-0.8.4.dist-info → ansys_pyensight_core-0.8.6.dist-info}/LICENSE +0 -0
- {ansys_pyensight_core-0.8.4.dist-info → ansys_pyensight_core-0.8.6.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any, Optional
|
|
3
|
+
from urllib.parse import urlparse
|
|
4
|
+
|
|
5
|
+
import ansys.geometry.service
|
|
6
|
+
import omni.ext
|
|
7
|
+
import omni.ui as ui
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AnsysGeometryServiceUIExtension(omni.ext.IExt):
|
|
11
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
12
|
+
super().__init__(*args, **kwargs)
|
|
13
|
+
self._window: Any = None
|
|
14
|
+
self._label_w: Any = None
|
|
15
|
+
self._logger = logging.getLogger(__name__.rsplit(".", 1)[0])
|
|
16
|
+
self._grpc = None
|
|
17
|
+
self._dsg_uri_w = None
|
|
18
|
+
self._dsg_token_w = None
|
|
19
|
+
self._omni_uri_w = None
|
|
20
|
+
self._temporal_w = None
|
|
21
|
+
self._vrmode_w = None
|
|
22
|
+
self._normalize_w = None
|
|
23
|
+
self._time_scale_w = None
|
|
24
|
+
self._connect_w = None
|
|
25
|
+
self._update_w = None
|
|
26
|
+
self._connected = False
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def service(self) -> Optional["AnsysGeometryServiceUIExtension"]:
|
|
30
|
+
return ansys.geometry.service.AnsysGeometryServiceServerExtension.get_instance()
|
|
31
|
+
|
|
32
|
+
def info(self, text: str) -> None:
|
|
33
|
+
self._logger.info(text)
|
|
34
|
+
|
|
35
|
+
def warning(self, text: str) -> None:
|
|
36
|
+
self._logger.warning(text)
|
|
37
|
+
|
|
38
|
+
def error(self, text: str) -> None:
|
|
39
|
+
self._logger.error(text)
|
|
40
|
+
|
|
41
|
+
def launch_server(self) -> None:
|
|
42
|
+
if self.service.is_server_running():
|
|
43
|
+
return
|
|
44
|
+
self.service.dsg_uri = self._dsg_uri_w.model.as_string
|
|
45
|
+
self.service.security_token = self._dsg_token_w.model.as_string
|
|
46
|
+
self.service.omni_uri = self._omni_uri_w.model.as_string
|
|
47
|
+
self.service.temporal = self._temporal_w.model.as_bool
|
|
48
|
+
self.service.vrmode = self._vrmode_w.model.as_bool
|
|
49
|
+
self.service.normalize_geometry = self._normalize_w.model.as_bool
|
|
50
|
+
scale = self._time_scale_w.model.as_float
|
|
51
|
+
if scale <= 0.0:
|
|
52
|
+
scale = 1.0
|
|
53
|
+
self.service.time_scale = scale
|
|
54
|
+
self.service.launch_server()
|
|
55
|
+
if not self.service.is_server_running():
|
|
56
|
+
self.error("Failed to launch omniverse service.")
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
# parse the DSG USI
|
|
60
|
+
parsed = urlparse(self.service.dsg_uri)
|
|
61
|
+
port = parsed.port
|
|
62
|
+
host = parsed.hostname
|
|
63
|
+
|
|
64
|
+
# make a direct grpc connection to the DSG server
|
|
65
|
+
from ansys.pyensight.core import ensight_grpc # pylint: disable=import-outside-toplevel
|
|
66
|
+
|
|
67
|
+
self._grpc = ensight_grpc.EnSightGRPC(
|
|
68
|
+
host=host, port=port, secret_key=self.service.security_token
|
|
69
|
+
)
|
|
70
|
+
self._grpc.connect()
|
|
71
|
+
if not self._grpc.is_connected():
|
|
72
|
+
self.error(f"Failed to connect to DSG service {host}:{port}")
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
self.info("Connected to DSG service")
|
|
76
|
+
self._connected = True
|
|
77
|
+
|
|
78
|
+
def stop_server(self) -> None:
|
|
79
|
+
if not self._connected:
|
|
80
|
+
return
|
|
81
|
+
self.service.stop_server()
|
|
82
|
+
self._grpc.shutdown()
|
|
83
|
+
self._grpc = None
|
|
84
|
+
|
|
85
|
+
self.info("Disconnect from DSG service")
|
|
86
|
+
self._connected = False
|
|
87
|
+
|
|
88
|
+
def connect_cb(self) -> None:
|
|
89
|
+
if self.service is None:
|
|
90
|
+
self.error("Unable to find ansys.geometry.service instance")
|
|
91
|
+
return
|
|
92
|
+
if self._connected:
|
|
93
|
+
self.stop_server()
|
|
94
|
+
else:
|
|
95
|
+
self.launch_server()
|
|
96
|
+
self.update_ui()
|
|
97
|
+
|
|
98
|
+
def update_cb(self) -> None:
|
|
99
|
+
if not self._connected:
|
|
100
|
+
self.error("No DSG service connected")
|
|
101
|
+
return
|
|
102
|
+
self._grpc.command("import enspyqtgui_int", do_eval=False)
|
|
103
|
+
update_cmd = "dynamicscenegraph://localhost/client/update"
|
|
104
|
+
if self._temporal_w.model.as_bool:
|
|
105
|
+
update_cmd += "?timesteps=1"
|
|
106
|
+
cmd = f'enspyqtgui_int.dynamic_scene_graph_command("{update_cmd}")'
|
|
107
|
+
self._grpc.command(cmd, do_eval=False)
|
|
108
|
+
|
|
109
|
+
def on_startup(self, ext_id: str) -> None:
|
|
110
|
+
self.info(f"ANSYS geometry service GUI startup: {ext_id}")
|
|
111
|
+
if self.service is None:
|
|
112
|
+
self.error("Unable to find ansys.geometry.service instance")
|
|
113
|
+
self.build_ui()
|
|
114
|
+
self.update_ui()
|
|
115
|
+
|
|
116
|
+
def update_ui(self) -> None:
|
|
117
|
+
if self._connected:
|
|
118
|
+
self._connect_w.text = "Disconnect from DSG Server"
|
|
119
|
+
self._label_w.text = f"Connected to: {self.service.dsg_uri}"
|
|
120
|
+
else:
|
|
121
|
+
self._connect_w.text = "Connect to DSG Server"
|
|
122
|
+
self._label_w.text = "No connected DSG server"
|
|
123
|
+
self._update_w.enabled = self._connected
|
|
124
|
+
self._temporal_w.enabled = True
|
|
125
|
+
self._vrmode_w.enabled = not self._connected
|
|
126
|
+
self._normalize_w.enabled = not self._connected
|
|
127
|
+
self._time_scale_w.enabled = not self._connected
|
|
128
|
+
self._dsg_uri_w.enabled = not self._connected
|
|
129
|
+
self._dsg_token_w.enabled = not self._connected
|
|
130
|
+
self._omni_uri_w.enabled = not self._connected
|
|
131
|
+
|
|
132
|
+
def build_ui(self) -> None:
|
|
133
|
+
self._window = ui.Window("ANSYS Geometry Service")
|
|
134
|
+
with self._window.frame:
|
|
135
|
+
with ui.VStack(height=0, spacing=5):
|
|
136
|
+
self._label_w = ui.Label("No connected DSG server")
|
|
137
|
+
|
|
138
|
+
with ui.HStack(spacing=5):
|
|
139
|
+
ui.Label("DSG Service URI:", alignment=ui.Alignment.RIGHT_CENTER, width=0)
|
|
140
|
+
self._dsg_uri_w = ui.StringField()
|
|
141
|
+
self._dsg_uri_w.model.as_string = self.service.dsg_uri
|
|
142
|
+
|
|
143
|
+
with ui.HStack(spacing=5):
|
|
144
|
+
ui.Label("DSG security code:", alignment=ui.Alignment.RIGHT_CENTER, width=0)
|
|
145
|
+
self._dsg_token_w = ui.StringField(password_mode=True)
|
|
146
|
+
self._dsg_token_w.model.as_string = self.service.security_token
|
|
147
|
+
|
|
148
|
+
with ui.HStack(spacing=5):
|
|
149
|
+
ui.Label("Omniverse URI:", alignment=ui.Alignment.RIGHT_CENTER, width=0)
|
|
150
|
+
self._omni_uri_w = ui.StringField()
|
|
151
|
+
self._omni_uri_w.model.as_string = self.service.omni_uri
|
|
152
|
+
|
|
153
|
+
with ui.HStack(spacing=5):
|
|
154
|
+
with ui.HStack(spacing=5):
|
|
155
|
+
self._temporal_w = ui.CheckBox(width=0)
|
|
156
|
+
self._temporal_w.model.set_value(self.service.temporal)
|
|
157
|
+
ui.Label("Temporal", alignment=ui.Alignment.LEFT_CENTER)
|
|
158
|
+
|
|
159
|
+
with ui.HStack(spacing=5):
|
|
160
|
+
self._vrmode_w = ui.CheckBox(width=0)
|
|
161
|
+
self._vrmode_w.model.set_value(self.service.vrmode)
|
|
162
|
+
ui.Label("VR Mode", alignment=ui.Alignment.LEFT_CENTER)
|
|
163
|
+
|
|
164
|
+
with ui.HStack(spacing=5):
|
|
165
|
+
self._normalize_w = ui.CheckBox(width=0)
|
|
166
|
+
self._normalize_w.model.set_value(self.service.normalize_geometry)
|
|
167
|
+
ui.Label("Normalize", alignment=ui.Alignment.LEFT_CENTER)
|
|
168
|
+
|
|
169
|
+
with ui.HStack(spacing=5):
|
|
170
|
+
ui.Label(
|
|
171
|
+
"Temporal scaling factor:", alignment=ui.Alignment.RIGHT_CENTER, width=0
|
|
172
|
+
)
|
|
173
|
+
self._time_scale_w = ui.FloatField()
|
|
174
|
+
self._time_scale_w.model.as_float = self.service.time_scale
|
|
175
|
+
|
|
176
|
+
with ui.HStack():
|
|
177
|
+
self._connect_w = ui.Button("Connect to DSG Server", clicked_fn=self.connect_cb)
|
|
178
|
+
self._update_w = ui.Button("Request Update", clicked_fn=self.update_cb)
|
|
179
|
+
|
|
180
|
+
def on_shutdown(self) -> None:
|
|
181
|
+
self.info("ANSYS geometry service shutdown")
|
|
182
|
+
self.stop_server()
|
|
183
|
+
self._window = None
|
|
184
|
+
self._label_w = None
|
|
185
|
+
self._dsg_uri_w = None
|
|
186
|
+
self._dsg_token_w = None
|
|
187
|
+
self._omni_uri_w = None
|
|
188
|
+
self._temporal_w = None
|
|
189
|
+
self._vrmode_w = None
|
|
190
|
+
self._normalize_w = None
|
|
191
|
+
self._time_scale_w = None
|
|
192
|
+
self._connect_w = None
|
|
193
|
+
self._update_w = None
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
# Semantic Versioning is used: https://semver.org/
|
|
3
|
+
version = "0.8.6"
|
|
4
|
+
|
|
5
|
+
# Lists people or organizations that are considered the "authors" of the package.
|
|
6
|
+
authors = ["ANSYS"]
|
|
7
|
+
|
|
8
|
+
# The title and description fields are primarily for displaying extension info in UI
|
|
9
|
+
title = "ANSYS Omniverse Geometry Service GUI"
|
|
10
|
+
description = "A geometry synchronization service that enables export of geometry scenes from ANSYS products to Omniverse."
|
|
11
|
+
|
|
12
|
+
# Path (relative to the root) or content of readme markdown file for UI.
|
|
13
|
+
readme = "docs/README.md"
|
|
14
|
+
|
|
15
|
+
# URL of the extension source repository.
|
|
16
|
+
repository = "https://github.com/ansys/pyensight"
|
|
17
|
+
|
|
18
|
+
# One of categories for UI.
|
|
19
|
+
category = "simulation"
|
|
20
|
+
|
|
21
|
+
# Keywords for the extension
|
|
22
|
+
keywords = ["ANSYS", "EnSight", "PyEnSight", "Fluent", "kit"]
|
|
23
|
+
|
|
24
|
+
# Location of change log file in target (final) folder of extension, relative to the root.
|
|
25
|
+
# More info on writing changelog: https://keepachangelog.com/en/1.0.0/
|
|
26
|
+
changelog = "docs/CHANGELOG.md"
|
|
27
|
+
|
|
28
|
+
# Preview image and icon. Folder named "data" automatically goes in git lfs (see .gitattributes file).
|
|
29
|
+
# Preview image is shown in "Overview" of Extensions window. Screenshot of an extension might be a good preview image.
|
|
30
|
+
preview_image = "data/preview.png"
|
|
31
|
+
|
|
32
|
+
# Icon is shown in Extensions window, it is recommended to be square, of size 256x256.
|
|
33
|
+
icon = "data/icon.png"
|
|
34
|
+
|
|
35
|
+
# Use omni.ui to build simple UI
|
|
36
|
+
[dependencies]
|
|
37
|
+
"omni.kit.uiapp" = {}
|
|
38
|
+
"ansys.geometry.service" = {}
|
|
39
|
+
|
|
40
|
+
# Main python module this extension provides, it will be publicly available as "import ansys.geometry.serviceui".
|
|
41
|
+
[[python.module]]
|
|
42
|
+
name = "ansys.geometry.serviceui"
|
|
43
|
+
|
|
44
|
+
[[test]]
|
|
45
|
+
# Extra dependencies only to be used during test run
|
|
46
|
+
dependencies = [
|
|
47
|
+
"omni.kit.ui_test" # UI testing extension
|
|
48
|
+
]
|
|
49
|
+
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# ANSYS Omniverse Geometry Service GUI [ansys.geometry.serviceui]
|
|
2
|
+
|
|
3
|
+
This Omniverse extension is a UI interface to the [ansys.geometry.service]
|
|
4
|
+
kit extension. It allows an Omniverse application user to connect to
|
|
5
|
+
a running copy of ANSYS EnSight or other application that supports the
|
|
6
|
+
Dynamic Scene Graph gRPC protocol. The GUI allows for the remote scene
|
|
7
|
+
to be pulled, on request, into a specified Omniverse location.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
For more details on this extension see:
|
|
11
|
+
https://ensight.docs.pyansys.com/version/dev/user_guide/omniverse_info.html
|
|
12
|
+
|
|
13
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
ansys.geometry.serviceui
|
|
2
|
+
########################
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
.. toctree::
|
|
6
|
+
:maxdepth: 1
|
|
7
|
+
|
|
8
|
+
README
|
|
9
|
+
CHANGELOG
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
.. automodule::"ansys.geometry.serviceui"
|
|
13
|
+
:platform: Windows-x86_64, Linux-x86_64
|
|
14
|
+
:members:
|
|
15
|
+
:undoc-members:
|
|
16
|
+
:show-inheritance:
|
|
17
|
+
:imported-members:
|
|
18
|
+
:exclude-members: contextmanager
|
ansys/pyensight/core/launcher.py
CHANGED
|
@@ -13,10 +13,13 @@ Examples:
|
|
|
13
13
|
"""
|
|
14
14
|
import os.path
|
|
15
15
|
import platform
|
|
16
|
+
import random
|
|
17
|
+
import re
|
|
16
18
|
import socket
|
|
17
19
|
from typing import TYPE_CHECKING, Dict, List, Optional
|
|
18
20
|
import warnings
|
|
19
21
|
|
|
22
|
+
import psutil
|
|
20
23
|
import requests
|
|
21
24
|
|
|
22
25
|
if TYPE_CHECKING:
|
|
@@ -165,6 +168,38 @@ class Launcher:
|
|
|
165
168
|
"""
|
|
166
169
|
return
|
|
167
170
|
|
|
171
|
+
def _find_ports_used_by_other_pyensight_and_ensight(self):
|
|
172
|
+
"""Find ports to avoid when looking for empty ports.
|
|
173
|
+
|
|
174
|
+
The ports are found iterating the current processes and
|
|
175
|
+
looking for PyEnSight/EnSight sessions and their command
|
|
176
|
+
lines.
|
|
177
|
+
"""
|
|
178
|
+
pyensight_found = []
|
|
179
|
+
ensight_found = []
|
|
180
|
+
for process in psutil.process_iter():
|
|
181
|
+
try:
|
|
182
|
+
process_cmdline = process.cmdline()
|
|
183
|
+
except (psutil.AccessDenied, psutil.ZombieProcess):
|
|
184
|
+
continue
|
|
185
|
+
if not process_cmdline:
|
|
186
|
+
continue
|
|
187
|
+
if len(process_cmdline) > 1:
|
|
188
|
+
if "websocketserver.py" in os.path.basename(process_cmdline[1]):
|
|
189
|
+
pyensight_found.append(process_cmdline)
|
|
190
|
+
if any(["ensight" in os.path.basename(x) for x in process_cmdline]):
|
|
191
|
+
if any([x == "-ports" for x in process_cmdline]):
|
|
192
|
+
ensight_found.append(process_cmdline)
|
|
193
|
+
ports = []
|
|
194
|
+
for command_line in pyensight_found:
|
|
195
|
+
for command in command_line:
|
|
196
|
+
if re.match(r"^\d{4,5}$", command):
|
|
197
|
+
ports.append(int(command))
|
|
198
|
+
for command_line in ensight_found:
|
|
199
|
+
idx = command_line.index("-ports") + 1
|
|
200
|
+
ports.append(int(command_line[idx]))
|
|
201
|
+
return list(set(ports))
|
|
202
|
+
|
|
168
203
|
@staticmethod
|
|
169
204
|
def _find_unused_ports(count: int, avoid: Optional[List[int]] = None) -> Optional[List[int]]:
|
|
170
205
|
"""Find "count" unused ports on the host system
|
|
@@ -191,7 +226,7 @@ class Launcher:
|
|
|
191
226
|
ports = list()
|
|
192
227
|
|
|
193
228
|
# pick a starting port number
|
|
194
|
-
start =
|
|
229
|
+
start = random.randint(1024, 64000)
|
|
195
230
|
# We will scan for 65530 ports unless end is specified
|
|
196
231
|
port_mod = 65530
|
|
197
232
|
end = start + port_mod - 1
|
|
@@ -123,7 +123,8 @@ class LocalLauncher(Launcher):
|
|
|
123
123
|
self.session_directory = tempfile.mkdtemp(prefix="pyensight_")
|
|
124
124
|
|
|
125
125
|
# gRPC port, VNC port, websocketserver ws, websocketserver html
|
|
126
|
-
|
|
126
|
+
to_avoid = self._find_ports_used_by_other_pyensight_and_ensight()
|
|
127
|
+
self._ports = self._find_unused_ports(5, avoid=to_avoid)
|
|
127
128
|
if self._ports is None:
|
|
128
129
|
raise RuntimeError("Unable to allocate local ports for EnSight session")
|
|
129
130
|
is_windows = self._is_windows()
|
|
@@ -152,6 +153,7 @@ class LocalLauncher(Launcher):
|
|
|
152
153
|
cmd.extend(["-grpc_server", str(self._ports[0])])
|
|
153
154
|
vnc_url = f"vnc://%%3Frfb_port={self._ports[1]}%%26use_auth=0"
|
|
154
155
|
cmd.extend(["-vnc", vnc_url])
|
|
156
|
+
cmd.extend(["-ports", str(self._ports[4])])
|
|
155
157
|
|
|
156
158
|
use_egl = self._use_egl()
|
|
157
159
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
3
|
import queue
|
|
4
|
+
import sys
|
|
4
5
|
import threading
|
|
5
6
|
from typing import Any, Dict, List, Optional
|
|
6
7
|
|
|
@@ -94,7 +95,7 @@ class Part(object):
|
|
|
94
95
|
else:
|
|
95
96
|
self.tcoords_var_id = None
|
|
96
97
|
|
|
97
|
-
def
|
|
98
|
+
def nodal_surface_rep(self):
|
|
98
99
|
"""
|
|
99
100
|
This function processes the geometry arrays and converts them into nodal representation.
|
|
100
101
|
It will duplicate triangles as needed (to preserve element normals) and will convert
|
|
@@ -115,10 +116,8 @@ class Part(object):
|
|
|
115
116
|
"""
|
|
116
117
|
if self.cmd is None:
|
|
117
118
|
return None, None, None, None, None, None
|
|
118
|
-
if self.
|
|
119
|
-
self.session.log(
|
|
120
|
-
f"Note: part '{self.cmd.name}' contains lines which are not currently supported."
|
|
121
|
-
)
|
|
119
|
+
if self.conn_tris.size == 0:
|
|
120
|
+
self.session.log(f"Note: part '{self.cmd.name}' contains no triangles.")
|
|
122
121
|
return None, None, None, None, None, None
|
|
123
122
|
verts = self.coords
|
|
124
123
|
if self.session.normalize_geometry and self.session.scene_bounds is not None:
|
|
@@ -237,7 +236,7 @@ class Part(object):
|
|
|
237
236
|
tcoords = tmp
|
|
238
237
|
|
|
239
238
|
self.session.log(
|
|
240
|
-
f"Part '{self.cmd.name}' defined: {self.coords.size/3} verts, {self.conn_tris.size/3} tris
|
|
239
|
+
f"Part '{self.cmd.name}' defined: {self.coords.size/3} verts, {self.conn_tris.size/3} tris."
|
|
241
240
|
)
|
|
242
241
|
command = self.cmd
|
|
243
242
|
|
|
@@ -350,6 +349,7 @@ class DSGSession(object):
|
|
|
350
349
|
verbose: int = 0,
|
|
351
350
|
normalize_geometry: bool = False,
|
|
352
351
|
vrmode: bool = False,
|
|
352
|
+
time_scale: float = 1.0,
|
|
353
353
|
handler: UpdateHandler = UpdateHandler(),
|
|
354
354
|
):
|
|
355
355
|
"""
|
|
@@ -379,6 +379,9 @@ class DSGSession(object):
|
|
|
379
379
|
vrmode : bool
|
|
380
380
|
If True, do not include the EnSight camera in the generated view group. The default
|
|
381
381
|
is to include the EnSight view in the scene transformations.
|
|
382
|
+
time_scale : float
|
|
383
|
+
All DSG protobuffers time values will be multiplied by this factor after
|
|
384
|
+
being received. The default is ``1.0``.
|
|
382
385
|
handler : UpdateHandler
|
|
383
386
|
This is an UpdateHandler subclass that is called back when the state of
|
|
384
387
|
a scene transfer changes. For example, methods are called when the
|
|
@@ -395,11 +398,17 @@ class DSGSession(object):
|
|
|
395
398
|
self._dsg = None
|
|
396
399
|
self._normalize_geometry = normalize_geometry
|
|
397
400
|
self._vrmode = vrmode
|
|
401
|
+
self._time_scale = time_scale
|
|
402
|
+
self._time_limits = [
|
|
403
|
+
sys.float_info.max,
|
|
404
|
+
-sys.float_info.max,
|
|
405
|
+
] # Min/max across all time steps
|
|
398
406
|
self._mesh_block_count = 0
|
|
399
407
|
self._variables: Dict[int, Any] = dict()
|
|
400
408
|
self._groups: Dict[int, Any] = dict()
|
|
401
409
|
self._part: Part = Part(self)
|
|
402
410
|
self._scene_bounds: Optional[List] = None
|
|
411
|
+
self._cur_timeline: List = [0.0, 0.0] # Start/End time for current update
|
|
403
412
|
self._callback_handler.session = self
|
|
404
413
|
|
|
405
414
|
@property
|
|
@@ -438,6 +447,20 @@ class DSGSession(object):
|
|
|
438
447
|
def part(self) -> Part:
|
|
439
448
|
return self._part
|
|
440
449
|
|
|
450
|
+
@property
|
|
451
|
+
def time_limits(self) -> List:
|
|
452
|
+
return self._time_limits
|
|
453
|
+
|
|
454
|
+
@property
|
|
455
|
+
def cur_timeline(self) -> List:
|
|
456
|
+
return self._cur_timeline
|
|
457
|
+
|
|
458
|
+
@cur_timeline.setter
|
|
459
|
+
def cur_timeline(self, timeline: List) -> None:
|
|
460
|
+
self._cur_timeline = timeline
|
|
461
|
+
self._time_limits[0] = min(self._time_limits[0], self._cur_timeline[0])
|
|
462
|
+
self._time_limits[1] = max(self._time_limits[1], self._cur_timeline[1])
|
|
463
|
+
|
|
441
464
|
@property
|
|
442
465
|
def grpc(self) -> ensight_grpc.EnSightGRPC:
|
|
443
466
|
return self._grpc
|
|
@@ -483,7 +506,7 @@ class DSGSession(object):
|
|
|
483
506
|
def end(self):
|
|
484
507
|
"""Stop a gRPC connection to the EnSight instance"""
|
|
485
508
|
self._callback_handler.end_connection()
|
|
486
|
-
self._grpc.
|
|
509
|
+
self._grpc.shutdown()
|
|
487
510
|
self._shutdown = True
|
|
488
511
|
self._thread.join()
|
|
489
512
|
self._grpc.shutdown()
|
|
@@ -515,8 +538,6 @@ class DSGSession(object):
|
|
|
515
538
|
cmd.init.allow_incremental_updates = False
|
|
516
539
|
cmd.init.maximum_chunk_size = 1024 * 1024
|
|
517
540
|
self._dsg_queue.put(cmd) # type:ignore
|
|
518
|
-
# Handle the update messages
|
|
519
|
-
self.handle_one_update()
|
|
520
541
|
|
|
521
542
|
def _poll_messages(self) -> None:
|
|
522
543
|
"""Core interface to grab DSG events from gRPC and queue them for processing
|
|
@@ -626,7 +647,6 @@ class DSGSession(object):
|
|
|
626
647
|
There is always a part being modified. This method completes the current part, committing
|
|
627
648
|
it to the handler.
|
|
628
649
|
"""
|
|
629
|
-
self._part.build()
|
|
630
650
|
self._callback_handler.finalize_part(self.part)
|
|
631
651
|
self._mesh_block_count += 1
|
|
632
652
|
|
|
@@ -686,10 +706,15 @@ class DSGSession(object):
|
|
|
686
706
|
"""Handle a DSG UPDATE_VIEW command
|
|
687
707
|
|
|
688
708
|
Parameters
|
|
689
|
-
----------
|
|
709
|
+
----------
|
|
690
710
|
view:
|
|
691
711
|
The command coming from the EnSight stream.
|
|
692
712
|
"""
|
|
713
|
+
self._finish_part()
|
|
693
714
|
self._scene_bounds = None
|
|
694
715
|
self._groups[view.id] = view
|
|
716
|
+
if len(view.timeline) == 2:
|
|
717
|
+
view.timeline[0] *= self._time_scale
|
|
718
|
+
view.timeline[1] *= self._time_scale
|
|
719
|
+
self.cur_timeline = [view.timeline[0], view.timeline[1]]
|
|
695
720
|
self._callback_handler.add_group(view.id, view=True)
|