ansys-pyensight-core 0.8.8__py3-none-any.whl → 0.8.10__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.

Files changed (41) hide show
  1. ansys/pyensight/core/__init__.py +1 -1
  2. ansys/pyensight/core/common.py +216 -0
  3. ansys/pyensight/core/dockerlauncher.py +85 -94
  4. ansys/pyensight/core/enshell_grpc.py +32 -0
  5. ansys/pyensight/core/launch_ensight.py +120 -19
  6. ansys/pyensight/core/launcher.py +10 -61
  7. ansys/pyensight/core/libuserd.py +1832 -0
  8. ansys/pyensight/core/locallauncher.py +47 -2
  9. ansys/pyensight/core/renderable.py +30 -0
  10. ansys/pyensight/core/session.py +6 -1
  11. ansys/pyensight/core/utils/dsg_server.py +227 -35
  12. ansys/pyensight/core/utils/omniverse.py +84 -24
  13. ansys/pyensight/core/utils/omniverse_cli.py +481 -0
  14. ansys/pyensight/core/utils/omniverse_dsg_server.py +236 -426
  15. ansys/pyensight/core/utils/omniverse_glb_server.py +279 -0
  16. ansys/pyensight/core/utils/readers.py +15 -11
  17. {ansys_pyensight_core-0.8.8.dist-info → ansys_pyensight_core-0.8.10.dist-info}/METADATA +10 -6
  18. ansys_pyensight_core-0.8.10.dist-info/RECORD +36 -0
  19. ansys/pyensight/core/exts/ansys.geometry.service/ansys/geometry/service/__init__.py +0 -1
  20. ansys/pyensight/core/exts/ansys.geometry.service/ansys/geometry/service/extension.py +0 -407
  21. ansys/pyensight/core/exts/ansys.geometry.service/config/extension.toml +0 -59
  22. ansys/pyensight/core/exts/ansys.geometry.service/data/icon.png +0 -0
  23. ansys/pyensight/core/exts/ansys.geometry.service/data/preview.png +0 -0
  24. ansys/pyensight/core/exts/ansys.geometry.service/docs/CHANGELOG.md +0 -11
  25. ansys/pyensight/core/exts/ansys.geometry.service/docs/README.md +0 -13
  26. ansys/pyensight/core/exts/ansys.geometry.service/docs/index.rst +0 -18
  27. ansys/pyensight/core/exts/ansys.geometry.serviceui/ansys/geometry/serviceui/__init__.py +0 -1
  28. ansys/pyensight/core/exts/ansys.geometry.serviceui/ansys/geometry/serviceui/extension.py +0 -193
  29. ansys/pyensight/core/exts/ansys.geometry.serviceui/config/extension.toml +0 -49
  30. ansys/pyensight/core/exts/ansys.geometry.serviceui/data/icon.png +0 -0
  31. ansys/pyensight/core/exts/ansys.geometry.serviceui/data/preview.png +0 -0
  32. ansys/pyensight/core/exts/ansys.geometry.serviceui/docs/CHANGELOG.md +0 -11
  33. ansys/pyensight/core/exts/ansys.geometry.serviceui/docs/README.md +0 -13
  34. ansys/pyensight/core/exts/ansys.geometry.serviceui/docs/index.rst +0 -18
  35. ansys/pyensight/core/utils/resources/Materials/Fieldstone/Fieldstone_BaseColor.png +0 -0
  36. ansys/pyensight/core/utils/resources/Materials/Fieldstone/Fieldstone_N.png +0 -0
  37. ansys/pyensight/core/utils/resources/Materials/Fieldstone/Fieldstone_ORM.png +0 -0
  38. ansys/pyensight/core/utils/resources/Materials/Fieldstone.mdl +0 -54
  39. ansys_pyensight_core-0.8.8.dist-info/RECORD +0 -52
  40. {ansys_pyensight_core-0.8.8.dist-info → ansys_pyensight_core-0.8.10.dist-info}/LICENSE +0 -0
  41. {ansys_pyensight_core-0.8.8.dist-info → ansys_pyensight_core-0.8.10.dist-info}/WHEEL +0 -0
@@ -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 time
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
- from ansys.pyensight.core import enshell_grpc
31
+ import docker
40
32
  except ModuleNotFoundError: # pragma: no cover
41
- raise RuntimeError("The enshell_grpc must be installed for DockerLauncher")
33
+ raise RuntimeError("The docker module must be installed for DockerLauncher")
42
34
  except Exception: # pragma: no cover
43
- raise RuntimeError("Cannot initialize enshell_grpc")
44
-
45
-
46
- try:
47
- from simple_upload_server.client import Client
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
- simple_upload_server_is_available = True # pragma: no cover
50
- except Exception:
51
- simple_upload_server_is_available = False
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[Any] = None
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
- if not set(("grpc_private", "http", "ws")).issubset(self._pim_instance.services):
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["grpc_private"] = self._get_host_port(
151
- self._pim_instance.services["grpc_private"].uri
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._get_file_service()
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
- ports = self._find_unused_ports(4, avoid=[1999])
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
- try:
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
- try:
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
 
@@ -355,6 +355,38 @@ class EnShellGRPC(object):
355
355
  else:
356
356
  return self.run_command_with_env(command_string, ensight_env) # pragma: no cover
357
357
 
358
+ def start_ensight_server(
359
+ self, ensight_args: Optional[str] = None, ensight_env: Optional[str] = None
360
+ ):
361
+ """Tell EnShell to start the EnSight server.
362
+
363
+ The string will be sent to EnShell via the EnShellService::run_command()
364
+ gRPC call. An IOError exception may be thrown
365
+ if there's a gRPC communication problem. The response
366
+ is the tuple of the EnShell return code and return string.
367
+
368
+ Parameters
369
+ ----------
370
+ ensight_args : Optional[str], optional
371
+ ensight_args arguments for the ensight command line, by default None
372
+
373
+ Returns
374
+ -------
375
+ Tuple
376
+ A tuple of (int, string) for (returnCode, returnString)
377
+ """
378
+ self.connect()
379
+ command_string = (
380
+ f"start_app OTHER /ansys_inc/v{self.ansys_version()}/CEI/bin/ensight_server"
381
+ )
382
+ if ensight_args and (ensight_args != ""):
383
+ command_string += " " + ensight_args
384
+
385
+ if ensight_env is None or ensight_env == "": # pragma: no cover
386
+ return self.run_command(command_string)
387
+ else:
388
+ return self.run_command_with_env(command_string, ensight_env) # pragma: no cover
389
+
358
390
  # @brief
359
391
  #
360
392
  # @param cmd The command line