Почему pySerial выдает прерывистые сериальные представления при общении с USB-устройствами?

#python #pyserial #hardware-interface

Вопрос:

Я использую pySerial 3.4 с Python 3.6.7 на компьютере с Windows 10 для взаимодействия с USB-устройствами. Таких устройств 12, и все они подключены к одному и тому же USB-концентратору. Взаимодействие с устройствами простое: отправьте на устройство конкретное сообщение с запросом, и оно отправит ответ после получения этого сообщения.

Проблема, которую я пытаюсь решить, заключается в том, что я периодически получаю сериальные исключения и теряю возможность общаться с одним или несколькими устройствами, если не выполняется какое-либо ручное вмешательство (например, включение питания USB-устройств). Конкретным исключением является:

 SerialException("ClearCommError failed (PermissionError(13, 'Access is denied.', None, 5))",)
 

Это иногда, но не всегда, совпадает с системным предупреждением от Windows:
USB-устройство не распознано (последнее USB-устройство, которое вы подключили к этому компьютеру, вышло из строя, и Windows не распознает его.)

Кажется маловероятным, что это фактическая аппаратная неисправность самого USB-устройства, так как она не происходит постоянно на одном из устройств, но может периодически происходить с любым из 12 из них.

Моя общая логика взаимодействия с каждым из устройств через pySerial выглядит так:

 # (Only done once, although serial port is closed and reinitialized like this after a SerialException occurring)
    serial_port = serial.Serial(port=port, baudrate=19200, timeout=10.0, write_timeout=10.0)
    serial_port.dtr = True
    serial_port.rts = False

# (Done for every read)
    # Open serial port if not already opened
    if not serial_port.is_open:
        serial_port.open()
    # If any old/stale data pending, clear it out first
    if serial_port.inWaiting() > 0:
        serial_port.read(serial_port.inWaiting())
    # Request measurement
    serial_port.write(bytearray([0xC1, 0xD2, 0x21]))
    # Wait for measurement
    while ...:  # logic for looping until timeout occurs
        if serial_port.inWaiting() >= 3:  # 3 bytes is expected return packet length
            bytes_read = serial_port.read(serial_port.inWaiting())
            break
    # Cleanup and close port
    serial_port.reset_input_buffer()
    serial_port.reset_output_buffer()
    serial_port.flush()
    serial_port.close()
 

Я пробовал различные методы чтения, такие как асинхронное чтение со всех устройств одновременно по сравнению с синхронным чтением по одному за раз или закрытие последовательного порта после каждого чтения по сравнению с оставлением его открытым между чтениями. Синхронное чтение с закрытием порта после каждого чтения кажется, что у него самая низкая частота встречающихся исключений, но неясно, насколько это важно.

After the SerialException starts occurring, I’ve been unable to find anything on the software side or operating system side that can recover the device (but it would be very convenient if there was a way to recover it from this end!).

Notable actions that do typically resolve the SerialException (at least temporarily):

  • Power-cycling the USB device that the SerialException is occurring on.
  • Обмен проводами между USB-устройствами. Обратите внимание, что:
    • Отсоединение провода от USB-устройства, а затем повторное подключение его обратно к тому же устройству не устраняет исключение (USB-устройство имеет внутреннюю батарею, поэтому отсоединение провода не выключает его)
    • Однако замена проводов между одним из неисправных устройств и одним из исправных обычно устраняет исключение SerialException.
    • Кроме того, после этого, при замене проводов на их первоначальные места, связь с устройствами по-прежнему остается хорошей.