Skip to content

Commit

Permalink
Fix/improve feedback (#394)
Browse files Browse the repository at this point in the history
  • Loading branch information
randallfrank authored Jul 29, 2024
1 parent f3c4125 commit 118d9b2
Show file tree
Hide file tree
Showing 23 changed files with 410 additions and 329 deletions.
Binary file added doc/source/_static/omniverse_app_exts.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/source/_static/omniverse_app_paths.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 34 additions & 12 deletions doc/source/user_guide/omniverse_info.rst
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,18 @@ scene to Omniverse.
``git://github.com/ansys/pyensight.git?branch=main&dir=exts``.


Developers: Running a "dev" build from the Command Line
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Developers: Running development builds
--------------------------------------

There are several different ways for developers working on these
features to debug and test them. There is a command line approach
perhaps more suited to the pyensight developer and there is an
Omniverse tool GUI approach that can be useful when looking to
develop/extend the UI kits.


From the Command Line
^^^^^^^^^^^^^^^^^^^^^

Omniverse kits can be run as command line tools and
the ``ansys.geometry.service`` is designed to support this mode
Expand All @@ -137,15 +147,15 @@ using the ``Settings`` option:
.. image:: /_static/omniverse_create_location.png

Consider an example where the create app has been installed and the
file ``C:\Users\me\AppData\Local\ov\pkg\create-2023.2.5\kit.bat``
file ``C:\Users\user1\AppData\Local\ov\pkg\create-2023.2.5\kit.bat``
exists. A copy of the pyensight repo is located and built here:
``D:\repos\pyensight``. With these conditions, one can run the extension
from the command line like this:

.. code-block:: bat
cd "C:\Users\me\AppData\Local\ov\pkg\create-2023.2.5"
.\kit.bat --ext-folder "D:\repos\pyensight\exts" --enable ansys.geometry.service --/exts/ansys.geometry.service/help=1
cd "C:\Users\user1\AppData\Local\ov\pkg\create-2023.2.5"
.\kit.bat --ext-folder "D:\repos\pyensight\src\ansys\pyensight\core\exts" --enable ansys.geometry.service --/exts/ansys.geometry.service/help=1
Will generate the following output in the logs:
Expand All @@ -172,17 +182,29 @@ Will generate the following output in the logs:
Documenting the various kit command line options. Using the ``run=1`` option will launch the server from
from the command line. This version of the service will be run using the version of the pyensight wheel
from the command line. This version of the service will be run using the version of the pyensight module
installed in the specified ``--ext-folder``. When run as above, the service will use the
latest release of the ansys.pyensight.core wheel. If a local build is located here:
``D:\repos\pyensight\dist\ansys_pyensight_core-0.9.0.dev0-py3-none-any.whl`` it can be
used in the above kit by installing it into the Omniverse kit Python:
latest released of the ansys.pyensight.core wheel. It is important the the ``--ext-folder`` option
point to the ``exts`` directory inside of the ``ansys\pyensight\core`` directories as this will
cause the kit to use the ``ansys.pyensight.core`` module from the directories above the kit
instead of the any version installed in the kit Python itself.


.. code-block:: bat
From an Omniverse Application GUI
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

This approach is very similar to the CLI approach in that one needs to get the GUI application
to use the kit from either a source code checkout or perhaps from a local EnSight install.
In either case, the key point is to add the same directory pointed out earlier to the GUI application.

For example, if one has a copy of the pyensight repo checked out as in the previous CLI example, the
key directory will be ``D:\repos\pyensight\src\ansys\pyensight\core\exts``. This pathname can be
added to the extensions path in an application like "Composer" through this GUI:

.\kit\python.bat -m pip install D:\repos\pyensight\dist\ansys_pyensight_core-0.9.0.dev0-py3-none-any.whl
.. image:: /_static/omniverse_app_paths.png

With the path in place, the kits will show up in the Third-party extensions list and can be
activated in the GUI.

This version will be used instead of the older version in the PyPi repository.
.. image:: /_static/omniverse_app_exts.png

122 changes: 110 additions & 12 deletions exts/ansys.geometry.service/ansys/geometry/service/extension.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,81 @@
from importlib import reload
import json
import logging
import os
import subprocess
import sys
import tempfile
from typing import Optional
from urllib.parse import urlparse
import uuid

import carb.settings
import omni.ext
import omni.kit.app
import omni.kit.pipapi
import psutil

"""
For development environments, it can be useful to use a "future versioned"
build of ansys-pyensight-core. If the appropriate environmental variables
are set, a pip install from another repository can be forced.
"""
extra_args = []
if "ANSYS_PYPI_INDEX_URL" in os.environ:
extra_args.append(os.environ["ANSYS_PYPI_INDEX_URL"])

if os.environ.get("ANSYS_PYPI_REINSTALL", "") == "1":
extra_args.extend(["--upgrade", "--no-deps", "--no-cache-dir", "--force-reinstall", "--pre"])

logging.warning("ansys.geometry.server - Forced reinstall ansys-pyensight-core")
omni.kit.pipapi.install("ansys-pyensight-core", extra_args=extra_args)

try:
# Checking to see if we need to install the module
import ansys.pyensight.core
import ansys.pyensight.core.utils.dsg_server as tmp_dsg_server # noqa: F401
import ansys.pyensight.core.utils.omniverse_dsg_server as tmp_ov_dsg_server # noqa: F401
except ModuleNotFoundError:
logging.warning("ansys.geometry.server - Installing ansys-pyensight-core")
omni.kit.pipapi.install("ansys-pyensight-core")
omni.kit.pipapi.install("ansys-pyensight-core", extra_args=extra_args)

"""
If we have a local copy of the module, the above installed the correct
dependencies, but we want to use the local copy. Do this by prefixing
the path and (re)load the modules. The pyensight wheel includes the
following for this file:
ansys\pyensight\core\exts\ansys.geometry.service\ansys\geometry\service\extension.py
"""
kit_dir = __file__
for _ in range(5):
kit_dir = os.path.dirname(kit_dir)
"""
At this point, the name should be: {something}\ansys\pyensight\core\exts
Check for the fragments in the right order.
"""
offsets = []
for name in ["ansys", "pyensight", "core", "exts"]:
offsets.append(kit_dir.find(name))
# if the order of the names in correct and there is no -1 in the offsets, we found it
if (sorted(offsets) == offsets) and (sorted(offsets)[0] != -1):
# name of 'ansys/pyensight/core' directory. We need three levels above.
name = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(kit_dir))))
sys.path.insert(0, name)
logging.info(f"Using ansys.pyensight.core from: {name}")

# at this point, we may need to repeat the imports that make have failed earlier
import ansys.pyensight.core # noqa: F811, E402
import ansys.pyensight.core.utils.dsg_server as dsg_server # noqa: E402
import ansys.pyensight.core.utils.omniverse_dsg_server as ov_dsg_server # noqa: E402

# force a reload if we changed the path or had a partial failure that lead
# to a pipapi install.
_ = reload(ansys.pyensight.core)
_ = reload(ansys.pyensight.core.utils)
_ = reload(ansys.pyensight.core.utils.dsg_server)
_ = reload(ansys.pyensight.core.utils.omniverse_dsg_server)

logging.warning(f"Using ansys.pyensight.core from: {ansys.pyensight.core.__file__}")


def find_kit_filename() -> Optional[str]:
Expand Down Expand Up @@ -93,6 +151,12 @@ def __init__(self, *args, **kwargs) -> None:
self._version = "unknown"
self._shutdown = False
self._server_process = None
self._status_filename: str = ""

@property
def pyensight_version(self) -> str:
"""The ansys.pyensight.core version"""
return ansys.pyensight.core.VERSION

@property
def dsg_uri(self) -> str:
Expand Down Expand Up @@ -344,27 +408,61 @@ def launch_server(self) -> None:
cmd.append(f"--/exts/ansys.geometry.service/dsgUrl={self.dsg_uri}")
cmd.append("--/exts/ansys.geometry.service/run=1")
env_vars = os.environ.copy()
# we are launching the kit from an Omniverse app. In this case, we
# inform the kit instance of:
# (1) the name of the "server status" file, if any
self._new_status_file()
env_vars["ANSYS_OV_SERVER_STATUS_FILENAME"] = self._status_filename
working_dir = os.path.join(os.path.dirname(ansys.pyensight.core.__file__), "utils")
self._server_process = subprocess.Popen(cmd, close_fds=True, env=env_vars, cwd=working_dir)

def _new_status_file(self, new=True) -> None:
"""
Remove any existing status file and create a new one if requested.
Parameters
----------
new : bool
If True, create a new status file.
"""
if self._status_filename:
try:
os.remove(self._status_filename)
except OSError:
self.warning(f"Unable to delete the status file: {self._status_filename}")
self._status_filename = ""
if new:
self._status_filename = os.path.join(
tempfile.gettempdir(), str(uuid.uuid1()) + "_gs_status.txt"
)

def read_status_file(self) -> dict:
"""Read the status file and return its contents as a dictionary.
Note: this can fail if the file is being written to when this call is made, so expect
failures.
Returns
-------
Optional[dict]
A dictionary with the fields 'status', 'start_time', 'processed_buffers', 'total_buffers' or empty
"""
if not self._status_filename:
return {}
try:
with open(self._status_filename, "r") as status_file:
data = json.load(status_file)
except Exception:
return {}
return data

def run_server(self) -> None:
"""
Run a DSG to Omniverse server in process.
Note: this method does not return until the DSG connection is dropped or
self.stop_server() has been called.
"""
try:
import ansys.pyensight.core.utils.dsg_server as dsg_server
import ansys.pyensight.core.utils.omniverse_dsg_server as ov_dsg_server
except ImportError as e:
self.error(f"Unable to load DSG service core: {str(e)}")
return

# Note: This is temporary. The correct fix will be included in
# the pyensight 0.8.5 wheel. The OmniverseWrapper assumes the CWD
# to be the directory with the "resource" directory.
os.chdir(os.path.dirname(ov_dsg_server.__file__))

# Build the Omniverse connection
omni_link = ov_dsg_server.OmniverseWrapper(path=self._omni_uri, verbose=1)
Expand Down
2 changes: 1 addition & 1 deletion exts/ansys.geometry.service/config/extension.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
# Semantic Versioning is used: https://semver.org/
version = "0.8.7"
version = "0.9.0-dev0"

# Lists people or organizations that are considered the "authors" of the package.
authors = ["ANSYS"]
Expand Down
3 changes: 3 additions & 0 deletions exts/ansys.geometry.service/docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).


## [0.8.9] - 2024-07-25
- Improved status feedback

## [0.8.7] - 2024-07-12
- Support for time varying data and temporal scaling

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import logging
import threading
import time
from typing import Any, Optional
from urllib.parse import urlparse

Expand Down Expand Up @@ -111,16 +113,28 @@ def on_startup(self, ext_id: str) -> None:
if self.service is None:
self.error("Unable to find ansys.geometry.service instance")
self.build_ui()
self._update_callback()

def _update_callback(self) -> None:
self.update_ui()
threading.Timer(0.5, self._update_callback).start()

def update_ui(self) -> None:
status = self.service.read_status_file()
if self._connected:
self._connect_w.text = "Disconnect from DSG Server"
self._label_w.text = f"Connected to: {self.service.dsg_uri}"
tmp = f"Connected to: {self.service.dsg_uri}"
if status.get("status", "idle") == "working":
count = status.get("processed_buffers", 0)
total = status.get("total_buffers", 0)
dt = time.time() - status.get("start_time", 0.0)
tmp = f"Transfer: {count} of {total} : {dt:.2f}s"
self._label_w.text = tmp
else:
self._connect_w.text = "Connect to DSG Server"
self._label_w.text = "No connected DSG server"
self._update_w.enabled = self._connected
self._update_w.enabled = self._connected and (status.get("status", "idle") == "idle")
self._connect_w.enabled = status.get("status", "idle") == "idle"
self._temporal_w.enabled = True
self._vrmode_w.enabled = not self._connected
self._normalize_w.enabled = not self._connected
Expand All @@ -130,7 +144,7 @@ def update_ui(self) -> None:
self._omni_uri_w.enabled = not self._connected

def build_ui(self) -> None:
self._window = ui.Window("ANSYS Geometry Service")
self._window = ui.Window(f"ANSYS Geometry Service ({self.service.pyensight_version})")
with self._window.frame:
with ui.VStack(height=0, spacing=5):
self._label_w = ui.Label("No connected DSG server")
Expand Down
2 changes: 1 addition & 1 deletion exts/ansys.geometry.serviceui/config/extension.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
# Semantic Versioning is used: https://semver.org/
version = "0.8.7"
version = "0.9.0-dev0"

# Lists people or organizations that are considered the "authors" of the package.
authors = ["ANSYS"]
Expand Down
3 changes: 3 additions & 0 deletions exts/ansys.geometry.serviceui/docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).


## [0.8.9] - 2024-07-25
- Improved status feedback

## [0.8.7] - 2024-07-12
- Support for time varying data and temporal scaling

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ build-backend = "flit_core.buildapi"

[project]
name = "ansys-pyensight-core"
version = "0.9.0.dev0"
version = "0.9.0-dev0"
description = "A python wrapper for Ansys EnSight"
readme = "README.rst"
requires-python = ">=3.9,<4"
Expand Down
Loading

0 comments on commit 118d9b2

Please sign in to comment.