{"id":3492,"date":"2013-03-31T12:00:39","date_gmt":"2013-03-31T11:00:39","guid":{"rendered":"http:\/\/www.blaess.fr\/christophe\/?p=3492"},"modified":"2013-04-29T08:16:20","modified_gmt":"2013-04-29T07:16:20","slug":"configuration-application-embarquee","status":"publish","type":"post","link":"https:\/\/www.blaess.fr\/christophe\/2013\/03\/31\/configuration-application-embarquee\/","title":{"rendered":"Configuration d&rsquo;une application embarqu\u00e9e (1\/2)"},"content":{"rendered":"<p dir=\"ltr\" style=\"text-align: justify;\"><a href=\"http:\/\/www.blaess.fr\/christophe\/2013\/03\/31\/configuration-application-embarquee\/\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-3500 alignright\" alt=\"Configuration Application Embarqu\u00e9\" src=\"http:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2013\/03\/figure-1.png\" width=\"200\" height=\"150\" \/><\/a>Cet article a \u00e9t\u00e9 \u00e9crit en collaboration avec mon ami Fran\u00e7ois Beaulier dont je vous recommande <a title=\"Ing\u00e9libre\" href=\"http:\/\/www.ingelibre.fr\/\" target=\"_blank\">le blog<\/a>. Nous avons r\u00e9fl\u00e9chi ensemble sur les possibilit\u00e9s qui s&rsquo;offrent au d\u00e9veloppeur d&rsquo;application embarqu\u00e9e pour enregistrer le param\u00e9trage, la configuration, les pr\u00e9f\u00e9rences&#8230; bref tout ce qui constitue les donn\u00e9es persistantes de son syst\u00e8me.<\/p>\n<p dir=\"ltr\">\n<!--more-->\n<\/p>\n<h1 dir=\"ltr\" id=\"internal-source-marker_0.5970981682171385\">Fichiers de configuration pour une application embarqu\u00e9e<\/h1>\n<p dir=\"ltr\" style=\"text-align: justify;\">La plupart des applications doivent lire des param\u00e8tres de fonctionnement et \u00e9ventuellement en sauvegarder certains. Ces param\u00e8tres de configuration qui agissent sur le comportement m\u00eame du programme, peuvent aussi \u00eatre des donn\u00e9es manipul\u00e9es par l\u2019utilisateur (par exemple les r\u00e9glages d\u2019un asservissement PID). Nous nous sommes int\u00e9ress\u00e9s aux possibilit\u00e9s qui nous sont offertes pour stocker ces donn\u00e9es de configuration, plus particuli\u00e8rement dans l\u2019optique des syst\u00e8mes embarqu\u00e9s &#8211; mais la plupart des remarques sont valables pour une application classique sur poste de travail.<\/p>\n<p dir=\"ltr\" style=\"text-align: justify;\">Pour illustrer nos propos nous imaginerons le cas d\u2019une application de gestion domotique tr\u00e8s simple. Elle doit disposer d\u2019une repr\u00e9sentation de son environnement sous forme de pi\u00e8ces de la maison, de capteurs (temp\u00e9rature, humidit\u00e9, d\u00e9tection de pr\u00e9sence, de fum\u00e9e&#8230;) et d\u2019actionneurs (thermostat, lampe, radiateur, alarme, etc.). Plusieurs possibilit\u00e9s se pr\u00e9sentent pour stocker ces informations, nous allons les examiner en d\u00e9taillant les avantages et inconv\u00e9nients de chacune.<\/p>\n<h1 dir=\"ltr\">Format brut binaire \u201csauvegarde de structures\u201d<\/h1>\n<p dir=\"ltr\" style=\"text-align: justify;\">Il s\u2019agit de la m\u00e9thode la plus simple possible, qui consiste juste en une s\u00e9rialisation brute d\u2019une structure. C\u2019est une solution adapt\u00e9e aux mod\u00e8les de donn\u00e9es statiques simples, lorsque rien n\u2019est allou\u00e9 dynamiquement. C\u2019est aussi un bon cas d\u2019\u00e9cole car on collectionne ici \u00e0 peu pr\u00e8s tout ce qu\u2019il ne faut surtout pas faire&nbsp;!<\/p>\n<p dir=\"ltr\" style=\"text-align: justify;\">Consid\u00e9rons le code suivant, version statique de notre application domotique:<\/p>\n<p>&nbsp;<\/p>\n<pre>#define MAX_CAPTEURS_PAR_PIECE \u00a0\u00a0\u00a0\u00a016\n#define MAX_ACTIONNEURS_PAR_PIECE \u00a016\n#define LG_NOM_PIECE \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a032\n#define MAX_PIECES \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a010\n#define LG_NOM_SITE \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a032\n\nstruct s_capteur {\n\u00a0\u00a0\u00a0 enum capteur_t \u00a0\u00a0\u00a0\u00a0type;\n\u00a0\u00a0\u00a0 int \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0identifiant;\n};\n\nstruct s_actionneur {\n    enum actionneur_t  type;\n    int               identifiant;\n};\n\nstruct s_piece{\n    wchar_t              Nom[LG_NOM_PIECE];\n    double               Volume;\n    struct s_capteur     TabCapteurs[MAX_CAPTEURS_PAR_PIECE];\n    struct s_actionneur  TabActionneurs[MAX_ACTIONNEURS_PAR_PIECE];\n};\n\nstruct s_site {\n    wchar_t         Nom[LG_NOM_SITE];\n    wchar_t         Adresse[LG_NOM_SITE];\n    struct s_piece  TabPieces[MAX_PIECES];\n};\n\nstruct s_site site_config;<\/pre>\n<p style=\"text-align: justify;\">L&rsquo;ensemble des donn\u00e9es se trouvent dans la structure <code>struct s_site<\/code>. La lecture de la configuration se fait par (traitement des erreur omis&nbsp;!)&nbsp;:<\/p>\n<pre>FILE * fp = fopen(\"config.bin\", \"r\");\nfread(&amp;site_config, sizeof(struct site_config), 1, fp);\nfclose(fp);<\/pre>\n<p style=\"text-align: justify;\">Et l&rsquo;\u00e9criture par&nbsp;:<\/p>\n<pre>FILE * fp = fopen(\"config.bin\", \"w\");\nfwrite(&amp;site_config, sizeof(struct site_config), 1, fp);\nfclose(fp);<\/pre>\n<p style=\"text-align: justify;\">Difficile de faire plus court&nbsp;! Et pourtant vous ne rencontrerez probablement jamais une application qui utilise cette m\u00e9thode. Tel quel les inconv\u00e9nients sont en effet assez d\u00e9courageants.<\/p>\n<ul>\n<li style=\"text-align: justify;\">Fichier illisible et donc non v\u00e9rifiable et non modifiable avec un \u00e9diteur de texte (on peut d&rsquo;ailleurs regretter la propension de certains \u00e9diteurs d&rsquo;applications propri\u00e9taires \u00e0 employer le format binaire justement pour le rendre incompatible avec leurs concurrents).<\/li>\n<li style=\"text-align: justify;\">D\u00e9pendance \u00e0 l&rsquo;alignement des structures, l&rsquo;architecture, l&rsquo;<i>endianisme<\/i> (ordre de stockage des octets d&rsquo;un entier&nbsp;: poids faible ou poids fort en premier)&#8230;<\/li>\n<li style=\"text-align: justify;\">Pas de gestion de version.<\/li>\n<li style=\"text-align: justify;\">Pas de contr\u00f4le d&rsquo;int\u00e9grit\u00e9.<\/li>\n<\/ul>\n<p style=\"text-align: justify;\">Certains de ces inconv\u00e9nients peuvent toutefois \u00eatre assez facilement lev\u00e9s.<\/p>\n<ul>\n<li style=\"text-align: justify;\">Utiliser des types de donn\u00e9es fig\u00e9s comme <code>uint32_t<\/code> au lieu de <code>unsigned long<\/code>.<\/li>\n<li style=\"text-align: justify;\">Forcer le type d&rsquo;alignement par des directives de compilation.<\/li>\n<li style=\"text-align: justify;\">Ajouter un ent\u00eate avec un \u00ab\u00a0num\u00e9ro magique\u00a0\u00bb pour identifier le fichier et un num\u00e9ro de version.<\/li>\n<li style=\"text-align: justify;\">\u00c9crire un CRC \u00e0 la fin du fichier pour v\u00e9rifier son int\u00e9grit\u00e9.<\/li>\n<\/ul>\n<p style=\"text-align: justify;\">Ce sera d\u00e9j\u00e0 mieux mais le format binaire reste un gros probl\u00e8me et la d\u00e9pendance \u00e0 l&rsquo;endianisme une faiblesse importante. Un avantage n\u00e9anmoins pour ce type de format se trouve dans sa rapidit\u00e9 de chargement et de sauvegarde ainsi que dans la compacit\u00e9 des fichiers de donn\u00e9es.<\/p>\n<p style=\"text-align: justify;\">En cas d&rsquo;allocation dynamique de m\u00e9moire il faudra enregistrer le nombre d&rsquo;\u00e9l\u00e9ments suivi par le tableau de structures \u00e0 plat en m\u00e9moire. On proc\u00e9dera donc en deux temps pour un tableau (traitement des erreurs omis).<\/p>\n<pre>struct s_capteur * tableau_capteurs= NULL;\nuint32_t nb_capteurs = 0;\nFILE * fp = fopen(\"config.bin\", \"r\");\n\n\/* Lecture *\/\nfread(&amp; nb_capteurs, sizeof(uint32_t), 1,fp);\nif (nb_capteurs != 0){\n    tableau_capteurs = calloc(nb_capteurs, sizeof(struct s_capteur));\n    uint32_t nb = fread(tableau_capteurs, sizeof(struct s_capteur), nb_capteurs, fp);\n}\nif (nb != nb_capteurs)\n    \/* erreur *\/\n\n\/* Ecriture *\/\nfwrite(&amp; nb_capteurs, sizeof(int), 1,fp);\nuint32_t nb = fwrite(tableau_capteurs, sizeof(struct s_capteur), nb_capteurs, fp);\nif (nb != nb_capteurs)\n    \/* erreur *\/<\/pre>\n<p style=\"text-align: justify;\">En conclusion, cette m\u00e9thode de sauvegarde binaire des structures de donn\u00e9es doit \u00eatre r\u00e9serv\u00e9e aux phases de prototypage de l&rsquo;application, lorsque les informations \u00e0 enregistrer ne repr\u00e9sentent pas encore une partie cruciale du projet et qu&rsquo;on n&rsquo;envisage pas de communication avec d&rsquo;autres modules logiciels, ni de portage vers d&rsquo;autres plates-formes.<\/p>\n<h1>Fichier texte sp\u00e9cifique \u00ab\u00a0fprintf()\/fscanf()\u00a0\u00bb<\/h1>\n<p style=\"text-align: justify;\">Pour contourner les principaux inconv\u00e9nients que nous avons relev\u00e9s avec le format pr\u00e9c\u00e9dent (illisibilit\u00e9, probl\u00e8mes d&rsquo;alignement et d&rsquo;endianisme), il est possible de stocker les donn\u00e9es dans un format textuel et de les relire dans le m\u00eame ordre. Voyons un exemple correspondant aux m\u00eames donn\u00e9es que pr\u00e9c\u00e9demment (encore une fois, nous avons omis le traitement des erreurs).<\/p>\n<pre>int sauvegarder_piece(FILE * fp, struct s_piece * piece)\n{\n  int i;\n  fprintf(fp, \"%s\\n\", piece-&gt;Nom);\n  fprintf(fp, \"%lf\\n\", piece-&gt;Volume);\n  fprintf(fp, \"%d\\n\", piece-&gt;nb_capteurs);\n  for (i = 0; i &lt; piece-&gt;nb_capteurs; i ++) {\n    fprintf(fp, \"%d\\n\", piece-&gt;TabCapteurs[i].type);\n    fprintf(fp, \"%d\\n\", piece-&gt;TabCapteurs[i].identifiant);\n  }\n  fprintf(fp, \"%d\\n\", piece-&gt;nb_actionneurs);\n  for (i = 0; i &lt; piece-&gt;nb_actionneurs; i ++) {\n    fprintf(fp, \"%d\\n\", piece-&gt;TabActionneurs[i].type);\n    fprintf(fp, \"%d\\n\", piece-&gt;TabActionneurs[i].identifiant);\n  }\n  return 0;\n}\n\nint charger_piece(FILE * fp, struct s_piece * piece)\n{\n  int i;\n  if ((fscanf(fp, \"%s\", piece-&gt;Nom) != 1)\n    || (fscanf(fp, \"%lf\", &amp; (piece-&gt;Volume)) != 1)\n    || (fscanf(fp, \"%d\", &amp; (piece-&gt;nb_capteurs)) != 1))\n      return -1;\n  if ((nb_capteurs &lt; 0) || (nb_capteurs &gt;= MAX_CAPTEURS_PAR_PIECE))\n    return -1;\n  for (i = 0; i &lt; piece-&gt;nb_capteurs; i ++) {\n    if ((fscanf(fp,\"%d\", &amp;(piece-&gt;TabCapteurs[i].type)) != 1)\n     || (fscanf(fp,\"%d\", &amp;(piece-&gt;TabCapteurs[i].identifiant))!=1))\n      return -1;\n  }\n  if (fscanf(fp, \"%d\", &amp; (piece-&gt;nb_actionneurs)) != 1)\n    return -1;\n  if ((piece-&gt;nb_actionneurs &lt; 0)\n   || (piece-&gt;nb_actionneurs &gt;= MAX_ACTIONNEURS_PAR_PIECE))\n    for (i = 0; i &lt; piece-&gt;nb_actionneurs; i ++) {\n      if ((fscanf(fp, \"%d\", &amp;(piece-&gt;TabActionneurs[i].type)) != 1)\n       || (fscanf(fp, \"%d\",\n        &amp;(piece-&gt;TabActionneurs[i].identifiant))!=1))\n      return -1;\n    }\n  return 0;\n}<\/pre>\n<p style=\"text-align: justify;\">On notera que l&rsquo;utilisation de <code>fscanf()<\/code> telle que nous l&rsquo;avons employ\u00e9e ici peut poser probl\u00e8me dans certains cas, et qu&rsquo;il est souvent pr\u00e9f\u00e9rable de proc\u00e9der en deux temps pour lire les valeurs, ainsi&nbsp;:<\/p>\n<pre>int lecture_entier (FILE * fp, int * resultat)\n{\n  char ligne[LONGUEUR_MAXI_LIGNE + 1];\n\n  if ((fgets(ligne, LONGUEUR_MAXI_LIGNE, fp) == NULL)\n   || (sscanf(ligne, \"%d\",resultat) != 1))\n    return -1;\n  return 0;\n}<\/pre>\n<p style=\"text-align: justify;\">Le fichier devient modifiable \u00e0 la main dans un \u00e9diteur de texte, mais c&rsquo;est tr\u00e8s fastidieux, car il est constitu\u00e9 d&rsquo;une suite interminable de valeurs que l&rsquo;on devra identifier \u00e0 l\u2019\u0153il. Cette approche fonctionne bien pour sauvegarder et r\u00e9cup\u00e9rer des cha\u00eenes de caract\u00e8res et des valeurs enti\u00e8res. Pour les valeurs r\u00e9elles, des probl\u00e8mes de pr\u00e9cision peuvent se poser (m\u00eame enregistr\u00e9 avec plusieurs d\u00e9cimales apr\u00e8s la virgule, 0.333333 ne sera pas relu comme \u00e9quivalent \u00e0 1\/3).<\/p>\n<p style=\"text-align: justify;\">Nous avons r\u00e9solu les probl\u00e8mes d&rsquo;alignement (des champs des structures par exemple), les probl\u00e8mes d&rsquo;endianisme (puisque la conversion de la valeur enti\u00e8re vers la repr\u00e9sentation en m\u00e9moire se fait avant la sauvegarde et apr\u00e8s la relecture), et gagn\u00e9 une certaine portabilit\u00e9. Le fichier est d&rsquo;une taille beaucoup plus cons\u00e9quente que pr\u00e9c\u00e9demment, mais il est possible de le compresser (dynamiquement ou apr\u00e8s son enregistrement) et retrouver en principe le m\u00eame ordre de grandeur qu&rsquo;avec la repr\u00e9sentation binaire.<\/p>\n<p style=\"text-align: justify;\">L&rsquo;analyse des donn\u00e9es se faisant dynamiquement, le chargement de la configuration est plus longue qu&rsquo;avec des donn\u00e9es binaires. Il est n\u00e9cessaire de faire appel \u00e0 des fonctions telles que <code>sscanf()<\/code>, <code>atoi()<\/code>, <code>strtol()<\/code>, qui consomment du temps CPU. En outre ces routines renvoient des codes de retour indiquant la r\u00e9ussite ou l&rsquo;\u00e9chec des conversions. V\u00e9rifier ces codes de r\u00e9ussite et agir en cons\u00e9quence alourdit \u00e0 nouveau le code.<\/p>\n<p style=\"text-align: justify; padding-left: 30px;\">Il m&rsquo;est arriv\u00e9 dans le cadre d&rsquo;une application embarqu\u00e9e d&rsquo;avoir \u00e0 charger un gros fichier repr\u00e9sentant une cartographie a\u00e9roportuaire. Le temps de chargement sur les premiers prototypes devenant prohibitif, nous avons utilis\u00e9 un profiler pour d\u00e9tecter qu&rsquo;une grosse partie de ce temps \u00e9tait perdue dans le <code>sscanf()<\/code> et dans le code de v\u00e9rification des bornes des valeurs num\u00e9riques. Sachant que les fichiers \u00e9taient valid\u00e9s sur un poste ind\u00e9pendant avant d&rsquo;\u00eatre d\u00e9ploy\u00e9s sur les syst\u00e8mes embarqu\u00e9s o\u00f9 ils ne pouvaient plus \u00eatre modifi\u00e9s, la r\u00e9p\u00e9tition de ces op\u00e9rations de v\u00e9rification \u00e9tait inutile et nous pouvions all\u00e9ger grandement notre programme en rempla\u00e7ant les <code>sscanf()<\/code> par des <code>atoi()<\/code>. Le gain de temps fut alors suffisant pour que le chargement paraisse instantan\u00e9.<\/p>\n<p style=\"text-align: justify;\">Le gros inconv\u00e9nient de ces formats est avant tout la difficult\u00e9 \u00e0 intervenir humainement dans les fichiers pour rechercher et modifier des informations. Il est impossible de v\u00e9rifier ou d&rsquo;\u00e9diter les donn\u00e9es sans avoir sous les yeux une description pr\u00e9cise du format du fichier. On peut am\u00e9liorer un peu la lisibilit\u00e9 en y ajoutant des commentaires&nbsp;: pour ce faire, il faut que le programme soit capable d&rsquo;ignorer certaines lignes (par exemple celles qui commencent par un caract\u00e8re di\u00e8se <code>#<\/code> ou un point virgule <code>;<\/code> ou encore un double slash <code>\/\/<\/code>). Si le programme peut ajouter automatiquement les lignes de commentaires durant la sauvegarde, le fichier devient plus accessible pour un humain. Il serait n\u00e9anmoins pr\u00e9f\u00e9rable que les informations soient document\u00e9es, par exemple en poss\u00e9dant des noms, ce qui nous conduit au format suivant.<\/p>\n<h1>Format \u00ab\u00a0.ini\u00a0\u00bb type Windows<\/h1>\n<p style=\"text-align: justify;\">Une approche courante dans les fichiers de configuration des applications Windows &#8211; et de certaines applications Unix &#8211; est de disposer d&rsquo;une liste de paires cl\u00e9-valeur, que l&rsquo;on peut regrouper en diff\u00e9rentes sections au sein du fichier. La syntaxe est simplissime et ne permet qu&rsquo;un seul niveau d&rsquo;imbrication:<\/p>\n<pre>; un commentaire commence par un point virgule\n[section]\n  cle1 = valeur1\n  cle2 = valeur2\n\n[autre section]\n  cle1 = valeur1\n  cle3 = valeur3<\/pre>\n<p style=\"text-align: justify;\">Il existe diverses biblioth\u00e8ques open sources pour lire et d&rsquo;\u00e9crire ce type de fichier facilement. La meilleure ressource que j&rsquo;ai pu trouver sur le sujet me semble \u00eatre la page suivante&nbsp;: <a href=\"http:\/\/blog.brush.co.nz\/2009\/02\/inih\/\">http:\/\/blog.brush.co.nz\/2009\/02\/inih\/<\/a><br \/>\nOn y trouvera plusieurs projets dont le code est adapt\u00e9 \u00e0 l&#8217;embarqu\u00e9 notamment \u00e0 l&rsquo;utilisation sur un micro-contr\u00f4leur, par exemple pour lire des fichiers de configuration sur une SD-Card. Apr\u00e8s une investigation rapide, pour avoir des fonctions de lecture et d&rsquo;\u00e9criture je penche pour ce projet&nbsp;: <a href=\"http:\/\/www.compuphase.com\/minini.htm\">http:\/\/www.compuphase.com\/minini.htm<\/a>. La biblioth\u00e8que tient en un seul fichier <code>minIni.c<\/code> et l&rsquo;API poss\u00e8de l&rsquo;int\u00e9r\u00eat d&rsquo;offrir une double approche.<\/p>\n<ul>\n<li style=\"text-align: justify;\">Lecture des valeurs une par une avec \u00e0 chaque fois l&rsquo;appel \u00e0 une fonction qui va ouvrir le fichier, le parcourir en entier et retourner le r\u00e9sultat, ce qui est simple mais lent.<\/li>\n<li style=\"text-align: justify;\">Parcours de tout le fichier avec appel \u00e0 un handler pour chaque valeur trouv\u00e9e (\u00e9quivalent \u00e0 l&rsquo;API SAX pour le xml).<\/li>\n<\/ul>\n<p style=\"text-align: justify;\">Dans le cas de donn\u00e9es de configurations lues au d\u00e9marrage de l&rsquo;application, la seconde approche sera bien s\u00fbr la meilleure. Afin de pouvoir \u00eatre facilement port\u00e9e sur micro-contr\u00f4leur la biblioth\u00e8que utilise des macros pour l&rsquo;interface avec le syst\u00e8me de fichier, ainsi l&rsquo;ouverture d&rsquo;un fichier est fait avec un appel \u00e0 <code>ini_openread(filename,file)<\/code> qui est d\u00e9finit comme ceci dans <code>minGlue-stdio.h<\/code>.<\/p>\n<pre>#define ini_openread(filename,file) ((*(file) = fopen((filename),\"rb\")) != NULL)<\/pre>\n<p style=\"text-align: justify;\">Si l&rsquo;on est sur un syst\u00e8me comme Linux avec une biblioth\u00e8que stdio op\u00e9rationnelle, il suffit de copier <code>minGlue-stdio.h<\/code> en <code>minGlue.h<\/code> pour avoir toutes les fonctions d\u00e9finies \u00e0 partir des fonctions de la <code>libc<\/code>. Sur une cible diff\u00e9rente il faudra adapter <code>minGlue.h<\/code> en fonction de l&rsquo;API de la stack filesystem utilis\u00e9e. Le projet propose d\u00e9ja des fichiers <code>minGlue-xxxx.h<\/code> pour quelques stack connues comme <em>Petit-FatFs<\/em>.<\/p>\n<p style=\"text-align: justify;\">Regardons l&rsquo;API propos\u00e9e pour parser un fichier <code>.ini<\/code> dans le cas de la m\u00e9thode avec callback. On d\u00e9finit tout d&rsquo;abord une fonction de callback dont le prototype est le suivant.<\/p>\n<pre>int Callback(const char *section, const char *key, const char *value, const void *userdata)<\/pre>\n<p style=\"text-align: justify;\">Ensuite on appelle cette autre fonction pour lancer le parsing.<\/p>\n<pre>int ini_browse(INI_CALLBACK Callback, const void *UserData, const mTCHAR *Filename);<\/pre>\n<p style=\"text-align: justify;\">C&rsquo;est dans la callback qu&rsquo;il faudra prendre en charge la conversion de texte vers le type de valeur cible, ainsi que la gestion des index des tableaux. Voyons comment notre exemple d&rsquo;application domotique peut \u00eatre sauvegard\u00e9e dans un fichier ini.<\/p>\n<pre>[Site]\nNom=\nAdresse=\nNbrPieces=\n\n[Piece]\nNom=\nVolume=\nNbrCapteurs=\nNbrActionneurs=\n\n[Capteur]\nType=\nIdentifiant=\n\n[Capteur]\nType=\nIdentifiant=\n\n[Actionneur]\nType=\nIdentifiant=\n\n[Piece]\nNom=\nVolume=\nNbrCapteurs=\nNbrActionneurs=\n\n[Capteur]\nType=\nIdentifiant=\n\n[Actionneur]\nType=\nIdentifiant=<\/pre>\n<p style=\"text-align: justify;\">Deux probl\u00e8me se posent&nbsp;:<\/p>\n<ul>\n<li style=\"text-align: justify;\">Comment g\u00e9rer les tableaux d&rsquo;objets, comme les pi\u00e8ces ou les capteurs de notre exemple&nbsp;?<\/li>\n<li style=\"text-align: justify;\">Comment g\u00e9rer les relations de composition au sens de la programmation orient\u00e9e objet, c&rsquo;est \u00e0 dire d\u00e9finir quel capteur appartient \u00e0 quelle pi\u00e8ce&nbsp;?<\/li>\n<\/ul>\n<p style=\"text-align: justify;\">Si chaque objet peut \u00eatre facilement identifi\u00e9 de mani\u00e8re unique, on peut d\u00e9finir explicitement les liens et les index en ajoutant des champs sp\u00e9cifiques.<\/p>\n<pre>[Capteur]\nType=\nIdentifiant=\nPiece=\nIndex=<\/pre>\n<p style=\"text-align: justify;\">Si le fichier respecte une certaine structure, comme par exemple que les capteurs d&rsquo;une pi\u00e8ce sont dispos\u00e9s \u00e0 la suite de sa d\u00e9claration, on pourrait d\u00e9terminer l&rsquo;index automatiquement. Mais cela cr\u00e9e un risque de confusion. Une autre approche consiste \u00e0 num\u00e9roter les sections en y reportant directement les indices. Nous aurions ainsi le 3\u00e8me capteur de la 1\u00e8re pi\u00e8ce d\u00e9clar\u00e9 avec le nom de section \u00ab\u00a0<code>Capteur_3_1<\/code>\u00ab\u00a0. Le probl\u00e8me est alors que le d\u00e9codage reste \u00e0 notre charge, les biblioth\u00e8ques ne nous aideront pas&nbsp;!<\/p>\n<p style=\"text-align: justify;\">On comprend vite que l&rsquo;aspect arborescent des structures de donn\u00e9es classiques se pr\u00eate tr\u00e8s mal \u00e0 une description dans ce type de fichier. On r\u00e9servera donc le <code>.ini<\/code> \u00e0 des applications \u00e9l\u00e9mentaires voire on l&rsquo;\u00e9vitera carr\u00e9ment car comme nous le verrons <strong><a href=\"http:\/\/www.blaess.fr\/christophe\/2013\/04\/29\/configuration-dune-application-embarquee\/\" title=\"Configuration d\u2019une application embarqu\u00e9e (2\/2)\">dans la deuxi\u00e8me partie<\/a><\/strong> de cet article.<\/p>","protected":false},"excerpt":{"rendered":"<p>Cet article a &eacute;t&eacute; &eacute;crit en collaboration avec mon ami Fran&ccedil;ois Beaulier dont je vous recommande le blog. Nous avons r&eacute;fl&eacute;chi ensemble sur les possibilit&eacute;s qui s&rsquo;offrent au d&eacute;veloppeur d&rsquo;application embarqu&eacute;e pour enregistrer le param&eacute;trage, la configuration, les pr&eacute;f&eacute;rences&hellip; bref tout ce qui constitue les donn&eacute;es persistantes de son syst&egrave;me.<\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,8],"tags":[],"class_list":["post-3492","post","type-post","status-publish","format-standard","hentry","category-embarque","category-linux-2"],"_links":{"self":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts\/3492","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/comments?post=3492"}],"version-history":[{"count":16,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts\/3492\/revisions"}],"predecessor-version":[{"id":3563,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts\/3492\/revisions\/3563"}],"wp:attachment":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/media?parent=3492"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/categories?post=3492"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/tags?post=3492"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}