Skip to content

Commit

Permalink
Handle Visa IO Error on first connection (#172)
Browse files Browse the repository at this point in the history
* fix: exception handling for clearing Visa IO buffer on  first connection

* fix: unit testing device_manager.py warnings on connection
  • Loading branch information
ldantek authored Mar 26, 2024
1 parent dfcded7 commit 2dd4876
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 12 deletions.
32 changes: 21 additions & 11 deletions src/tm_devices/device_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -1072,20 +1072,30 @@ def __clear_visa_output_buffer_and_get_idn(visa_resource: MessageBasedResource)
Raises:
SystemError: Indicates that the buffer was unable to be cleared.
"""
if 16 & visa_resource.read_stb():
# 16 is the MAV bit (Message Available) from the Status Byte register
# MAV flag is only one bit and turns off after a single response is successfully read,
# even if there's more in the buffer
# use a try-except block to avoid any errors caused by Visa instruments that may not
# respond correctly to the read_stb or clear functions.
try:
if 16 & visa_resource.read_stb():
# 16 is the MAV bit (Message Available) from the Status Byte register
# MAV flag is only one bit and turns off after a single response is
# successfully read, even if there is more in the buffer.
warnings.warn(
f"\nThe device `{visa_resource.resource_info.resource_name}` had data "
"sitting in the VISA Output Buffer on first connection. "
"\nDetected data in the buffer via the Status Byte register. "
"\nThe device_clear() will be called so VISA I/O buffers get flushed.",
stacklevel=1,
)
# always flush the VISA I/O Buffers on the device to clean up any stale data.
# (note: the Events are kept in different buffers, so *ESR? is not impacted)
visa_resource.clear()
except visa.VisaIOError as e:
warnings.warn(
f"\nThe device `{visa_resource.resource_info.resource_name}` had data "
"sitting in the VISA Output Buffer on first connection. "
"\nDetected data in the buffer via the Status Byte register. "
"\nThe device_clear() will be called so VISA I/O buffers get flushed.",
f"A VISA IO error occurred when attempting to read the status byte or clear the "
f"output buffer of the resource `{visa_resource.resource_info.resource_name}`.\n"
f"Error: {e}",
stacklevel=1,
)
# always flush the VISA I/O Buffers on the device to clean up any stale data.
# (note: the Events are kept in different buffers, so *ESR? is not impacted)
visa_resource.clear()
visa_resource.write("*IDN?")
idn_response = ""
error_msg = None
Expand Down
34 changes: 33 additions & 1 deletion tests/test_device_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,12 +459,44 @@ def test_failed_cleanup(self, device_manager: DeviceManager) -> None:
device_manager.cleanup_all_devices()
device_manager.remove_device(alias="bad-afg")

def test_warnings(self, device_manager: DeviceManager) -> None:
"""Verify some of the warning printouts that the DeviceManager can log.
Args:
device_manager: The DeviceManager object.
"""
# Remove all previous devices
device_manager.remove_all_devices()
# Test the warning logged with bad read_stb() call
with mock.patch(
"pyvisa.resources.messagebased.MessageBasedResource.read_stb",
mock.MagicMock(side_effect=visa.VisaIOError(pyvisa.constants.VI_ERROR_INV_SETUP)),
), pytest.warns(
UserWarning,
match="A VISA IO error occurred when attempting to read the status byte or "
"clear the output buffer of the resource",
):
device_manager.add_afg("afg3kc-hostname", alias="warning_bad_read_stb")
device_manager.remove_device(alias="warning_bad_read_stb")

# Test the warning logged with message available (MAV) bit set
# patched the stb to return 16 (message available)
with mock.patch(
"pyvisa.resources.messagebased.MessageBasedResource.read_stb",
mock.MagicMock(return_value=16),
), pytest.warns(
UserWarning, match="had data sitting in the VISA Output Buffer on first connection."
):
device_manager.add_afg("afg3kc-hostname", alias="warning_mav")
device_manager.remove_device(alias="warning_mav")

def test_exceptions(self, device_manager: DeviceManager) -> None:
"""Verify some of the exceptions that the DeviceManager can raise.
Args:
device_manager: The DeviceManager object.
"""
# Remove all previous devices
device_manager.remove_all_devices()

# Test getting a device type that was not the specified type
Expand Down Expand Up @@ -502,7 +534,7 @@ def test_exceptions(self, device_manager: DeviceManager) -> None:
mock.MagicMock(return_value=5),
), mock.patch(
"pyvisa.resources.messagebased.MessageBasedResource.read",
mock.MagicMock(side_effect=visa.VisaIOError(pyvisa.constants.StatusCode.error_timeout)),
mock.MagicMock(side_effect=visa.VisaIOError(pyvisa.constants.VI_ERROR_TMO)),
):
# patched the stb to return 16 (message available)
with pytest.warns(UserWarning), pytest.raises(SystemError) as error:
Expand Down

0 comments on commit 2dd4876

Please sign in to comment.