fix: compute instrument read timeout before sending the read command
The previous fix computed the host-side wait (via a read_tmo_ms query) after already sending "++read eoi" -- but the controller doesn't answer any further command while that read is outstanding, so the read_tmo_ms query itself raced the read it was meant to be timing and could time out. Reordered so the timeout is resolved first in both query() and read(); added a regression test pinning the wire ordering. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -275,8 +275,13 @@ class GpibController:
|
||||
if not (0 <= char <= 255):
|
||||
raise ValueError("terminator char must be between 0 and 255")
|
||||
command = f"read {char}"
|
||||
# Compute the timeout (may itself query read_tmo_ms) *before* sending
|
||||
# the read command: once issued, the controller won't answer any
|
||||
# further command until it completes, so querying read_tmo_ms after
|
||||
# would race the very read it's trying to time.
|
||||
wait = self._instrument_read_timeout(timeout)
|
||||
self._send_command(command)
|
||||
return self._transport.read_until_quiet(timeout=self._instrument_read_timeout(timeout))
|
||||
return self._transport.read_until_quiet(timeout=wait)
|
||||
|
||||
# -- raw instrument I/O ---------------------------------------------
|
||||
|
||||
@@ -295,5 +300,9 @@ class GpibController:
|
||||
`vendortools/arb_eth.py` (`++auto 0` + explicit `++read`).
|
||||
"""
|
||||
self.write(data)
|
||||
# Compute the timeout (may itself query read_tmo_ms) before sending
|
||||
# "++read eoi" -- see the comment in read() for why the ordering
|
||||
# matters.
|
||||
wait = self._instrument_read_timeout(read_timeout)
|
||||
self._send_command("read eoi")
|
||||
return self._transport.read_until_quiet(timeout=self._instrument_read_timeout(read_timeout))
|
||||
return self._transport.read_until_quiet(timeout=wait)
|
||||
|
||||
@@ -67,6 +67,21 @@ def test_status_and_lon_raise_in_controller_mode(fake_gpib_server):
|
||||
_ = ctrl.lon
|
||||
|
||||
|
||||
def test_query_fetches_read_tmo_ms_before_sending_read_eoi(fake_gpib_server):
|
||||
# Regression test: found against real hardware that the controller
|
||||
# doesn't answer any further command while a "++read" it issued is
|
||||
# still outstanding (waiting for EOI or its own read_tmo_ms timeout).
|
||||
# So query()/read() must fetch read_tmo_ms (to size the host-side wait)
|
||||
# *before* sending "++read eoi" -- fetching it after races the read
|
||||
# it's meant to be timing. Assert that ordering on the wire.
|
||||
server, tcp_server = fake_gpib_server
|
||||
with GpibController.ethernet(tcp_server.host, tcp_server.port) as ctrl:
|
||||
ctrl.query("*IDN?", read_timeout=None)
|
||||
assert _wait_until(lambda: b"++read eoi" in server.raw_received)
|
||||
raw = bytes(server.raw_received)
|
||||
assert raw.index(b"++read_tmo_ms") < raw.index(b"++read eoi")
|
||||
|
||||
|
||||
def test_status_and_spoll_return_bits_with_rqs_at_index_6(fake_gpib_server):
|
||||
server, tcp_server = fake_gpib_server
|
||||
server.state["mode"] = "0" # DEVICE
|
||||
|
||||
Reference in New Issue
Block a user