故事
我正在編程科學相機的驅動程序。它使用賽普拉斯FX3 USB外設控制器。爲了與它溝通,我使用libusb1 for python,特別是模塊usb1。我的操作系統是Ubuntu 16.04 LTS。python libusb1:異步TRANSFER_NO_DEVICE狀態成功同步傳輸後
通信有兩個步驟:
的相機被配置。計算機同步發送指令對攝像機進行編程,並且在每次指令後攝像機響應一個同步讀取的狀態字。
拍照。計算機同步發送一條指令,相機開始流式傳輸數據。計算機以異步方式讀取這些數據。
異步通信在主線程中完成。所以即使通訊本身是異步的,操作也是阻塞的。
問題我得到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]
更新
所以當設備被openned界面打開我已經改變了設備和端口類。這樣的接口僅openned(閉)端口數的一次獨立openned:
# 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):
...
我開始懷疑我不需要爲了執行異步傳輸打開一個界面。
我不完全理解你的代碼我很害怕,因爲我不流利Python編寫的。但最後的錯誤清楚地表明設備已經消失。你確定它將等待傳輸在數據釋放之前返回數據嗎?既然你知道C#我會建議它是一個已經處置對象的Python等價物。當我讀到'recieved_image'是_TransferCollector的一個實例而不是收到的數據,但是我可能誤解了Python如何處理這個。 – dryman
@dryman你可能是對的。現在你提到它了,我正在接口兩次。首先在'with port_read'中,然後在'port_write.write_sync'中。然後第二次調用在調用data_collector()之前完成並關閉接口(這與data_collector .__ call __()')相同。現在我不用相機。我會回來的結果。 – jabozzo
我嘗試了上述修復,並沒有工作。我會更新我的問題。 – jabozzo