"""R1 head video-service control via the robot_state DDS service (api_id 1001). Phone-free equivalent of the Unitree app's Settings → Service Status toggle for the stereo camera: `RobotStateClient.ServiceSwitch("stereo_patch_pc1", on)`. VIDEO ONLY — this never touches motion/loco services. Reuses the app's already-initialized DDS ChannelFactory (arm_controller.init() calls ChannelFactoryInitialize once per process), so the client is created lazily on first use — by then the arm subsystem has set the factory up. If the SDK/DDS isn't available it degrades to a no-op (returns False) instead of raising. """ from __future__ import annotations import threading from typing import Optional from Project.Sanad.core.logger import get_logger log = get_logger("stream_service") STEREO_SERVICE = "stereo_patch_pc1" class StreamService: """Thin, thread-safe wrapper around RobotStateClient for the stereo stream.""" def __init__(self) -> None: self._lock = threading.Lock() self._client = None self._unavailable = False def _client_or_none(self): if self._client is not None: return self._client if self._unavailable: return None try: # Reuses the process-wide ChannelFactory (already initialized by the # arm controller). Does NOT re-init it — that's the single-init rule. from unitree_sdk2py.go2.robot_state.robot_state_client import ( RobotStateClient, ) c = RobotStateClient() c.Init() c.SetTimeout(3.0) self._client = c log.info("robot_state client ready (stereo video-service control)") return c except Exception as exc: # No SDK, DDS not up yet, or robot link down — mark unavailable so we # don't retry-spam; a process restart re-attempts. log.info("robot_state client unavailable (%s) — stream auto-control off", exc) self._unavailable = True return None def set_stereo(self, on: bool) -> bool: """Start (on=True) / stop (on=False) the stereo stream. Idempotent — starting an already-running service is harmless. Returns True on success.""" with self._lock: c = self._client_or_none() if c is None: return False try: code = c.ServiceSwitch(STEREO_SERVICE, bool(on)) if code == 0: log.info("stereo stream -> %s", "ON" if on else "OFF") return True log.warning("stereo ServiceSwitch(%s) returned code=%s", on, code) return False except Exception as exc: log.warning("stereo ServiceSwitch(%s) failed: %s", on, exc) return False _instance: Optional[StreamService] = None def get_stream_service() -> StreamService: global _instance if _instance is None: _instance = StreamService() return _instance