2016-08-04 5 views
0

ストーリー

私は科学的なカメラ用のドライバをプログラミングしています。 Cypress FX3 USB周辺機器コントローラを使用します。それと通信するために、私はPython、特にモジュールusb1にlibusb1を使用しています。私のOSはUbuntu 16.04 LTSです。カメラが設定されているpython libusb1:同期転送が成功した直後の非同期TRANSFER_NO_DEVICEステータス

  • 通信は、2つのステップを有します。コンピュータは、カメラをプログラムする命令を同期的に送信し、各命令の後に、カメラは同期して読み出されるステータスワードに応答する。

  • 写真が撮られます。コンピュータは同期して1つの命令を送信し、カメラはストリーミングデータを開始します。コンピュータはこのデータを非同期的に読み込みます。

メインスレッドで非同期通信が行われます。したがって、通信自体が非同期であっても、操作はブロックされています。

問題

私はただの構成ステップでカメラと通信していることを考えれば奇妙である各非同期転送のためのTRANSFER_NO_DEVICEのステータスを取得しています。私はサイプレスライブラリを使用しているWindowsのC#で同様のコードを使用していますが、正しく動作するため、カメラを除外できます。また、写真を撮ろうとした後にFX3バッファに画像データの一部が表示されます。これはサイプレスが提供するサンプルアプリケーションを使用して回復できます。

私は最小限のサンプルスクリプトを作成しました。

#!/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されたときにインタフェースが開かれたように、デバイスとポートのクラスを変更しました。そうすれば、オープンされているポートの数とは無関係に、インターフェイスは1回だけオープン(およびクローズ)されます。

私はまだ同じエラーがあります。

>>> 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あなたはおそらく正しいでしょう。今あなたはそれを言います、私はインターフェイスを2回取っています。まず 'with port_read'の中で、次に' port_write.write_sync'の中で。次に、 'data_collector()'が呼び出される前に2番目の呼び出しが終了し、インタフェースを閉じます(これは 'data_collector .__ call __()'と同じです)。今私はカメラではありません。私は結果に戻ってきます。 – jabozzo

+0

私は上記の修正を試み、動作しませんでした。私は私の質問を更新します。 – jabozzo

答えて

0

drymanが指摘したように、私はコンテキストを2回開いたので、終了する前にコンテキストを解放していました。私たちは、コードの抽出にread_asyncとwrite_sync呼び出しをインライン化した場合:私は上のインターフェイスの請求を削除するのを忘れ、私の質問の更新で

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) 

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) 

我々は、次の擬似コードのようなものを得るでしょう_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.";