pyvisa / pyvisa-py

A pure python PyVISA backend
https://pyvisa-py.readthedocs.io
MIT License
288 stars 124 forks source link

Pipe error on reconnect to USB instrument #371

Open Ryan-Draves opened 1 year ago

Ryan-Draves commented 1 year ago

When opening a resource to a USB instrument after a resource to the same instrument has been closed, an [Errno 32] Pipe error may appear. The re-opening doesn't need to happen in the same process; the error will persist until the USB connection is reset by unplugging the cable or until the instrument is power cycled. I have been able to consistently reproduce this on some machines with a Rigol DP832, but other machines don't encounter this issue, even with the same pyvisa-py version and, to my knowledge, system installations.

I searched for versions affected by this and isolated it to 0.6.2 and the #352 and related changes.

To Reproduce

Steps to reproduce the behavior:

import pyvisa

rm = pyvisa.ResourceManager()
inst = rm.open_resource('some_usb_address')
inst.query('*IDN?')  # make sure communication works
inst.close()  # clean up resource

rm_2 = pyvisa.ResourceManager()
inst_2 = rm.open_resource('some_usb_address')
inst_2.query('*IDN?')  # USBError: [Errno 32] Pipe error

Output of pyvisa-info

Machine Details:
   Platform ID:    Linux-5.14.0-1059-oem-x86_64-with-glibc2.29
   Processor:      x86_64

Python:
   Implementation: CPython
   Executable:     /usr/bin/python3.8
   Version:        3.8.10
   Compiler:       GCC 9.4.0
   Bits:           64bit
   Build:          Mar 13 2023 10:26:41 (#default)
   Unicode:        UCS4

PyVISA Version: 1.13.0

Backends:
   ivi:
      Version: 1.13.0 (bundled with PyVISA)
      Binary library: Not found
   py:
      Version: 0.6.3
      ASRL INSTR: Available via PySerial (3.5)
      USB INSTR: Available via PyUSB (1.2.1). Backend: libusb1
      USB RAW: Available via PyUSB (1.2.1). Backend: libusb1
      TCPIP INSTR: Available 
         Resource discovery:
         - VXI-11: ok
         - hislip: disabled (zeroconf not installed)
      VICP INSTR:
         Please install PyVICP to use this resource type.
      TCPIP SOCKET: Available 
      GPIB INSTR:
         Please install linux-gpib (Linux) or gpib-ctypes (Windows, Linux) to use this resource type. Note that installing gpib-ctypes will give you access to a broader range of funcionality.
         No module named 'gpib'
   sim:
      Version: 0.5.1
      Spec version: 1.1
MatthieuDartiailh commented 1 year ago

This sounds quite mysterious. But it seems always so with USB issues.

If you figure out what differs between your machines, it would be great. On the machine on which things work, does it also work before 0.6.2 ?

Ryan-Draves commented 1 year ago

Yeah, no issues of this type before 0.6.2 across any machines I've tested with. I can get back to you on Monday to run pyvisa-info on some different machines and see if they have different outputs.

MatthieuDartiailh commented 1 year ago

Could you also post the full traceback just to help understand where in the stack the error occurs ?

Ryan-Draves commented 1 year ago

The original post had a pyvisa-info output from my laptop, which is working on 0.6.3 (pardon the confusion). I ran pyvisa-info from a machine with the issue, the only difference being the machine details section:

Machine Details:
   Platform ID:    Linux-5.15.0-71-generic-x86_64-with-glibc2.29
   Processor:      x86_64

Here's the requested traceback. I confirmed the same commands work if I downgrade to 0.6.1:

In [7]: rm = pyvisa.ResourceManager()

In [8]: inst = rm.open_resource('USB0::6833::3601::DP8B242902278::0::INSTR')

In [9]: inst.query('*IDN?')
Out[9]: 'RIGOL TECHNOLOGIES,DP832A,DP8B242902278,00.01.16\n'

In [10]: inst.close()

In [11]: rm = pyvisa.ResourceManager()

In [12]: inst = rm.open_resource('USB0::6833::3601::DP8B242902278::0::INSTR')

In [13]: inst.query('*IDN?')
---------------------------------------------------------------------------
USBError                                  Traceback (most recent call last)
~/.local/lib/python3.8/site-packages/pyvisa_py/protocols/usbtmc.py in write(self, data)
    276         try:
--> 277             return self.usb_send_ep.write(data)
    278         except usb.core.USBError as e:

~/.local/lib/python3.8/site-packages/usb/core.py in write(self, data, timeout)
    407         """
--> 408         return self.device.write(self, data, timeout)
    409 

~/.local/lib/python3.8/site-packages/usb/core.py in write(self, endpoint, data, timeout)
    988 
--> 989         return fn(
    990                 self._ctx.handle,

~/.local/lib/python3.8/site-packages/usb/backend/libusb1.py in bulk_write(self, dev_handle, ep, intf, data, timeout)
    836     def bulk_write(self, dev_handle, ep, intf, data, timeout):
--> 837         return self.__write(self.lib.libusb_bulk_transfer,
    838                             dev_handle,

~/.local/lib/python3.8/site-packages/usb/backend/libusb1.py in __write(self, fn, dev_handle, ep, intf, data, timeout)
    937         if not (transferred.value and retval == LIBUSB_ERROR_TIMEOUT):
--> 938             _check(retval)
    939 

~/.local/lib/python3.8/site-packages/usb/backend/libusb1.py in _check(ret)
    603         else:
--> 604             raise USBError(_strerror(ret), ret, _libusb_errno[ret])
    605 

USBError: [Errno 32] Pipe error

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
<ipython-input-13-550ce4f5045a> in <cell line: 1>()
----> 1 inst.query('*IDN?')

~/.local/lib/python3.8/site-packages/pyvisa/resources/messagebased.py in query(self, message, delay)
    640 
    641         """
--> 642         self.write(message)
    643 
    644         delay = self.query_delay if delay is None else delay

~/.local/lib/python3.8/site-packages/pyvisa/resources/messagebased.py in write(self, message, termination, encoding)
    195             message += term
    196 
--> 197         count = self.write_raw(message.encode(enco))
    198 
    199         return count

~/.local/lib/python3.8/site-packages/pyvisa/resources/messagebased.py in write_raw(self, message)
    155 
    156         """
--> 157         return self.visalib.write(self.session, message)[0]
    158 
    159     def write(

~/.local/lib/python3.8/site-packages/pyvisa_py/highlevel.py in write(self, session, data)
    541         # from the session handle, dispatch to the write method of the session object.
    542         try:
--> 543             written, status_code = self.sessions[session].write(data)
    544         except KeyError:
    545             return 0, self.handle_return_value(session, StatusCode.error_invalid_object)

~/.local/lib/python3.8/site-packages/pyvisa_py/usb.py in write(self, data)
    177         send_end, _ = self.get_attribute(ResourceAttribute.send_end_enabled)
    178 
--> 179         count = self.interface.write(data)
    180 
    181         return count, StatusCode.success

~/.local/lib/python3.8/site-packages/pyvisa_py/protocols/usbtmc.py in write(self, data)
    451             data = BulkOutMessage.build_array(self._btag, eom, data[begin:end])
    452 
--> 453             bytes_sent += raw_write(data)
    454 
    455         return size

~/.local/lib/python3.8/site-packages/pyvisa_py/protocols/usbtmc.py in write(self, data)
    277             return self.usb_send_ep.write(data)
    278         except usb.core.USBError as e:
--> 279             raise ValueError(str(e))
    280 
    281     def read(self, size):

ValueError: [Errno 32] Pipe error
ius commented 4 weeks ago

I'm experiencing the same issue on my Rigol DS1102Z-E and have determined the root cause: USB autosuspend.

# cat /sys/module/usbcore/parameters/autosuspend
# 2

Which means devices are autosuspended on a two second timeout. For whatever reason this only happens after the device has been opened/used once, so that means you can access the device once and then repeatedly until you leave it untouched for two seconds.

(Note the pipe error originates from the USB layer (URB status: Broken pipe (-EPIPE)).)

Disabling autosuspend as a workaround fixes it for me:

# echo -1 > /sys/module/usbcore/parameters/autosuspend

More importantly, reverting #352 also fixes the issue - ie. ensuring SET CONFIGURATION is executed after resume from suspend - at least on Rigol devices (?).

I don't really know USB, so perhaps someone else is able to tell whether this is a pyvisa, pyusb or Rigol bug?