2016-08-04 52 views
0

故事

我正在編程科學相機的驅動程序。它使用賽普拉斯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): 
... 

我開始懷疑我不需要爲了執行異步傳輸打開一個界面。

+0

我不完全理解你的代碼我很害怕,因爲我不流利Python編寫的。但最後的錯誤清楚地表明設備已經消失。你確定它將等待傳輸在數據釋放之前返回數據嗎?既然你知道C#我會建議它是一個已經處置對象的Python等價物。當我讀到'recieved_image'是_TransferCollector的一個實例而不是收到的數據,但是我可能誤解了Python如何處理這個。 – dryman

+0

@dryman你可能是對的。現在你提到它了,我正在接口兩次。首先在'with port_read'中,然後在'port_write.write_sync'中。然後第二次調用在調用data_collector()之前完成並關閉接口(這與data_collector .__ call __()')相同。現在我不用相機。我會回來的結果。 – jabozzo

+0

我嘗試了上述修復,並沒有工作。我會更新我的問題。 – jabozzo

回答

0

由於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,所以我有一個類似的問題。應用問題更新並將_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.";