ストーリー
私は科学的なカメラ用のドライバをプログラミングしています。 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):
...
私は非同期転送を実行するためにインターフェースを開く必要はありません疑い始めている。しかし、印刷は、読み取りの準備で、それは以前に失敗した私を示しています。
私はPythonで流暢ないよ原因私は完全に、私は怖いですあなたのコードを理解していません。しかし、最後のエラーは、デバイスがすでに消えていることを明確に示しています。あなたのデバイスを解放する前に転送がデータを返すのを待つことは確かですか?あなたはC#を知っているので、既に配置されているオブジェクトのPythonに相当することをお勧めします。私が読むと、 'recieved_image'は受け取ったデータではなく_TransferCollectorのインスタンスですが、Pythonがこれをどのように処理しているのか誤解しているかもしれません。 – dryman
@drymanあなたはおそらく正しいでしょう。今あなたはそれを言います、私はインターフェイスを2回取っています。まず 'with port_read'の中で、次に' port_write.write_sync'の中で。次に、 'data_collector()'が呼び出される前に2番目の呼び出しが終了し、インタフェースを閉じます(これは 'data_collector .__ call __()'と同じです)。今私はカメラではありません。私は結果に戻ってきます。 – jabozzo
私は上記の修正を試み、動作しませんでした。私は私の質問を更新します。 – jabozzo