Incompréhension du problème au niveau d'un parseur

bonjour je n'arrive pas a trouver ou je fais l'erreur de raisonnement
voici le bout de code :

void parse_command()
{
  char * pch;
  int ii = 0;

  //parsing et Identification des attributs

  pch = strtok (command_line,"|");
  while (pch != NULL)
  {
    sprintf(arg_list[ii],"%s", pch);    
    pch = strtok (NULL, "|");
   Serial.println(arg_list[ii]);
    ii++;
  }

  //identification de l'ID
  Command_ID = -1;
  for (ii=0; ii<NB_COMMANDS; ii++)
  {
    if (strcmp(Command_List[ii], arg_list[0]) == 0)
    {
      Command_ID = ii;
      Serial.println("ok commande");
      break;
    }
  }
}

si je tape la console répond :

CmdCfg
ok commande
CmdCfg
Command executée : CmdCfg->done

si je tape <CmdCfg|52|23> la console répond :

CmdCfg
52
23
Unknown Command -> CmdCfg

la chaine est parsée la commande semble correcte mais le code n'est pas reconnu j'ai posé des serialprint pour tracer et que j’enlèverai ...

pour résumer dans un cas CmdCfg est reconnu lorsqu'il est seul
CmdCfg n'est pas reconnu avec plusieurs valeurs alors que c'est bien la même chaine qui est affichée

je soupçonne un probléme de fin de chaine genre caractère zéro mais je vois pas comment modifier.

merci de votre aide

postez un code compréhensible.. on n'a aucune idée de ce qu'est arg_list ou Command_List ni comment command_line est déclarée ou entrée

si vous démarrez le programme et tapé directement <CmdCfg|52|23> est-ce le même comportement que si vous tapez juste avant ?

Essaie d'ajouter des séparateurs, en remplaçant "¦" par "¦<>"

bonjour et merci

Oui j'ai le même comportement

je veux bien poster les autres bout de code mais a partir du moment ou

mon ID de commande n'est pas détecté le reste est pas vraiment utile..

//Déclaration de la liste des identifiants commandes.

enum Command_IDs { 
  CmdCfg = 0,
  CmdEnr = 1,
  CmdSim = 2,
  DemCfg = 3,
  DemVer = 4
};

//Syntaxe des commandes

char* Command_List[NB_COMMANDS] = { 
  "CmdCfg",
  "CmdEnr",
  "CmdSim",
  "DemCfg",
  "DemVer"
};
void lireCommande(){
  
  if (Serial.available()) {
    incomingByte = Serial.read(); 
    //Serial.println(incomingByte);
    switch (incomingByte)
    {
      case '<': 
        index = 0;
        memset(command_line, 0, MAX_CMD_LEN - 1);
        break;
      case '>':
        command_line[index+1] = 0;
        parse_command();
        exec_command();
        break;
      default : 
        command_line[index++] = incomingByte;
    }
  }
}
void exec_command()
{
  
    switch (Command_ID)
    {
      case CmdCfg: 
        Serial.println("CmdCfg");
	      ack_command("done");
  
        break;
     
      case CmdEnr:
        Serial.println("CmdEnr");
  ack_command("done");
        break;
        
         case CmdSim:
        Serial.println("CmdSim");
  ack_command("done");
        break;
        
      case DemCfg:
        Serial.println("DemCfg");
  ack_command("done");

        break;
        
      case DemVer:
        Serial.println("DemVer");
       ack_command("done");

        break; 
               
      default : 
        Serial.print("Unknown Command -> ");
        Serial.println(command_line);
    }
}

évidemment qq soit la commande ca marche seul et pas si accompagné

pourquoi ma comparaison est fausse des que c'est accompagné alors que la chaine affichée est la même

merci

J'ai essayé en mettant d'autres caractères même combat ! seul c'est bon a plusieurs rien

pourtant c'est bien décomposé en différents éléments

je veux bien poster les autres bout de code mais a partir du moment ou

mon ID de commande n'est pas détecté le reste est pas vraiment utile..

généralement le diable se cache dans les détails... vous ne pourriez pas poster un code complet, qui compile, qui permet de saisir le commande sur la console série et qui montrerait le problème que vous rencontrez ?

ça nous éviterez d'avoir à l'écrire pour tester :slight_smile:

bon je m'en suis fait un petit....

const uint8_t maxLengthofCommand = 63;
char command_line[maxLengthofCommand + 1];  // +1 as we want to add a trailing '\0' to terminate a cSrting
uint8_t commandIndex = 0;

//Déclaration de la liste des identifiants commandes.

enum Command_IDs : byte {
  CmdCfg = 0,
  CmdEnr = 1,
  CmdSim = 2,
  DemCfg = 3,
  DemVer = 4
};

//Syntaxe des commandes
const char* Command_List[] = {
  "CmdCfg",
  "CmdEnr",
  "CmdSim",
  "DemCfg",
  "DemVer"
};

const byte NB_COMMANDS = sizeof(Command_List) / sizeof(Command_List[0]);

const uint8_t maxArguments = 10;
char arg_list[maxArguments][maxLengthofCommand + 1];

void parse_command()
{
  char * pch;
  int ii = 0;

  //parsing et Identification des attributs

  pch = strtok (command_line, "|");
  while (pch != NULL)
  {
    sprintf(arg_list[ii], "%s", pch); // normalement tester si ii < maxArguments avant de faire cela
    pch = strtok (NULL, "|");
    Serial.println(arg_list[ii]);
    ii++;
  }

  //identification de l'ID
  int Command_ID = -1;
  for (int ii = 0; ii < NB_COMMANDS; ii++)
  {
    if (strcmp(Command_List[ii], arg_list[0]) == 0)
    {
      Command_ID = ii;
      Serial.println("ok commande");
      break;
    }
  }
}
void lireCommande() {

  if (Serial.available()) {
    int incomingByte = Serial.read();
    switch (incomingByte)
    {
      case '<':
        commandIndex = 0;
        memset(command_line, 0, sizeof(command_line));
        break;
      case '>':
        command_line[commandIndex + 1] = 0;
        parse_command();
        Serial.println(F("\nEntrez commande:"));
        break;
      default :
        if (commandIndex < maxLengthofCommand) command_line[commandIndex++] = incomingByte;
    }
  }
}

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Entrez commande:"));
}

void loop()
{
  lireCommande();
}

j'ai pas changé votre code tellement (juste testé le dépassement de buffer) dans le default du switch et ça a l'air de marcher...

Bon j'ai ajouté ca

if (index < MAX_CMD_LEN) command_line[index++] = incomingByte;

même résultat je regarde ca plus avant des que possible j'ai des invités et ma femme rale un peu

merci

Comment command_line est-il déclaré ?

jfs59:
Bon j'ai ajouté ca

if (index < MAX_CMD_LEN) command_line[index++] = incomingByte;

même résultat je regarde ca plus avant des que possible j'ai des invités et ma femme rale un peu

merci

votre femme a raison ! Elle devrait râler plus, c'est la Journée Internationale des Femmes en plus !!!

Après le départ des invités et avoir fait la vaisselle, le ménage le repassage et le repas du soir :grin: , regardez mon code et en quoi il diffère du votre... vous avez peut-être un débordement mémoire quelque part…

Tu devrais définir ta fonction parse non pas comme void the mais comme bool et renvoyer un true si la commande a été correctement parsée et un false sinon, que tu pourrais tester juste après l'appel.

De plus, pour déboguer, tu devrais afficher la valeur de ii lorsque tu affiches arg_list[ii]

Juste une remarque à propos de la liste d'arguments :

char arg_list[maxArguments][maxLengthofCommand + 1];

Cette liste occupe 10 fois plus de place que la commande elle-même.

Les variables globales utilisent 953 octets (46%) de mémoire dynamique, ce qui laisse 1095 octets pour les variables locales. Le maximum est de 2048 octets.

Les arguments n'ont pas besoin d'être recopiés dans une liste, les adresses suffisent. La liste devient une liste de pointeurs.

char *arg_list[maxArguments];

// parse_command :

  pch = strtok (command_line, "|");
  while (pch != NULL)
  {
    if (ii < maxArguments) {
      arg_list[ii] = pch;
      Serial.println(arg_list[ii]);
    }
    pch = strtok (NULL, "|");
    ii++;
  }

Les variables globales utilisent 333 octets (16%) de mémoire dynamique, ce qui laisse 1715 octets pour les variables locales. Le maximum est de 2048 octets.

Et hop : 620 octets économisés, et le fonctionnement est identique.

hbachetti:
Juste une remarque à propos de la liste d'arguments :

vu son demi bout de code je pense qu’il veut peut être garder une copie indépendante du buffer d’entrée qui peut se ré-remplir dans la loop s’il y a des trucs qui tournent en machine à état par exemple... dans ce cadre je ne voulais pas juste garder les pointeurs.

De plus j’ai Un doute aussi sur l’origine de son bug, s’il a pris des pointeurs seulement sans réserver d’espace et fait son sprintf (au lieu de strcpy pour gâcher encore plus de mémoire :wink: ) alors il a un souci là et je voulais qu’il regarde cette partie

Mais Oui dans l’absolu il faudrait une taille adaptée pour chaque paramètre j’ai fait ça à l’arrache avec la constante que j’avais sous la main Sans m’ennuyer, c'était pour tester le parser

C'est l'inconvénient d'un code incomplet :confused:

vu son demi bout de code je pense qu'il veut peut être garder une copie indépendante du buffer d'entrée qui peut se ré-remplir dans la loop s'il y a des trucs qui tournent en machine à état par exemple... dans ce cadre je ne voulais pas juste garder les pointeurs.

Oui mais le problème est que strtok() place des ZÉRO à la place des séparateurs, donc la commande est perdue de toutes façons.

On verra peut-être un code complet après le repas du soir :slight_smile:

hbachetti:
C'est l'inconvénient d'un code incomplet :confused:

Oui mais le problème est que strtok() place des ZÉRO à la place des séparateurs, donc la commande est perdue de toutes façons.

oui mais une fois le parsing fait elle est réutilisée par son code d'attente de commande

      case '<':
        commandIndex = 0;
        memset(command_line, 0, sizeof(command_line));
        break;

vu que Serial est asynchrone, je me disais qu'il reçoit une commande, ça tourne un moment avec les paramètres reçus jusqu'à la réception de la prochaine commande. il faut donc bien un buffer séparé pour les paramètres en cours et ceux qui arrivent ensuite.

mais pure spéculation, si ça se trouve il exécute la commande sur place et tout est synchrone, auquel cas oui, autant garder une liste de pointeurs dans le buffer série (et c'est pour cela que strtok met des 0 sur les séparateurs)

bonsoir

alors code complet bah y a 11 unités avec du code qui n'a strictement rien a voir

donc je donne le code de l'unité parseur

#include "Arduino.h"
#include "configvariable.h"
#include "parseur.h"

/*DETECTION ET DECOMPOSITION D'UNE COMMANDE*/

#define NB_COMMANDS 5  // nombre de commandes
#define MAX_ARGS 15  // nombre maximum  d'arguments d'une commande
#define MAX_CMD_LEN 80
#define MAX_ARGS_LEN 4

//Déclaration de la liste des identifiants commandes.

enum Command_IDs { 
  CmdCfg = 0,
  CmdEnr = 1,
  CmdSim = 2,
  DemCfg = 3,
  DemVer = 4
};

//Syntaxe des commandes

char* Command_List[NB_COMMANDS] = { 
  "CmdCfg",
  "CmdEnr",
  "CmdSim",
  "DemCfg",
  "DemVer"
};



//Reception commande
int Command_ID;
char arg_list[MAX_ARGS][MAX_ARGS_LEN];
char incomingByte;
int index;
char command_line[MAX_CMD_LEN];
int iPrintConfirmation;

void lireCommande(){
  
  if (Serial.available()) {
    incomingByte = Serial.read(); 
    //Serial.println(incomingByte);
    switch (incomingByte)
    {
      case '<': 
        index = 0;
        memset(command_line, 0, MAX_CMD_LEN - 1);
        break;
      case '>':
        command_line[index+1] = 0;
        parse_command();
        exec_command();
        break;
      default : 
        if (index < MAX_CMD_LEN) command_line[index++] = incomingByte; 
    }
  }
}

//Accusé de reception commande

void ack_command(char *message)
{
  iPrintConfirmation =1;
  if (iPrintConfirmation>0)
  {
    Serial.print("Command executée : ");
    Serial.print(Command_List[Command_ID]);
    Serial.print("->");
    Serial.print(message);
    Serial.println();
  }
}




//execution des commandes

void exec_command()
{
  
    switch (Command_ID)
    {
      case CmdCfg: 
        Serial.println("CmdCfg");
	      ack_command("done");
  
        break;
     
      case CmdEnr:
        Serial.println("CmdEnr");
  ack_command("done");
        break;
        
         case CmdSim:
        Serial.println("CmdSim");
  ack_command("done");
        break;
        
      case DemCfg:
        Serial.println("DemCfg");
  ack_command("done");

        break;
        
      case DemVer:
        Serial.println("DemVer");
       ack_command("done");

        break; 
               
      default : 
        Serial.print("Unknown Command -> ");
        Serial.println(command_line);
    }
}

//Parsing identifiant commande et arguments

void parse_command()
{
  char * pch;
  int ii = 0;

  //parsing et Identification des attributs

  pch = strtok (command_line,"X");
  while (pch != NULL)
  {
    sprintf(arg_list[ii],"%s", pch);    
    pch = strtok (NULL, "X");
   Serial.println(arg_list[ii]);
    ii++;
  }

  //identification de l'ID
  Command_ID = -1;
  for (ii=0; ii<NB_COMMANDS; ii++)
  {
    if (strcmp(Command_List[ii], arg_list[0]) == 0)
    {
      Command_ID = ii;
      Serial.println("ok commande");
      break;
    }
  }
}

le code du .h

/*
DETECTION ET DECOMPOSITION D'UNE COMMANDE
*/

#include <stdlib.h>
#include <string.h>


void lireCommande();

//Accusé de reception commande
void ack_command(char *message);

//This function execute a command. Amend this function with your own commands
void exec_command();

//Parsing identifiant commande et arguments
void parse_command();

je sais que je dois avoir des gros voir tres gros progrès a faire dans la structuration du code en unités et dans les includes vu les problèmes de compilation parfois avec des déclarations repetitive ou les variables inconnues car non déclarées

je m'en sors toujours avec des solutions de bric et de broc
j'ai lu relu et rerelu l'organisation d'un programme vec la décomposition en unité et avec les .h et les includes mais j'ai toujours autant de mal a savoir ou mettre telle et telle procédure ou telle déclarations
mais ca c'est un autre probléme

Après a dire vrai toute cette partie de code je l'avais faites avec des Strings et ca fonctionnait sans problème .... j'ai voulu jouer au gros bras en utilisant des char mais peut etre je suis pas de taille ...
de même par conséquent les fonctions sprintf strcpy etc .... pfiou ....

Command_ID est une variable locale à la fonction void parse_command() ce qui fait qu'elle n'est pas visible ailleurs...

Hors vous l'utilisez aussi dans exec_command et là il prend la variable globale qui est initialisée à 0....

pour répondre a toutes les questions

je reçois un code par Bluetooth il doit être analysé puis traité de suite .

ça part de la

void serialEvent() {

// essai remplacement string buffer

lireCommande();

/* ancien code */

}

supprimer la liste double évidemment ça m’intéresse car le code est plus lourd comme ça qu'avec les String