Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ jobs:
env:
ACCEPT_CLOUDXR_EULA: Y
CXR_BUILD_CONTEXT: ${{ github.workspace }}
CXR_RUNTIME_NETWORK_MODE: bridge
PYTHON_VERSION: ${{ matrix.python_version }}
ISAACTELEOP_PIP_DEBUG: "0"
ISAACTELEOP_PIP_FIND_LINKS: /workspace/install/wheels
Expand Down
2 changes: 1 addition & 1 deletion deps/cloudxr/docker-compose.test.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# Test docker-compose override for running Isaac Teleop tests with CloudXR.
Expand Down
5 changes: 5 additions & 0 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ if(BUILD_TESTING)
# Teleop session manager tests (Python)
add_subdirectory(teleop_session_manager_tests)

# CloudXR tests (Python — requires the built wheel from python/)
if(BUILD_PYTHON_BINDINGS)
add_subdirectory(cloudxr_tests)
endif()

# MCAP tests (C++)
add_subdirectory(mcap_tests/cpp)
endif()
1 change: 1 addition & 0 deletions src/core/cloudxr/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ add_custom_target(cloudxr_python ALL
"${CMAKE_CURRENT_SOURCE_DIR}/__init__.py"
"${CMAKE_CURRENT_SOURCE_DIR}/__main__.py"
"${CMAKE_CURRENT_SOURCE_DIR}/env_config.py"
"${CMAKE_CURRENT_SOURCE_DIR}/launcher.py"
"${CMAKE_CURRENT_SOURCE_DIR}/runtime.py"
"${CMAKE_CURRENT_SOURCE_DIR}/wss.py"
"${CLOUDXR_PYTHON_DIR}/"
Expand Down
4 changes: 4 additions & 0 deletions src/core/cloudxr/python/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
# SPDX-License-Identifier: Apache-2.0

"""CloudXR integration for isaacteleop."""

from .launcher import CloudXRLauncher

__all__ = ["CloudXRLauncher"]
85 changes: 33 additions & 52 deletions src/core/cloudxr/python/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,18 @@
"""Entry point for python -m isaacteleop.cloudxr. Runs CloudXR runtime and WSS proxy; main process winds both down on exit."""

import argparse
import asyncio
import multiprocessing
import os
import signal
import sys
from datetime import datetime, timezone
import time

from isaacteleop import __version__ as isaacteleop_version
from isaacteleop.cloudxr.env_config import EnvConfig
from isaacteleop.cloudxr.runtime import (
check_eula,
latest_runtime_log,
run as runtime_run,
runtime_version,
terminate_or_kill_runtime,
wait_for_runtime_ready,
)
from isaacteleop.cloudxr.wss import run as wss_run
from isaacteleop.cloudxr.env_config import get_env_config
from isaacteleop.cloudxr.launcher import CloudXRLauncher
from isaacteleop.cloudxr.runtime import latest_runtime_log, runtime_version


def _parse_args() -> argparse.Namespace:
"""Parse command-line arguments for the CloudXR runtime entry point."""
parser = argparse.ArgumentParser(description="CloudXR runtime and WSS proxy")
parser.add_argument(
"--cloudxr-install-dir",
Expand All @@ -48,60 +39,50 @@ def _parse_args() -> argparse.Namespace:
return parser.parse_args()


async def _main_async() -> None:
def main() -> None:
"""Launch the CloudXR runtime and WSS proxy, then block until interrupted."""
args = _parse_args()
env_cfg = EnvConfig.from_args(args.cloudxr_install_dir, args.cloudxr_env_config)
check_eula(accept_eula=args.accept_eula or None)
logs_dir_path = env_cfg.ensure_logs_dir()
wss_ts = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H%M%SZ")
wss_log_path = logs_dir_path / f"wss.{wss_ts}.log"

runtime_proc = multiprocessing.Process(target=runtime_run)
runtime_proc.start()

cxr_ver = runtime_version()
print(
f"Running Isaac Teleop \033[36m{isaacteleop_version}\033[0m, CloudXR Runtime \033[36m{cxr_ver}\033[0m"
)

try:
ready = await wait_for_runtime_ready(runtime_proc)
if not ready:
if not runtime_proc.is_alive() and runtime_proc.exitcode != 0:
sys.exit(
runtime_proc.exitcode if runtime_proc.exitcode is not None else 1
)
print("CloudXR runtime failed to start, terminating...")
sys.exit(1)

stop = asyncio.get_running_loop().create_future()

def on_signal() -> None:
if not stop.done():
stop.set_result(None)

loop = asyncio.get_running_loop()
for sig in (signal.SIGINT, signal.SIGTERM):
loop.add_signal_handler(sig, on_signal)
with CloudXRLauncher(
install_dir=args.cloudxr_install_dir,
env_config=args.cloudxr_env_config,
accept_eula=args.accept_eula,
) as launcher:
cxr_ver = runtime_version()
print(
f"Running Isaac Teleop \033[36m{isaacteleop_version}\033[0m, CloudXR Runtime \033[36m{cxr_ver}\033[0m"
)

env_cfg = get_env_config()
logs_dir_path = env_cfg.ensure_logs_dir()
cxr_log = latest_runtime_log() or logs_dir_path
print(
f"CloudXR runtime: \033[36mrunning\033[0m, log file: \033[90m{cxr_log}\033[0m"
)
wss_log = launcher.wss_log_path
print(
f"CloudXR WSS proxy: \033[36mrunning\033[0m, log file: \033[90m{wss_log_path}\033[0m"
f"CloudXR WSS proxy: \033[36mrunning\033[0m, log file: \033[90m{wss_log}\033[0m"
)
print(
f"Activate CloudXR environment in another terminal: \033[1;32msource {env_cfg.env_filepath()}\033[0m"
)
print("\033[33mKeep this terminal open, Ctrl+C to terminate.\033[0m")

await wss_run(log_file_path=wss_log_path, stop_future=stop)
finally:
terminate_or_kill_runtime(runtime_proc)
stop = False

def on_signal(sig, frame):
nonlocal stop
stop = True

signal.signal(signal.SIGINT, on_signal)
signal.signal(signal.SIGTERM, on_signal)

while not stop:
launcher.health_check()
time.sleep(0.1)

print("Stopped.")


if __name__ == "__main__":
asyncio.run(_main_async())
main()
Loading
Loading