J'essaie de trouver une solution intéressante pour lire les données série, et que faire quand un read() est fait mais qu'il contient un message incomplet.

Les messages attendus entre les périphériques ont un octet de début et de fin défini, de sorte qu'il est facile de voir quand un message commence et se termine.

Je peux ouvrir une amende de port série et lire à partir du port série. Mais je constate que l'ordinateur lit plus vite que les données et que je reçois un message incomplet.

Pour cet exemple, disons que le message attendu est

0x10 0xFF 0xFF 0xFF 0xFF 0x11

Avec 0x10 le début, 0x11 la fin et 0xFF les octets de données

Je suis nouveau en C donc il me manque peut-être quelque chose d'évident, Ma solution actuelle

int main() {
     /* Ommited serial port opening and checking*/
     char read_buffer[80];
     char message_buffer[80];
     int message_buffer_index = 0;
     int start_index = -1;
     int end_index = -1;
     int read_bytes;
     read_bytes = read(serial_port, read_buffer, sizeof(read_buffer) - 1);
     /* Now lets say read_bytes returns 3 and read buffer is {0x10, 0xFF, 0xFF} */
     /* What should I do with the read_buffer? Currently appending to message buffer*/
     memcpy(&message_buffer[message_buffer_index], &read_buffer[0], read_bytes);
     /* Now check the message buffer for a full message */
     for (int i = 0; i < 80; i++) {
          if (message_buffer[i] = 0x10) {
               start_index = i;
               continue;
          }
          if (message_buffer[i] = 0x11) {
               end_index = i;
          }
          if (start_index != -1 && end_index != -1) {
               /* Found a message, do something with it, not super important here */
               process_message();
               /* Now how to erase the full message from the 
               buffer and push any non processed data to the 
               front? */
               remove_message();
          }
    }
}

int process_message();  
int remove_message();
0
user2840470 27 janv. 2019 à 23:17

3 réponses

Meilleure réponse

Pour minimiser la surcharge de faire de nombreux appels système read () de petits nombres d'octets (par exemple, la solution erronée de lire un octet à la fois), utilisez un tampon intermédiaire dans votre code.
Le read () du terminal série doit être en mode bloquant pour éviter un code retour de zéro octet.

#define BLEN    1024
unsigned char rbuf[BLEN];
unsigned char *rp = &rbuf[BLEN];
int bufcnt = 0;

/* get a byte from intermediate buffer of serial terminal */
static unsigned char getbyte(void)
{
    if ((rp - rbuf) >= bufcnt) {
        /* buffer needs refill */
        bufcnt = read(fd, rbuf, BLEN);
        if (bufcnt <= 0) {
            /* report error, then abort */
        }
        rp = rbuf;
    }
    return *rp++;
}

Pour obtenir le code d’initialisation termios approprié pour le terminal série, voir cette réponse. Vous devez augmenter le paramètre VMIN à quelque chose de plus proche de la valeur BLEN.

Vous pouvez désormais accéder facilement aux données reçues octet à la fois avec une pénalité minimale en termes de performances.

#define MLEN    1024  /* choose appropriate value for message protocol */
int main() 
{
    unsigned char mesg[MLEN];
    ...

    while (1) {
        while (getbyte() != 0x10)
            /* discard data until start found */ ;

        length = 0;
        while ((mesg[length] = getbyte()) != 0x11) {
            /* accumulate data until end found */ 
            length++;
        }

        /* process the message */
        ...


    }  /* loop for next message */
...
}

Notez que votre définition d'un cadre de message n'est pas robuste.
Si les données sont binaires et peuvent donc utiliser les mêmes valeurs que les octets de début et de fin, alors cette analyse des données reçues est sujette à des télégrammes mal alignés.

1
sawdust 17 avril 2019 à 21:12

Vous avez besoin d'un tampon circulaire. Placez les données dans la mémoire tampon et le processus les prend lorsque, par exemple, il y a suffisamment de données ou à tout moment opportun.

Wikipedia a un excellent article à ce sujet https://en.wikipedia.org/wiki/Circular_buffer

enter image description here

0
P__J__ 27 janv. 2019 à 20:57

Mieux vaut utiliser stdio pour la lecture, quelque chose comme ceci:

FILE *fp = fdopen(serial_port, "r");

while (blabla) {
  while (fgetc(fp) != 0x10)
    ;  // wait until start
  while ((c = fgetc(fp)) != 0x11)
    message_buffer[message_buffer_index++] = c;
  // here you have a complete message
}

Insérez les contrôles EOF et les erreurs si nécessaire

0
ctrl-d 27 janv. 2019 à 23:32