diff --git a/parol6/client/async_client.py b/parol6/client/async_client.py index ac41cd1..e48a825 100644 --- a/parol6/client/async_client.py +++ b/parol6/client/async_client.py @@ -1003,11 +1003,27 @@ async def shapes(self) -> ShapeWorld | None: return None return ShapeWorld( installation=tuple( - shape_from_wire(w.kind, w.params, w.pose, w.collision, w.margin, w.name) + shape_from_wire( + w.kind, + w.params, + w.pose, + w.collision, + w.margin, + w.name, + w.physics, + ) for w in resp.installation ), program=tuple( - shape_from_wire(w.kind, w.params, w.pose, w.collision, w.margin, w.name) + shape_from_wire( + w.kind, + w.params, + w.pose, + w.collision, + w.margin, + w.name, + w.physics, + ) for w in resp.program ), ) @@ -1015,6 +1031,14 @@ async def shapes(self) -> ShapeWorld | None: async def tcp_offset(self) -> list[float]: """Query current TCP offset in mm [x, y, z]. + Raises ``ConnectionError`` when the controller does not answer. + ``[0, 0, 0]`` is a legitimate offset -- a tool deliberately cleared + -- so returning it as a not-answered sentinel leaves the caller + unable to tell "the offset is zero" from "there is no controller", + and a host that adopts the readback quietly erases the offset the + user just set. ``_request`` already retries, so reaching the end + here means unreachable, not one lost datagram. + Category: Configuration Example: @@ -1023,7 +1047,7 @@ async def tcp_offset(self) -> list[float]: resp = await self._request(TcpOffsetCmd()) if isinstance(resp, TcpOffsetResultStruct): return [resp.x, resp.y, resp.z] - return [0.0, 0.0, 0.0] + raise ConnectionError("the controller did not answer tcp_offset()") async def select_profile(self, profile: str) -> int: """Set the motion profile (e.g. ``"TOPPRA"``). diff --git a/parol6/protocol/wire.py b/parol6/protocol/wire.py index 8a821f7..11bb709 100644 --- a/parol6/protocol/wire.py +++ b/parol6/protocol/wire.py @@ -638,7 +638,14 @@ class SetTcpOffsetCmd( class ShapeWire(msgspec.Struct, array_like=True, frozen=True, gc=False): - """One workspace shape — mirrors waldoctl ``Shape.to_wire()``.""" + """One workspace shape — mirrors waldoctl ``Shape.to_wire()``. + + ``physics`` is seventh and defaulted, which is what keeps this readable + both ways across the waldoctl 0.13 boundary: a peer still sending the + six-element form decodes with ``physics`` absent, and one sending seven + decodes fully. Without the field at all, every ``set_shapes`` against + 0.13 raised ``TypeError: Extra positional arguments provided``. + """ kind: str params: list[float] @@ -646,6 +653,7 @@ class ShapeWire(msgspec.Struct, array_like=True, frozen=True, gc=False): collision: bool margin: float | None name: str + physics: list | None = None class SetShapesCmd( diff --git a/pyproject.toml b/pyproject.toml index 692601c..a2c61a8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ dependencies = [ "psutil>=5.9", "msgspec>=0.18", "ormsgpack>=1.4.0", - "waldoctl @ git+https://github.com/Jepson2k/waldoctl.git@v0.12.0", + "waldoctl @ git+https://github.com/Jepson2k/waldoctl.git@v0.13.1", ] [tool.setuptools.packages.find]