feat(lib): Add resilient remote calls (retry + reconnect) to CompositeDevice

🏗️ Core Architecture Blueprint Proposal

Proposer: @YuanYunshuang Target Module: plesty-lib

📝 1. Abstract / Intent

While building hub/experiments/plesty-pl-image-scan on its PLScanner (a camera + LED + piezo-stage composite), the resilient remote-call handling turned out to be pure, device-agnostic infrastructure that every multi-device experiment needs but re-implements by hand. This proposes promoting it into CompositeDevice so any composition gets it for free.

🛠️ 2. Proposed Python Interface Block

CompositeDevice today is minimal (identity / connect_all / disconnect_all / context manager). PLScanner had to add ~60 lines of _call / _rebuild / _connect to survive transient ZMQ timeouts. Fold that generic behaviour up into the base — it applies to any device combination:

class CompositeDevice:
    def call(self, dev: str, func: str, *args,
             retries: int = 3, sleep: float = 0.0, **kwargs):
        """Call a sub-device method, retrying on TimeoutError and rebuilding the
        client (to flush the stale ZMQ reply left in the socket) before the
        final failure propagates."""

    def _reconnect(self, dev: str) -> None:
        """Recreate a sub-device client, discarding its stale socket/context."""

    # Connection-handshake retry when first opening each client, so a transient
    # miss while several clients connect back-to-back does not abort startup.

PLScanner then drops its private copies and calls self.call("cam", ...).

⚠️ 3. System Impact

  • Additive and backward-compatible. Existing composites keep working; they simply gain call() and connection resilience.
  • plesty-pl-image-scan removes its _call / _rebuild / _connect copies and uses the base — validates the change against a real user.
  • Requires the composite to know each sub-device's address (to rebuild it); PLScanner already stores these, so the base should accept/track them.
  • No new external dependencies.
  • Follows the core-repo exp-branch flow.

Scope note: other reusable patterns observed in the PL scan — camera capability contracts (measure_focus / exposure), a cam+stage autofocus composite, serpentine scan-path ordering, a reference-frame coordinate transform — are intentionally deferred. We will revisit them once more demo experiments give us the experience to generalise them well.

💬 Team Feedback / Sign-off Checklist

  • Architectural Review
  • Implementation Review
Edited by yys