2017-08-07 4 views
0

Je suis en train de créer un pilote de périphériques qui consomme des données à partir d'un périphérique d'émulation de clavier.Comportement du périphérique de l'émulateur de clavier sur Ubuntu

Le dispositif est un simple glissement de la carte, de sorte que son comportement est comme suit:

  • utilisateur se dirige, Swipes carte
  • Je reçois une chaîne de caractères (codes clés, vraiment, y compris les touches de modification du capital lettres)
  • Je ne sais pas combien de personnages que je vais obtenir
  • Je ne sais pas quand je reçois quelque chose

Puisque je ne sais pas combien de caractères je vais avoir, bloquer les lectures sur le clavier tty n'est pas utile - je finirais par bloquer après le dernier personnage. Ce que je fais est, dans Ruby, en utilisant le module IO pour effectuer des lectures asynchrones sur le clavier, et en utilisant un timeout pour déterminer que la fin des données a été atteinte. Cela fonctionne très bien logiquement (même un utilisateur balayant sa carte rapidement le fera plus lentement que le taux d'envoi entre les caractères).

Le problème est que, parfois, je perds des données à partir du milieu de la chaîne. Mon intuition est qu'il y a une sorte de débordement de tampon qui se produit parce que je lis les données trop lentement. En essayant de confirmer cela, j'ai inséré de petites attentes entre chaque processus clé. Des attentes plus longues (20ms +) aggravent le problème. Cependant, une attente d'environ 5ms le fait disparaître? La seule explication que je peux trouver est que la lecture asynchrone elle-même est chère (parce que Ruby), et les faire sans limite de vitesse est en fait plus lent que de les faire avec un délai de 5ms.

Cela semble-t-il rationnel? Y a-t-il d'autres idées sur ce que cela pourrait être?

Le rubis est en fait JRuby 9000. La machine est Ubuntu LTS 16.

Edit: voici un extrait du code correspondant

private def read_swipe(buffer_size, card_reader_input, pause_between_reads, seconds_to_complete) 
    limit = Time.now + seconds_to_complete.seconds 
    swipe_data = '' 
    begin 
     start_time = Time.now 
     sleep pause_between_reads 
     batch = card_reader_input.read_nonblock(buffer_size) 
     swipe_data << batch 
    rescue IO::WaitReadable 
     IO.select([card_reader_input], nil, nil, 0.5) 
     retry unless limit < start_time 
    end while start_time < limit 
    swipe_data 
    end 

card_reader_input = File.new(event_handle, 'rb')

+0

pouvez-vous montrer une partie de votre code pour avoir une meilleure idée –

+0

@maxpleaner done – kolosy

Répondre

0

Je ne suis pas sûr à propos du code Ruby, mais vous pouvez utiliser linux sysfs pour accéder aux caractères qui sortent du périphérique «j'aime» du clavier, et si possible, vous pouvez appeler le code C de ruby ​​app lication. Je l'avais fait pour lecteur de code à barres et le code suivant est:

static int init_barcode_com(char* bcr_portname) 
{ 
    int fd; 

    /* Open the file descriptor in non-blocking mode */ 
    fd = open(bcr_portname, O_RDONLY | O_NOCTTY | O_NDELAY); 
    cout << "Barcode Reader FD: " << fd <<endl; 
    if (fd == -1) 
    { 
     cerr << "ERROR: Cannot open fd for barcode communication with error " << fd <<endl; 
    } 

    fcntl(fd, F_SETFL, 0); 

    /* Set up the control structure */ 
    struct termios toptions; 

    /* Get currently set options for the tty */ 
    tcgetattr(fd, &toptions); 

    /* Set custom options */ 

    /* 9600 baud */ 
    cfsetispeed(&toptions, B9600); 
    cfsetospeed(&toptions, B9600); 
    /* 8 bits, no parity, no stop bits */ 
    toptions.c_cflag &= ~PARENB; 
    toptions.c_cflag &= ~CSTOPB; 
    toptions.c_cflag &= ~CSIZE; 
    toptions.c_cflag |= CS8; 
    /* no hardware flow control */ 
    toptions.c_cflag &= ~CRTSCTS; 
    /* enable receiver, ignore status lines */ 
    toptions.c_cflag |= CREAD | CLOCAL; 
    /* disable input/output flow control, disable restart chars */ 
    toptions.c_iflag &= ~(IXON | IXOFF | IXANY); 
    /* disable canonical input, disable echo, 
    * disable visually erase chars, 
    * disable terminal-generated signals */ 
    toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); 
    /* disable output processing */ 
    toptions.c_oflag &= ~OPOST; 

    /* wait for n (in our case its 1) characters to come in before read returns */ 
    /* WARNING! THIS CAUSES THE read() TO BLOCK UNTIL ALL */ 
    /* CHARACTERS HAVE COME IN! */ 
    toptions.c_cc[VMIN] = 0; 
    /* no minimum time to wait before read returns */ 
    toptions.c_cc[VTIME] = 100; 

    /* commit the options */ 
    tcsetattr(fd, TCSANOW, &toptions); 

    /* Wait for the Barcode to reset */ 
    usleep(10*1000); 

    return fd; 
} 

static int read_from_barcode_reader(int fd, char* bcr_buf) 
{ 
    int i = 0, nbytes = 0; 
    char buf[1]; 

    /* Flush anything already in the serial buffer */ 
    tcflush(fd, TCIFLUSH); 

    while (1) { 
     nbytes = read(fd, buf, 1);   // read a char at a time 
     if (nbytes == -1) { 
      return -1;      // Couldn't read 
     } 
     if (nbytes == 0) { 
      return 0; 
     } 
     if (buf[0] == '\n' || buf[0] == '\r') { 
      return 0; 
     } 
     bcr_buf[i] = buf[0]; 
     i++; 
    } 

    return 0; 
} 

Maintenant que vous ne savez pas combien de personnages vous allez vous obtenir pouvez utiliser VMIN et VTIME combinaison pour répondre à votre préoccupation. Ce document détaille diverses possibilités avec VMIN et VTIME.