60 lines
2.0 KiB
Python

"""Robot backend factory.
Selects and constructs the concrete :class:`RobotInterface` implementation
based on the runtime config. The chosen backend is *lazy-imported* so the
unused backend's heavy dependencies are never pulled in:
* ``cfg.mock`` is ``True`` -> :class:`~gowelcome.robot.mock_robot.MockRobot`
(webcam / video file; no robot SDK required).
* ``cfg.transport == "webrtc"`` -> :class:`~gowelcome.robot.webrtc_robot.Go2WebRTCRobot`
(default; app WebRTC protocol via ``unitree_webrtc_connect``; wifi + audio).
* ``cfg.transport == "dds"`` -> :class:`~gowelcome.robot.go2_robot.Go2Robot`
(official ``unitree_sdk2py`` over CycloneDDS; wired / EDU).
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from gowelcome.robot.interface import RobotInterface
if TYPE_CHECKING: # pragma: no cover - type hints only
from config import GoWelcomeConfig
def build_robot(cfg: "GoWelcomeConfig") -> RobotInterface:
"""Build the appropriate robot backend for ``cfg``.
``cfg.mock`` -> MockRobot; else ``cfg.transport`` selects the real backend
(``"webrtc"`` default, ``"dds"`` for the official SDK). The unselected
backends are never imported, so their heavy/optional dependencies need not
be installed.
Args:
cfg: The fully-populated :class:`~config.GoWelcomeConfig`.
Returns:
A ready-to-use :class:`RobotInterface` instance.
Raises:
ValueError: if ``cfg.transport`` is not a known value.
"""
if cfg.mock:
from gowelcome.robot.mock_robot import MockRobot
return MockRobot(cfg)
transport = (cfg.transport or "webrtc").strip().lower()
if transport == "webrtc":
from gowelcome.robot.webrtc_robot import Go2WebRTCRobot
return Go2WebRTCRobot(cfg)
if transport == "dds":
from gowelcome.robot.go2_robot import Go2Robot
return Go2Robot(cfg)
raise ValueError(
f"Unknown transport {cfg.transport!r} (expected 'webrtc' or 'dds')."
)