2016-08-04 7 views
0

스토리

나는 과학 카메라 용 드라이버를 프로그래밍하고있어 후 비동기 TRANSFER_NO_DEVICE 상태. Cypress FX3 USB 주변기기 컨트롤러를 사용합니다. 그것과 통신하기 위해서 저는 파이썬, 특히 모듈 usb1에 libusb1을 사용하고 있습니다. 내 OS는 우분투 16.04 LTS입니다. 카메라가 구성파이썬 libusb1 : 단지 성공 syncronous 전송

  • :

    통신은 두 단계를 갖는다. 컴퓨터는 카메라를 프로그래밍하기위한 명령을 동 기적으로 전송하고 각 명령 후에 카메라는 동 기적으로 읽힌 상태 어로 응답합니다.

  • 사진을 찍습니다. 컴퓨터가 동 기적으로 단일 명령을 보내고 카메라가 스트리밍 데이터를 시작합니다. 컴퓨터는 비동기 방식으로이 데이터를 읽습니다.

비동기 통신은 주 스레드에서 수행됩니다. 따라서 통신 자체가 비동기 일지라도 작업이 차단됩니다.

문제

난 그냥 구성 단계에서 카메라와 통신 것으로 주어진 이상한 각 비동기 전송에 대한 TRANSFER_NO_DEVICE 상태를 얻고있다. Windows에서 Cypress 라이브러리를 사용하는 C#에서 비슷한 코드를 사용 했으므로 제대로 작동하므로 카메라를 제외 할 수 있습니다. 또한 사진 데이터를 가져 와서 FX3 버퍼에 이미지 데이터의 일부가 나타납니다.이 이미지는 cypress에서 제공하는 예제 응용 프로그램을 사용하여 복구 할 수 있습니다.

최소 예제 스크립트를 만들었습니다. 구성 및 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." 

이 필요한 상황화에 대한/usb.py GuideCamLib입니다. _AsyncReader는 상태가있는 함수 일뿐 아니라 _TransferCollector 클래스가 대부분의 작업을 수행합니다.

#!/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 때 인터페이스가 열릴 수 있도록 내가 장치 및 포트 클래스를 변경 한

업데이트. 그렇게하면 인터페이스는 열려있는 포트의 수와 관계없이 한 번 열리고 닫힙니다.

여전히 같은 오류가 있습니다. 그러나 인쇄 나 표시는 이전 읽기 준비에 실패 : 나는 의심하기 시작하고있어

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

내가 비동기 전송을 수행하기 위해 인터페이스를 열 필요가 없습니다.

+0

나는 완전히 내가 파이썬를 유창하게 구사하지 않다 원인 내가 두려워 코드를 이해하지 않습니다. 그러나 끝에있는 오류는 장치가 이미 사라 졌음을 분명히 말합니다. 장치가 해제되기 전에 전송이 데이터를 반환 할 때까지 기다릴 것입니까? C#을 알고 있기 때문에 이미 배치 된 객체와 동일한 파이썬이라고 제안합니다. 내가 읽었을 때'recieved_image'는받은 데이터가 아닌 _TransferCollector의 인스턴스입니다.하지만 파이썬이 어떻게 처리하는지 오해 할 수도 있습니다. – dryman

+0

@dryman 당신은 아마 맞을 것입니다. 이제는 인터페이스에 대해 두 번 언급하고 있습니다. 'with port_read' 그리고'port_write.write_sync'에서 우선. 그런 다음 두 번째 호출이 완료되고'data_collector()'가 호출되기 전에 인터페이스를 닫습니다 (이는'data_collector .__ call __()'과 동일합니다). 지금 나는 카메라가 아니다. 나는 결과와 함께 돌아올 것이다. – jabozzo

+0

위의 수정을 시도했지만 작동하지 않았습니다. 나는 내 질문을 업데이트 할 것이다. – jabozzo

답변

0

dryman으로 지적했듯이 나는 (문맥을 두 번 열었 기 때문에) 끝내기 전에 컨텍스트를 해제하고있었습니다.

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) 

우리는 다음과 같은 의사 코드 같은 것을 얻을 것이다 : 우리는 코드 추출물의 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) 

을 나는의 인터페이스 주장을 제거하는 것을 잊었다 _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.";