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
11 changes: 9 additions & 2 deletions isaaclab_arena/assets/background_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR, ISAACLAB_NUCLEUS_DIR

from isaaclab_arena.assets.background import Background
from isaaclab_arena.assets.lightwheel_utils import acquire_lightwheel_asset
from isaaclab_arena.assets.register import register_asset
from isaaclab_arena.utils.pose import Pose

Expand Down Expand Up @@ -155,8 +156,14 @@ def __init__(self, layout_id: int = 1, style_id: int = 1):

# Lazily download the USD
self.usd_path = str(
floorplan_loader.get_usd(
scene="robocasakitchen", layout_id=layout_id, style_id=style_id, backend="robocasa"
acquire_lightwheel_asset(
floorplan_loader,
floorplan_loader.get_usd,
description=f"{self.name} background layout={layout_id} style={style_id}",
scene="robocasakitchen",
layout_id=layout_id,
style_id=style_id,
backend="robocasa",
)[0]
)
super().__init__()
Expand Down
55 changes: 55 additions & 0 deletions isaaclab_arena/assets/lightwheel_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Copyright (c) 2025-2026, The Isaac Lab Arena Project Developers (https://github.com/isaac-sim/IsaacLab-Arena/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0

import time
from collections.abc import Callable
from typing import Any

_MISSING = object()


def acquire_lightwheel_asset(
loader: Any,
acquire_fn: Callable,
description: str,
attempts: int = 3,
timeout_sec: int | None = 60,
delay_sec: float = 2.0,
**kwargs,
):
"""Acquire a Lightwheel asset with scoped timeout and retry handling."""

assert attempts > 0, "attempts must be positive"
assert delay_sec >= 0, "delay_sec must be non-negative"
Comment thread
xyao-nv marked this conversation as resolved.

client = getattr(loader, "client", None)
old_timeout = getattr(client, "base_timeout", _MISSING)
for attempt in range(1, attempts + 1):
try:
if timeout_sec is not None and old_timeout is not _MISSING:
client.base_timeout = timeout_sec
return acquire_fn(**kwargs)
except Exception as exc:
if not _looks_like_timeout(exc) or attempt == attempts:
raise
print(f"[isaaclab-arena] {description} timed out; retrying {attempt + 1}/{attempts} in {delay_sec:g}s.")
if delay_sec > 0:
time.sleep(delay_sec)
finally:
if old_timeout is not _MISSING:
client.base_timeout = old_timeout

raise AssertionError("unreachable")


def _looks_like_timeout(exc: BaseException) -> bool:
"""Return whether an exception is a timeout."""
current: BaseException | None = exc
while current is not None:
text = f"{type(current).__name__}: {current}".lower()
if "timeout" in text or "timed out" in text:
return True
current = current.__cause__ or current.__context__
Comment thread
xyao-nv marked this conversation as resolved.
return False
55 changes: 43 additions & 12 deletions isaaclab_arena/assets/object_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from isaaclab_arena.affordances.placeable import Placeable
from isaaclab_arena.affordances.pressable import Pressable
from isaaclab_arena.affordances.turnable import Turnable
from isaaclab_arena.assets.lightwheel_utils import acquire_lightwheel_asset
from isaaclab_arena.assets.object import Object
from isaaclab_arena.assets.object_base import ObjectType
from isaaclab_arena.assets.object_utils import (
Expand Down Expand Up @@ -176,8 +177,13 @@ class Microwave(LibraryObject, Openable):

name = "microwave"
tags = ["object", "openable"]
file_path, object_name, metadata = object_loader.acquire_by_registry(
registry_type="fixtures", file_name="Microwave039", file_type="USD"
file_path, object_name, metadata = acquire_lightwheel_asset(
object_loader,
object_loader.acquire_by_registry,
description="microwave asset",
registry_type="fixtures",
file_name="Microwave039",
file_type="USD",
)
usd_path = file_path
object_type = ObjectType.ARTICULATION
Expand Down Expand Up @@ -209,8 +215,13 @@ class CoffeeMachine(LibraryObject, Pressable):

name = "coffee_machine"
tags = ["object", "pressable"]
file_path, object_name, metadata = object_loader.acquire_by_registry(
registry_type="fixtures", file_name="CoffeeMachine108", file_type="USD"
file_path, object_name, metadata = acquire_lightwheel_asset(
object_loader,
object_loader.acquire_by_registry,
description="coffee_machine asset",
registry_type="fixtures",
file_name="CoffeeMachine108",
file_type="USD",
)
usd_path = file_path
object_type = ObjectType.ARTICULATION
Expand Down Expand Up @@ -677,8 +688,13 @@ class Broccoli(LibraryObject):

name = "broccoli"
tags = ["object", "vegetable", "graspable"]
file_path, object_name, metadata = object_loader.acquire_by_registry(
registry_type="objects", registry_name=["broccoli"], file_type="USD"
file_path, object_name, metadata = acquire_lightwheel_asset(
object_loader,
object_loader.acquire_by_registry,
description="broccoli asset",
registry_type="objects",
registry_name=["broccoli"],
file_type="USD",
)
usd_path = file_path
object_type = ObjectType.RIGID
Expand All @@ -704,8 +720,13 @@ class SweetPotato(LibraryObject):

name = "sweet_potato"
tags = ["object", "vegetable", "graspable"]
file_path, object_name, metadata = object_loader.acquire_by_registry(
registry_type="objects", file_name="SweetPotato005", file_type="USD"
file_path, object_name, metadata = acquire_lightwheel_asset(
object_loader,
object_loader.acquire_by_registry,
description="sweet_potato asset",
registry_type="objects",
file_name="SweetPotato005",
file_type="USD",
)
usd_path = file_path
object_type = ObjectType.RIGID
Expand All @@ -732,8 +753,13 @@ class Jug(LibraryObject):

name = "jug"
tags = ["object", "graspable"]
file_path, object_name, metadata = object_loader.acquire_by_registry(
registry_type="objects", file_name="Jug005", file_type="USD"
file_path, object_name, metadata = acquire_lightwheel_asset(
object_loader,
object_loader.acquire_by_registry,
description="jug asset",
registry_type="objects",
file_name="Jug005",
file_type="USD",
)
usd_path = file_path
object_type = ObjectType.RIGID
Expand All @@ -760,8 +786,13 @@ class BeerBottle(LibraryObject):

name = "beer_bottle"
tags = ["object", "graspable"]
file_path, object_name, metadata = object_loader.acquire_by_registry(
registry_type="objects", file_name="beer016", file_type="USD"
file_path, object_name, metadata = acquire_lightwheel_asset(
object_loader,
object_loader.acquire_by_registry,
description="beer_bottle asset",
registry_type="objects",
file_name="beer016",
file_type="USD",
)
usd_path = file_path
object_type = ObjectType.RIGID
Expand Down
67 changes: 67 additions & 0 deletions isaaclab_arena/tests/test_lightwheel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Copyright (c) 2025-2026, The Isaac Lab Arena Project Developers (https://github.com/isaac-sim/IsaacLab-Arena/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0

from pathlib import Path

import pytest

from isaaclab_arena.assets.lightwheel_utils import acquire_lightwheel_asset


def test_acquire_lightwheel_asset_fetches_object():
loader_module = pytest.importorskip("lightwheel_sdk.loader")
object_loader = loader_module.object_loader
old_timeout = object_loader.client.base_timeout

file_path, object_name, metadata = acquire_lightwheel_asset(
object_loader,
object_loader.acquire_by_registry,
"microwave asset",
attempts=2,
delay_sec=0,
registry_type="fixtures",
file_name="Microwave039",
file_type="USD",
)

assert Path(file_path).exists()
assert object_name == "Microwave039"
assert metadata["fileName"].startswith("Microwave039")
assert object_loader.client.base_timeout == old_timeout


def test_acquire_lightwheel_asset_retries_timeout_failure():
class Client:
base_timeout = 10

class Loader:
def __init__(self):
self.client = Client()
self.calls = 0
self.timeouts_seen = []

def acquire(self, **kwargs):
self.calls += 1
self.timeouts_seen.append(self.client.base_timeout)
if self.calls == 1:
raise TimeoutError("read timed out")
return kwargs["asset_name"]

loader = Loader()

result = acquire_lightwheel_asset(
loader,
loader.acquire,
"test asset",
attempts=2,
timeout_sec=45,
delay_sec=0,
asset_name="foo",
)

assert result == "foo"
assert loader.calls == 2
assert loader.timeouts_seen == [45, 45]
assert loader.client.base_timeout == 10
Loading