ansys-pyensight-core 0.8.8__tar.gz → 0.8.10__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.
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/PKG-INFO +10 -6
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/pyproject.toml +13 -9
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/__init__.py +1 -1
- ansys_pyensight_core-0.8.10/src/ansys/pyensight/core/common.py +216 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/dockerlauncher.py +85 -94
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/enshell_grpc.py +32 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/launch_ensight.py +120 -19
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/launcher.py +10 -61
- ansys_pyensight_core-0.8.10/src/ansys/pyensight/core/libuserd.py +1832 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/locallauncher.py +47 -2
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/renderable.py +30 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/session.py +6 -1
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/dsg_server.py +227 -35
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/omniverse.py +84 -24
- ansys_pyensight_core-0.8.10/src/ansys/pyensight/core/utils/omniverse_cli.py +481 -0
- ansys_pyensight_core-0.8.10/src/ansys/pyensight/core/utils/omniverse_dsg_server.py +690 -0
- ansys_pyensight_core-0.8.10/src/ansys/pyensight/core/utils/omniverse_glb_server.py +279 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/readers.py +15 -11
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/exts/ansys.geometry.service/ansys/geometry/service/__init__.py +0 -1
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/exts/ansys.geometry.service/ansys/geometry/service/extension.py +0 -407
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/exts/ansys.geometry.service/config/extension.toml +0 -59
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/exts/ansys.geometry.service/data/icon.png +0 -0
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/exts/ansys.geometry.service/data/preview.png +0 -0
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/exts/ansys.geometry.service/docs/CHANGELOG.md +0 -11
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/exts/ansys.geometry.service/docs/README.md +0 -13
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/exts/ansys.geometry.service/docs/index.rst +0 -18
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/exts/ansys.geometry.serviceui/ansys/geometry/serviceui/__init__.py +0 -1
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/exts/ansys.geometry.serviceui/ansys/geometry/serviceui/extension.py +0 -193
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/exts/ansys.geometry.serviceui/config/extension.toml +0 -49
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/exts/ansys.geometry.serviceui/data/icon.png +0 -0
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/exts/ansys.geometry.serviceui/data/preview.png +0 -0
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/exts/ansys.geometry.serviceui/docs/CHANGELOG.md +0 -11
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/exts/ansys.geometry.serviceui/docs/README.md +0 -13
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/exts/ansys.geometry.serviceui/docs/index.rst +0 -18
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/utils/omniverse_dsg_server.py +0 -880
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/utils/resources/Materials/Fieldstone/Fieldstone_BaseColor.png +0 -0
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/utils/resources/Materials/Fieldstone/Fieldstone_N.png +0 -0
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/utils/resources/Materials/Fieldstone/Fieldstone_ORM.png +0 -0
- ansys_pyensight_core-0.8.8/src/ansys/pyensight/core/utils/resources/Materials/Fieldstone.mdl +0 -54
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/LICENSE +0 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/README.rst +0 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/deep_pixel_view.html +0 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/enscontext.py +0 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/ensight_grpc.py +0 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/ensobj.py +0 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/listobj.py +0 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/py.typed +0 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/sgeo_poll.html +0 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/__init__.py +0 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/adr.py +0 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/export.py +0 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/parts.py +0 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/query.py +0 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/resources/Materials/000_sky.exr +0 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/support.py +0 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/utils/variables.py +0 -0
- {ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/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.
|
|
3
|
+
Version: 0.8.10
|
|
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>
|
|
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
18
|
Requires-Dist: importlib-metadata>=4.0; python_version<='3.8'
|
|
19
|
-
Requires-Dist: ansys-api-pyensight==0.4.
|
|
19
|
+
Requires-Dist: ansys-api-pyensight==0.4.2
|
|
20
20
|
Requires-Dist: requests>=2.28.2
|
|
21
21
|
Requires-Dist: docker>=6.1.0
|
|
22
22
|
Requires-Dist: urllib3<3.0.0
|
|
@@ -24,29 +24,33 @@ Requires-Dist: numpy>=1.21.0,<2
|
|
|
24
24
|
Requires-Dist: Pillow>=9.3.0
|
|
25
25
|
Requires-Dist: pypng>=0.0.20
|
|
26
26
|
Requires-Dist: psutil>=5.9.2
|
|
27
|
+
Requires-Dist: usd-core>=24.8
|
|
28
|
+
Requires-Dist: pygltflib>=1.16.2
|
|
27
29
|
Requires-Dist: build>=0.10.0 ; extra == "dev"
|
|
28
30
|
Requires-Dist: bump2version>=1.0.1 ; extra == "dev"
|
|
29
31
|
Requires-Dist: ipdb>=0.9.4 ; extra == "dev"
|
|
30
32
|
Requires-Dist: dill>=0.3.5.1 ; extra == "dev"
|
|
31
33
|
Requires-Dist: pre-commit>=3.3.3 ; extra == "dev"
|
|
32
|
-
Requires-Dist: Sphinx==
|
|
34
|
+
Requires-Dist: Sphinx==8.0.2 ; extra == "doc"
|
|
33
35
|
Requires-Dist: numpydoc==1.5.0 ; extra == "doc"
|
|
34
36
|
Requires-Dist: ansys-sphinx-theme==0.9.9 ; extra == "doc"
|
|
35
37
|
Requires-Dist: sphinx-copybutton==0.5.2 ; extra == "doc"
|
|
36
38
|
Requires-Dist: sphinx-gallery==0.13.0 ; extra == "doc"
|
|
37
39
|
Requires-Dist: sphinxcontrib-mermaid==0.9.2 ; extra == "doc"
|
|
38
40
|
Requires-Dist: docker>=6.1.0 ; extra == "doc"
|
|
39
|
-
Requires-Dist: matplotlib==3.
|
|
41
|
+
Requires-Dist: matplotlib==3.9.1.post1 ; extra == "doc"
|
|
40
42
|
Requires-Dist: requests>=2.28.2 ; extra == "doc"
|
|
41
43
|
Requires-Dist: sphinxcontrib.jquery==4.1 ; extra == "doc"
|
|
42
44
|
Requires-Dist: coverage-badge==1.1.0 ; extra == "doc"
|
|
43
45
|
Requires-Dist: sphinxcontrib-openapi==0.8.1 ; extra == "doc"
|
|
44
46
|
Requires-Dist: sphinxcontrib-video==0.2.0 ; extra == "doc"
|
|
45
|
-
Requires-Dist:
|
|
47
|
+
Requires-Dist: usd-core>=24.8 ; extra == "doc"
|
|
48
|
+
Requires-Dist: pygltflib>=1.16.2 ; extra == "doc"
|
|
49
|
+
Requires-Dist: pytest==8.3.2 ; extra == "tests"
|
|
46
50
|
Requires-Dist: pytest-cov==4.1.0 ; extra == "tests"
|
|
47
51
|
Requires-Dist: dill>=0.3.5.1 ; extra == "tests"
|
|
48
52
|
Requires-Dist: pytest-mock==3.10.0 ; extra == "tests"
|
|
49
|
-
Requires-Dist: urllib3==
|
|
53
|
+
Requires-Dist: urllib3==2.2.2 ; extra == "tests"
|
|
50
54
|
Requires-Dist: requests>=2.28.2 ; extra == "tests"
|
|
51
55
|
Requires-Dist: docker>=6.1.0 ; extra == "tests"
|
|
52
56
|
Project-URL: Changelog, https://github.com/ansys/pyensight/blob/main/CHANGELOG.rst
|
|
@@ -6,7 +6,7 @@ build-backend = "flit_core.buildapi"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "ansys-pyensight-core"
|
|
9
|
-
version = "0.8.
|
|
9
|
+
version = "0.8.10"
|
|
10
10
|
description = "A python wrapper for Ansys EnSight"
|
|
11
11
|
readme = "README.rst"
|
|
12
12
|
requires-python = ">=3.9,<4"
|
|
@@ -27,7 +27,7 @@ classifiers = [
|
|
|
27
27
|
|
|
28
28
|
dependencies = [
|
|
29
29
|
"importlib-metadata>=4.0; python_version<='3.8'",
|
|
30
|
-
"ansys-api-pyensight==0.4.
|
|
30
|
+
"ansys-api-pyensight==0.4.2",
|
|
31
31
|
"requests>=2.28.2",
|
|
32
32
|
"docker>=6.1.0",
|
|
33
33
|
"urllib3<3.0.0",
|
|
@@ -35,6 +35,8 @@ dependencies = [
|
|
|
35
35
|
"Pillow>=9.3.0",
|
|
36
36
|
"pypng>=0.0.20",
|
|
37
37
|
"psutil>=5.9.2",
|
|
38
|
+
"usd-core>=24.8",
|
|
39
|
+
"pygltflib>=1.16.2",
|
|
38
40
|
]
|
|
39
41
|
|
|
40
42
|
[project.optional-dependencies]
|
|
@@ -46,28 +48,30 @@ dev = [
|
|
|
46
48
|
"pre-commit>=3.3.3",
|
|
47
49
|
]
|
|
48
50
|
tests = [
|
|
49
|
-
"pytest==
|
|
51
|
+
"pytest==8.3.2",
|
|
50
52
|
"pytest-cov==4.1.0",
|
|
51
53
|
"dill>=0.3.5.1",
|
|
52
54
|
"pytest-mock==3.10.0",
|
|
53
|
-
"urllib3==
|
|
55
|
+
"urllib3==2.2.2",
|
|
54
56
|
"requests>=2.28.2",
|
|
55
57
|
"docker>=6.1.0",
|
|
56
58
|
]
|
|
57
59
|
doc = [
|
|
58
|
-
"Sphinx==
|
|
60
|
+
"Sphinx==8.0.2",
|
|
59
61
|
"numpydoc==1.5.0",
|
|
60
62
|
"ansys-sphinx-theme==0.9.9",
|
|
61
63
|
"sphinx-copybutton==0.5.2",
|
|
62
64
|
"sphinx-gallery==0.13.0",
|
|
63
65
|
"sphinxcontrib-mermaid==0.9.2",
|
|
64
66
|
"docker>=6.1.0",
|
|
65
|
-
"matplotlib==3.
|
|
67
|
+
"matplotlib==3.9.1.post1",
|
|
66
68
|
"requests>=2.28.2",
|
|
67
69
|
"sphinxcontrib.jquery==4.1",
|
|
68
70
|
"coverage-badge==1.1.0",
|
|
69
71
|
"sphinxcontrib-openapi==0.8.1",
|
|
70
72
|
"sphinxcontrib-video==0.2.0",
|
|
73
|
+
"usd-core>=24.8",
|
|
74
|
+
"pygltflib>=1.16.2",
|
|
71
75
|
]
|
|
72
76
|
|
|
73
77
|
[project.urls]
|
|
@@ -88,8 +92,8 @@ omit = [
|
|
|
88
92
|
"*/omniverse*.py",
|
|
89
93
|
"*/dsg_server.py",
|
|
90
94
|
"*/readers.py",
|
|
91
|
-
"*/
|
|
92
|
-
"*/
|
|
95
|
+
"*/omniverse/core/*.py",
|
|
96
|
+
"*/omniverse/dsgui/*.py",
|
|
93
97
|
]
|
|
94
98
|
|
|
95
99
|
[tool.coverage.report]
|
|
@@ -158,7 +162,7 @@ python_version = 3.9
|
|
|
158
162
|
strict = false
|
|
159
163
|
namespace_packages = true
|
|
160
164
|
explicit_package_bases = true
|
|
161
|
-
exclude = ["tests/", "^doc"
|
|
165
|
+
exclude = ["tests/", "^doc"]
|
|
162
166
|
no_site_packages = true
|
|
163
167
|
follow_imports = "skip"
|
|
164
168
|
|
{ansys_pyensight_core-0.8.8 → ansys_pyensight_core-0.8.10}/src/ansys/pyensight/core/__init__.py
RENAMED
|
@@ -12,7 +12,7 @@ __ansys_version__ = DEFAULT_ANSYS_VERSION
|
|
|
12
12
|
__ansys_version_str__ = f"{2000+(int(__ansys_version__) // 10)} R{int(__ansys_version__) % 10}"
|
|
13
13
|
|
|
14
14
|
from ansys.pyensight.core.dockerlauncher import DockerLauncher
|
|
15
|
-
from ansys.pyensight.core.launch_ensight import launch_ensight
|
|
15
|
+
from ansys.pyensight.core.launch_ensight import launch_ensight, launch_libuserd
|
|
16
16
|
from ansys.pyensight.core.launcher import Launcher
|
|
17
17
|
from ansys.pyensight.core.listobj import ensobjlist
|
|
18
18
|
from ansys.pyensight.core.locallauncher import LocalLauncher
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
""" This module provides a list of common utilities shared between different PyEnSight modules."""
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
import re
|
|
5
|
+
import socket
|
|
6
|
+
import time
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
|
|
8
|
+
|
|
9
|
+
from ansys.pyensight.core import enshell_grpc
|
|
10
|
+
import urllib3
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
from simple_upload_server.client import Client
|
|
14
|
+
|
|
15
|
+
simple_upload_server_is_available = True # pragma: no cover
|
|
16
|
+
except Exception:
|
|
17
|
+
simple_upload_server_is_available = False
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from docker import DockerClient
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def find_unused_ports(count: int, avoid: Optional[List[int]] = None) -> Optional[List[int]]:
|
|
24
|
+
"""Find "count" unused ports on the host system
|
|
25
|
+
|
|
26
|
+
A port is considered unused if it does not respond to a "connect" attempt. Walk
|
|
27
|
+
the ports from 'start' to 'end' looking for unused ports and avoiding any ports
|
|
28
|
+
in the 'avoid' list. Stop once the desired number of ports have been
|
|
29
|
+
found. If an insufficient number of ports were found, return None.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
count: int :
|
|
34
|
+
Number of unused ports to find
|
|
35
|
+
avoid: Optional[List[int]] :
|
|
36
|
+
An optional list of ports not to check
|
|
37
|
+
|
|
38
|
+
Returns
|
|
39
|
+
-------
|
|
40
|
+
The detected ports or None on failure
|
|
41
|
+
|
|
42
|
+
"""
|
|
43
|
+
if avoid is None:
|
|
44
|
+
avoid = []
|
|
45
|
+
ports = list()
|
|
46
|
+
|
|
47
|
+
# pick a starting port number
|
|
48
|
+
start = random.randint(1024, 64000)
|
|
49
|
+
# We will scan for 65530 ports unless end is specified
|
|
50
|
+
port_mod = 65530
|
|
51
|
+
end = start + port_mod - 1
|
|
52
|
+
# walk the "virtual" port range
|
|
53
|
+
for base_port in range(start, end + 1):
|
|
54
|
+
# Map to physical port range
|
|
55
|
+
# There have been some issues with 65534+ so we stop at 65530
|
|
56
|
+
port = base_port % port_mod
|
|
57
|
+
# port 0 is special
|
|
58
|
+
if port == 0: # pragma: no cover
|
|
59
|
+
continue # pragma: no cover
|
|
60
|
+
# avoid admin ports
|
|
61
|
+
if port < 1024: # pragma: no cover
|
|
62
|
+
continue # pragma: no cover
|
|
63
|
+
# are we supposed to skip this one?
|
|
64
|
+
if port in avoid: # pragma: no cover
|
|
65
|
+
continue # pragma: no cover
|
|
66
|
+
# is anyone listening?
|
|
67
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
68
|
+
result = sock.connect_ex(("127.0.0.1", port))
|
|
69
|
+
if result != 0:
|
|
70
|
+
ports.append(port)
|
|
71
|
+
else:
|
|
72
|
+
sock.close() # pragma: no cover
|
|
73
|
+
if len(ports) >= count:
|
|
74
|
+
return ports
|
|
75
|
+
# in case we failed...
|
|
76
|
+
if len(ports) < count: # pragma: no cover
|
|
77
|
+
return None # pragma: no cover
|
|
78
|
+
return ports # pragma: no cover
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def get_host_port(uri: str) -> Tuple[str, int]:
|
|
82
|
+
"""Get the host port for the input uri
|
|
83
|
+
|
|
84
|
+
Parameters
|
|
85
|
+
----------
|
|
86
|
+
|
|
87
|
+
uri: str
|
|
88
|
+
The Uri to inspect
|
|
89
|
+
|
|
90
|
+
Returns
|
|
91
|
+
-------
|
|
92
|
+
(tuple):
|
|
93
|
+
A tuple containing the host and the port of the input uri
|
|
94
|
+
"""
|
|
95
|
+
parse_results = urllib3.util.parse_url(uri)
|
|
96
|
+
port = (
|
|
97
|
+
parse_results.port
|
|
98
|
+
if parse_results.port
|
|
99
|
+
else (443 if re.search("^https|wss$", parse_results.scheme) else None)
|
|
100
|
+
)
|
|
101
|
+
return (parse_results.host, port)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def get_file_service(pim_instance: Any) -> Optional[Any]: # pragma: no cover
|
|
105
|
+
"""Get the file service object for the input pim instance.
|
|
106
|
+
|
|
107
|
+
Parameters
|
|
108
|
+
----------
|
|
109
|
+
|
|
110
|
+
pim_instance:
|
|
111
|
+
the PIM instance to get the service from.
|
|
112
|
+
|
|
113
|
+
Returns
|
|
114
|
+
-------
|
|
115
|
+
|
|
116
|
+
pim_file_service:
|
|
117
|
+
the PIM file service object
|
|
118
|
+
"""
|
|
119
|
+
if simple_upload_server_is_available is False:
|
|
120
|
+
return None
|
|
121
|
+
if pim_instance is None:
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
if "http-simple-upload-server" in pim_instance.services:
|
|
125
|
+
pim_file_service = Client(
|
|
126
|
+
token="token",
|
|
127
|
+
url=pim_instance.services["http-simple-upload-server"].uri,
|
|
128
|
+
headers=pim_instance.services["http-simple-upload-server"].headers,
|
|
129
|
+
)
|
|
130
|
+
return pim_file_service
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def populate_service_host_port( # pragma: no cover
|
|
135
|
+
pim_instance: Any, service_host_port: Dict[str, Tuple[str, int]], webui: bool = False
|
|
136
|
+
) -> Dict[str, Tuple[str, int]]:
|
|
137
|
+
"""Populate the service host port dictionary with the services available in the PIM instance.
|
|
138
|
+
|
|
139
|
+
Parameters
|
|
140
|
+
----------
|
|
141
|
+
pim_instance:
|
|
142
|
+
the PIM instance to get the servicea from.
|
|
143
|
+
service_host_port: dict
|
|
144
|
+
the dictionary to be updated with the services from the PIM instance
|
|
145
|
+
webui: bool
|
|
146
|
+
if True retrieve also the webUI service
|
|
147
|
+
|
|
148
|
+
Returns
|
|
149
|
+
-------
|
|
150
|
+
service_host_port: dict
|
|
151
|
+
the dictionary updated with the services from the PIM instance
|
|
152
|
+
"""
|
|
153
|
+
if not set(("grpc_private", "http", "ws")).issubset(pim_instance.services):
|
|
154
|
+
raise RuntimeError(
|
|
155
|
+
"If channel is specified, the PIM instance must have a list of length 3 "
|
|
156
|
+
+ "containing the appropriate service URIs. It does not."
|
|
157
|
+
)
|
|
158
|
+
service_host_port["grpc_private"] = get_host_port(pim_instance.services["grpc_private"].uri)
|
|
159
|
+
service_host_port["http"] = get_host_port(pim_instance.services["http"].uri)
|
|
160
|
+
service_host_port["ws"] = get_host_port(pim_instance.services["ws"].uri)
|
|
161
|
+
service_host_port["grpc"] = ("127.0.0.1", -1)
|
|
162
|
+
if webui:
|
|
163
|
+
service_host_port["webui"] = get_host_port(pim_instance.services["webui"].uri)
|
|
164
|
+
return service_host_port
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def launch_enshell_interface(
|
|
168
|
+
enshell_grpc_channel: Any, grpc_port: int, timeout: float
|
|
169
|
+
) -> enshell_grpc.EnShellGRPC:
|
|
170
|
+
"""Launch the EnShell gRPC Interface.
|
|
171
|
+
|
|
172
|
+
Parameters
|
|
173
|
+
----------
|
|
174
|
+
enshell_grpc_channel:
|
|
175
|
+
An eventual gRPC channel already available, like in the PIM case
|
|
176
|
+
grpc_port: int
|
|
177
|
+
the gRPC port to connect to
|
|
178
|
+
timeout: float
|
|
179
|
+
a timeout to wait for the gRPC connection
|
|
180
|
+
|
|
181
|
+
Returns
|
|
182
|
+
-------
|
|
183
|
+
enshell: enshell_grpc.EnShellGRPC
|
|
184
|
+
the enshell gRPC interface
|
|
185
|
+
"""
|
|
186
|
+
if enshell_grpc_channel: # pragma: no cover
|
|
187
|
+
enshell = enshell_grpc.EnShellGRPC() # pragma: no cover
|
|
188
|
+
enshell.connect_existing_channel(enshell_grpc_channel) # pragma: no cover
|
|
189
|
+
else:
|
|
190
|
+
enshell = enshell_grpc.EnShellGRPC(port=grpc_port)
|
|
191
|
+
time_start = time.time()
|
|
192
|
+
while time.time() - time_start < timeout: # pragma: no cover
|
|
193
|
+
if enshell.is_connected():
|
|
194
|
+
break
|
|
195
|
+
try:
|
|
196
|
+
enshell.connect(timeout=timeout)
|
|
197
|
+
except OSError: # pragma: no cover
|
|
198
|
+
pass # pragma: no cover
|
|
199
|
+
return enshell
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def pull_image(docker_client: "DockerClient", image_name: str) -> None:
|
|
203
|
+
"""Pull the input docker image using the input Docker Client
|
|
204
|
+
|
|
205
|
+
Parameters
|
|
206
|
+
----------
|
|
207
|
+
docker_client: DockerClient
|
|
208
|
+
the current DockerClient to pull the image with
|
|
209
|
+
image_name: str
|
|
210
|
+
the image to pull
|
|
211
|
+
"""
|
|
212
|
+
try:
|
|
213
|
+
if docker_client is not None: # pragma: no cover
|
|
214
|
+
docker_client.images.pull(image_name)
|
|
215
|
+
except Exception:
|
|
216
|
+
raise RuntimeError(f"Can't pull Docker image: {image_name}")
|
|
@@ -16,14 +16,10 @@ Examples:
|
|
|
16
16
|
"""
|
|
17
17
|
import logging
|
|
18
18
|
import os.path
|
|
19
|
-
import re
|
|
20
19
|
import subprocess
|
|
21
|
-
import
|
|
22
|
-
from typing import Any, Dict, Optional
|
|
20
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional
|
|
23
21
|
import uuid
|
|
24
22
|
|
|
25
|
-
import urllib3
|
|
26
|
-
|
|
27
23
|
try:
|
|
28
24
|
import grpc
|
|
29
25
|
except ModuleNotFoundError: # pragma: no cover
|
|
@@ -31,24 +27,27 @@ except ModuleNotFoundError: # pragma: no cover
|
|
|
31
27
|
except Exception: # pragma: no cover
|
|
32
28
|
raise RuntimeError("Cannot initialize grpc")
|
|
33
29
|
|
|
34
|
-
from ansys.pyensight.core.launcher import Launcher
|
|
35
|
-
import ansys.pyensight.core.session
|
|
36
|
-
from ansys.pyensight.core.session import Session
|
|
37
|
-
|
|
38
30
|
try:
|
|
39
|
-
|
|
31
|
+
import docker
|
|
40
32
|
except ModuleNotFoundError: # pragma: no cover
|
|
41
|
-
raise RuntimeError("The
|
|
33
|
+
raise RuntimeError("The docker module must be installed for DockerLauncher")
|
|
42
34
|
except Exception: # pragma: no cover
|
|
43
|
-
raise RuntimeError("Cannot initialize
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
35
|
+
raise RuntimeError("Cannot initialize Docker")
|
|
36
|
+
|
|
37
|
+
from ansys.pyensight.core.common import (
|
|
38
|
+
find_unused_ports,
|
|
39
|
+
get_file_service,
|
|
40
|
+
launch_enshell_interface,
|
|
41
|
+
populate_service_host_port,
|
|
42
|
+
pull_image,
|
|
43
|
+
)
|
|
44
|
+
from ansys.pyensight.core.launcher import Launcher
|
|
45
|
+
import ansys.pyensight.core.session
|
|
46
|
+
from ansys.pyensight.core.session import Session
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
if TYPE_CHECKING:
|
|
49
|
+
from docker import DockerClient
|
|
50
|
+
from docker.models.containers import Container
|
|
52
51
|
|
|
53
52
|
|
|
54
53
|
class DockerLauncher(Launcher):
|
|
@@ -87,6 +86,9 @@ class DockerLauncher(Launcher):
|
|
|
87
86
|
Number of EnSight servers to use for SOS (Server of Server) mode.
|
|
88
87
|
This parameter is defined on the parent ``Launcher`` class, where
|
|
89
88
|
the default is ``None``, in which case SOS mode is not used.
|
|
89
|
+
additional_command_line_options: list, optional
|
|
90
|
+
Additional command line options to be used to launch EnSight.
|
|
91
|
+
Arguments that contain spaces are not supported.
|
|
90
92
|
|
|
91
93
|
Examples
|
|
92
94
|
--------
|
|
@@ -114,8 +116,8 @@ class DockerLauncher(Launcher):
|
|
|
114
116
|
self._enshell_grpc_channel = channel
|
|
115
117
|
self._service_uris: Dict[Any, str] = {}
|
|
116
118
|
self._image_name: Optional[str] = None
|
|
117
|
-
self._docker_client: Optional[
|
|
118
|
-
self._container = None
|
|
119
|
+
self._docker_client: Optional["DockerClient"] = None
|
|
120
|
+
self._container: Optional["Container"] = None
|
|
119
121
|
self._enshell: Optional[Any] = None
|
|
120
122
|
self._pim_instance: Optional[Any] = pim_instance
|
|
121
123
|
|
|
@@ -140,27 +142,20 @@ class DockerLauncher(Launcher):
|
|
|
140
142
|
)
|
|
141
143
|
|
|
142
144
|
if self._enshell_grpc_channel and self._pim_instance:
|
|
143
|
-
|
|
145
|
+
service_set = ["grpc_private", "http", "ws"]
|
|
146
|
+
# if self._launch_webui:
|
|
147
|
+
# service_set.append("webui")
|
|
148
|
+
if not set(service_set).issubset(self._pim_instance.services):
|
|
144
149
|
raise RuntimeError(
|
|
145
150
|
"If channel is specified, the PIM instance must have a list of length 3 "
|
|
146
151
|
+ "containing the appropriate service URIs. It does not."
|
|
147
152
|
)
|
|
148
|
-
self._service_host_port = {}
|
|
149
153
|
# grab the URIs for the 3 required services passed in from PIM
|
|
150
|
-
self._service_host_port
|
|
151
|
-
self._pim_instance.
|
|
152
|
-
)
|
|
153
|
-
self._service_host_port["http"] = self._get_host_port(
|
|
154
|
-
self._pim_instance.services["http"].uri
|
|
155
|
-
)
|
|
156
|
-
self._service_host_port["ws"] = self._get_host_port(
|
|
157
|
-
self._pim_instance.services["ws"].uri
|
|
154
|
+
self._service_host_port = populate_service_host_port(
|
|
155
|
+
self._pim_instance, {}, webui=self._launch_webui
|
|
158
156
|
)
|
|
159
|
-
# for parity, add 'grpc' as a placeholder even though using PIM sets up the gRPC channel.
|
|
160
|
-
# this isn't used in this situation.
|
|
161
|
-
self._service_host_port["grpc"] = ("127.0.0.1", -1)
|
|
162
157
|
# attach to the file service if available
|
|
163
|
-
self.
|
|
158
|
+
self._pim_file_service = get_file_service(self._pim_instance)
|
|
164
159
|
# if using PIM, we have a query parameter to append to http requests
|
|
165
160
|
if self._pim_instance is not None:
|
|
166
161
|
d = {"instance_name": self._pim_instance.name}
|
|
@@ -170,7 +165,10 @@ class DockerLauncher(Launcher):
|
|
|
170
165
|
|
|
171
166
|
# EnShell gRPC port, EnSight gRPC port, HTTP port, WSS port
|
|
172
167
|
# skip 1999 as that internal to the container is used to the container for the VNC connection
|
|
173
|
-
|
|
168
|
+
num_ports = 4
|
|
169
|
+
if self._launch_webui:
|
|
170
|
+
num_ports = 5
|
|
171
|
+
ports = find_unused_ports(num_ports, avoid=[1999])
|
|
174
172
|
if ports is None: # pragma: no cover
|
|
175
173
|
raise RuntimeError(
|
|
176
174
|
"Unable to allocate local ports for EnSight session"
|
|
@@ -180,6 +178,8 @@ class DockerLauncher(Launcher):
|
|
|
180
178
|
self._service_host_port["grpc_private"] = ("127.0.0.1", ports[1])
|
|
181
179
|
self._service_host_port["http"] = ("127.0.0.1", ports[2])
|
|
182
180
|
self._service_host_port["ws"] = ("127.0.0.1", ports[3])
|
|
181
|
+
if self._launch_webui:
|
|
182
|
+
self._service_host_port["webui"] = ("127.0.0.1", ports[4])
|
|
183
183
|
|
|
184
184
|
# get the optional user-specified image name
|
|
185
185
|
# Note: the default name needs to change over time... TODO
|
|
@@ -190,14 +190,7 @@ class DockerLauncher(Launcher):
|
|
|
190
190
|
self._image_name = docker_image_name
|
|
191
191
|
|
|
192
192
|
# Load up Docker from the user's environment
|
|
193
|
-
|
|
194
|
-
import docker
|
|
195
|
-
|
|
196
|
-
self._docker_client = docker.from_env()
|
|
197
|
-
except ModuleNotFoundError:
|
|
198
|
-
raise RuntimeError("The docker module must be installed for DockerLauncher")
|
|
199
|
-
except Exception:
|
|
200
|
-
raise RuntimeError("Cannot initialize Docker")
|
|
193
|
+
self._docker_client = docker.from_env()
|
|
201
194
|
|
|
202
195
|
def ansys_version(self) -> Optional[str]:
|
|
203
196
|
"""Get the Ansys version (three-digit string) found in the Docker container.
|
|
@@ -219,11 +212,7 @@ class DockerLauncher(Launcher):
|
|
|
219
212
|
If the Docker image couldn't be pulled.
|
|
220
213
|
|
|
221
214
|
"""
|
|
222
|
-
|
|
223
|
-
if self._docker_client is not None: # pragma: no cover
|
|
224
|
-
self._docker_client.images.pull(self._image_name)
|
|
225
|
-
except Exception:
|
|
226
|
-
raise RuntimeError(f"Can't pull Docker image: {self._image_name}")
|
|
215
|
+
pull_image(self._docker_client, self._image_name)
|
|
227
216
|
|
|
228
217
|
def _get_container_env(self) -> Dict:
|
|
229
218
|
# Create the environmental variables
|
|
@@ -248,6 +237,10 @@ class DockerLauncher(Launcher):
|
|
|
248
237
|
if "ENSIGHT_ANSYS_APIP_CONFIG" in os.environ:
|
|
249
238
|
container_env["ENSIGHT_ANSYS_APIP_CONFIG"] = os.environ["ENSIGHT_ANSYS_APIP_CONFIG"]
|
|
250
239
|
|
|
240
|
+
if self._launch_webui:
|
|
241
|
+
container_env["SIMBA_WEBSERVER_TOKEN"] = self._secret_key
|
|
242
|
+
container_env["FLUENT_WEBSERVER_TOKEN"] = self._secret_key
|
|
243
|
+
|
|
251
244
|
return container_env
|
|
252
245
|
|
|
253
246
|
def start(self) -> "Session":
|
|
@@ -296,7 +289,13 @@ class DockerLauncher(Launcher):
|
|
|
296
289
|
+ "/tcp": str(self._service_host_port["http"][1]),
|
|
297
290
|
str(self._service_host_port["ws"][1]) + "/tcp": str(self._service_host_port["ws"][1]),
|
|
298
291
|
}
|
|
299
|
-
|
|
292
|
+
if self._launch_webui:
|
|
293
|
+
ports_to_map.update(
|
|
294
|
+
{
|
|
295
|
+
str(self._service_host_port["webui"][1])
|
|
296
|
+
+ "/tcp": str(self._service_host_port["webui"][1])
|
|
297
|
+
}
|
|
298
|
+
)
|
|
300
299
|
# The data directory to map into the container
|
|
301
300
|
data_volume = None
|
|
302
301
|
if self._data_directory:
|
|
@@ -307,13 +306,6 @@ class DockerLauncher(Launcher):
|
|
|
307
306
|
#
|
|
308
307
|
enshell_cmd = "-app -v 3 -grpc_server " + str(grpc_port)
|
|
309
308
|
|
|
310
|
-
try:
|
|
311
|
-
import docker
|
|
312
|
-
except ModuleNotFoundError: # pragma: no cover
|
|
313
|
-
raise RuntimeError("The docker module must be installed for DockerLauncher")
|
|
314
|
-
except Exception: # pragma: no cover
|
|
315
|
-
raise RuntimeError("Cannot initialize Docker")
|
|
316
|
-
|
|
317
309
|
use_egl = self._use_egl()
|
|
318
310
|
|
|
319
311
|
logging.debug("Starting Container...\n")
|
|
@@ -386,6 +378,33 @@ class DockerLauncher(Launcher):
|
|
|
386
378
|
logging.debug("Container started.\n")
|
|
387
379
|
return self.connect()
|
|
388
380
|
|
|
381
|
+
def launch_webui(self, container_env_str):
|
|
382
|
+
# Run websocketserver
|
|
383
|
+
cmd = f"cpython /ansys_inc/v{self._ansys_version}/CEI/"
|
|
384
|
+
cmd += f"nexus{self._ansys_version}/nexus_launcher/webui_launcher.py"
|
|
385
|
+
# websocket port - this needs to come first since we now have
|
|
386
|
+
# --add_header as a optional arg that can take an arbitrary
|
|
387
|
+
# number of optional headers.
|
|
388
|
+
webui_port = self._service_host_port["webui"][1]
|
|
389
|
+
grpc_port = self._service_host_port["grpc_private"][1]
|
|
390
|
+
http_port = self._service_host_port["http"][1]
|
|
391
|
+
ws_port = self._service_host_port["ws"][1]
|
|
392
|
+
cmd += f" --server-listen-port {webui_port}"
|
|
393
|
+
cmd += (
|
|
394
|
+
f" --server-web-roots /ansys_inc/v{self._ansys_version}/CEI/nexus{self._ansys_version}/"
|
|
395
|
+
)
|
|
396
|
+
cmd += f"ansys{self._ansys_version}/ensight/WebUI/web/ui/"
|
|
397
|
+
cmd += f" --ensight-grpc-port {grpc_port}"
|
|
398
|
+
cmd += f" --ensight-html-port {http_port}"
|
|
399
|
+
cmd += f" --ensight-ws-port {ws_port}"
|
|
400
|
+
cmd += f" --ensight-session-directory {self._session_directory}"
|
|
401
|
+
cmd += f" --ensight-secret-key {self._secret_key}"
|
|
402
|
+
logging.debug(f"Starting WebUI: {cmd}\n")
|
|
403
|
+
ret = self._enshell.start_other(cmd, extra_env=container_env_str)
|
|
404
|
+
if ret[0] != 0: # pragma: no cover
|
|
405
|
+
self.stop() # pragma: no cover
|
|
406
|
+
raise RuntimeError(f"Error starting WebUI: {cmd}\n") # pragma: no cover
|
|
407
|
+
|
|
389
408
|
def connect(self):
|
|
390
409
|
"""Create and bind a :class:`Session<ansys.pyensight.core.Session>` instance
|
|
391
410
|
to the created EnSight gRPC connection started by EnShell.
|
|
@@ -405,24 +424,12 @@ class DockerLauncher(Launcher):
|
|
|
405
424
|
#
|
|
406
425
|
#
|
|
407
426
|
# Start up the EnShell gRPC interface
|
|
427
|
+
self._enshell = launch_enshell_interface(
|
|
428
|
+
self._enshell_grpc_channel, self._service_host_port["grpc"][1], self._timeout
|
|
429
|
+
)
|
|
408
430
|
log_dir = "/data"
|
|
409
431
|
if self._enshell_grpc_channel: # pragma: no cover
|
|
410
|
-
self._enshell = enshell_grpc.EnShellGRPC()
|
|
411
|
-
self._enshell.connect_existing_channel(self._enshell_grpc_channel)
|
|
412
432
|
log_dir = "/work"
|
|
413
|
-
else:
|
|
414
|
-
logging.debug(
|
|
415
|
-
f"Connecting to EnShell over gRPC port: {self._service_host_port['grpc'][1]}...\n"
|
|
416
|
-
)
|
|
417
|
-
self._enshell = enshell_grpc.EnShellGRPC(port=self._service_host_port["grpc"][1])
|
|
418
|
-
time_start = time.time()
|
|
419
|
-
while time.time() - time_start < self._timeout: # pragma: no cover
|
|
420
|
-
if self._enshell.is_connected():
|
|
421
|
-
break
|
|
422
|
-
try:
|
|
423
|
-
self._enshell.connect(timeout=self._timeout)
|
|
424
|
-
except OSError: # pragma: no cover
|
|
425
|
-
pass # pragma: no cover
|
|
426
433
|
|
|
427
434
|
if not self._enshell.is_connected(): # pragma: no cover
|
|
428
435
|
self.stop() # pragma: no cover
|
|
@@ -504,6 +511,9 @@ class DockerLauncher(Launcher):
|
|
|
504
511
|
|
|
505
512
|
vnc_url = "vnc://%%3Frfb_port=1999%%26use_auth=0"
|
|
506
513
|
ensight_args += " -vnc " + vnc_url
|
|
514
|
+
if self._additional_command_line_options:
|
|
515
|
+
ensight_args += " "
|
|
516
|
+
ensight_args += " ".join(self._additional_command_line_options)
|
|
507
517
|
|
|
508
518
|
logging.debug(f"Starting EnSight with args: {ensight_args}\n")
|
|
509
519
|
ret = self._enshell.start_ensight(ensight_args, ensight_env_vars)
|
|
@@ -575,10 +585,13 @@ class DockerLauncher(Launcher):
|
|
|
575
585
|
timeout=self._timeout,
|
|
576
586
|
sos=use_sos,
|
|
577
587
|
rest_api=self._enable_rest_api,
|
|
588
|
+
webui_port=self._service_host_port["webui"][1] if self._launch_webui else None,
|
|
578
589
|
)
|
|
579
590
|
session.launcher = self
|
|
580
591
|
self._sessions.append(session)
|
|
581
592
|
|
|
593
|
+
if self._launch_webui:
|
|
594
|
+
self.launch_webui(container_env_str)
|
|
582
595
|
logging.debug("Return session.\n")
|
|
583
596
|
|
|
584
597
|
return session
|
|
@@ -613,32 +626,10 @@ class DockerLauncher(Launcher):
|
|
|
613
626
|
self._pim_instance = None
|
|
614
627
|
super().stop()
|
|
615
628
|
|
|
616
|
-
def _get_file_service(self) -> None: # pragma: no cover
|
|
617
|
-
if simple_upload_server_is_available is False:
|
|
618
|
-
return
|
|
619
|
-
if self._pim_instance is None:
|
|
620
|
-
return
|
|
621
|
-
|
|
622
|
-
if "http-simple-upload-server" in self._pim_instance.services:
|
|
623
|
-
self._pim_file_service = Client(
|
|
624
|
-
token="token",
|
|
625
|
-
url=self._pim_instance.services["http-simple-upload-server"].uri,
|
|
626
|
-
headers=self._pim_instance.services["http-simple-upload-server"].headers,
|
|
627
|
-
)
|
|
628
|
-
|
|
629
629
|
def file_service(self) -> Optional[Any]:
|
|
630
630
|
"""Get the PIM file service object if available."""
|
|
631
631
|
return self._pim_file_service
|
|
632
632
|
|
|
633
|
-
def _get_host_port(self, uri: str) -> tuple:
|
|
634
|
-
parse_results = urllib3.util.parse_url(uri)
|
|
635
|
-
port = (
|
|
636
|
-
parse_results.port
|
|
637
|
-
if parse_results.port
|
|
638
|
-
else (443 if re.search("^https|wss$", parse_results.scheme) else None)
|
|
639
|
-
)
|
|
640
|
-
return (parse_results.host, port)
|
|
641
|
-
|
|
642
633
|
def _is_system_egl_capable(self) -> bool:
|
|
643
634
|
"""Check if the system is EGL capable.
|
|
644
635
|
|