gpt4 book ai didi

python libusb1 : asynchronous TRANSFER_NO_DEVICE status just after successful syncronous transfers

转载 作者:太空宇宙 更新时间:2023-11-04 12:45:39 25 4
gpt4 key购买 nike

故事

我正在为科学相机编写驱动程序。它使用 Cypress FX3 USB 外设 Controller 。为了与之通信,我使用 libusb1 for python,特别是模块 usb1。我的操作系统是 Ubuntu 16.04 LTS。

通信有两个步骤:

  • 摄像头已配置。计算机同步发送指令对摄像头进行编程,每条指令后摄像头响​​应一个状态字,同步读取。

  • 拍了一张照片。计算机同步发送一条指令,相机开始传输数据。计算机以异步方式读取此数据。

异步通信在主线程中完成。所以即使通信本身是异步的,操作也是阻塞的。

问题

每次异步传输时我都得到 TRANSFER_NO_DEVICE 状态,这很奇怪,因为我刚刚在配置步骤中与相机通信。我在 Windows 中使用 cypress 库在 C# 中有一个类似的代码并且它工作正常,所以我可以排除相机。此外,在尝试拍照后,部分图像数据出现在 FX3 缓冲区中,我可以使用 cypress 提供的示例应用程序恢复这些数据。

我已经构建了一个最小的示例脚本。注意 configure 和 take_picture 函数:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#
# StackOverflow.py

import usb1 as usb1 # Libusb, needed to provide a usb context

import GuideCamLib.binary as binary # Handles bytecode formatting
import GuideCamLib.ccd as ccd # Generates configuration information
import GuideCamLib.usb as usb # Manages communication

# Camera usb parameters
vid = 0x04B4;
pid = 0x00F1;

read_addr = 0x81;
write_addr = 0x01;

interface = 0;

success = [0x55, 0x55, 0x55, 0x55] + [0]*(512 - 4); # A successful response

# Objects that generate instructions for the camera
ccd0 = ccd.CCD_47_10();
formatter = binary.ByteCode();

def configure(context):
# Generating bytes to transfer, outputs a list of int lists
bytecode_lines = ccd0.create_configuration_bytecode(formatter);

# Opens device
with usb.Device(vid=vid, pid=pid, context= context) as dev:

# Opens read / write ports
port_write = dev.open_port(write_addr);
port_read = dev.open_port(read_addr);

print(" Start configuration...")
# Transfer synchronously
for line in bytecode_lines:
written_bytes = port_write.write_sync(line);
response = port_read.read_sync(512);
if(response != success):
raise RuntimeError(" Configuration failed. (" + str(response) + ")");
print(" End configuration")

def take_picture(context):
# Generating bytes to transfer, generates a list of ints
take_photo_bytecode = formatter.seq_take_photo(ccd0.get_take_photo_mode_address());

# Opens device
with usb.Device(vid=vid, pid=pid, context= context) as dev:

# Opens read / write ports
port_write = dev.open_port(write_addr);
port_read = dev.open_port(read_addr, 10000); # 10 sec timeout

# Prepare asynchronous read
print(" Prepare read")
with port_read.read_async(512) as data_collector:
print(" Writing")
written_bytes = port_write.write_sync(take_photo_bytecode); # Write synchronously
print(" Reading...")
recieved_image = data_collector(); # Read asynchronously (but in a blocking manner)

print " Recieved: " + str(len(recieved_image)) + " bytes.";

with usb1.USBContext() as context:
print "Configuring camera:"
configure(context); # Configure camera
print "Taking picture:"
take_picture(context); # Take picture
print "Done."

这是用于所需上下文的 GuideCamLib/usb.py。类 _TransferCollector 完成大部分工作,而 _AsyncReader 只是一个具有状态的函数。端口和设备只是辅助类,以减少每次传输中的样板代码:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#
# GuideCamLib/usb.py

import usb1 as usb
import six as six

import traceback

# For human-readable printing
transfer_status_dict = \
{ \
usb.TRANSFER_COMPLETED : "TRANSFER_COMPLETED",
usb.TRANSFER_ERROR : "TRANSFER_ERROR",
usb.TRANSFER_TIMED_OUT : "TRANSFER_TIMED_OUT",
usb.TRANSFER_CANCELLED : "TRANSFER_CANCELLED",
usb.TRANSFER_STALL : "TRANSFER_STALL",
usb.TRANSFER_NO_DEVICE : "TRANSFER_NO_DEVICE",
usb.TRANSFER_OVERFLOW : "TRANSFER_OVERFLOW" \
};

# Callback to accumulate succesive transfer calls
class _AsyncReader:
def __init__(self):
self.transfers = [];

def __call__(self, transfer):
print "Status: " + transfer_status_dict[transfer.getStatus()]; # Prints the status of the transfer
if(transfer.getStatus() != usb.TRANSFER_COMPLETED):
return;
else:
self.transfers.append(transfer.getBuffer()[:transfer.getActualLength()]);
transfer.submit();

# A collector of asyncronous transfer's data.
# Stops collection after port.timeout time of recieving the last transfer.
class _TransferCollector:
# Prepare data collection
def __init__(self, transfer_size, pararell_transfers, port):
self.interface_handle = port.device.dev.claimInterface(port.interface);
self.reader = _AsyncReader();
self.port = port;
transfers = [];

# Queue transfers
for ii in range(pararell_transfers):
transfer = port.device.dev.getTransfer();
transfer.setBulk(
port.address,
transfer_size,
callback=self.reader,
timeout=port.timeout );
transfer.submit();
transfers.append(transfer);
self.transfers = transfers;

def __enter__(self):
self.interface_handle.__enter__();
return self;

def __exit__(self, exception_type, exception_value, traceback):
self.interface_handle.__exit__(exception_type, exception_value, traceback);

# Activate data collection
def __call__(self):
# Collect tranfers with _AsyncReader while there are active transfers.
while any(x.isSubmitted() for x in self.transfers):
try:
self.port.device.context.handleEvents();
except usb.USBErrorInterrupted:
pass;
return [six.byte2int(d) for data in self.reader.transfers for d in data];

# Port class for creating syncronous / asyncronous transfers
class Port:
def __init__(self, device, address, timeout = None):
self.device = device;
self.address = address;
self.interface = self.device.interface;
self.timeout = timeout;
if(timeout is None):
self.timeout = 0;

def read_sync(self, length):
with self.device.dev.claimInterface(self.interface):
data = self.device.dev.bulkRead(self.address, length, timeout=self.timeout);
return [six.byte2int(d) for d in data];

def write_sync(self, data):
data = [six.int2byte(d) for d in data];
with self.device.dev.claimInterface(self.interface):
return self.device.dev.bulkWrite(self.address, data, timeout=self.timeout);

# Make asyncronous transfers blocking. Collects data as long as the device
# sends data more frecuently than self.timeout or all the transfers fails
def read_async(self, length, pararell_transfers = 32):
return _TransferCollector(length, pararell_transfers, self);

# Device class for creating ports
class Device:
def __init__(self, vid = None, pid = None, context = None, interface = 0):

if(not context):
self.backend = usb.USBContext();
context = self.backend.__enter__();

self.context = context;
self.interface = interface;

self.dev = context.openByVendorIDAndProductID(vid, pid, skip_on_error = False);
if self.dev is None:
raise RuntimeError('Device not found');

def __enter__(self):
return self;

def __exit__(self, exception_type, exception_value, traceback):
if(hasattr(self, "backend")):
self.backend.__exit__(exception_type, exception_value, traceback);

def open_port(self, address, timeout = None):
return Port(self, address, timeout);

脚本输出以下内容,清楚地表明同步传输是成功的,但每个排队的异步传输都失败并返回 NO_DEVICE:

>>> python StackOverflow.py 
Configuring camera:
Start configuration...
End configuration
Taking picture:
Prepare read
Writing
Reading...
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Status: TRANSFER_NO_DEVICE
Traceback (most recent call last):
File "StackOverflow.py", line 70, in <module>
take_picture(context); # Take picture
File "StackOverflow.py", line 62, in take_picture
recieved_image = data_collector();
File "/media/jabozzo/Data/user_data/jabozzo/desktop/sigmamin/code/workspace_Python/USB/USB wxglade libusb1/GuideCamLib/usb.py", line 62, in __exit__
self.interface_handle.__exit__(exception_type, exception_value, traceback);
File "/home/jabozzo/.local/lib/python2.7/site-packages/usb1.py", line 1036, in __exit__
self._handle.releaseInterface(self._interface)
File "/home/jabozzo/.local/lib/python2.7/site-packages/usb1.py", line 1171, in releaseInterface
libusb1.libusb_release_interface(self.__handle, interface),
File "/home/jabozzo/.local/lib/python2.7/site-packages/usb1.py", line 121, in mayRaiseUSBError
raiseUSBError(value)
File "/home/jabozzo/.local/lib/python2.7/site-packages/usb1.py", line 117, in raiseUSBError
raise STATUS_TO_EXCEPTION_DICT.get(value, USBError)(value)
usb1.USBErrorNotFound: LIBUSB_ERROR_NOT_FOUND [-5]

更新

我已经更改了 Device 和 Port 类,以便在打开设备时打开接口(interface)。这样接口(interface)只打开(和关闭)一次,与打开的端口数无关:

# Port class for creating syncronous / asyncronous transfers
class Port:
def __init__(self, device, address, timeout = None):
self.device = device;
self.address = address;
self.timeout = timeout;
if(timeout is None):
self.timeout = 0;

def read_sync(self, length):
data = self.device.dev.bulkRead(self.address, length, timeout=self.timeout);
return [six.byte2int(d) for d in data];

def write_sync(self, data):
data = [six.int2byte(d) for d in data];
return self.device.dev.bulkWrite(self.address, data, timeout=self.timeout);

# Make asyncronous transfers blocking. Collects data as long as the device
# sends data more frecuently than self.timeout or all the transfers fails
def read_async(self, length, pararell_transfers = 32):
return _TransferCollector(length, pararell_transfers, self);

# Device class for creating ports
class Device:
def __init__(self, vid = None, pid = None, context = None, interface = 0):

if(not context):
self.backend = usb.USBContext();
context = self.backend.__enter__();

self.context = context;
self.interface = interface;

self.dev = context.openByVendorIDAndProductID(vid, pid, skip_on_error = False);
if self.dev is None:
raise RuntimeError('Device not found');

self.interface_handle = self.dev.claimInterface(self.interface);

def __enter__(self):
self.interface_handle.__enter__();
return self;

def __exit__(self, exception_type, exception_value, traceback):
self.interface_handle.__exit__(exception_type, exception_value, traceback);
if(hasattr(self, "backend")):
self.backend.__exit__(exception_type, exception_value, traceback);

def open_port(self, address, timeout = None):
return Port(self, address, timeout);

我仍然有同样的错误。但是打印显示它在读取准备时失败了:

>>> python StackOverflow.py 
Configuring camera:
Start configuration...
End configuration
Taking picture:
Prepare read
Traceback (most recent call last):
...

我开始怀疑我不需要打开接口(interface)来执行异步传输。

最佳答案

作为dryman指出,我在完成之前释放了上下文(因为我打开了上下文两次)。如果我们在代码提取中内联 read_async 和 write_sync 调用:

print(" Prepare read")
with port_read.read_async(512) as data_collector:
print(" Writing")
written_bytes = port_write.write_sync(take_photo_bytecode); # Write synchronously
print(" Reading...")
recieved_image = data_collector(); # Read asynchronously (but in a blocking manner)

我们会得到类似下面的伪代码:

print(" Prepare read")
with port_read.claimInterface(0) as ?:
# Read preparation code
print(" Writing")
with port_write.claimInterface(0) as ?:
written_bytes = # Write synchronously
# Here the port_write.claimInterface context has exited,
# leaving the prepared read transfers in a invalid state.
print(" Reading...")
recieved_image = # Read asynchronously (Fails, out of interface context)

在我的问题更新中,我忘记删除 _TransferCollector 上的接口(interface)声明,所以我遇到了类似的问题。应用问题更新并将 _TransferCollector 定义为:

# A collector of asyncronous transfer's data.
# Stops collection after port.timeout time of recieving the last transfer.
class _TransferCollector:
# Prepare data collection
def __init__(self, transfer_size, pararell_transfers, port):
self.reader = _AsyncReader();
self.port = port;
transfers = [];

# Queue transfers
for ii in range(pararell_transfers):
transfer = port.device.dev.getTransfer();
transfer.setBulk(
port.address,
transfer_size,
callback=self.reader,
timeout=port.timeout );
transfer.submit();
transfers.append(transfer);
self.transfers = transfers;

# Activate data collection
def __call__(self):
# Collect tranfers with _AsyncReader while there are active transfers.
while any(x.isSubmitted() for x in self.transfers):
try:
self.port.device.context.handleEvents();
except usb.USBErrorInterrupted:
pass;
return [six.byte2int(d) for data in self.reader.transfers for d in data];

解决了这个问题。

请注意,现在必须对调用 read_async 进行一些更改:

# Prepare asynchronous read
print(" Prepare read")
data_collector = port_read.read_async(512):
print(" Writing")
written_bytes = port_write.write_sync(take_photo_bytecode); # Write synchronously
print(" Reading...")
recieved_image = data_collector(); # Read asynchronously (but in a blocking manner)


print " Recieved: " + str(len(recieved_image)) + " bytes.";

关于 python libusb1 : asynchronous TRANSFER_NO_DEVICE status just after successful syncronous transfers,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38779019/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com