Skip to content
Open
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ MUJOCO_LOG.TXT
# Agent session logs (never committed)
tmp/

# Local unpacked toolchain/dependency trees are not repository inputs.
.local-deps/

# SO-101 leader-gripper and follower-arm assets, fetched by
# scripts/fetch-so-arm.sh into the package's assets directory, so only the
# authored wrapper XML is tracked.
Expand Down
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,8 @@ pre-commit install --hook-type commit-msg
- Format touched Python source with `ruff format` before staging. If the
pre-commit hook rewrites a file, restage it and rerun the hooks.
- **REUSE:** files covered by the REUSE hook need **`SPDX-FileCopyrightText`** and **`SPDX-License-Identifier`** in the form the repo already uses (for example the HTML comment block at the top of `README.md` also applies to **`AGENTS.md`** and similar docs).
- Keep local dependency or toolchain trees outside the repository or ignored:
the REUSE `--fix` hook enumerates non-ignored files and can rewrite them.
- **C++ formatting is enforced by CI, not pre-commit.** The hook set runs `ruff` for Python but does **not** run `clang-format`; CI (`build-ubuntu.yml`) installs **`clang-format-14`** and rejects unformatted C++ as `-Wclang-format-violations`. Before pushing, format touched C++ with the system `clang-format` (match CI's version 14) and verify:

```bash
Expand Down
3 changes: 2 additions & 1 deletion examples/sharpa_avatar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ cmake -B build \
cmake --build build --parallel
cmake --install build
uv pip install -e ./examples/sharpa_avatar
python -m isaaccapture_examples.sharpa_avatar
python -m isaaccapture_examples.sharpa_avatar --print-health
```

Open the URL it prints (default <http://127.0.0.1:8080>). The left hand is cyan
Expand All @@ -43,6 +43,7 @@ above first. `uv pip install -e` pulls `viser`; CloudXR comes from the
```text
--no-haptic disable pinch feedback
--no-viz terminal + haptic only (no browser view)
--print-health print Sharpa device health initially and on changes
--host / --port viser bind (default 127.0.0.1:8080)
--no-launch-plugin connect to an already running plugin
--no-launch-cloudxr-runtime connect to an already running CloudXR runtime
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@

from .live_view import main

sys.exit(main())
try:
sys.exit(main())
except KeyboardInterrupt:
pass
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,21 @@ def _fmt_optional(group, *, kind: str, hand_input_index) -> str:
return f"error:{type(exc).__name__}"


def _print_health_changes(snapshot, previous: dict[str, tuple]) -> None:
for device in snapshot.devices:
if not device.id.startswith(f"{PLUGIN_ROOT_ID}/"):
continue
current = (device.status, device.reason, device.error)
if previous.get(device.id) == current:
continue
previous[device.id] = current
detail = f": {device.error}" if device.error else ""
print(
f"[HEALTH] {device.id}: {device.status.value} "
f"({device.reason.value}){detail}"
)


def _build_haptic_pipeline(hands, mapping, sinks, hands_source_cls) -> None:
from isaaccapture.haptic_devices.glove import haptic_glove_device
from isaaccapture.retargeters.tactile_retargeters import (
Expand Down Expand Up @@ -489,6 +504,11 @@ def main() -> int:
default=DEFAULT_PRINT_HZ,
help="Terminal status rate. 0 silences prints.",
)
parser.add_argument(
"--print-health",
action="store_true",
help="Print Sharpa device health initially and whenever it changes.",
)
parser.add_argument(
"--haptic",
action=argparse.BooleanOptionalAction,
Expand Down Expand Up @@ -621,6 +641,7 @@ def main() -> int:
print_period = (1.0 / args.print_hz) if args.print_hz > 0 else None
next_print = time.monotonic()
last_offline_hint = 0.0
previous_health: dict[str, tuple] = {}
upright = not args.world_frame

print("=" * 72)
Expand All @@ -644,13 +665,21 @@ def main() -> int:
print(f" viser: http://{args.host}:{args.port} (Ctrl+C to exit)")
else:
print(" viser: off (--no-viz); terminal + haptic only. Ctrl+C to exit.")
print(f" health: {'changes' if args.print_health else 'off'}")
print("=" * 72)

with CloudXRLauncher.launch_context(args), TeleopSession(config) as session:
if args.viz:
_server, viz_left, viz_right = _start_viser(args)
while True:
result = session.step()
try:
result = session.step()
except Exception:
if args.print_health:
_print_health_changes(session.get_status(), previous_health)
raise
if args.print_health:
_print_health_changes(session.get_status(), previous_health)
n_l = n_r = 0
if viz_left is not None and viz_right is not None:
pts, mask, n_l = _hand_points(
Expand Down
6 changes: 6 additions & 0 deletions src/plugins/sharpa_avatar/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ SPDX-License-Identifier: Apache-2.0
- OpenXR setup is required for publication; initialization errors must abort
construction instead of leaving an Avatar-only idle plugin.
- Keep initialization non-blocking; `update()` owns retries for absent gloves.
- In pinned SDK 1.7.3 wireless mode, `DeviceInfo.online` means recent joint
activity, not an independent physical/RF connection signal. Report it as an
SDK stream signal and keep sample freshness and USB dongle presence separate.
- Track health freshness independently for HUMAN, RAW, and ROBOT outputs; one
advancing category must not keep another category healthy. Replace cached SDK
inventory only after a complete refresh succeeds.
- Keep the static HUMAN-to-OpenXR index table synchronized with the pinned
production SDK's landmark order. Do not read joint names from
`sdk_config.json` at runtime or fill unsupported OpenXR slots with neighbouring
Expand Down
17 changes: 17 additions & 0 deletions src/plugins/sharpa_avatar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,23 @@ The gloves do not provide a world-space wrist pose. Downstream consumers that
need a display frame (the example) place both skeletons locally; a headset
wrist source is used when one is available.

## Health reporting

When launched by `TeleopSession`, the plugin reports the USB dongle and every
manifest data path through the standard plugin health interface. Left and right
states are independent. A path is connected only while Avatar SDK
`DeviceInfo.online` is true and fresh samples are arriving; missing current data
is reported as degraded rather than diagnosed as a physical glove disconnect.
HUMAN, RAW, and ROBOT freshness are tracked independently from their sample
timestamps. A path becomes stale after three seconds without a newer timestamp;
the elapsed timeout uses `std::chrono::steady_clock` and is unaffected by wall
clock adjustments.

In the pinned Avatar SDK 1.7.3 wireless transport, `DeviceInfo.online` follows
recent joint-frame activity. It does not independently distinguish an RF-linked
glove that stopped streaming from a disconnected glove. Dongle insertion is
reported separately from Linux USB enumeration (`1915:520f`).

## Troubleshooting

| Symptom | Resolution |
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/sharpa_avatar/app/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ AvatarPluginConfig parse_args(int argc, char** argv)
}
else if (starts_with(arg, "--plugin-root-id="))
{
// Injected by the PluginManager; unused by this plugin.
config.monitoring_plugin_root_id = arg.substr(std::string("--plugin-root-id=").size());
}
else if (!starts_with(arg, "--"))
{
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/sharpa_avatar/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

add_library(avatar_plugin_core STATIC
avatar_glove_collection.hpp
avatar_health.cpp
avatar_hand_tracking_plugin.cpp
inc/avatar/avatar_health.hpp
inc/avatar/avatar_hand_tracking_plugin.hpp
)

Expand Down
Loading