diff --git a/progpib/controller.py b/progpib/controller.py index 470b624..1966d0c 100644 --- a/progpib/controller.py +++ b/progpib/controller.py @@ -7,7 +7,7 @@ from .exceptions import CommandError from .transport import EthernetTransport, SerialTransport, Transport _DEFAULT_COMMAND_TIMEOUT = 1.0 # seconds, for ++cmd query replies -_DEFAULT_READ_TIMEOUT = 2.0 # seconds, for ++read / query() instrument data +_READ_TIMEOUT_MARGIN = 0.5 # seconds, added on top of read_tmo_ms (see _instrument_read_timeout) class GpibController: @@ -59,6 +59,22 @@ class GpibController: if self.mode != 0: raise CommandError(f"++{command_name} is only valid in DEVICE mode") + def _instrument_read_timeout(self, explicit: float | None) -> float: + """Host-side timeout for reading instrument data. + + Must be longer than the controller's own `read_tmo_ms`: the + controller keeps a "++read" outstanding internally (waiting for + EOI or its own timeout) before it will process the next command, + so a shorter host-side timeout gives up and sends the next command + while the controller is still finishing the previous read -- that + reply then arrives late and gets mistaken for the next command's + reply (or times out entirely). Costs one extra round trip when the + caller doesn't pass an explicit timeout; pass one to skip it. + """ + if explicit is not None: + return explicit + return (self.read_tmo_ms / 1000.0) + _READ_TIMEOUT_MARGIN + # -- config properties ------------------------------------------------ @property @@ -260,7 +276,7 @@ class GpibController: raise ValueError("terminator char must be between 0 and 255") command = f"read {char}" self._send_command(command) - return self._transport.read_until_quiet(timeout=timeout or _DEFAULT_READ_TIMEOUT) + return self._transport.read_until_quiet(timeout=self._instrument_read_timeout(timeout)) # -- raw instrument I/O --------------------------------------------- @@ -280,4 +296,4 @@ class GpibController: """ self.write(data) self._send_command("read eoi") - return self._transport.read_until_quiet(timeout=read_timeout or _DEFAULT_READ_TIMEOUT) + return self._transport.read_until_quiet(timeout=self._instrument_read_timeout(read_timeout))