Skip to content

Commit a6b3660

Browse files
committed
feat: enable launcher on Windows
1 parent f9aa895 commit a6b3660

File tree

3 files changed

+183
-12
lines changed

3 files changed

+183
-12
lines changed

src/ansys/geometry/core/connection/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
from ansys.geometry.core.connection.launcher import (
5151
launch_docker_modeler,
5252
launch_modeler,
53+
launch_modeler_with_core_service,
5354
launch_modeler_with_discovery,
5455
launch_modeler_with_discovery_and_pimlight,
5556
launch_modeler_with_geometry_service,

src/ansys/geometry/core/connection/launcher.py

Lines changed: 146 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ def launch_modeler(mode: str = None, **kwargs: dict | None) -> "Modeler":
7575
* For ``"docker"`` mode, see the :func:`launch_docker_modeler` method.
7676
* For ``"geometry_service"`` mode, see the
7777
:func:`launch_modeler_with_geometry_service` method.
78+
* For ``"core_service"`` mode, see the :func:`launch_modeler_with_core_service` method.
7879
* For ``"spaceclaim"`` mode, see the :func:`launch_modeler_with_spaceclaim` method.
7980
* For ``"discovery"`` mode, see the :func:`launch_modeler_with_discovery` method.
8081
@@ -108,6 +109,8 @@ def _launch_with_launchmode(mode: str, **kwargs: dict | None) -> "Modeler":
108109
* ``"docker"``: Launches the ``Modeler`` service locally using Docker.
109110
* ``"geometry_service"``: Launches the ``Modeler`` service locally using the
110111
Ansys Geometry Service.
112+
* ``"core_service"``: Launches the ``Modeler`` service locally using the
113+
Ansys Geometry Core Service.
111114
* ``"spaceclaim"``: Launches the ``Modeler`` service locally using Ansys SpaceClaim.
112115
* ``"discovery"``: Launches the ``Modeler`` service locally using Ansys Discovery.
113116
@@ -132,6 +135,8 @@ def _launch_with_launchmode(mode: str, **kwargs: dict | None) -> "Modeler":
132135
return launch_remote_modeler(**kwargs)
133136
elif mode == "docker":
134137
return launch_docker_modeler(**kwargs)
138+
elif mode == "core_service":
139+
return launch_modeler_with_core_service(**kwargs)
135140
elif mode == "geometry_service":
136141
return launch_modeler_with_geometry_service(**kwargs)
137142
elif mode == "spaceclaim":
@@ -167,8 +172,9 @@ def _launch_with_automatic_detection(**kwargs: dict | None) -> "Modeler":
167172
#
168173
# 1. Check if PyPIM is configured and if the environment is configured for it.
169174
# 2. Check if Docker is installed and if the environment is configured for it.
170-
# 3. If you are on a Windows machine:
171-
# - check if the Ansys Geometry service is installed.
175+
# 3. Check if the Ansys Geometry Core service is installed. (OS agnostic)
176+
# 4. If you are on a Windows machine:
177+
# - check if the Ansys Geometry DMS service is installed.
172178
# - check if Ansys SpaceClaim is installed.
173179
# - check if Ansys Discovery is installed.
174180

@@ -189,15 +195,26 @@ def _launch_with_automatic_detection(**kwargs: dict | None) -> "Modeler":
189195
" Trying to start the Geometry service locally."
190196
)
191197

198+
try:
199+
LOG.info("Starting Geometry Core service locally.")
200+
return launch_modeler_with_core_service(**kwargs)
201+
except Exception:
202+
LOG.warning(
203+
"The Geometry Core service could not be started locally."
204+
" Trying to start Geometry DMS service locally."
205+
if os.name == "nt"
206+
else ""
207+
)
208+
192209
# If we are on a Windows machine, we can try to start the Geometry service locally,
193210
# through various methods: Geometry service, SpaceClaim, Discovery.
194211
if os.name == "nt":
195212
try:
196-
LOG.info("Starting Geometry service locally.")
213+
LOG.info("Starting Geometry DMS service locally.")
197214
return launch_modeler_with_geometry_service(**kwargs)
198215
except Exception:
199216
LOG.warning(
200-
"The Geometry service could not be started locally."
217+
"The Geometry DMS service could not be started locally."
201218
" Trying to start Ansys SpaceClaim locally."
202219
)
203220

@@ -962,3 +979,128 @@ def _launch_pim_instance(
962979
logging_level=client_log_level,
963980
logging_file=client_log_file,
964981
)
982+
983+
984+
def launch_modeler_with_core_service(
985+
product_version: int = None,
986+
host: str = "localhost",
987+
port: int = None,
988+
enable_trace: bool = False,
989+
timeout: int = 60,
990+
server_log_level: int = 2,
991+
client_log_level: int = logging.INFO,
992+
server_logs_folder: str = None,
993+
client_log_file: str = None,
994+
**kwargs: dict | None,
995+
) -> "Modeler":
996+
"""Start the Geometry Core service locally using the ``ProductInstance`` class.
997+
998+
When calling this method, a standalone Geometry Core service is started.
999+
By default, if an endpoint is specified (by defining `host` and `port` parameters)
1000+
but the endpoint is not available, the startup will fail. Otherwise, it will try to
1001+
launch its own service.
1002+
1003+
Parameters
1004+
----------
1005+
product_version: int, optional
1006+
The product version to be started. Goes from v25.2 to
1007+
the latest. Default is ``None``.
1008+
If a specific product version is requested but not installed locally,
1009+
a SystemError will be raised.
1010+
1011+
**Ansys products versions and their corresponding int values:**
1012+
1013+
* ``252`` : Ansys 25R2
1014+
* ``261`` : Ansys 26R1
1015+
host: str, optional
1016+
IP address at which the service will be deployed. By default,
1017+
its value will be ``localhost``.
1018+
port : int, optional
1019+
Port at which the service will be deployed. By default, its
1020+
value will be ``None``.
1021+
enable_trace : bool, optional
1022+
Boolean enabling the logs trace on the service console window.
1023+
By default its value is ``False``.
1024+
timeout : int, optional
1025+
Timeout for starting the backend startup process. The default is 60.
1026+
server_log_level : int, optional
1027+
Backend's log level from 0 to 3:
1028+
0: Chatterbox
1029+
1: Debug
1030+
2: Warning
1031+
3: Error
1032+
1033+
The default is ``2`` (Warning).
1034+
client_log_level : int, optional
1035+
Logging level to apply to the client. By default, INFO level is used.
1036+
Use the logging module's levels: DEBUG, INFO, WARNING, ERROR, CRITICAL.
1037+
server_logs_folder : str, optional
1038+
Sets the backend's logs folder path. If nothing is defined,
1039+
the backend will use its default path.
1040+
client_log_file : str, optional
1041+
Sets the client's log file path. If nothing is defined,
1042+
the client will log to the console.
1043+
**kwargs : dict, default: None
1044+
Placeholder to prevent errors when passing additional arguments that
1045+
are not compatible with this method.
1046+
1047+
Returns
1048+
-------
1049+
Modeler
1050+
Instance of the Geometry Core service.
1051+
1052+
Raises
1053+
------
1054+
ConnectionError
1055+
If the specified endpoint is already in use, a connection
1056+
error will be raised.
1057+
SystemError
1058+
If there is not an Ansys product 25.2 version or later installed
1059+
a SystemError will be raised.
1060+
1061+
Examples
1062+
--------
1063+
Starting a geometry core service with the default parameters and getting back a ``Modeler``
1064+
object:
1065+
1066+
>>> from ansys.geometry.core import launch_modeler_with_core_service
1067+
>>> modeler = launch_modeler_with_core_service()
1068+
1069+
Starting a geometry service, on address ``10.171.22.44``, port ``5001``, with chatty
1070+
logs, traces enabled and a ``300`` seconds timeout:
1071+
1072+
>>> from ansys.geometry.core import launch_modeler_with_core_service
1073+
>>> modeler = launch_modeler_with_core_service(host="10.171.22.44",
1074+
port=5001,
1075+
enable_trace= True,
1076+
timeout=300,
1077+
server_log_level=0)
1078+
"""
1079+
# if api_version is passed, throw a warning saying that it is not used
1080+
if "api_version" in kwargs:
1081+
LOG.warning(
1082+
"The 'api_version' parameter is not used in 'launch_modeler_with_core_service'. "
1083+
"Please remove it from the arguments."
1084+
)
1085+
1086+
# If we are in a Windows environment, we are going to write down the server
1087+
# logs in the %PUBLIC%/Documents/Ansys/GeometryService folder.
1088+
if os.name == "nt" and server_logs_folder is None:
1089+
# Writing to the "Public" folder by default - no write permissions specifically required.
1090+
server_logs_folder = Path(os.getenv("PUBLIC"), "Documents", "Ansys", "GeometryService")
1091+
LOG.info(f"Writing server logs to the default folder at {server_logs_folder}.")
1092+
1093+
return prepare_and_start_backend(
1094+
BackendType.LINUX_SERVICE,
1095+
product_version=product_version,
1096+
host=host,
1097+
port=port,
1098+
enable_trace=enable_trace,
1099+
api_version=ApiVersions.LATEST,
1100+
timeout=timeout,
1101+
server_log_level=server_log_level,
1102+
client_log_level=client_log_level,
1103+
server_logs_folder=server_logs_folder,
1104+
client_log_file=client_log_file,
1105+
specific_minimum_version=252,
1106+
)

src/ansys/geometry/core/connection/product_instance.py

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@
4141

4242

4343
WINDOWS_GEOMETRY_SERVICE_FOLDER = "GeometryService"
44-
"""Default Geometry Service's folder name into the unified installer."""
44+
"""Default Geometry Service's folder name into the unified installer (DMS)."""
45+
46+
CORE_WINDOWS_GEOMETRY_SERVICE_FOLDER = "CoreGeometryService"
47+
"""Default Geometry Service's folder name into the unified installer (Core Service)."""
4548

4649
DISCOVERY_FOLDER = "Discovery"
4750
"""Default Discovery's folder name into the unified installer."""
@@ -62,7 +65,10 @@
6265
"""
6366

6467
GEOMETRY_SERVICE_EXE = "Presentation.ApiServerDMS.exe"
65-
"""The Windows Geometry Service's filename."""
68+
"""The Windows Geometry Service's filename (DMS)."""
69+
70+
CORE_GEOMETRY_SERVICE_EXE = "Presentation.ApiServerLinux.dll"
71+
"""The Windows Geometry Service's filename (Core Service)."""
6672

6773
DISCOVERY_EXE = "Discovery.exe"
6874
"""The Ansys Discovery's filename."""
@@ -179,6 +185,7 @@ def prepare_and_start_backend(
179185
client_log_level: int = logging.INFO,
180186
server_logs_folder: str = None,
181187
client_log_file: str = None,
188+
specific_minimum_version: int = None,
182189
log_level: int = None, # DEPRECATED
183190
logs_folder: str = None, # DEPRECATED
184191
) -> "Modeler":
@@ -232,6 +239,9 @@ def prepare_and_start_backend(
232239
client_log_file : str, optional
233240
Sets the client's log file path. If nothing is defined,
234241
the client will log to the console.
242+
specific_minimum_version : int, optional
243+
Sets a specific minimum version to be checked. If this is not defined,
244+
the minimum version will be set to 24.1.0.
235245
log_level : int, optional
236246
DEPRECATED. Use ``server_log_level`` instead.
237247
logs_folder : str, optional
@@ -285,7 +295,7 @@ def prepare_and_start_backend(
285295
product_version = get_latest_ansys_installation()[0]
286296

287297
# Verify that the minimum version is installed.
288-
_check_minimal_versions(product_version)
298+
_check_minimal_versions(product_version, specific_minimum_version)
289299

290300
if server_logs_folder is not None:
291301
# Verify that the user has write permissions to the folder and that it exists.
@@ -348,6 +358,18 @@ def prepare_and_start_backend(
348358
GEOMETRY_SERVICE_EXE,
349359
)
350360
)
361+
# This should be modified to Windows Core Service in the future
362+
elif backend_type == BackendType.LINUX_SERVICE:
363+
args.append("dotnet")
364+
env_copy["ANS_DSCO_REMOTE_IP"] = host
365+
env_copy["ANS_DSCO_REMOTE_PORT"] = str(port)
366+
args.append(
367+
Path(
368+
installations[product_version],
369+
CORE_WINDOWS_GEOMETRY_SERVICE_FOLDER,
370+
CORE_GEOMETRY_SERVICE_EXE,
371+
)
372+
)
351373
else:
352374
raise RuntimeError(
353375
f"Cannot connect to backend {backend_type.name} using ``prepare_and_start_backend()``"
@@ -491,15 +513,21 @@ def __start_program(args: list[str], local_env: dict[str, str]) -> subprocess.Po
491513
)
492514

493515

494-
def _check_minimal_versions(latest_installed_version: int) -> None:
516+
def _check_minimal_versions(
517+
latest_installed_version: int, specific_minimum_version: int | None
518+
) -> None:
495519
"""Check client is compatible with Ansys Products.
496520
497-
Check that at least V241 is installed.
521+
Check that at least V241 is installed. Or, if a specific version is requested,
522+
check that it is installed.
498523
"""
499-
if abs(latest_installed_version) < 241:
524+
min_ver = specific_minimum_version or 241
525+
if abs(latest_installed_version) < min_ver:
526+
# Split the version into its components.
527+
major, minor = divmod(min_ver, 10)
500528
msg = (
501-
"PyAnsys Geometry is compatible with Ansys Products from version 24.1.0. "
502-
+ "Please install Ansys products 24.1.0 or later."
529+
f"PyAnsys Geometry is compatible with Ansys Products from version {major}.{minor}.0. "
530+
+ f"Please install Ansys products {major}.{minor}.0 or later."
503531
)
504532
raise SystemError(msg)
505533

0 commit comments

Comments
 (0)