Parser, encore & encore

Bonsoir,
je veux parser des messages de ce type, à extraire d'une chaîne qui se remplit par un moyen série quelconque :
<t 1 03 20 1> <t 1 257 120 1> etc.
les "<" et ">" délimitent les messages
dans chaque message je veux extraire ce qui est utile, cad. par exemple pour le 1er :
"t" puis "1" puis "03" puis "20" puis "1"
le message doit être supprimé de la chaîne à la fin

en cherchant sur la toile, on voit que chacun y va de sa méthode ou de sa bibliothèque
je n'ai pas pu déterminer ce qui répond le mieux à mon besoin
vous avez sûrement une idée ?

Vous recevez une cString? strtok() fera l’extraction simplement
Pourquoi stocker le < et le > dans le message ?

le < et le > délimitent chaque message, le format m'est imposé
je (peux) dois pouvoir par contre fixer le type de string
je reçois les messages sous la forme <t 1 03 20 1>, je les mets dans une fifo unique, puis je traite chaque message en extrayant les données utiles ; puis le message traité est supprimé ce qui raccourcit (ou vide) la fifo

strtok est expliqué ici :

https://www.cplusplus.com/reference/cstring/strtok/

L'exemple est transposable, en mettant les séparateurs à "< >" :

  char str[] ="<t 1 03 20 1> <t 1 257 120 1>";
  char * pch;
  Serial.printf ("Splitting string \"%s\" into tokens:\n",str);
  pch = strtok (str,"< >");
  while (pch != NULL)
  {
    Serial.printf ("%s\n",pch);
    pch = strtok (NULL, "< >");
  }

A tester...

Je suppose un ESP32 (Serial.printf)

Qui disait déjà "je parse donc je suis" ?

merci, parse queue :
--- Miniterm on COM3 460800,8,N,1 ---
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
�Splitting string "<t 1 03 20 1> <t 1 257 120 1>" into tokens:
t
1
03
20
1
t
1
257
120
1

j'ai encore un couac :

char str[] ="<t 1 03 20 1> <t 1 257 120 1>";
Serial.print("<t 1 03 20 1> <t 1 257 120 1>") ; /// ça, ça marche
Serial.print(str[]) ; /// ça, ça marche pas

y a un truc que c'est pareil mais que c'est pas la même chose (ou l'inverse) ?

Enlevez les []

Serial.print(str) ;

Sinon ce que je disais c’est qu’à la réception du < vous savez que vous commencez une trame donc pas la peine de l’écrire, vous mettez l’index à zéro et vous préparez à recevoir le payload, puis à la réception du > vous mettez un caractère nul pour terminer la cString
Deux octets de gagné et ça de moins à parser

merci,
oui, c'est faisable ; avec un esp32 j'ai pris le principe d'en mettre le moins possible dans les isr (ici la réception des messages), pour en mettre le maximum dans la loop du main (ici, le parsing)
mais je dois en effet pouvoir faire cet arrangement à la réception, sans le moindre risque de planter le rtos

Si c’est une réception série une ISR généralement n’est pas nécessaire
Je suppose que vous testez de toutes façons la réception du début et fin de message dans une mini machine à états ?

le début et la fin d'un message c'est les caractères < >
normalement, les liaisons série sont fiables, et les messages ne doivent pas être abîmés, mais pour bien faire il faut vérifier le format du message, cad. la présence du < au début et du > à la fin de chaque message, ainsi que la cohérence du contenu entre les < >
en cas d'incohérence, le message doit être rejeté et l'erreur signalée (c'est basique) ; par conséquent un parsing qui se contente d'éliminer les > < n'est pas suffisant

je ne sais pas comment vous traitez la réception mais ce que j'ai en tête c'est une lecture asynchrone du port série avec réception d'un message et traitement dans la loop. la présence des <> dans le message n'apporte rien puisque qu'on ne reçoit le message que si ces 2 éléments ont été reçus.

un truc du genre (tapé ici)

const byte longeurMaxMessage = 30;
char message[longeurMaxMessage + 1]; // + 1 pour le caractère nul en fin de chaîne

bool messageRecu() {
  enum t_etat : byte {ATTENTE, EN_COURS};
  static t_etat etat = ATTENTE;
  static byte index = 0;
  bool messagePret = false;
  int r = Serial.read();

  if (r != -1) { // -1 veut dire rien à lire
    // ici on pourrait aussi virer les blancs : if (! isspace(r))  
    switch (etat) {
      case ATTENTE:
        if (r == '<') { // nouvelle réception
          index = 0;
          message[0] = '\0';
          etat = EN_COURS;
        }
        break;

      case EN_COURS:
        if (r == '>') { // nouvelle réception
          message[index] = '\0';
          etat = ATTENTE;
          messagePret = true;
        } else {
          if (index < longeurMaxMessage) {
            message[index++] = r;
          }
        }
        break;
    }
  }
  return messagePret;
}

void analyserMessage() {
  Serial.print("J'analyse \"");
  Serial.print(message);
  Serial.println("\"");
}

void setup() {
  Serial.begin(115200); Serial.println();
}

void loop() {
  if (messageRecu()) {
    analyserMessage();
  }
  // ici le reste du code
}

essayez d'envoyer depuis la console dfjhkjdfghkds<coucou>hgsjfgfjdkghjdfg<hello>
vous devriez voir coucou et hello comme messages identifiés à analyser

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.