2016-08-04 1 views
0

Story

Je suis la programmation d'un pilote pour un appareil scientifique. Il utilise le contrôleur de périphérique USB Cypress FX3. Pour communiquer avec lui, j'utilise libusb1 pour python, en particulier le module usb1. Mon système d'exploitation est Ubuntu 16.04 LTS.python libusb1: état de TRANSFER_NO_DEVICE asynchrone juste après transfert syncronous succès

La communication comporte deux étapes:

  • L'appareil est configuré. L'ordinateur envoie de manière synchrone des instructions pour programmer la caméra et, après chaque instruction, la caméra répond à un mot d'état qui est lu de manière synchrone.

  • Une photo est prise. L'ordinateur envoie de manière synchrone une seule instruction et la caméra commence à diffuser des données. L'ordinateur lit ces données de manière asynchrone.

La communication asynchrone est effectuée dans l'unité d'exécution principale. Donc, même si la communication elle-même est asynchrone, l'opération est bloquante.

Problème

Je reçois le statut de TRANSFER_NO_DEVICE pour chaque transfert asynchrone, ce qui est étrange étant donné que je viens de communiquer avec l'appareil photo à l'étape de configuration. J'ai un code similaire en C# dans Windows en utilisant la librairie cypress et cela fonctionne correctement, donc je peux exclure l'appareil photo. En outre, une partie des données d'image apparaît dans le tampon FX3 après avoir essayé de prendre une photo, que je peux récupérer en utilisant un exemple d'application fournie par Cypress.

J'ai construit un exemple de script minimum. Notez que les fonctions de configure et 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." 

est ici GuideCamLib/usb.py pour la contextualisation nécessaire. La classe _TransferCollector fait la plupart du travail, tandis que _AsyncReader est juste une fonction avec l'état. Port et périphériques sont des classes juste helper, pour réduire le code boilerplate dans chaque transfert:

#!/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); 

Le script produit les éléments suivants, qui montrent clairement les transferts synchrones réussissent, mais chacun des transferts asynchrones en attente échouent avec un 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] 

Mise à jour

J'ai changé l'appareil et les classes du port si l'interface est ouverte lorsque l'appareil est openned. De cette façon, l'interface est uniquement openned (et fermé) une fois, indépendamment du nombre de ports 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); 

J'ai toujours la même erreur. Mais l'impression me montre qu'il échoue plus tôt, à la préparation de lecture:

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

Je commence à soupçonner que je ne ai pas besoin d'ouvrir une interface afin d'effectuer des transferts asynchrones.

+0

Je ne comprends pas bien votre code que je crains, parce que je ne suis pas couramment en Python. Mais l'erreur à la fin indique clairement que l'appareil est déjà parti. Êtes-vous sûr qu'il attendra que les transferts renvoient des données avant qu'il ne libère votre appareil? Puisque vous connaissez C#, je dirais que c'est l'équivalent Python d'un objet déjà disposé. Comme je l'ai lu 'recieved_image' est une instance de _TransferCollector pas les données reçues, mais peut-être que j'interprète mal comment Python gère cela. – dryman

+0

@dryman Vous avez probablement raison. Maintenant que vous le mentionnez, je prends l'interface deux fois. D'abord dans 'avec port_read' et ensuite dans' port_write.write_sync'. Ensuite, le second appel termine et ferme l'interface avant l'appel de 'data_collector()' (qui est identique à 'data_collector .__ call __()'). En ce moment je ne suis pas avec l'appareil photo. Je serai de retour avec les résultats. – jabozzo

+0

J'ai essayé le correctif ci-dessus et n'a pas fonctionné. Je mettrai à jour ma question. – jabozzo

Répondre

0

Comme dryman a souligné, je libérant ainsi le contexte avant de terminer (car deux fois j'ai ouvert le contexte).Si nous en ligne les READ_ASYNC et appels write_sync dans l'extrait de code:

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) 

on obtiendrait quelque chose comme le pseudo-code suivant:

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) 

Dans ma mise à jour de question que j'oublié de retirer la demande d'interface sur _TransferCollector j'ai donc eu un problème similaire. L'application de la mise à jour de question et de définir _TransferCollector comme:

# 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]; 

résout le problème.

Notez qu'un peu de changement doit être fait appeler maintenant 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.";