Optimiser la recherche dans un fichier SD

Bonjour,
Voila,
je cherche à exploiter une base de donnée sauvegardé sur une SD.
J’ai dans un 1er temps créé un tableau de “STRING” (désolé) contenant différents paramètres, texte, ligne de commentaires séparé par des tabulation.
Ce tableau ne tiens pas compte des lignes de commentaires, des ligne vide.

/-------------------------------------------------------------------
/Tableau RFID
/Si vide contiens '000	000	000	000	000	0'
/
/	TAG UIHD				Droits
020002	250	110	142	203	209	1	
020002	090	213	083	204	016	3	
020002	160	110	152	168	254	3	
020002	000	000	000	000	000	0	
020002	198	084	107	144	105	5	
020002	000	000	000	000	000	0	
/
/-------------------------------------------------------------------
/Description des Zones
/
/	Num	Texte clair
/020004	1	Zone Perimetrique	
/020004	2	Zone Volumetrique	
/020004	3	Incendie	
/020004	4	Zone Cabane	
/020004	5	Zone Garage	
/020004	6	Zone Piscine	
/020004	7	Alarme technique	
/
/-------------------------------------------------------------------

Ça fonctionne super bien, mais maintenant, je souhaite ajouter des lignes supplémentaire et bien sûre la mémoire manque. Je me suis donc dit, au lieu de créer un tableau je vais lire le fichier à chaque recherche. La ça fonctionne également, mais le temps de traitement des 145 lignes du fichier est énorme (~12sec). Bien que mon code ne soit pas optimisé et que j’emploie des string, je pense que je perd du temps à l’ouverture et à la fermeture du fichier.

Auriez vous une idée pour optimiser le temps et la mémoire. Merci.

void searchBDDlignePARligne()
{
  // Ouvre la base de donnée
  File myFile = SD.open(Fichier, FILE_READ);
  if (myFile)
  {
    //------------------------------------------------
    if (myFile.available() > 0)                      // Scanne toute la BDD
    {
      a = 1;
      for ( a; a < x; a++)
      {
        i = 0;                                                    // nombre de carractere de la ligne
        while ((SDbuffer[i++] = myFile.read()) != '\n')           // Récupère une ligne entière dans le buffer
        {}
      }

      if (SDbuffer[0] != '\n' && SDbuffer[0] != '/')            // Elimine les lignes vide et les lignes de commentaires
      {
        SDbuffer[i++] = '\0';                                   // Ajoute le carractére nul
        texte = (char*)SDbuffer;                                // Convertion en String
        buffer3 = texte.substring(0, 6);                        // Récupère le marqueur
        if (buffer3 == Search[0])                               // Si c'est le marqueur recherché
        {
          i = 0; //Cellule du tableau Recu
          do
          {
            index = texte.indexOf('\t');                             // Recherche la position du marqueur '\t'
            if (index != -1) {
              Recu[i] = texte.substring(0, index);                   // on découpe la chaine et on stocke le bout dans le tableau
              i++;
              texte = texte.substring(index + 1, texte.length());      // on enlève du message le bout stocké
            } else {
              if (texte.length() > 0) {                              // après le dernier espace on s'assure que la chaine n'est pas vide
                Recu[i] = texte.substring(0, index);                 // dernier bout
              }
            }
          } while (index > 0);                                      // Boucle jusqu'à que read soit vide
          analyse = true;


        }
      }

      myFile.close();                                           // Ferme le fichier
    }
  }
  else
  {
    Serial.println("Erreur de lecture carte SD!");

  }
}

Je vai diviser ma base en 3 fichiers, mais du coup un autre problème se pose:
3 tableaux seront utiliser et ils seront de taille différant. Mais ma fonction doit renseigner le bon tableau avec le bon fichier. Dans ma fonction, comment pointer sur un tableau exemple: "byte tableauX [1] / tableauY[1] qui sera diférent à chaque appel?
Je cherche dans ce sens, mais je bute

  String a[4] {"a0", "a1", "a2", "a3"};
  String b[4] {"b0", "b1", "b2", "b3"};
  String c[4] {"c0", "c1", "c2", "c3"};

  void *pPointeurTableau = a;
Serial.println(*pPointeurTableau[1]);

exit status 1
no match for 'operator*' (operand type is 'String')

Bonjour,
La meilleure stratégie dépend de la taille maximale des tableaux à traiter. Quelle est cette taille ?

Si la vitesse de traitement pose problème je propose deux modifications:
La première consiste à formater le fichier ascii de sorte qu'une ligne du tableau occupe exactement un sous-multiple de 512 octets qui est la taille des buffers SD et aussi des "secteurs" de la carte.
La deuxième consiste à réduire au maximum la taille du fichier:
Limiter au maximum la longueur de la ligne de texte en retirant tous les espaces inutiles est aussi une bonne idée.
La solution radicale consiste à utiliser un fichier binaire au lieu d'un fichier ascii. Cela optimise à la fois le temps et la mémoire, mais demande un peu de travail pour écrire le fichier sur la carte. On peut imaginer ajouter au code arduino un transcodeur qui, la première fois qu'on met la carte dans l'arduino, convertit le ou les fichiers ascii en binaire et les écrit sur la carte.

Le temps d'ouverture du fichier est important (il faut trouver le fichier dans le directory, lire la FAT puis lire le fichier), laisser le fichier ouvert peut être intéressant.
Enfin si la base de données est trop grosse pour tenir en binaire dans la ram de l'arduino, la recherche sur la carte SD risque d'être lente malgré tout. Stocker en ram une partie de la base peut être intéressant: par exemple le premier octet du TAG avec un pointeur vers l'endroit où se trouve la ligne correspondante dans le fichier, pour faire un premier tri rapide avant de vérifier le reste du tag sur la carte SD... si le fichier est binaire et que les tags sont stockés dans l'ordre, le pointeur est implicitement le n-ième enregistrement binaire dans le fichier.

Il faut savoir que si on peut se contenter de lire des blocs entiers de 512 octets sur la carte SD, il est possible de réduire fortement la taille occupée par la librairie SD en mémoire (c'est alors une librairie aux fonctionnalités très réduite), mais en gros un seul buffer de 512 octets peut suffire.

Bon courage.
Benoit

Bonjour,

Il suffit de déclarer un pointeur de type String

  String *pPointeurTableau = a;
  Serial.println(pPointeurTableau[1]);

casimir_isere:
Bonjour,
La meilleure stratégie dépend de la taille maximale des tableaux à traiter. Quelle est cette taille ?

Alors le tableau d'origine (en String) comporte 66 lignes pour 9 colonnes. Toutes les cellules ne sont pas utilisées.
La stratégie de départ consisté à créer ce tableau dans le setup, mais la mémoire manque.
La seconde est d'ouvrir le fichier, de rechercher un "marquer" puis d'exploiter la ligne.

casimir_isere:
Si la vitesse de traitement pose problème je propose deux modifications:
La première consiste à formater le fichier ascii de sorte qu'une ligne du tableau occupe exactement un sous-multiple de 512 octets qui est la taille des buffers SD et aussi des "secteurs" de la carte.

Oui, mais du coup, je ne maitrise plus ce que contienne mes cellules.

casimir_isere:
La deuxième consiste à réduire au maximum la taille du fichier:
Limiter au maximum la longueur de la ligne de texte en retirant tous les espaces inutiles est aussi une bonne idée.
La solution radicale consiste à utiliser un fichier binaire au lieu d'un fichier ascii. Cela optimise à la fois le temps et la mémoire, mais demande un peu de travail pour écrire le fichier sur la carte. On peut imaginer ajouter au code arduino un transcodeur qui, la première fois qu'on met la carte dans l'arduino, convertit le ou les fichiers ascii en binaire et les écrit sur la carte.

L'idée de ce fichiers est qu'il soit convivial et lisible pour le modifier facilement.
La solution de la convertion est pas mal.

casimir_isere:
Le temps d'ouverture du fichier est important (il faut trouver le fichier dans le directory, lire la FAT puis lire le fichier), laisser le fichier ouvert peut être intéressant.
Enfin si la base de données est trop grosse pour tenir en binaire dans la ram de l'arduino, la recherche sur la carte SD risque d'être lente malgré tout. Stocker en ram une partie de la base peut être intéressant: par exemple le premier octet du TAG avec un pointeur vers l'endroit où se trouve la ligne correspondante dans le fichier, pour faire un premier tri rapide avant de vérifier le reste du tag sur la carte SD... si le fichier est binaire et que les tags sont stockés dans l'ordre, le pointeur est implicitement le n-ième enregistrement binaire dans le fichier.

Je fais déjà ça avec mon systéme de marqueur. Mais je dois pouvoir optimiser davantage.

casimir_isere:
Il faut savoir que si on peut se contenter de lire des blocs entiers de 512 octets sur la carte SD, il est possible de réduire fortement la taille occupée par la librairie SD en mémoire (c'est alors une librairie aux fonctionnalités très réduite), mais en gros un seul buffer de 512 octets peut suffire.

Bon courage.
Benoit

Merci pour les info

kamill:
Bonjour,

Il suffit de déclarer un pointeur de type String

  String *pPointeurTableau = a;

Serial.println(pPointeurTableau[1]);

kamill:
Bonjour,

Il suffit de déclarer un pointeur de type String

  String *pPointeurTableau = a;

Serial.println(pPointeurTableau[1]);

Merci kamill, ça fonctionne!

Je vais faire la chose suivante:
Les données de ma base que j'utilise couramment seront inséré dans un tableau. Comme ça je ne perd pas de temps au traitement. Je trouve l'idée de casimir_isere intéressante.

casimir_isere:
La solution radicale consiste à utiliser un fichier binaire au lieu d'un fichier ascii. Cela optimise à la fois le temps et la mémoire, mais demande un peu de travail pour écrire le fichier sur la carte. On peut imaginer ajouter au code arduino un transcodeur qui, la première fois qu'on met la carte dans l'arduino, convertit le ou les fichiers ascii en binaire et les écrit sur la carte.

Les données d'initialisation quand à elle seront recherché directement dans le fichier.

Merci à vous.

Bonjour,
Si vos données sont c'est des chiffres qui tiennent dans un octet, 66 lignes de 9 occuperont 600 octets. Avec les 2ko d'un Uno ça devrait rentrer en mémoire: c'est peut être un peu juste avec la librairie complète de la carte SD mais largement possible avec une librairie allégée. Si vous avez encore des soucis de vitesse de lecture, sachez que c'est une voie possible... Je vais peut-être poster ma librairie allégée quelque part, le principe consiste à n'utiliser qu'un seul buffer de 512 octets et à lire et écrire directement dedans. Il faut donc faire des lectures et écritures alignées sur des multiples de 512 octets dans le fichier...
Benoit

Je suis sur un mega, et tout est rentré dans l'ordre. Merci à vous!