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/_connectcopies and uses the base — validates the change against a real user. - Requires the composite to know each sub-device's address (to rebuild it);
PLScanneralready 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+stageautofocuscomposite, 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