Получение ошибки отказа в доступе к libusb windows при повторном запуске виджета в JupyterPython

Программы на Python
Ответить
Anonymous
 Получение ошибки отказа в доступе к libusb windows при повторном запуске виджета в Jupyter

Сообщение Anonymous »

Резюме
Я пытаюсь создать виджет для чтения данных датчиков с USB-устройства в Jupyter Notebook. Я пробовал как matplotlib ipywidgets, Dash-plotly, так и Bokeh.
При повторном выполнении ячейки записной книжки виджета любые запросы конфигурации к USB-устройству возвращают ошибку отказа в доступе Windows 5. Перезапуск ядра решает проблему.
Проблема не возникает при запуске вне виджета, выполнении запросов напрямую. Этого также не происходит при использовании Dash-plotly, но его производительность недостаточна, чтобы обрабатывать данные датчика на частоте 20 Гц.
Устройство, о котором идет речь, представляет собой старый вращающийся датчик движения, который поставляется с предустановленным программным обеспечением для его активации и управления. Записывая сообщения между программным обеспечением и устройством, элемент управления можно воспроизводить вслепую.
У меня нет большого опыта работы с USB-устройствами или вводом-выводом в целом, но я попробовал запустить usb.util.dispose_resources в качестве обработчика ошибок внутри класса. Моя текущая логика подключения:
  • Найти устройство, которое при первоначальном плагине регистрируется как загрузчик.
  • Настроить и отправить инструкции устройству, которое запускает перезагрузку до состояния приложения (это можно проверить в диспетчере устройств и остается таковым, пока устройство подключено).
  • Найти устройство приложения после сброса и настроить конфигурацию по умолчанию 1.

Код: Выделить всё

t0 = time.time()
while time.time() - t0 < timeout_s:
try:
self.__dev: usb.core.Device | None = libusb_package.find(
idVendor=ROTATION_BOOT_VID, idProduct=ROTATION_BOOT_PID
)
if self.__dev:
logging.info("Found ROTATION bootloader device.")
self.__initialize_configuration()
logging.info("Connected to ROTATION bootloader device.")
self.__write_firmware()
self.__wait_for_disconnect()
self.__dev = None  # Reset device to connect to application
except usb.core.USBError as e:
time.sleep(0.05)

try:
self.__dev: usb.core.Device = libusb_package.find(
idVendor=ROTATION_APP_VID, idProduct=ROTATION_APP_PID
)
if self.__dev:
logging.info("Found ROTATION application device.")

bConfiguration = self.__dev.get_active_configuration()
logging.info(f"Active configuration: {bConfiguration}")

self.__initialize_configuration()
logging.info("Connected to ROTATION application device.")
return
except (usb.core.USBError, NotImplementedError) as e:
if (isinstance(e, usb.core.USBError) and e.errno == 13):
logging.warning("Access Denied error here") # Taken out of handler for debug
return
self.__handle_usb_error(e)
time.sleep(0.05)

raise ValueError("No ROTATION device found within timeout period")
Очистка устройства выполняется измененным элементом del:

Код: Выделить всё

def __del__(self):
"""Ensure the device is properly released"""
if self.__dev is not None:
logging.info("Cleaning up ROTATION device resources...")
usb.util.dispose_resources(self.__dev)
self.__dev = None
Этот класс используется в текущей (самой производительной) реализации виджета с эффектом боке:

Код: Выделить всё

def gyroscope_app(doc):
source = ColumnDataSource(data=dict(time=[], radial_velocity=[]))
rotation_sensor = RotationSensor()
rotation_sensor.open_device()

plot = figure(
x_axis_label='Time (s)', y_axis_label='Radial velocity (arcsec / s)',
min_width=800, min_height=400
)
plot.line('time', 'radial_velocity', source=source)

callback_id = {'value': None}

@count()
def update(t):
reading = rotation_sensor.read()

if reading is None or len(reading) != 4:
print("Invalid reading received, skipping update.")
return

reading_data = reading[0] if reading[1] >  0 else -reading[0]

new_data = dict(time=[t], radial_velocity=[reading_data])

source.stream(new_data, rollover=30)

def start():
if callback_id['value'] is None:
rotation_sensor.start_sensor_reading()
callback_id['value'] = doc.add_periodic_callback(update, 100)

def stop():
cid = callback_id['value']
if cid is not None:
doc.remove_periodic_callback(cid)
callback_id['value'] = None
rotation_sensor.stop_sensor_reading()

def reset():
stop()
source.data = dict(time=[], radial_velocity=[])

start_button = Button(label="Start", button_type="success")
start_button.on_click(start)

stop_button = Button(label="Stop", button_type="danger")
stop_button.on_click(stop)

reset_button = Button(label="Reset", button_type="warning")
reset_button.on_click(reset)

doc.add_root(
column(
row(start_button, stop_button, reset_button),
plot
)
)
show(gyroscope_app)
Примечания:
  • Пользователь имеет соответствующие разрешения и функции подключения без проблем, если он не запускается внутри виджета.
  • Ни один объект Python, связанный с устройством, не остается видимым в трассировке стека отладчика после выполнения ячейки и очистки вывода. Тем не менее, проблема остается.
  • Конфигурацию при сбое невозможно проверить, поскольку вызов usb.core.Device.get_active_configuration() приводит к такому же отказу в доступе. Проверка активности драйвера ядра не реализована в Windows.
Попытки решения
  • После выполнения ячейки для реализации Bokeh не остается объектов Jupyter. Однако для matplotlib ipywidget их удаление не оказало никакого влияния.
  • Журнал для libusb показывает:

Код: Выделить всё

[1898.785734] [000103bc] libusb: debug [libusb_open] open 2.2
[1898.785900] [000103bc] libusb: error [winusbx_open] could not open device \\?\USB#VID_0945&PID_0002#6&2F625D12&0&1#{A5DCBF10-6530-11D2-901F-00C04FB951ED} (interface 0): [5] Access is denied.
[1898.785973] [000103bc] libusb: debug [libusb_open] open 2.2 returns -3
  • Вывод ячейки Jupyter:

Код: Выделить всё

2025-08-20 15:46:56,447 DEBUG:usb.backend.libusb1:_LibUSB.get_device_descriptor()
DEBUG:usb.backend.libusb1:_LibUSB.get_device_descriptor()
2025-08-20 15:46:56,447 DEBUG:usb.backend.libusb1:_Device._finalize_object()
DEBUG:usb.backend.libusb1:_Device._finalize_object()
INFO:root:Found ROTATION application device.
2025-08-20 15:46:56,448 DEBUG:usb.backend.libusb1:_LibUSB.open_device()
DEBUG:usb.backend.libusb1:_LibUSB.open_device()

WARNING:root:Device already configured /* Issue caught here but prevents reading */

INFO:tornado.access:200 GET /autoload.js?bokeh-autoload-element=e97c48c3-168f-4e7c-9b22-33e50eb6464d&bokeh-absolute-url=http://localhost:53080&resources=none (127.0.0.1) 89.15ms
INFO:tornado.access:101 GET /ws (127.0.0.1) 1.00ms
INFO:bokeh.server.views.ws:WebSocket connection opened
INFO:bokeh.server.views.ws:ServerConnection created
  • При чтении пакетов с помощью Wireshark и Usbpcan отображаются только сообщения USB_INTERRUPT. Не отображает никаких сообщений, если ячейка не перезапускается, поэтому связь не активна.

Код: Выделить всё

No.      Time           Source                Destination           Protocol Length Info
52 7.472741       host                  2.4.1                 USB      27     URB_INTERRUPT in

Frame 52: 27 bytes on wire (216 bits), 27 bytes captured (216 bits) on interface \\.\USBPcap2, id 0
Section number: 1
Interface id: 0 (\\.\USBPcap2)
Interface name: \\.\USBPcap2
Interface description: USBPcap2
Encapsulation type: USB packets with USBPcap header (152)
Arrival Time: Aug 20, 2025 15:46:51.434821000 GMT Daylight Time
UTC Arrival Time: Aug 20, 2025 14:46:51.434821000 UTC
Epoch Arrival Time: 1755701211.434821000
[Time shift for this packet: 0.000000000 seconds]
[Time delta from previous captured frame: 0.000037000 seconds]
[Time delta from previous displayed frame: 0.000037000 seconds]
[Time since reference or first frame: 7.472741000 seconds]
Frame Number: 52
Frame Length: 27 bytes (216 bits)
Capture Length: 27 bytes (216 bits)
[Frame is marked: False]
[Frame is ignored: False]
[Protocols in frame: usb]
USB URB
[Source: host]
[Destination: 2.4.1]
USBPcap pseudoheader length: 27
IRP ID: 0xffffe48f6a499820
IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000)
URB Function: URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER (0x0009)
IRP information: 0x00, Direction: FDO -> PDO
0000 000. = Reserved: 0x00
.... ...0 = Direction: FDO -> PDO (0x0)
URB bus id: 2
Device address: 4
Endpoint: 0x81, Direction: IN
1... .... = Direction: IN (1)
.... 0001 = Endpoint number: 1
URB transfer type: URB_INTERRUPT (0x01)
Packet Data Length: 0
[Response in: 55]
[bInterfaceClass: HID (0x03)]

No.     Time           Source                Destination           Protocol Length Info
53 12.032803      2.4.1                 host                  USB      35     URB_INTERRUPT in

Frame 53: 35 bytes on wire (280 bits), 35 bytes captured (280 bits) on interface \\.\USBPcap2, id 0
Section number: 1
Interface id: 0 (\\.\USBPcap2)
Interface name: \\.\USBPcap2
Interface description: USBPcap2
Encapsulation type: USB packets with USBPcap header (152)
Arrival Time: Aug 20, 2025 15:46:55.994883000 GMT Daylight Time
UTC Arrival Time: Aug 20, 2025 14:46:55.994883000 UTC
Epoch Arrival Time: 1755701215.994883000
[Time shift for this packet: 0.000000000 seconds]
[Time delta from previous captured frame: 4.560062000 seconds]
[Time delta from previous displayed frame: 4.560062000 seconds]
[Time since reference or first frame: 12.032803000 seconds]
Frame Number: 53
Frame Length: 35 bytes (280 bits)
Capture Length: 35 bytes (280 bits)
[Frame is marked: False]
[Frame is ignored: False]
[Protocols in frame: usb:usbhid]
USB URB
[Source: 2.4.1]
[Destination: host]
USBPcap pseudoheader length: 27
IRP ID: 0xffffe48f6a4b2010
IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000)
URB Function: URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER (0x0009)
IRP information: 0x01, Direction: PDO -> FDO
0000 000. = Reserved: 0x00
.... ...1 = Direction: PDO -> FDO (0x1)
URB bus id: 2
Device address: 4
Endpoint: 0x81, Direction: IN
1... .... = Direction: IN (1)
....  0001 = Endpoint number: 1
URB transfer type: URB_INTERRUPT (0x01)
Packet Data Length: 8
[Request in: 50]
[Time from request: 4.928069000 seconds]
[bInterfaceClass: HID (0x03)]
HID Data: 0200000000000000
Информация о драйвере

Код: Выделить всё

oem67.inf:
;
; MyDriver.inf
;
; Installs WinUsb
;

[Version]
Signature   = "$Windows NT$"
Class       = "NAME Interface"
ClassGUID   = {4CCE036B-4B8C-40c9-9606-A2F6E54202D1}
Provider    = %ManufacturerName%
CatalogFile = NAMEUSB.cat
PnpLockDown = 1
DriverVer = 08/26/2024,12.3.18.533

; ========== Manufacturer/Models sections ===========

[Manufacturer]
%ManufacturerName% = Standard,NTamd64

[Standard.NTamd64]
%DeviceName0001% = USB_Install, USB\VID_0945&PID_0001
%DeviceName0002% = USB_Install, USB\VID_0945&PID_0002

; =================== Installation ===================

[ClassInstall32.NTamd64]
AddReg=Class_Addreg

[Class_Addreg]
HKR,,,,"NAME Interface Class"
HKR,,Icon,,"-20"

[USB_Install]
Include = winusb.inf
Needs = WINUSB.NT

[USB_Install.Services]
Include = winusb.inf
Needs = WINUSB.NT.Services

[USB_Install.HW]
AddReg = Dev_AddReg
Include = winusb.inf
Needs = WINUSB.NT.HW

[Dev_AddReg]
; By default, USBDevice class uses iProduct descriptor to name the device in
; device manager.  Uncomment for this device to use %DeviceName%:
;HKR,,FriendlyName,,%DeviceName%
HKR,,DeviceInterfaceGUIDs,0x10000,"{9A9A65EE-A425-482d-AE44-80DA1A1210C6}"

; =================== Strings ===================

[Strings]
ManufacturerName = "NAME"
DiskName = "NAME Installation Disk"
DeviceName0001 = "NAME USBLink Bootloader"
DeviceName0002 = "NAME Device"

Код: Выделить всё

oem107.inf:
[Version]
Signature = "$Windows NT$"
Class = "NAME Interface"
ClassGuid={4CCE036B-4B8C-40c9-9606-A2F6E54202D1}
Provider = %NAME%
DriverVer=08/14/2008,1.0.0.0
CatalogFile=NAMEUSB.cat

; ========== Manufacturer/Models sections ===========

[Manufacturer]
%NAME% = NAME_WinUSB,NTx86,NTamd64

[NAME_WinUSB.NTx86]
%USB\NAME.DeviceDesc.0001% = USB_Install, USB\VID_0945&PID_0001
%USB\NAME.DeviceDesc.0002% = USB_Install, USB\VID_0945&PID_0002

[NAME_WinUSB.NTamd64]
%USB\NAME.DeviceDesc.0001% = USB_Install, USB\VID_0945&PID_0001
%USB\NAME.DeviceDesc.0002% = USB_Install, USB\VID_0945&PID_0002

; =================== Installation ===================

[ClassInstall32.nt]
AddReg=Class_Addreg

[Class_Addreg]
HKR,,,,"NAME Interface Class"
HKR,,Icon,,"-20"

;[1]
[USB_Install]
Include=winusb.inf
Needs=WINUSB.NT

;[2]
[USB_Install.Services]
Include=winusb.inf
AddService=WinUSB,0x00000002,WinUSB_ServiceInstall

;[3]
[WinUSB_ServiceInstall]
DisplayName     = %WinUSB_SvcDesc%
ServiceType     = 1
StartType       = 3
ErrorControl    = 1
ServiceBinary   = %12%\WinUSB.sys

;[4]
[USB_Install.Wdf]
KmdfService=WINUSB, WinUsb_Install

[WinUSB_Install]
KmdfLibraryVersion=1.5

;[5]
[USB_Install.HW]
AddReg=Dev_AddReg

[Dev_AddReg]
HKR,,DeviceInterfaceGUIDs,0x10000,"{9A9A65EE-A425-482d-AE44-80DA1A1210C6}"

;[6]
[USB_Install.CoInstallers]
AddReg=CoInstallers_AddReg
CopyFiles=CoInstallers_CopyFiles

[CoInstallers_AddReg]
HKR,,CoInstallers32,0x00010000,"WdfCoInstaller01005.dll,WdfCoInstaller","WinUSBCoInstaller.dll"

[CoInstallers_CopyFiles]
WinUSBCoInstaller.dll
WdfCoInstaller01005.dll

[DestinationDirs]
CoInstallers_CopyFiles=11

; ================= Source Media Section =====================
;[7]

[SourceDisksNames]
1 = %DISK_NAME%,,,\x86
2 = %DISK_NAME%,,,\amd64

[SourceDisksFiles.x86]
WinUSBCoInstaller.dll=1
WdfCoInstaller01005.dll=1

[SourceDisksFiles.amd64]
WinUSBCoInstaller.dll=2
WdfCoInstaller01005.dll=2

; =================== Strings ===================

[Strings]
NAME="NAME"
USB\NAME.DeviceDesc.0001="NAME USBLink Bootloader"
USB\NAME.DeviceDesc.0002="NAME USBLink"
WinUSB_SvcDesc="NAME WinUSB"
DISK_NAME="NAME Install Disk"

Ограничения
Моя задача требует, чтобы конечный продукт работал на компьютере с Windows 10 как блокнот Jupyter со всеми зависимостями, доступными через Conda или PyPi.
Есть ли способ отладить это на стороне js, которого мне не хватает?
(Ищу способ удалить фоновый процесс, вызывающий проблему)
Не знаю, как Я бы сделал это с помощью Jupyter.

Подробнее здесь: https://stackoverflow.com/questions/797 ... in-jupyter
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»