Installer et utiliser Perl pour tous vos programmes

Cet article n'est pas un cours complet sur le langage de programmation Perl mais récapitule l'ensemble des paquets à installer dans Ubuntu ou dans Windows pour bénéficier d'un environnement de développement Perl sous Linux comme sous Windows, et rappelle l'essentiel de la syntaxe du langage afin d'écrire rapidement de petits scripts en Perl.

Les exemples de programmes Perl donnés sur cette page sont orientés vers la modification ou la génération automatique de fichiers texte ou binaire.

Sommaire de cette page

Installation de Perl sous Linux

Installation de Perl sous Windows

Premier programme pour tester Perl

Les bases du langage Perl

Installer un module Perl

Les expressions régulières en Perl

Utilisation de Perl comme un simple filtre en ligne de commande

Génération automatique de questions pour QCM

Génération de questions pour QCM relatives à un grafcet Automgen

Génération de questions pour QCM relatives à un logigramme Automgen

Génération de questions relatives à un grafcet linéaire à 4 étapes

Génération du fichier de donées grâce à Automgen

Filtrage d'un fichier binaire en Perl

Les structures de données complexes en Perl

Exécution d'un script bash dans un programme Perl

 

 

Retour au sommaire

Installation de Perl sous Linux

L'interpréteur de Perl utilisable en ligne de commande se nomme perl. Sous Ubuntu 10.04 LTS il faut installer le paquet perl pour pouvoir interpréter des programmes en Perl en ligne de commande.

Pour installer perl sous Ubuntu on pourra taper la commande suivante en tant que root :

apt-get install perl (ou utiliser l'interface graphique Synaptic d'apt-get).

 

Retour au sommaire

Installation de Perl sous Windows

Pour Windows il existe différentes distributions de Perl. Parmi les plus courantes il y a ActivePerl et Strawberry Perl :

Télécharger et installer ActivePerl

Télécharger et installer Strawberry Perl

Ces distributions sont simples à installer car elles existent en un simple fichier MSI facile à installer sous Windows. Après installation on dispose de l'interpréteur perl dans un shell Windows avec le chemin de Perl ajouté dans le path.

Dans tout les cas le site de base pour télécharger Perl reste www.perl.org.

 

Retour au sommaire

Premier programme pour tester Perl

Editons un nouveau fichier texte dans Gedit nommé toto.pl :

gedit toto.pl

Voici le code source en Perl de ce premier programme qui se contente d'afficher "Bonjour" sur la sortie standard :

#!/usr/bin/perl

print("Bonjour\n");

Remarques :

Pour pouvoir exécuter le programme toto.pl en ligne de commande sous Linux il faut lui donner l'attribut éexécutable" :

chmod +x toto.pl

Remarques :

Exécutons le programme toto.pl :

./toto.pl

Remarques :

 

Retour au sommaire

Les bases du langage Perl

Perl est dérivé du langage C et a conservé un grand nombre d'éléments de sa syntaxe (syntaxe de la boucle for, bloc délimité par des accolades, point-virgule en fin de ligne, indice des tableaux entre crochet, etc.).

Exemple de boucle FOR en Perl :

for ($i=1;$i<10;$i++)
{
  print "$i\n";
}

Exemple de boucle FOREACH en Perl :

foreach $i (1,5,8,13,19,37,56)
{
  print "$i\n";
}

Exemple de boucle While en Perl :

$i=1;
while ($i<10)
{
  print "$i\n";
  $i++;
}

Exemple de test If en Perl :

$i=2;
if ($i==1)
{
  print "un";
}
elsif ($i==2)
{
  print "deux";
}
elsif ($i==3)
{
  print "trois";
}
else
{
  print "\$i est supérieur à 3";
}

Exemple de tableau en Perl :

En Perl une variable scalaire commence par $, un tableau commence par @ et une table de hachage commence par %.

my @tab = (24,'oui',3.14);
print "$tab[0]\n";
print "$tab[1]\n";
print "$tab[2]\n"
;

Un tableau en Perl peut contenir des éléments de différents types (entier, chaîne de caractères, nombre réel, tableau, etc.) et est aussi appelé "une liste".

Enfin un commentaire en Perl commence par un # :

# Exemple de table de hachage en Perl :
# Programme réalisé le 30 décembre 2014

# %tab est une table de hachage
my %tab = (nom=>"DUPONT",prenom=>"Pierre",age=>23);

# $tab{nom} est l'élément "nom" de la table de hachage : il vaut "DUPONT" et c'est un scalaire
print "NOM : $tab{nom}\n";
print "Prénom : $tab{prenom}\n";
print "Age : $tab{age}\n";

Remarque : les tables de hachage sont aussi appelées tableaux associatifs (comme en PHP), dictionnaires (comme en Python), ou simplement hachages (hash en anglais) et contituent les structures de données les plus intéressantes de Perl.

 

Retour au sommaire

Installer un module Perl

Le programme permettant d'installer un module Perl en ligne de commande s'appelle cpan. Il est remplacé depuis peu par cpanp (CpanPlus). Avant d'installer un module il faut déjà mettre à jour cpan en lançant les 2 commandes suivantes dans le compte root (réponde yes à toutes les questions posées) :

cpan YAML

cpan CPANPLUS

Lancer ensuite la commande cpanp, puis h pour obtenir l'aide.

Commande dans CPANP

Action
i
installe un nouveau module
o
liste les modules Perl installés
q
quitte CPANP
h
affiche l'aide

En cas de dépendance durant l'installation d'un nouveau module, cpanp propose d'installer les modules dépendants (répondre yes aux questions posées pendant l'installation pour installer tous les modules nécessaires).

Cependant on préfèrera installer les modules Perl présent sous forme de paquet Debian par apt-get ou Synaptic.

 

Retour au sommaire

Les expressions régulières en Perl

Un des principaux avantages de Perl est de disposer en natif d'un support d'expressions régulières étendues utilisant une syntaxe simple, ce qui n'est pas le cas des autres langages.

En Perl toutes les actions utilisant les expressions régulières se font avec l'opérateur =~ (sans utiliser de fonctions ou d'objets spécifiques).

Test d'une chaîne de caractères :

if ($chaine =~ /[0-9]+/) {
print "Cette chaîne contient des chiffres";
}

Remplacement :

$chaine =~ s/[0-9]/z/g;

Les groupes :

if ($chaine =~ /(\d+)/) {
print "Le premier nombre est $1";
}

Afficher tous les groupes d'une chaîne :

while($chaine=~ /(\d+)/g) {
print "Un nombre trouvé : $1\n";
}

Filtrage de l'entrée standard par expressions régulières :

#!/usr/bin/perl
while($chaine=<STDIN>) {
$chaine =~ s/[0-9]+/#/g;
print $chaine;
}

Une des applications originales des expressions régulières est la recherche des nombres premiers. Quel rapport me diriez-vous entre les nombres premiers et les expressions régulières ? Et bien sachant que si un entier est écrit en base 1 alors il est premier si certains motifs ne se répètent pas, la recherche des motifs répétitifs (et donc la détection des nombres premiers) peut alors être confiée à une expression régulière.

En base 1 il y a un seul symbole (le chiffre 1) et un entier s'écrit avec autant de 1 que la valeur du nombre :

En base 1 le nombre 2 s'écrit 11

En base 1 le nombre 3 s'écrit 111

En base 1 le nombre 7 s'écrit 1111111

En base 1 le nombre 18 s'écrit 111111111111111111

etc.

Pour écrire un nombre entier en base 1 en Perl on utilise l'opérateur x :

perl -e "print 1x7" affiche 1111111

perl -e "$i=9; print 1x$i" affiche 111111111

Voici le code source condensé qui affiche tous les nombres premiers en analysant leur écriture en base 1 grâce à une simple expression régulière :

perl -le "$_=1; (1x$_) !~ /^(11+)\1+$/ && print while $_++"

L'expression régulière détecte des groupes d'au moins deux 1 à partir de la gauche et regarde si le groupe se répète. Si un groupe se répète exactement jusqu'au bout (dernier chiffre à droite) alors le nombre est le produit de plusieurs autres nombres et il n'est donc pas premier.

Par exemple le nombre 6 s'écrit 111111 en base 1 : il contient deux fois le groupe 111 car 6=3*2. En répétant 2 fois le groupe 111 à partir de la gauche on arrive exactement à la fin de la chaîne 111111 : on en déduit que 6 n'est pas premier et c'est précisément cette analyse qu'effectue l'expression régulière ci-dessus.

Seuls les nombres ne correspondant pas au motif répétitif de l'expression régulière sont affichés : il s'agit des nombres premiers.

Plus les nombres sont grands, plus leur écriture en base 1 prendra de la place en mémoire et plus le programme sera lent. En partant par exemple de 20000 à la place de 1 on obtient environ un nombre premier par seconde :

perl -le "$_=20000; (1x$_) !~ /^(11+)\1+$/ && print while $_++" affiche

20011
20021
20023
20029
20047
20051
20063
20071
20089
etc.

Bien sûr pour rechercher des nombres premiers on aurait pu également effectuer un test de primalité plus classique, mais le programme aurait été plus long.

Cet exemple prouve une fois de plus qu'en Perl il y a toujours plusieurs solutions pour résoudre un problème donné, et que si on optimise le code source très peu de caractères suffisent au final pour écrire le code source en comparaison des autres langages impératifs (C, Pascal, Java, etc.).

 

Retour au sommaire

Utilisation de Perl comme un simple filtre en ligne de commande

Perl peut être vu comme un langage de programmation complet permettant de développer des programmes et des applications complexes, mais aussi comme une simple commande de plus dans un shell (sous Linux comme sous Windows) permettant de filtrer facilement un fichier en redirigeant l'entrée et la sortie standard.

En utilisant la commande Perl dans un shell :

S'agissant de filtre à taper en ligne de commande le code source en Perl sera ici condensé en utilisant certaines syntaxes de Perl permettant d'écrire le minimum de caractères dans le programme.

Avec la syntaxe while(<STDIN>) chaque ligne du fichier texte redirigé vers l'entrée standard du programme Perl est récupéré dans le programme dans la variable par défaut $_. La commande print (sans paramètre) affiche la variable par défaut $_.

L'exemple suivant ouvre le fichier texte toto.txt puis affiche toutes ses lignes sans aucune condition. Ce programme utilise la variable par défaut $_ (qui n'apparaît jamais dans le programme) :

perl -e "while(<STDIN>) { print }" < toto.txt

L'exemple suivant affiche seulement les lignes correspondant à une expression régulière (ici les lignes commençant par la lettre C) :

perl -e "while(<STDIN>) { $_ =~ /^C/ && print }" < toto.txt

L'exemple suivant affiche seulement les lignes contenant les lettres G E C I F dans l'ordre :

perl -e "while(<STDIN>) { $_ =~ /^.*G.*E.*C.*I.*F.*$/ && print }" < toto.txt

L'exemple suivant remplace le mot ubuntu par le mot linux sur chacune des lignes puis affiche seulement les lignes modifiées :

perl -e "while(<STDIN>) { $_ =~ s/ubuntu/linux/g && print }" < toto.txt

L'exemple suivant remplace le mot ubuntu par le mot linux sur chacune des lignes et affiche toutes les lignes du fichier :

perl -e "while(<STDIN>) { $_ =~ s/ubuntu/linux/g ; print }" < toto.txt

L'exemple suivant convertit toutes les lettres minuscules non accentuées d'un fichier texte en lettres majuscules :

perl -e "while(<STDIN>) { $_ =~ tr/[a-z]/[A-Z]/ ; print }" < toto.txt

 

Retour au sommaire

Génération automatique de questions pour QCM

La génération automatique de questions pour QCM est avant tout une application de traitement d'informations textuelles. Plusieurs générateurs ont déjà été exposés sur le site Gecif.net en utilisant différents langages (JavaScript, Python, Ruby, Prolog, AWK, etc.) mais pas encore grâce à Perl. Perl étant le langage conçu à l'origine pour un traitement d'informations textuelles il est particulièrement adapté à la génération automatique de questions pour QCM.

La commande printf est particulièrement adaptée pour générer des questions sous forme de chaînes formatées. Placée dans une boucle en version infixe (c'est-à-dire en utilisant la syntaxe simplifiée de Perl) il est facile d'écrire un générateur direct comme le suivant (il génère directement les questions, sans passer par l'intermédiaire d'un fichier de données) :

# Générateur direct en Perl : il ne contient que 2 lignes
# 10 février 2015
$i=5;
printf "quest(\"Comment s'écrit le nombre décimal %u en hexadécimal ?//a\");
rep(\"(o) %X\");
rep(\"( ) %X\");
rep(\"( ) %X\");
rep(\"( ) %X\");
",$i,$i,$i+1,$i-2,$i+int(rand(11))+3
while $i++<=50;

Ici printf réalise à la fois la mise en forme de la question et la conversion du nombre $i dans différentes base.

Voici par exemple certaines des questions générées en quelques secondes avec ce générateur direct (adaptable à beaucoup d'autres cas) :

quest("Comment s'écrit le nombre décimal 21 en hexadécimal ?//a");
rep("(o) 15");
rep("( ) 16");
rep("( ) 13");
rep("( ) 21");
quest("Comment s'écrit le nombre décimal 22 en hexadécimal ?//a");
rep("(o) 16");
rep("( ) 17");
rep("( ) 14");
rep("( ) 1A");
quest("Comment s'écrit le nombre décimal 23 en hexadécimal ?//a");
rep("(o) 17");
rep("( ) 18");
rep("( ) 15");
rep("( ) 20");
quest("Comment s'écrit le nombre décimal 24 en hexadécimal ?//a");
rep("(o) 18");
rep("( ) 19");
rep("( ) 16");
rep("( ) 20");
quest("Comment s'écrit le nombre décimal 25 en hexadécimal ?//a");
rep("(o) 19");
rep("( ) 1A");
rep("( ) 17");
rep("( ) 20");
quest("Comment s'écrit le nombre décimal 26 en hexadécimal ?//a");
rep("(o) 1A");
rep("( ) 1B");
rep("( ) 18");
rep("( ) 27");

Autre exemple de générateur direct concernant cette fois l'algorigramme suivant :

Pour toutes les valeurs de la constante a supérieures à 1, à la fin de l'algorigramme on aura toujours x=a et y=2.a-1.

Le générateur direct suivant permet de générer instantanément 49 questions pour cet algorigramme (avec a qui va de 2 à 50), sans passer par un fichier de données ni traduire le code en langage C généré par Flowcode :

# Générateur direct pour algorigramme :
# Programme réalisé le 17 février 2015
$i=1;
printf "quest(\"Donnez les valeurs affectées aux variables x et y par l'algorigramme ci-contre sachant que la constante a vaut %u :||Sachant que a=%u combien valent les variables x et y après l'exécution de l'algorigramme ci-contre ?||Combien vaudront les variables x et y à la fin de cet algorigramme si a=%u ?\");
rep(\"x = [%u]\");
rep(\"y = [%u]\");
debut(\"Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :\");
caracteres_speciaux(\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\");
sch(\"images/algorigramme_3.png\",\"77\",\"412\");
",$i,$i,$i,$i,2*$i-1
while $i++<50;

Voici quelques questions générées parmi les 49 :

quest("Donnez les valeurs affectées aux variables x et y par l'algorigramme ci-contre sachant que la constante a vaut 5 :||Sachant que a=5 combien valent les variables x et y après l'exécution de l'algorigramme ci-contre ?||Combien vaudront les variables x et y à la fin de cet algorigramme si a=5 ?");
rep("x = [5]");
rep("y = [9]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algorigramme_3.png","77","412");
quest("Donnez les valeurs affectées aux variables x et y par l'algorigramme ci-contre sachant que la constante a vaut 6 :||Sachant que a=6 combien valent les variables x et y après l'exécution de l'algorigramme ci-contre ?||Combien vaudront les variables x et y à la fin de cet algorigramme si a=6 ?");
rep("x = [6]");
rep("y = [11]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algorigramme_3.png","77","412");
quest("Donnez les valeurs affectées aux variables x et y par l'algorigramme ci-contre sachant que la constante a vaut 7 :||Sachant que a=7 combien valent les variables x et y après l'exécution de l'algorigramme ci-contre ?||Combien vaudront les variables x et y à la fin de cet algorigramme si a=7 ?");
rep("x = [7]");
rep("y = [13]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algorigramme_3.png","77","412");
quest("Donnez les valeurs affectées aux variables x et y par l'algorigramme ci-contre sachant que la constante a vaut 8 :||Sachant que a=8 combien valent les variables x et y après l'exécution de l'algorigramme ci-contre ?||Combien vaudront les variables x et y à la fin de cet algorigramme si a=8 ?");
rep("x = [8]");
rep("y = [15]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algorigramme_3.png","77","412");
quest("Donnez les valeurs affectées aux variables x et y par l'algorigramme ci-contre sachant que la constante a vaut 9 :||Sachant que a=9 combien valent les variables x et y après l'exécution de l'algorigramme ci-contre ?||Combien vaudront les variables x et y à la fin de cet algorigramme si a=9 ?");
rep("x = [9]");
rep("y = [17]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algorigramme_3.png","77","412");

Dans le cas d'un algorigramme plus complexe on peut aussi demander à Perl de simuler le fonctionnement de l'algorigramme. Partons par exemple de l'algorigramme suivant :

Le programme Perl suivant calcule la valeur de la variable x pour différentes valeurs des constantes a, b et c en utilisant le même algorithme que celui décrit par l'algorigramme et génère 100 questions :

# Générateur direct pour algorigramme :
# Programme réalisé le 19 février 2015

foreach $a (10,15,21,24,32)
{
  foreach $b (15,20,26,30)
  {
    foreach $c (11,15,18,26,40)
    {
      $x=1;
      while ($x<=$a)
      {
        if ($x<$b)
        {
          while ($x<=$b)
          {
          $x=$x+3;
          }
        }
        else
        {
          while ($x<=$c)
          {
          $x=$x+2;
          }
        }
        $x=$x+1;
      }
      printf "quest(\"Combien vaudra la variable x à la fin de cet algorigramme si a=%u b=%u et c=%u ?\");
      rep(\"x = [%u]\");
      debut(\"Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :\");
      caracteres_speciaux(\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\");
      sch(\"images/algo8.png\",\"190\",\"610\");
      ",$a,$b,$c,$x;
    }
  }
}

Voici quelques unes des questions générées parmi les 100 :

quest("Combien vaudra la variable x à la fin de cet algorigramme si a=32 b=30 et c=11 ?");
rep("x = [33]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algo8.png","190","610");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=32 b=30 et c=15 ?");
rep("x = [33]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algo8.png","190","610");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=32 b=30 et c=18 ?");
rep("x = [33]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algo8.png","190","610");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=32 b=30 et c=26 ?");
rep("x = [33]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algo8.png","190","610");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=32 b=30 et c=40 ?");
rep("x = [43]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algo8.png","190","610");

Dans le cas d'algorigrammes complexes on peut aussi s'inspirer du code en langage C généré par Flowcode pour le convertir en Perl, sachant que la syntaxe de Perl est très proche de celle du C : la plupart du temps seul un caractère $ devant le nom des variables est à rajouter pour convertir le code source en langage C en code source en Perl.

Par exemple pour l'algorigramme ci-dessus Flowcode donne le code source en C suivant :

//Calcul
//Calcul:
// x = 1
FCV_X = 1;

//Boucle
//Boucle: Tant que x<=a
while (FCV_X<=FCV_A)
{
//Décision
//Décision: x<b?
if (FCV_X<FCV_B)
{
//Boucle
//Boucle: Tant que x<=b
while (FCV_X<=FCV_B)
{
//Calcul
//Calcul:
// x = x+3
FCV_X = FCV_X+3;

}

} else {
//Boucle
//Boucle: Tant que x<=c
while (FCV_X<=FCV_C)
{
//Calcul
//Calcul:
// x = x+2
FCV_X = FCV_X+2;

}

}

//Calcul
//Calcul:
// x = x+1
FCV_X = FCV_X+1;

}

Chaque variable est préfixée par FCV_ : en remplaçant simplement FCV_ par $ (et // par # pour les commentaires) on obtient un code source en Perl parfaitement fonctionnel :

#Calcul
#Calcul:
# x = 1
$X = 1;

#Boucle
#Boucle: Tant que x<=a
while ($X<=$A)
{
#Décision
#Décision: x<b?
if ($X<$B)
{
#Boucle
#Boucle: Tant que x<=b
while ($X<=$B)
{
#Calcul
#Calcul:
# x = x+3
$X = $X+3;

}

} else {
#Boucle
#Boucle: Tant que x<=c
while ($X<=$C)
{
#Calcul
#Calcul:
# x = x+2
$X = $X+2;

}

}

#Calcul
#Calcul:
# x = x+1
$X = $X+1;

}

Exemple d'application avec l'algorigramme suivant :


algorigramme_65.png

Flowcode nous donne le code en langage C suivant :

     //Calcul
   //Calcul:
   // x = 0
   FCV_X = 0;
   //Décision
   //Décision: a>=b?
   if (FCV_A>=FCV_B)
   {
      //Calcul
      //Calcul:
      // x = x+2
      FCV_X = FCV_X+2;
      //Décision
      //Décision: a=b?
      if (FCV_A==FCV_B)
      {
         //Décision
         //Décision: b>=c?
         if (FCV_B>=FCV_C)
         {
            //Calcul
            //Calcul:
            // x = x+2
            FCV_X = FCV_X+2;
         } else {
            //Calcul
            //Calcul:
            // x = x+1
            FCV_X = FCV_X+1;
         }
      } else {
         //Calcul
         //Calcul:
         // x = x+4
         FCV_X = FCV_X+4;
         //Décision
         //Décision: b<c?
         if (FCV_B<FCV_C)
         {
            //Calcul
            //Calcul:
            // x = x+1
            FCV_X = FCV_X+1;
         } else {
            //Calcul
            //Calcul:
            // x = x-1
            FCV_X = FCV_X-1;
         }
      }
   } else {
      //Calcul
      //Calcul:
      // x = x+1
      FCV_X = FCV_X+1;
      //Décision
      //Décision: b>=c?
      if (FCV_B>=FCV_C)
      {
         //Décision
         //Décision: c=b?
         if (FCV_C==FCV_B)
         {
            //Calcul
            //Calcul:
            // x = x+7
            FCV_X = FCV_X+7;
         } else {
            //Calcul
            //Calcul:
            // x = x+1
            FCV_X = FCV_X+1;
         }
      } else {
         //Calcul
         //Calcul:
         // x = x+3
         FCV_X = FCV_X+3;
         //Décision
         //Décision: a<x?
         if (FCV_A<FCV_X)
         {
            //Calcul
            //Calcul:
            // x = x-3
            FCV_X = FCV_X-3;
         } else {
            //Calcul
            //Calcul:
            // x = x+2
            FCV_X = FCV_X+2;
         }
      }
   }

En remplaçant FCV_ par $ et // par # dans le programme source en langage C généré par Flowcode on obtient instantanément le programme source en Perl suivant à qui il a fallu juste ajouter 3 boucles foreach et le printf final :

######################################################################################
# Programme de génération de questions à partir du code C donné par Flowcode
# Ce programme génère 100 questions relatives à l'algorigramme_65.png
# Réalisé par Jean-Christophe MICHEL le 21 février 2015
# www.gecif.net
##########################################
############################################

foreach $A (2,4,7,8,9)
{
foreach $B (2,4,7,8,9)
{
foreach $C (1,4,7,9)
{
   #Calcul
   #Calcul:
   # x = 0
   $X = 0;
   #Décision
   #Décision: a>=b?
   if ($A>=$B)
   {
      #Calcul
      #Calcul:
      # x = x+2
      $X = $X+2;
      #Décision
      #Décision: a=b?
      if ($A==$B)
      {
         #Décision
         #Décision: b>=c?
         if ($B>=$C)
         {
            #Calcul
            #Calcul:
            # x = x+2
            $X = $X+2;
         } else {
            #Calcul
            #Calcul:
            # x = x+1
            $X = $X+1;
         }
      } else {
         #Calcul
         #Calcul:
         # x = x+4
         $X = $X+4;
         #Décision
         #Décision: b<c?
         if ($B<$C)
         {
            #Calcul
            #Calcul:
            # x = x+1
            $X = $X+1;
         } else {
            #Calcul
            #Calcul:
            # x = x-1
            $X = $X-1;
         }
      }
   } else {
      #Calcul
      #Calcul:
      # x = x+1
      $X = $X+1;
      #Décision
      #Décision: b>=c?
      if ($B>=$C)
      {
         #Décision
         #Décision: c=b?
         if ($C==$B)
         {
            #Calcul
            #Calcul:
            # x = x+7
            $X = $X+7;
         } else {
            #Calcul
            #Calcul:
            # x = x+1
            $X = $X+1;
         }
      } else {
         #Calcul
         #Calcul:
         # x = x+3
         $X = $X+3;
         #Décision
         #Décision: a>x?
         if ($A<$X)
         {
            #Calcul
            #Calcul:
            # x = x-3
            $X = $X-3;
         } else {
            #Calcul
            #Calcul:
            # x = x+2
            $X = $X+2;
         }
      }
   }
printf "quest(\"Donnez la valeur affectée à la variable x par l'algorigramme ci-contre sachant que a=%u b=%u et c=%u :||Sachant que a=%u b=%u et c=%u combien vaut la variable x après l'exécution de l'algorigramme ci-contre ?||Combien vaudra la variable x à la fin de cet algorigramme si a=%u b=%u et c=%u ?||On donne a=%u b=%u et c=%u. Déduisez-en la valeur de x à la fin de cet algorigramme :\");
rep(\"x = [%u]\");
debut(\"Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :\");
caracteres_speciaux(\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\");
sch(\"images/algorigramme_65.png\",\"865\",\"598\");
",$A,$B,$C,$A,$B,$C,$A,$B,$C,$A,$B,$C,$X;
}
}
}

Et voici quelques questions obtenues instantanément parmi les 100 questions générées par le programme ci-dessus :

quest("Donnez la valeur affectée à la variable x par l'algorigramme ci-contre sachant que a=9 b=7 et c=9 :||Sachant que a=9 b=7 et c=9 combien vaut la variable x après l'exécution de l'algorigramme ci-contre ?||Combien vaudra la variable x à la fin de cet algorigramme si a=9 b=7 et c=9 ?||On donne a=9 b=7 et c=9. Déduisez-en la valeur de x à la fin de cet algorigramme :");
rep("x = [7]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algorigramme_65.png","865","598");
quest("Donnez la valeur affectée à la variable x par l'algorigramme ci-contre sachant que a=9 b=8 et c=1 :||Sachant que a=9 b=8 et c=1 combien vaut la variable x après l'exécution de l'algorigramme ci-contre ?||Combien vaudra la variable x à la fin de cet algorigramme si a=9 b=8 et c=1 ?||On donne a=9 b=8 et c=1. Déduisez-en la valeur de x à la fin de cet algorigramme :");
rep("x = [5]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algorigramme_65.png","865","598");
quest("Donnez la valeur affectée à la variable x par l'algorigramme ci-contre sachant que a=9 b=8 et c=4 :||Sachant que a=9 b=8 et c=4 combien vaut la variable x après l'exécution de l'algorigramme ci-contre ?||Combien vaudra la variable x à la fin de cet algorigramme si a=9 b=8 et c=4 ?||On donne a=9 b=8 et c=4. Déduisez-en la valeur de x à la fin de cet algorigramme :");
rep("x = [5]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algorigramme_65.png","865","598");
quest("Donnez la valeur affectée à la variable x par l'algorigramme ci-contre sachant que a=9 b=8 et c=7 :||Sachant que a=9 b=8 et c=7 combien vaut la variable x après l'exécution de l'algorigramme ci-contre ?||Combien vaudra la variable x à la fin de cet algorigramme si a=9 b=8 et c=7 ?||On donne a=9 b=8 et c=7. Déduisez-en la valeur de x à la fin de cet algorigramme :");
rep("x = [5]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algorigramme_65.png","865","598");

Voici un dernier exemple de génération automatique de questions relatives à un algorigramme grâce à Perl :


algorigramme_95.png

Le programme suivant génère cette fois des questions avec des boutons radio, en choisissant les fausses réponses "proches" de la bonne réponse :

######################################################################################
# Programme de génération de questions à partir du code C donné par Flowcode
# Ce programme génère 232 questions relatives à l'algorigramme_95.png
# Réalisé par Jean-Christophe MICHEL le 23 février 2015
# www.gecif.net
##########################################
############################################

sub valeur_fausse
{
   # Tire un nombre aléatoire entre 0 et 4 :
   $i=int(rand(5));

   # Donne à $valeur une valeur fausse selon 5 calculs différents :
   if ($i==0)
   {
      $valeur=$A;
   }
   elsif ($i==1)
   {
      $valeur=$A+1;
   }
   elsif ($i==2)
   {
      $valeur=$A+2;
   }
   elsif ($i==3)
   {
      $valeur=$A+3;
   }
   else
   {
      $valeur=$X+1;
   }
   return $valeur;
   }
   # Fin de la fonction

for ($A=0;$A<=57;$A++)
{
   foreach $B (1,2,3,4)
   {
   #Calcul
   #Calcul:
   # x = b
   $X = $B;

   #Décision
   #Décision: a>25?
   if ($A>25)
   {
      #Boucle
      #Boucle: Tant que x<=a
      while ($X<=$A)
      {
         #Calcul
         #Calcul:
         # x = x+2
         $X = $X+2;
      }

   } else {
      #Boucle
      #Boucle: Tant que x<a
      while ($X<$A)
      {
      #Calcul
      #Calcul:
      # x = x+2
      $X = $X+2;

      }
   }

   # Recherche 3 mauvaises réponses toutes différentes :
   $faux1=0;
   $faux2=0;
   $faux3=0;

   while ($faux1==$X || $faux2==$X || $faux3==$X || $faux1==$faux2 || $faux1==$faux3 || $faux2==$faux3)
   {
      $faux1=valeur_fausse;
      $faux2=valeur_fausse;
      $faux3=valeur_fausse;
   }

   printf "quest(\"Combien vaudra la variable x à la fin de cet algorigramme si a=%u et b=%u ?//a\");
  rep(\"(o) %u\");
  rep(\"( ) %u\");
  rep(\"( ) %u\");
  rep(\"( ) %u\");
  sch(\"images/algorigramme_95.png\",\"190\",\"424\");
  ",$A,$B,$X,$faux1,$faux2,$faux3;
  }
}

Et voici quelques questions obtenues :

quest("Combien vaudra la variable x à la fin de cet algorigramme si a=16 et b=3 ?//a");
rep("(o) 17");
rep("( ) 16");
rep("( ) 18");
rep("( ) 19");
sch("images/algorigramme_95.png","190","424");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=16 et b=4 ?//a");
rep("(o) 16");
rep("( ) 18");
rep("( ) 19");
rep("( ) 17");
sch("images/algorigramme_95.png","190","424");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=17 et b=1 ?//a");
rep("(o) 17");
rep("( ) 20");
rep("( ) 18");
rep("( ) 19");
sch("images/algorigramme_95.png","190","424");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=17 et b=2 ?//a");
rep("(o) 18");
rep("( ) 20");
rep("( ) 19");
rep("( ) 17");
sch("images/algorigramme_95.png","190","424");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=17 et b=3 ?//a");
rep("(o) 17");
rep("( ) 20");
rep("( ) 19");
rep("( ) 18");
sch("images/algorigramme_95.png","190","424");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=17 et b=4 ?//a");
rep("(o) 18");
rep("( ) 17");
rep("( ) 20");
rep("( ) 19");
sch("images/algorigramme_95.png","190","424");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=18 et b=1 ?//a");
rep("(o) 19");
rep("( ) 18");
rep("( ) 21");
rep("( ) 20");
sch("images/algorigramme_95.png","190","424");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=18 et b=2 ?//a");
rep("(o) 18");
rep("( ) 20");
rep("( ) 21");
rep("( ) 19");
sch("images/algorigramme_95.png","190","424");

Contrairement à Python ou une adaptation du code en C était nécessaire, la conversion du langage C vers Perl est pratiquement automatique.

Et inversement si on veut écrire rapidement un programme de calcul en Perl utilisant des affectations, des tests et des boucles on peut utiliser Flowcode comme interface graphique permettant d'écrire le programme sous forme d'algorigramme : en remplaçant FCV_ par $ et // par # dans le source en langage C généré par Flowcode on obtient instantanément le programme source en Perl désiré avec l'imbrication correcte des boucles, des tests, et des différents blocs délimités par les accolades.

Pour générer les valeurs des mauvaises réponses on peut appeler la fonction valeur_fausse() plusieurs fois jusqu'à avoir toutes les valeurs fausses différentes comme dans l'exemple ci-dessus, mais si on veut 7 valeurs fausses la condition pour vérifier qu'elles sont toutes différentes commence à être extrêmement complexe. Dans ce cas on peut utiliser une table de hachage permettant d'obtenir facilement un grand nombre de valeurs fausses toutes différentes comme le montre le programme suivant :

######################################################################################
# Programme de génération de questions à partir du code C donné par Flowcode
# avec recherche de 7 fausses réponses grâce à une table de hachage
# Réalisé par Jean-Christophe MICHEL le 21 mai 2015
# www.gecif.net
##########################################
############################################

sub valeur_fausse
{
      $i=int(rand(4));

      # Donne à $valeur une valeur fausse selon 4 calculs différents, $X étant la bonne réponse
      if ($i==0)
      {
            $valeur=$X+int(rand(2+int(rand(20))));
      }
      elsif ($i==1)
      {
            $valeur=$X-int(rand(2+int(rand(20))));
      }
      elsif ($i==2)
      {
            $valeur=150+int(rand(2+int(rand(20))));
      }
      else
      {
            $valeur=150-int(rand(2+int(rand(20))));
      }

      # Élimine les valeurs indésirables en donnant à $valeur la valeur de la bonne réponse $X :
      if ($valeur>1000) { $valeur=$X; }
      if ($valeur<0) { $valeur=$X; }
      if ($valeur==0) { $valeur=$X; }
      return $valeur;
}
# Fin de la fonction

foreach $A (2..19)
{
      foreach $B (1..4)
      {
            #Calcul
            #Calcul:
            # x = 100
            # i = 0
            $X = 150;
            $I = 0;
            

            #Boucle
            #Boucle: Tant que i<a
            while ($I<$A)
            {
                  #Calcul
                  #Calcul:
                  # i = i+1
                  $I = $I+1;
                  

                  #Décision
                  #Décision: i AND b?
                  if ($I & $B)
                  {
                  #Calcul
                  #Calcul:
                  # x = x+1
                  $X = $X-1;
                  

                  } else {
                  #Calcul
                  #Calcul:
                  # x = x-1
                  $X = $X+1;
            

                  }

            }

            # Recherche les mauvaises réponses toutes différentes grâce à une table de hachage %h_faux :
            # vide la table de hachage :
            %h_faux=();

            # Recherche 7 mauvaises réponses toutes différentes :
            while (scalar (keys %h_faux)<7)
            {
            
      $i=valeur_fausse;
                  # Ajoute la fausse valeur à la table de hachage seulement si elle est différente de $X et strictement positive :
                  if ($i!=$X && $i>0) {
                        $h_faux{$i}++;
                  }
            }

            # le tableau @t_faux contient seulement les clés de la table de hachage soit les 7 réponses fausses toutes différentes       :
            @t_faux=keys %h_faux;

            printf "quest(\"Combien vaudra la variable x à la fin de cet algorigramme si a=%u et b=%u ?//a\");
            rep(\"(o) %u\");
            rep(\"( ) %u\");
            rep(\"( ) %u\");
            rep(\"( ) %u\");
            rep(\"( ) %u\");
            rep(\"( ) %u\");
            rep(\"( ) %u\");
            rep(\"( ) %u\");
            sch(\"images/algo12_2.png\",\"190\",\"472\");
            ",$A,$B,$X,$t_faux[0],$t_faux[1],$t_faux[2],$t_faux[3],$t_faux[4],$t_faux[5],$t_faux[6];

            printf "quest(\"Combien vaudra la variable x à la fin de cet algorigramme si a=%u et b=%u ?\");
            rep(\"x = [%u]\");
            debut(\"Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :\");
            caracteres_speciaux(\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\");
            sch(\"images/algo12_2.png\",\"190\",\"472\");
            ",$A,$B,$X;
      }
}

Voici l'algorigramme relatif à ce programme :


algo12_2.png

Et voici quelques exemples de questions générées avec ce générateur. Elles proposent 8 réponses, toutes différentes et réparties autour de la bonne valeur et autour de 150 (valeur initiale de x dans l'algorigramme), dont une seule est juste :

quest("Combien vaudra la variable x à la fin de cet algorigramme si a=2 et b=3 ?//a");
rep("(o) 148");
rep("( ) 142");
rep("( ) 161");
rep("( ) 152");
rep("( ) 162");
rep("( ) 141");
rep("( ) 151");
rep("( ) 146");
sch("images/algo12_2.png","190","472");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=2 et b=3 ?");
rep("x = [148]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algo12_2.png","190","472");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=2 et b=4 ?//a");
rep("(o) 152");
rep("( ) 154");
rep("( ) 140");
rep("( ) 138");
rep("( ) 150");
rep("( ) 148");
rep("( ) 139");
rep("( ) 137");
sch("images/algo12_2.png","190","472");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=2 et b=4 ?");
rep("x = [152]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algo12_2.png","190","472");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=3 et b=1 ?//a");
rep("(o) 149");
rep("( ) 136");
rep("( ) 152");
rep("( ) 155");
rep("( ) 146");
rep("( ) 150");
rep("( ) 159");
rep("( ) 143");
sch("images/algo12_2.png","190","472");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=3 et b=1 ?");
rep("x = [149]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algo12_2.png","190","472");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=3 et b=2 ?//a");
rep("(o) 149");
rep("( ) 138");
rep("( ) 150");
rep("( ) 151");
rep("( ) 145");
rep("( ) 139");
rep("( ) 158");
rep("( ) 147");
sch("images/algo12_2.png","190","472");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=3 et b=2 ?");
rep("x = [149]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algo12_2.png","190","472");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=3 et b=3 ?//a");
rep("(o) 147");
rep("( ) 142");
rep("( ) 140");
rep("( ) 148");
rep("( ) 150");
rep("( ) 146");
rep("( ) 149");
rep("( ) 159");
sch("images/algo12_2.png","190","472");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=3 et b=3 ?");
rep("x = [147]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algo12_2.png","190","472");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=3 et b=4 ?//a");
rep("(o) 153");
rep("( ) 152");
rep("( ) 155");
rep("( ) 150");
rep("( ) 159");
rep("( ) 163");
rep("( ) 143");
rep("( ) 157");
sch("images/algo12_2.png","190","472");
quest("Combien vaudra la variable x à la fin de cet algorigramme si a=3 et b=4 ?");
rep("x = [153]");
debut("Vous pouvez entrer les chiffres au clavier ou utiliser les boutons ci-dessous :");
caracteres_speciaux("0","1","2","3","4","5","6","7","8","9");
sch("images/algo12_2.png","190","472");

 

 

Retour au sommaire

Générateur de questions pour QCM relatives à un grafcet Automgen

Nous allons voir ici comment générer automatiquement en Perl des questions relatives à un grafcet. Le grafcet de base sera créé dans Automgen 7.103, et Perl devra alors réaliser différentes fonctions bien distinctes permettant de convertir 1 grafcet en une multitude de questions pour QCM :

- convertir le grafcet en différentes variantes en modifiant ses actions et ses réceptivités et créer plusieurs fichier .ZON ou .GR7

- calculer l'état des sorties et la valeur des variables utilisateurs à différentes étapes du grafcet et créer un fichier de donnée

- convertir le fichier de donnée contenant l'état des sorties et des variables en un fichier de questions pour QCM

Voyons en détail comment utiliser Perl pour réaliser ces différentes tâches.

 

Conversion d'une grafcet modèle en différentes variantes en modifiant ses actions et les réceptivités et créer plusieurs fichiers .ZON :

On part du grafcet suivant dessiné dans Automgen 7.103 :


Grafcet 1

On sélectionne la zone du grafcet dans le folio puis on l'enregistre dans un fichier grafcet1.ZON grâce à la commande "Copier vers ..." disponible dans le menu Edition d'Automgen 7.103. Un fichier .ZON peut être vu comme un fichier texte dans lequel les différentes lignes sont séparées par le caractère LF (caractère de code ASCII 10 en décimal, soit 0A en hexadécimal). Voici le contenu du fichier binaire grafcet1.ZON dans un éditeur hexadécimal : on remarque que les actions des étapes et les réceptivités des transitions sont écrites en texte clair dans le fichier .ZON :


Fichier grafcat1.zon avec LF comme fin de ligne

Télécharger l'éditeur hexadécimal HexEdit 4.0

Grâce à la commande suivante on convertit dans un shell Windows le fichier grafcet1.ZON en fichier grafcet2.zon en remplaçant o0 par o2 et en remplaçant m200 par m204 :

perl -e "while(<STDIN>) { $_ =~ s/o0/o2/g ; $_=~ s/m200/m204/g ; print }" < grafcet1.zon > grafcet2.zon

Voici le contenu du fichier grafcet2.zon que Perl vient de créer :


Fichier grafcat2.zon avec CR LF comme fin de ligne

Comme la transformation du fichier grafcet1.zon en fichier grafcet2.zon a été faite ici dans un shell Windows, les caractères de fin de ligne sont maintenant CR LF et non LF seulement : le nouveau fichier .zon n'est alors plus exploitable sous Automgen 7.103. Pour rendre ce nouveau fichier utilisable par Automgen il faut y remplacer toutes les séquences CR LF par le caractère unique LF, soit remplacer les 2 octets hexadécimaux 0D 0A par un seul octet de valeur 0A. Cette conversion sera confiée ici à l'éditeur hexadécimal HexEdit 4.0 qui peut appliquer facilement cette transformation sur l'ensemble des fichiers ouverts :


Fichier grafcat2.zon avec LF comme fin de ligne

Et si on insère le fichier grafcet2.zon dans le folio grâce à la commande "Coller à partir de ..." disponible dans le menu Edition d'Automgen 7.103 on obtient la nouvelle variante du grafcet parfaitement fonctionnelle :


Variante du grafcet d'origine modifié grâce à Perl

Nous venons de voir en détail comment effectuer une transformation sur un grafcet pour obtenir une nouvelle variante. Voyons maintenant comment transformer 1 grafcet en une multitude de variantes en automatisant cette procédure grâce à Perl.

Le programme en Perl suivant ouvre le fichier .zon dont le nom lui est passé en paramètre, puis crée une multitude de fichiers .zon en effectuant les modifications suivantes en utilisant toutes les combinaisons possibles :

Les fichiers générés sont nommés grafcet10.zon, grafcet11.zon, grafcet12.zon ... jusqu'à plusieurs centaines de fichiers.

Remarque : pour lire le fichier texte dont le nom est passé en paramètre sur le ligne de commande ce programme utilise l'opérateur 'diamant' de Perl qui est bien pratique. Appelé le diamant de par son apparence (<>), cet opérateur signifie "pour chaque ligne du fichier dont le nom est passé en paramètre".

###################################################
# Programme de génération de fichiers .ZON
# Réalisé par Jean-Christophe MICHEL
# www.gecif.net
# Créé le 17 janvier 2015
###################################################

my @tab;

# Charge dans le tableau @tab toutes les lignes du fichier passé en paramètre grâce à l'opérateur diamant :
while (<>)
{
push @tab,$_;
}

# Numéro du premier fichier à créer :
$n=10;

for ($m200=201;$m200<=208;$m200++)
{
    for ($o0=1;$o0<=5;$o0++)
    {
        for ($num=10;$num<95;$num=$num+3)
        {
        # crée un nouveau fichier (ouverture en écriture)
        open($fic,">","grafcet".$n.".zon");
        # Traite toutes les lignes du tableau @tab :
        foreach(@tab)
            {
            $ligne = $_;
            # effectue les différentes transformations sur chaque ligne :
            $ligne =~ s/o0/o$o0/g;
            $ligne =~ s/m200/m$m200/g;
            $ligne =~ s/:=47/:=$num/g;
            # écrit dans le fichier $fic :
            print $fic $ligne;
            }
        close $fic;
        $n++;
        }
    }
}

Remarque : dans les remplacements de chaînes de caractères il faut veiller à remplacer une chaîne par une autre DE MEME TAILLE. Par exemple on ne peut pas remplacer m200:=47; par m200:=158;

Ce programme a généré 1160 variantes différentes du grafcet d'origine, en les enregistrant dans les fichiers grafcet10.zon à grafcet1169.zon.

Voici le grafcet d'origine :


grafcet1.zon

Et voici quelques grafcet obtenus grâce à ce générateur (4 exemples pris parmi les 1160 grafcet générés) :


grafcet21.zon

grafcet780.zon

grafcet967.zon

grafcet1143.zon

Mais le plus gros problème qu'il reste à résoudre si on veut générer automatiquement un grand nombre de fichiers .zon exploitables directement dans Automgen 7.103 est la conversion des caractères CR LF en simple caractère LF sous Windows XP (problème classique, n'est-ce pas ??). Avec l'éditeur hexadécimal il est possible de traiter un petit nombre de fichiers, mais pas 1000 fichiers d'un coup. Le plus simple pour convertir un grand nombre de fichiers .zon du format DOS (fin de ligne CR LF) vers le format Unix (fin de ligne LF) est d'utiliser le programme historique dos2unix disponible sous Linux et qui fait ça très bien. Comme les fichiers .zon d'Automgen sont des fichiers binaires et non de simples fichiers texte on précise à dos2unix qu'on veut forcer le traitement d'un fichier binaire grâce au paramètre -f.

Commande à taper dans un shell Linux pour convertir un seul fichier .zon :

dos2unix -f grafcet34.zon

Commande à taper dans un shell Linux pour convertir automatiquement tous les fichiers .zon du répertoire courant :

dos2unix -f *.zon

Mais il existe aussi des versions de dos2unix fonctionnant sous Windows XP. En voici une programmée sous Delphi 3 : dos2unix.exe

Commande à taper dans un shell Windows XP pour convertir un seul fichier .zon :

dos2unix grafcet34.zon

Commande à taper dans un shell Windows XP pour convertir automatiquement tous les fichiers .zon du répertoire courant :

for %f in (*.zon) do dos2unix %f

Remarque : contrairement à la version Linux, cette version de dos2unix fonctionnant sous Windows XP convertit naturellement les fichiers binaires comme les fichiers .zon d'Automgen 7.103 sans paramètre particulier.

Une fois que ce programme dos2unix.exe est enregistré dans le répertoire de travail sous Windows XP où on va traiter les fichiers .zon grâce à Perl, il est possible de générer des dizaines de grafcet parfaitement fonctionnels sous Automgen 7.103 et tous dérivés d'un même grafcet modèle de base. Plus que jamais le rôle de Perl est ici de se concentrer sur les transformations à faire dans les grafcets et non de gérer les problèmes de fins de lignes qui seront réparés à part avec dos2unix et après le traitement que Perl fait sur la partie textuelle des fichiers .zon.

Mais les fichiers .zon ne sont pas la seule solution pour exporter un grafcet afin de le modifier par Perl. On peut aussi exporter le folio en entier dans un fichier .GR7 (clic droit sur le folio + Exporter). Les fichiers .GR7 sont plus volumineux que les fichiers .ZON car ils contiennent tout le folio en entier, y compris les zones vides : la taille du fichier .GR7 dépend donc de la taille du folio. Pour obtenir des fichiers .GR7 d'une taille inférieure on pourait demander un format A4 lors de la création du folio dans Automgen à la place du format XXL par défaut mais la modification du fichier par Perl ne fonctionne plus car le fichier .GR7 contiendrait alors des retours à la ligne : il faut bien garder un folio au format XXL afin qu'il ne contienne aucun retour à la ligne. Les fichiers .GR7 contiennent uniquement les codes ASCII des caractères du folio, avec les réceptivités et les actions écrites en clair et donc modifiables facilement par Perl, avec des espaces dans toutes les zones vides, et sans aucun retour à la ligne si le folio est de taille maximale XXL. De plus il est possible de charger dans Automgen 7.103 plusieurs folios à la fois au format GR7.

Pour éviter les problèmes de retour à la ligne il faut sauvegarder le folio format XXL dans un fichier .GR7 et traiter avec Perl les fichiers .GR7 à la place des fichiers .ZON

De plus, comme le fichier .GR7 d'un folio au format XXL ne contient qu'une seule ligne, le programme en Perl permettant de le transformer est encore plus simple (plus de boucles while ou foreach pour traiter les lignes une à une, plus de tableau @tab pour enregistrer tout le fichier : une simple variable scalaire $grafcet permet de charger tout le fichier .GR7 dans une seule chaîne de caractères). Le programme suivant par exemple crée 112 grafcets nommés grafcet10.gr7 à grafcet121.gr7 à partir du grafcet de départ grafcet1.gr7 :

########################################################
# Programme grafcet.pl de génération de fichiers .GR7
# Réalisé par Jean-Christophe MICHEL
# www.gecif.net
# Créé le 18 janvier 2015
########################################################

# Lit l'unique ligne du fichier .GR7 (folio au format XXL) passé en paramètre :
$grafcet=<>;

# Numéro du premier fichier à créer :
$n=10;

for ($m200=201;$m200<=204;$m200++)
{
    for ($o0=1;$o0<=4;$o0++)
    {
        for ($num=10;$num<30;$num=$num+3)
        {
            open($fic,">","grafcet".$n.".gr7");
            # Traite la ligne unique du fichier .GR7 :
            $ligne = $grafcet;
            $ligne =~ s/o0/o$o0/g;
            $ligne =~ s/m200/m$m200/g;
            $ligne =~ s/:=47/:=$num/g;
            print $fic $ligne;
            close $fic;
            $n++;
        }
    }
}

Pour dupliquer le fichier grafcet1.gr7 en 112 variantes il suffit de lancer en ligne de commande (le fichier grafcet1.gr7 est alors ouvert et lu par l'opérateur diamant de Perl) :

perl grafcet.pl grafcet1.gr7

Et les 112 nouveaux fichiers .gr7 obtenus sont immédiatement importables dans Automgen 7.103 sans problème de caractères de fin de ligne.

Mais disposer d'une centaine d'images de grafcet n'est utile que si Perl est capable de déterminer les réponses aux questions qui seront posées plus tard avec ces images. Si chaque image générée donne lieu à une seule question qu'il faut réfléchir et éditer à la main la rentabilité d'un tel générateur est alors relative, voire négative ... Il reste donc à trouver une solution pour que Perl, tout en générant les différentes images de grafcet, garde une trace dans un fichier de données sur le fonctionnement du grafcet lui-même, afin d'en déterminer plus tard l'état des sorties à chaque étape. Evidemment la conversion d'un grafcet en programme Perl équivalent n'est pas automatique (contrairement à la conversion d'un algorigramme en Perl qui utilise le code en langage C généré par Flowcode), et chaque structure de grafcet est un cas particulier. De plus les informations à conserver pour chaque grafcet dans le fichier de données dépend du type de questions que l'on voudra poser plus tard (Exemple : "Quelles sont les sorties actives à l'étape n ?", "Combien vaut telle ou telle variable à l'étape n ?", "Quelle est l'étape active lorsque le grafcet sera stabilisé ?", "Combien vaudra le compteur C0 lorsque la sortie o3 passera à 1 ?", etc.).

Il est bien plus simple de demander à Perl d'évaluer les sorties dans le cas d'un système combinatoire, comme nous allons le voir maintenant.

 

Retour au sommaire

Générateur de questions pour QCM relatives à un logigramme Automgen

Nous venons de voir comment générer automatiquement des images de grafcet en modifiant un grafcet modèle. Voyons maintenant comment, à partir d'une image fixe de logigramme, générer un ensemble de quesions pour QCM.

On dispose de l'image suivante indiquant dans un logigramme la condition d'activation de chacune des 4 premières sorties o0 à o3 :


image automgen3.png

On désire obtenir automatiquement un ensemble de questions imposant la valeur des 3 constantes m200, m201 et m202 et demandant de cocher les sorties qui sont à 1 ou les sorties qui sont à 0.

Voici le programme en Perl générant de telles questions et utilisant les mêmes conditions logiques que celles du logigramme afin d'estimer l'état des sorties en fonction de la valeur des variables :

###########################################################################
# Générateur de questions pour QCM relatives à un logigramme Automgen :
# Programme réalisé le 8 janvier 2015
par Jean-Christophe MICHEL
# www.gecif.net
###########################################################################

# On dispose d'une image avec 4 logigrammes donnant les conditions d'activation des 4 sorties o0, o1, o2 et o3

# En donnant différentes valeurs aux 3 variables m200, m201 et m202 on désire obtenir une série de questions du style "Cochez les sorties qui sont à 1 si m200=4 m201=8 et m202=3" ou "Cochez les sorties qui sont à 0 si m200=4 m201=8 et m202=3"

my $m200,$m201,$m202,$o0,$o1,$o2,$o3;

# On donne 3 valeurs différentes à la variable $m200 :
foreach $m200 (4,6,11)
    {
    
# On donne 3 valeurs différentes à la variable $m201 :
        foreach $m201 (1,5,11)
        {
            
# On donne 3 valeurs différentes à la variable $m202 :
                    foreach $m202 (1,6,15)
            {
                
# On calcule l'état de chacune des 4 sorties en utilisant les conditions logiques du logigramme :
                          if ($m201>=$m202)
                    { $o0=0; }
                else
                    { $o0=1; }

                if ($m200<$m201)
                    { $o1=1; }
                else
                    { $o1=0; }

                if (($m201!=$m202)&&($m200<$m201))
                    { $o2=0; }
                else
                    { $o2=1; }

                if (($m200<=$m202)||($m201==$m202))
                    { $o3=1; }
                else
                    { $o3=0; }

                # Question demandant les sorties à 1 :
                print "quest(\"Cochez les sorties qui sont &agrave; 1 dans le logigramme ci-contre si m200=".$m200.", m201=".$m201." et m202=".$m202." :\");\n";
                if ($o0==1)
                    { print "rep(\"[x] o0\");\n"; }
                else
                    { print "rep(\"[ ] o0\");\n"; }
                if ($o1==1)
                    { print "rep(\"[x] o1\");\n"; }
                else
                    { print "rep(\"[ ] o1\");\n"; }
                if ($o2==1)
                    { print "rep(\"[x] o2\");\n"; }
                else
                    { print "rep(\"[ ] o2\");\n"; }
                if ($o3==1)
                    { print "rep(\"[x] o3\");\n"; }
                else
                    { print "rep(\"[ ] o3\");\n"; }
                print "sch(\"images/automgen3.png\",\"480\",\"171\");\n";
                

                # Question demandant les sorties à 0 :
                print "quest(\"Cochez les sorties qui sont &agrave; 0 dans le logigramme ci-contre si m200=".$m200.", m201=".$m201." et m202=".$m202." :\");\n";
                if ($o0==0)
                    { print "rep(\"[x] o0\");\n"; }
                else
                    { print "rep(\"[ ] o0\");\n"; }
                if ($o1==0)
                    { print "rep(\"[x] o1\");\n"; }
                else
                    { print "rep(\"[ ] o1\");\n"; }
                if ($o2==0)
                    { print "rep(\"[x] o2\");\n"; }
                else
                    { print "rep(\"[ ] o2\");\n"; }
                if ($o3==0)
                    { print "rep(\"[x] o3\");\n"; }
                else
                    { print "rep(\"[ ] o3\");\n"; }
                print "sch(\"images/automgen3.png\",\"480\",\"171\");\n";

        }
    }
}

Et voici un exemple de questions générées avec ce générteur (4 questions parmi les 54 questions générées) :

quest("Cochez les sorties qui sont &agrave; 1 dans le logigramme ci-contre si m200=11, m201=11 et m202=6 :");
rep("[ ] o0");
rep("[ ] o1");
rep("[x] o2");
rep("[ ] o3");
sch("images/automgen3.png","480","171");
quest("Cochez les sorties qui sont &agrave; 0 dans le logigramme ci-contre si m200=11, m201=11 et m202=6 :");
rep("[x] o0");
rep("[x] o1");
rep("[ ] o2");
rep("[x] o3");
sch("images/automgen3.png","480","171");
quest("Cochez les sorties qui sont &agrave; 1 dans le logigramme ci-contre si m200=11, m201=11 et m202=15 :");
rep("[x] o0");
rep("[ ] o1");
rep("[x] o2");
rep("[x] o3");
sch("images/automgen3.png","480","171");
quest("Cochez les sorties qui sont &agrave; 0 dans le logigramme ci-contre si m200=11, m201=11 et m202=15 :");
rep("[ ] o0");
rep("[x] o1");
rep("[ ] o2");
rep("[ ] o3");
sch("images/automgen3.png","480","171");

Comme Perl permet sans problème de modifier automatiquement une image de logigramme en transformant un fichier .GR7, puis d'interpréter les équations logiques qu'il contient, il est désormais possible de générer automatiquement un grand nombre de logigrammes différents avec pour chacun d'entre eux des questions associés.

Retour au sommaire

 

Exemple de génération de grafcet et d'un fichier de données associé :

On part du grafcet suivant contenant 3 branches (une branche d'initialisation, une branche d'incrémentation, et une branche finale où le grafcet se bloquera) et enregistré dans le fichier grafcet.gr7 :


fichier grafcet.gr7

En modifiant la valeur initiale de m200 (à l'étape 0), l'incrément (à l'étape 20), et la valeur du test final (avant les étapes 20 et 40) on génère automatiquement 144 grafcet nommés grafcet1.gr7 à grafcet144.gr7 grâce à Perl :

###################################################
# Programme de génération de fichiers .GR7
# Réalisé par Jean-Christophe MICHEL
# www.gecif.net
# Créé le 18 janvier 2015
###################################################

# Lit l'unique ligne du fichier .GR7 (folio au format XXL) :
$grafcet=<>;

# Numéro du premier fichier .gr7 à créer :
$n=1;

foreach $init (1,2,3,4,7,9)
{
    foreach $increment (2,3,4,5)
    {
        foreach $max (13,14,15,18,20,23)
        {
            open($fic,">","grafcet".$n.".gr7");
            # Traite la ligne unique du fichier .GR7 :
            $ligne = $grafcet;
            $ligne =~ s/m200:=0;/m200:=$init;/g;
            # ATTENTION : pour détecter le signe + dans le motif de recherche il faut l'échapper par un anti-slash :
            $ligne =~ s/m200:=m200\+1;/m200:=m200+$increment;/g;
            $ligne =~ s/m200<12/m200<$max/g;
            $ligne =~ s/m200>=12/m200>=$max/g;
            print $fic $ligne;
            close $fic;
            
            # Envoie sur la sortie standard les données permettant de créer les questions :
            $fraction=($max-$init)/$increment;
            # Nombre de passage dans la branche centrale (entier supérieur à $fraction) :
            $entier=int($fraction + 0.99);
            # Valeur de m200 à la fin du grafcet :
            $m200=$init+$entier*$increment;
            print "grafcet".$n.";".$entier.";".$m200."\n";
            
            $n++;
        }
    }
}

On récupère les données calculées par Perl pour chaque grafcet dans un fichier grafcet.txt grâce à une redirection de la sortie standard dans le shell :

perl grafcet.pl grafcet.gr7 > grafcet.txt

Pour convertir les 144 fichiers .GR7 obtenus en 144 images de grafcet au format .PNG grâce à Fireworks on utilise une macro de Super Macro :

Le raccourcis Alt = règle automatiquement le zoom dans Automgen pour voir le grafcet en entier (la résolution finale de l'image dépend alors de la taille de la fenêtre Automgen), et le raccourcis Ctrl-M lance une commande enregistrée dans Fireworks pour recadrer au pixel prés l'image de l'écran afin de ne garder que le grafcet. En lançant 144 fois cette macro (pour chacun des 144 folio .GR7) on obtient 144 images .PNG ayant toutes la même résolution.

Dans Fireworks on peut demander à la macro de recadrer "large", puis faire un Ctrl-Alt-t pour réduire automatiquement l'image au grafcet.

Le raccourcis Alt = peut être remplacé par Alt-o s qui supprime dans Automgen tous les espaces dans le folio afin que le grafcet soit positionné dans le coin supérieur gauche du folio. Il faut alors laisser 1 seconde à Automgen (avant de faire Imprime écran) pour qu'il ait le temps de décaler tout le grafcet dans le folio. Une autre macro pour convertir les fichiers .GR7 en fichier .PNG pourait alors être la suivante :

Remarque : dans ces macros le premier Alt-Tab sur la première ligne permet de rendre la main à la fenêtre Automgen après avoir cliqué sur l'icône de la macro.

En plus des 144 fichiers .GR7 le programme ci-dessus a généré un fichier de données grafcet.txt contenant les valeurs utiles pour générer des questions :

grafcet1;6;13
grafcet2;7;15
grafcet3;7;15
grafcet4;9;19
grafcet5;10;21
grafcet6;11;23
grafcet7;4;13
grafcet8;5;16
grafcet9;5;16
grafcet10;6;19
grafcet11;7;22
grafcet12;8;25
grafcet13;3;13
grafcet14;4;17
grafcet15;4;17
grafcet16;5;21
grafcet17;5;21
grafcet18;6;25
grafcet19;3;16
grafcet20;3;16
grafcet21;3;16
grafcet22;4;21
grafcet23;4;21
grafcet24;5;26
grafcet25;6;14
grafcet26;6;14
grafcet27;7;16
grafcet28;8;18
grafcet29;9;20
grafcet30;11;24
grafcet31;4;14
grafcet32;4;14
grafcet33;5;17
grafcet34;6;20
grafcet35;6;20
grafcet36;7;23
grafcet37;3;14
grafcet38;3;14
grafcet39;4;18
grafcet40;4;18
grafcet41;5;22
grafcet42;6;26
grafcet43;3;17
grafcet44;3;17
grafcet45;3;17
grafcet46;4;22
grafcet47;4;22
grafcet48;5;27
grafcet49;5;13
grafcet50;6;15
grafcet51;6;15
grafcet52;8;19
grafcet53;9;21
grafcet54;10;23
grafcet55;4;15
grafcet56;4;15
grafcet57;4;15
grafcet58;5;18
grafcet59;6;21
grafcet60;7;24
grafcet61;3;15
grafcet62;3;15
grafcet63;3;15
grafcet64;4;19
grafcet65;5;23
grafcet66;5;23
grafcet67;2;13
grafcet68;3;18
grafcet69;3;18
grafcet70;3;18
grafcet71;4;23
grafcet72;4;23
grafcet73;5;14
grafcet74;5;14
grafcet75;6;16
grafcet76;7;18
grafcet77;8;20
grafcet78;10;24
grafcet79;3;13
grafcet80;4;16
grafcet81;4;16
grafcet82;5;19
grafcet83;6;22
grafcet84;7;25
grafcet85;3;16
grafcet86;3;16
grafcet87;3;16
grafcet88;4;20
grafcet89;4;20
grafcet90;5;24
grafcet91;2;14
grafcet92;2;14
grafcet93;3;19
grafcet94;3;19
grafcet95;4;24
grafcet96;4;24
grafcet97;3;13
grafcet98;4;15
grafcet99;4;15
grafcet100;6;19
grafcet101;7;21
grafcet102;8;23
grafcet103;2;13
grafcet104;3;16
grafcet105;3;16
grafcet106;4;19
grafcet107;5;22
grafcet108;6;25
grafcet109;2;15
grafcet110;2;15
grafcet111;2;15
grafcet112;3;19
grafcet113;4;23
grafcet114;4;23
grafcet115;2;17
grafcet116;2;17
grafcet117;2;17
grafcet118;3;22
grafcet119;3;22
grafcet120;4;27
grafcet121;2;13
grafcet122;3;15
grafcet123;3;15
grafcet124;5;19
grafcet125;6;21
grafcet126;7;23
grafcet127;2;15
grafcet128;2;15
grafcet129;2;15
grafcet130;3;18
grafcet131;4;21
grafcet132;5;24
grafcet133;1;13
grafcet134;2;17
grafcet135;2;17
grafcet136;3;21
grafcet137;3;21
grafcet138;4;25
grafcet139;1;14
grafcet140;1;14
grafcet141;2;19
grafcet142;2;19
grafcet143;3;24
grafcet144;3;24

Ce fichier de données contient :

En traitant ce fichier de données par expressions régulières il est possible de générer pour chaque grafcet une série de questions pour QCM du style :

Les réponses à toutes ces questions sont obtenues pour chaque grafcet en analysant les données présentes dans le fichier grafcet.txt.

Voici le programme en Perl qui convertit le fichier de données grafcet.txt en un fichier qcm.txt contenant 3 questions par grafcet :

#######################################################################
# Programme questions.pl de génération de questions
# Ce programme pose 3 questions pour chaque image de grafcet
# Réalisé par Jean-Christophe MICHEL
# www.gecif.net
# Créé le 20 janvier 2015
###############################
########################################

# Charge dans le tableau @tab toutes les lignes du fichier passé par l'entrée standard :
while (<STDIN>)
{
    push @tab,$_;
}

# Applique le même traitement à toutes les lignes du tableau @tab :
foreach (@tab)
{
    # découpe la ligne selon les point-virgules et récupère chacun des 3 champs dans une variable :
    ($nom,$i,$m200)=split /;/;
    # supprime le retour à la ligne à la fin de $m200 :
    $m200=$m200+0;
    
    # Question 1 :
    print "quest(\"Combien de fois l'&eacute;tape 20 est-elle active avant que le grafcet ne soit bloqu&eacute; ?||Combien de fois l'&eacute;tape 30 devient-elle active avant que le grafcet ne se stabilise ?\");\n";
    print "rep(\"[".$i."] fois\");\n";
    print "sch(\"images/".$nom.".png\",\"600\",\"434\");\n";

    # Question 2 :
    print "quest(\"Combien vaudra la variable m200 lorsque l'&eacute;tape 50 sera active ?||Combien vaudra la variable m200 lorsque le grafcet sera stable ?\");\n";
    print "rep(\"m200 = [".$m200."]\");\n";
    print "sch(\"images/".$nom.".png\",\"600\",\"434\");\n";

    # Question 2 :
    print "quest(\"Quel est l'&eacute;tat de la sortie o0 &agrave; l'&eacute;tape 50 ?||Combien vaudra la sortie o0 lorsque le grafcet sera stable ?\");\n";
    if ($i % 2==0)
    {
        print "rep(\"( ) 0\");\n";
        print "rep(\"(o) 1\");\n";
    }
    else
    {
        print "rep(\"(o) 0\");\n";
        print "rep(\"( ) 1\");\n";
    }
    print "sch(\"images/".$nom.".png\",\"600\",\"434\");\n";
    
}

La ligne de commande permettant de convertir le fichier de données grafcet.txt en un fichier texte qcm.txt redirige l'entrée et le sortie standard :

perl questions.pl < grafcet.txt > qcm.txt

Remarque : dans ce programme le fichier d'entrée grafcet.txt est transmis à Perl par l'entrée standard grâce à l'opérateur <STDIN> mais il aurait pu être transmis par paramètre grâce à l'opérateur diamant <> et en donnant simplement son nom sur la ligne de commande (sans redirection de l'entrée standard).

Ce principe peut être repris et étendu à des grafcets plus complexes, mais à la condition que Perl ne perde pas de vue l'état des sorties et des variables durant l'évolution des différents grafcets.

 

Retour au sommaire

Génération de questions relatives à 1 grafcet linéaire à 4 étapes :

On part du grafcet suivant et on désire un ensemble de questions du style "Cochez toutes les étapes où la sortie o2 est à 1" et "Cochez toutes les sorties qui sont à 1 à l'étape 20" :

 

Dans le programme suivant on renseigne l'état de chaque sortie à chacune des 4 étapes du grafcet dans un tableau à 2 dimensions puis en utilisant cette structure de données le programme génère 16 questions relatives à la même image de grafcet :

###################################################
# Programme de génération de questions
# Réalisé par Jean-Christophe MICHEL
# www.gecif.net
# Créé le 23 janvier 2015
###################################################

# Ce programme génère 16 questions de type "Cochez toutes les sorties qui sont à 1 à l'étape n"
# et "Cochez toutes les étapes où la sortie o2 est à 1" à partir d'un grafcet linéaire à 4 étapes

# On indique les propriétés de l'image :
$image="grafcet30.png";
$x=230;
$y=406;

# On indique pour chacune des 4 sorties o0 à o3 son état à chaque étape dans un tableau à 2 dimensions :
# Etat de o0 aux 4 étapes :
$tab[0][0]=1;
$tab[10][0]=1;
$tab[20][0]=0;
$tab[30][0]=1;

# Etat de o1 aux 4 étapes :
$tab[0][1]=1;
$tab[10][1]=0;
$tab[20][1]=1;
$tab[30][1]=0;

# Etat de o2 aux 4 étapes :
$tab[0][2]=0;
$tab[10][2]=1;
$tab[20][2]=1;
$tab[30][2]=1;

# Etat de o3 aux 4 étapes :
$tab[0][3]=0;
$tab[10][3]=1;
$tab[20][3]=1;
$tab[30][3]=1;

# Génère 2 questions pour chaque étape :
foreach $etape (0,10,20,30)
{
    # Question 1 :
    $aucune=1;
    print "quest(\"Cochez les sorties qui sont ou qui basculent à 1 à l'étape ".$etape." :\");\n";
    foreach $sortie (0,1,2,3)
    {
        if ($tab[$etape][$sortie]==0)
        {
            print "rep(\"[ ] o".$sortie."\");\n";
        }
        else
        {
            $aucune=0;
            print "rep(\"[x] o".$sortie."\");\n";
        }
    }
    if ($aucune==1)
    {
        print "rep(\"[x] Aucune : toutes les sorties sont à 0 à l'étape ".$etape."\");\n";
    }
    else
    {
        print "rep(\"[ ] Aucune : toutes les sorties sont à 0 à l'étape ".$etape."\");\n";
    }
    print "sch(\"images/".$image.".png\",\"".$x."\",\"".$y."\");\n";

    # Question 2 :
    $aucune=1;
    print "quest(\"Cochez les sorties qui sont ou qui basculent à 0 à l'étape ".$etape." :\");\n";
    foreach $sortie (0,1,2,3)
    {
        if ($tab[$etape][$sortie]==0)
        {
            $aucune=0;
            print "rep(\"[x] o".$sortie."\");\n";
        }
        else
        {
            print "rep(\"[ ] o".$sortie."\");\n";
        }
    }
        if ($aucune==1)
        {
            print "rep(\"[x] Aucune : toutes les sorties sont à 1 à l'étape ".$etape."\");\n";
        }
        else
        {
            print "rep(\"[ ] Aucune : toutes les sorties sont à 1 à l'étape ".$etape."\");\n";
        }
        print "sch(\"images/".$image.".png\",\"".$x."\",\"".$y."\");\n";
       }

# Génère 2 questions pour chaque sortie :
foreach $sortie (0,1,2,3)
   {
    # Question 1 :
    $aucune=1;
       print "quest(\"Cochez les étapes où la sortie o".$sortie." est ou bascule à 1 :\");\n";
       foreach $etape (0,10,20,30)
       {
           if ($tab[$etape][$sortie]==0)
           {
               print "rep(\"[ ] étape ".$etape."\");\n";
           }
           else
           {
               $aucune=0;
               print "rep(\"[x] étape ".$etape."\");\n";
           }
       }
       if ($aucune==1)
       {
           print "rep(\"[x] Aucune : o".$sortie." ne passe jamais à 1\");\n";
       }
       else
       {
           print "rep(\"[ ] Aucune : o".$sortie." ne passe jamais à 1\");\n";
       }
       print "sch(\"images/".$image.".png\",\"".$x."\",\"".$y."\");\n";

    # Question 2 :
    $aucune=1;
       print "quest(\"Cochez les étapes où la sortie o".$sortie." est ou bascule à 0 :\");\n";
       foreach $etape (0,10,20,30)
       {
           if ($tab[$etape][$sortie]==0)
           {
               $aucune=0;
               print "rep(\"[x] étape ".$etape."\");\n";
           }
           else
           {
               print "rep(\"[ ] étape ".$etape."\");\n";
           }
       }
       if ($aucune==1)
       {
           print "rep(\"[x] Aucune : o".$sortie." ne passe jamais à 0\");\n";
       }
       else
       {
           print "rep(\"[ ] Aucune : o".$sortie." ne passe jamais à 0\");\n";
       }
       print "sch(\"images/".$image.".png\",\"".$x."\",\"".$y."\");\n";
}

 

Retour au sommaire

Génération du fichier de données grâce à Automgen :

Pour que Perl génère des questions sur l'état des sorties, des variables ou des étapes actives relatives à un grafcet il doit connaître l'état et le fonctionnement du grafcet. Pour cela nous avons déjà vu 3 solutions difféntes :

Mais après tout le mieux placé pour analyser un grafcet complexe et nous donner un fichier de données correspondant c'est Automgen lui même. La question qui se pose alors est "Comment demander à Automgen de générer un fichier texte de données correspondant aux états des variables à un instant donné ?". La réponse est : en utilisant l'Archivage de données qui est un objet IRIS 2D.

On part du grafcet suivant et on veut une série de questions du style "Cochez les étapes actives après stabilisation des grafcet si au démarage i0=1, i1=0 et i2=1" :

En ajoutant un archivage de données on demande à Automgen d'écrire dans le fichier grafcet.txt les 8 états de chacune des étapes pour les 8 combinaisons des entrées (i3 remet les grafcets à zéro et i4 ajoute une nouvelle ligne dans le fichier de données) :

Dans l'onglet Données de la boîte de configuration de l'archivage de données on précise le type de variables à enregistrer (ici les sorties en partant de o0), le nombre de variables consécutives (9 pour o0 à o8) et on précise que les données seront enregistrées à chaque activation de i4 :

Dans l'onglet Options de la boîte de configuration de l'archivage de données on précise le nom du fichier de données (ici grafcet.txt) et on demande à enregistrer seulement les valeurs sans la date ni l'heure :

Et voici le fichier de données grafcet.txt généré par Automgen (enregistré sur le Bureau ou dans "Mes Documents" selon les cas ...) :

0;0;0;1;0;0;1;0;0
1;0;0;0;1;0;1;0;0
0;1;0;1;0;0;1;0;0
1;1;0;0;0;1;1;0;0
0;0;1;1;0;0;0;1;0
1;0;1;0;1;0;0;0;1
0;1;1;1;0;0;0;1;0
1;1;1;0;0;1;0;0;1

Ce fichier à 9 colonnes contient l'état des 9 sorties o0 à o8, et grâce à la boîte de code sur le folio les 3 premières colonnes correspondent à l'état des entrées i0 i1 et i2, puis les colonnes suivantes indiquent pour chacun des 8 cas l'état des étapes 0 10 20 30 40 et 50.

Il reste à convertir ce fichier de données en questions de QCM grâce à Perl. Voici un exemple de programme qui génère 7 questions pour chacune des lignes du fichier de données :

###################################################
# Programme de génération de questions
# Ce programme utilise le fichier de données généré par l'archivage de données
# d'Automgen pour créer des questions relatives à un grafcet complexe
# Réalisé par Jean-Christophe MICHEL
# www.gecif.net
# Créé le 24 janvier 2015
###################################################

#############################################################
# Cette fonction reçoit 2 paramètres : une variable à 0 ou à 1 (ex : $x20) et le nom de l'étape (ex : "20").
# Elle affiche une case à cochée validée si la variable vaut 1
#############################################################
sub case_a_cocher
{
    # Récupère les paramètres dans 2 variables scalaires distinctes :
    my ($var,$etape)=@_;
    if ($var==1) { print "rep(\"[x] étape ".$etape."\");\n"; }
    else { print "rep(\"[ ] étape ".$etape."\");\n"; }
}
#############################################################

#############################################################
# Cette fonction reçoit 2 paramètres : une variable à 0 ou à 1 (ex : $x20) et le nom de l'étape (ex : "20").
# Elle affiche 2 options : étape active si la variable vaut 1 ou pas active si la variable vaut 0
#############################################################
sub bouton_radio
{
    # Récupère les paramètres dans 2 variables scalaires distinctes :
    my ($var,$etape)=@_;
    if ($var==1)
    {
        print "rep(\"(o) l'étape ".$etape." est active\");\n";
        print "rep(\"( ) l'étape ".$etape." n'est pas active\");\n";
    }
    else
    {
        print "rep(\"( ) l'étape ".$etape." est active\");\n";
        print "rep(\"(o) l'étape ".$etape." n'est pas active\");\n";
    }
}
#############################################################

# Paramètres de l'image commune à toutes les questions :
$nom="grafcet46";
$x=422;
$y=316;

# Charge dans le tableau @tab toutes les lignes du fichier de données Automgen passé par l'entrée standard :
while (<STDIN>)
{
    push @tab,$_;
}

# Applique le même traitement à toutes les lignes du tableau @tab :
foreach (@tab)
{
    # découpe la ligne selon les point-virgules et récupère chacun des champs dans une variable :
    ($i0,$i1,$i2,$x0,$x10,$x20,$x30,$x40,$x50)=split /;/;
    # supprime le retour à la ligne dans $x50 :
    $x50=$x50+0;
    
    # Pose 7 questions pour chacune des lignes du fichier de données :
    
    # Question 1 :
    print "quest(\"Cochez les étapes actives après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1." et i2=".$i2." :\");\n";
    case_a_cocher($x0,"0");
    case_a_cocher($x10,"10");
    case_a_cocher($x20,"20");
    case_a_cocher($x30,"30");
    case_a_cocher($x40,"40");
    case_a_cocher($x50,"50");
    print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";

    # Question 2 :
    print "quest(\"Quel est l'état de l'étape 0 après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1." et i2=".$i2." ?\");\n";
    bouton_radio($x0,"0");
    print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";

    # Question 3 :
    print "quest(\"Quel est l'état de l'étape 10 après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1." et i2=".$i2." ?\");\n";
    bouton_radio($x10,"10");
    print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";

    # Question 4 :
    print "quest(\"Quel est l'état de l'étape 20 après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1." et i2=".$i2." ?\");\n";
    bouton_radio($x20,"20");
    print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";

    # Question 5 :
    print "quest(\"Quel est l'état de l'étape 30 après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1." et i2=".$i2." ?\");\n";
    bouton_radio($x30,"30");
    print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";

    # Question 6 :
    print "quest(\"Quel est l'état de l'étape 40 après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1." et i2=".$i2." ?\");\n";
    bouton_radio($x40,"40");
    print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";

    # Question 7 :
    print "quest(\"Quel est l'état de l'étape 50 après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1." et i2=".$i2." ?\");\n";
    bouton_radio($x50,"50");
    print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";

}

Voici la version du programme pour 4 grafcets à 3 étapes et utilisant les 4 premières entrées, soit un fichier de données à 16 lignes :

###################################################
# Programme de génération de questions
# Ce programme utilise le fichier de données généré par l'archivage de données
# d'Automgen pour créer des questions relatives à un grafcet complexe
# Réalisé par Jean-Christophe MICHEL
# www.gecif.net
# Créé le 24 janvier 2015
###################################################

#############################################################
# Cette fonction reçoit 2 paramètres : une variable à 0 ou à 1 (ex : $x20) et le nom de l'étape (ex : "20").
# Elle affiche une case à cochée validée si la variable vaut 1
#############################################################
sub case_a_cocher
{
# Récupère les paramètres dans 2 variables scalaires distinctes :
my ($var,$etape)=@_;
if ($var==1) { print "rep(\"[x] étape ".$etape."\");\n"; }
else { print "rep(\"[ ] étape ".$etape."\");\n"; }
}
#############################################################

#############################################################
# Cette fonction reçoit 2 paramètres : une variable à 0 ou à 1 (ex : $x20) et le nom de l'étape (ex : "20").
# Elle affiche 2 options : étape active si la variable vaut 1 ou pas active si la variable vaut 0
#############################################################
sub bouton_radio
{
# Récupère les paramètres dans 2 variables scalaires distinctes :
my ($var,$etape)=@_;
if ($var==1)
{
print "rep(\"(o) l'étape ".$etape." est active\");\n";
print "rep(\"( ) l'étape ".$etape." n'est pas active\");\n";
}
else
{
print "rep(\"( ) l'étape ".$etape." est active\");\n";
print "rep(\"(o) l'étape ".$etape." n'est pas active\");\n";
}
}
#############################################################

# Paramètres de l'image commune à toutes les questions :
$nom="grafcet48";
$x=758;
$y=316;

# Charge dans le tableau @tab toutes les lignes du fichier de données Automgen passé par l'entrée standard :
while (<STDIN>)
{
push @tab,$_;
}

# Applique le même traitement à toutes les lignes du tableau @tab :
foreach (@tab)
{
# découpe la ligne selon les point-virgules et récupère chacun des champs dans une variable :
($i0,$i1,$i2,$i3,$x0,$x10,$x20,$x30,$x40,$x50,$x60,$x70,$x80,$x90,$x100,$x110)=split /;/;
# supprime le retour à la ligne dans $x110 :
$x110=$x110+0;

# Pose 13 questions pour chacune des lignes du fichier de données :

# Question 1 :
print "quest(\"Cochez les étapes actives après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1.", i2=".$i2." et i3=".$i3." :\");\n";
case_a_cocher($x0,"0");
case_a_cocher($x10,"10");
case_a_cocher($x20,"20");
case_a_cocher($x30,"30");
case_a_cocher($x40,"40");
case_a_cocher($x50,"50");
case_a_cocher($x60,"60");
case_a_cocher($x70,"70");
case_a_cocher($x80,"80");
case_a_cocher($x90,"90");
case_a_cocher($x100,"100");
case_a_cocher($x110,"110");
print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";

# 3 questions sur le premier grafcet :
print "quest(\"Quel est l'état de l'étape 0 après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1.", i2=".$i2." et i3=".$i3." ?\");\n";
bouton_radio($x0,"0");
print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";
print "quest(\"Quel est l'état de l'étape 10 après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1.", i2=".$i2." et i3=".$i3." ?\");\n";
bouton_radio($x10,"10");
print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";
print "quest(\"Quel est l'état de l'étape 20 après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1.", i2=".$i2." et i3=".$i3." ?\");\n";
bouton_radio($x20,"20");
print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";

# 3 questions sur le deuxième grafcet :
print "quest(\"Quel est l'état de l'étape 30 après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1.", i2=".$i2." et i3=".$i3." ?\");\n";
bouton_radio($x30,"30");
print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";
print "quest(\"Quel est l'état de l'étape 40 après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1.", i2=".$i2." et i3=".$i3." ?\");\n";
bouton_radio($x40,"40");
print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";
print "quest(\"Quel est l'état de l'étape 50 après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1.", i2=".$i2." et i3=".$i3." ?\");\n";
bouton_radio($x50,"50");
print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";

# 3 questions sur le troisième grafcet :
print "quest(\"Quel est l'état de l'étape 60 après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1.", i2=".$i2." et i3=".$i3." ?\");\n";
bouton_radio($x60,"60");
print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";
print "quest(\"Quel est l'état de l'étape 70 après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1.", i2=".$i2." et i3=".$i3." ?\");\n";
bouton_radio($x70,"70");
print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";
print "quest(\"Quel est l'état de l'étape 80 après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1.", i2=".$i2." et i3=".$i3." ?\");\n";
bouton_radio($x80,"80");
print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";

# 3 questions sur le quatrième grafcet :
print "quest(\"Quel est l'état de l'étape 90 après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1.", i2=".$i2." et i3=".$i3." ?\");\n";
bouton_radio($x90,"90");
print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";
print "quest(\"Quel est l'état de l'étape 100 après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1.", i2=".$i2." et i3=".$i3." ?\");\n";
bouton_radio($x100,"100");
print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";
print "quest(\"Quel est l'état de l'étape 110 après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1.", i2=".$i2." et i3=".$i3." ?\");\n";
bouton_radio($x110,"110");
print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";

}

Exemple de grafcet utilisé :

Le fichier de données correspondant est :

0;0;0;0;1;0;0;1;0;0;1;0;0;1;0;0
1;0;0;0;0;1;0;0;0;1;1;0;0;1;0;0
0;1;0;0;1;0;0;0;1;0;1;0;0;0;0;1
1;1;0;0;0;1;0;0;0;1;1;0;0;0;0;1
0;0;1;0;0;0;1;1;0;0;0;1;0;1;0;0
1;0;1;0;0;0;1;0;0;1;0;1;0;1;0;0
0;1;1;0;0;0;1;0;1;0;0;1;0;0;0;1
1;1;1;0;0;0;1;0;0;1;0;1;0;0;0;1
0;0;0;1;1;0;0;1;0;0;0;0;1;0;1;0
1;0;0;1;0;1;0;0;0;1;0;0;1;0;1;0
0;1;0;1;1;0;0;0;1;0;0;0;1;0;0;1
1;1;0;1;0;1;0;0;0;1;0;0;1;0;0;1
0;0;1;1;1;0;0;1;0;0;0;0;1;0;1;0
1;0;1;1;0;1;0;0;0;1;0;0;1;0;0;1
0;1;1;1;0;0;1;0;1;0;0;0;1;0;0;1
1;1;1;1;0;0;1;0;0;1;0;0;1;0;0;1

Et voici les questions générées :

quest("Cochez les étapes actives après stabilisation des grafcet si au démarrage i0=0, i1=1, i2=0 et i3=1 :");
rep("[x] étape 0");
rep("[ ] étape 10");
rep("[ ] étape 20");
rep("[ ] étape 30");
rep("[x] étape 40");
rep("[ ] étape 50");
rep("[ ] étape 60");
rep("[ ] étape 70");
rep("[x] étape 80");
rep("[ ] étape 90");
rep("[ ] étape 100");
rep("[x] étape 110");
sch("images/grafcet48.png","758","316");
quest("Quel est l'état de l'étape 0 après stabilisation des grafcet si au démarrage i0=0, i1=1, i2=0 et i3=1 ?");
rep("(o) l'étape 0 est active");
rep("( ) l'étape 0 n'est pas active");
sch("images/grafcet48.png","758","316");
quest("Quel est l'état de l'étape 10 après stabilisation des grafcet si au démarrage i0=0, i1=1, i2=0 et i3=1 ?");
rep("( ) l'étape 10 est active");
rep("(o) l'étape 10 n'est pas active");
sch("images/grafcet48.png","758","316");
quest("Quel est l'état de l'étape 20 après stabilisation des grafcet si au démarrage i0=0, i1=1, i2=0 et i3=1 ?");
rep("( ) l'étape 20 est active");
rep("(o) l'étape 20 n'est pas active");
sch("images/grafcet48.png","758","316");
quest("Quel est l'état de l'étape 30 après stabilisation des grafcet si au démarrage i0=0, i1=1, i2=0 et i3=1 ?");
rep("( ) l'étape 30 est active");
rep("(o) l'étape 30 n'est pas active");
sch("images/grafcet48.png","758","316");
quest("Quel est l'état de l'étape 40 après stabilisation des grafcet si au démarrage i0=0, i1=1, i2=0 et i3=1 ?");
rep("(o) l'étape 40 est active");
rep("( ) l'étape 40 n'est pas active");
sch("images/grafcet48.png","758","316");
quest("Quel est l'état de l'étape 50 après stabilisation des grafcet si au démarrage i0=0, i1=1, i2=0 et i3=1 ?");
rep("( ) l'étape 50 est active");
rep("(o) l'étape 50 n'est pas active");
sch("images/grafcet48.png","758","316");
quest("Quel est l'état de l'étape 60 après stabilisation des grafcet si au démarrage i0=0, i1=1, i2=0 et i3=1 ?");
rep("( ) l'étape 60 est active");
rep("(o) l'étape 60 n'est pas active");
sch("images/grafcet48.png","758","316");
quest("Quel est l'état de l'étape 70 après stabilisation des grafcet si au démarrage i0=0, i1=1, i2=0 et i3=1 ?");
rep("( ) l'étape 70 est active");
rep("(o) l'étape 70 n'est pas active");
sch("images/grafcet48.png","758","316");
quest("Quel est l'état de l'étape 80 après stabilisation des grafcet si au démarrage i0=0, i1=1, i2=0 et i3=1 ?");
rep("(o) l'étape 80 est active");
rep("( ) l'étape 80 n'est pas active");
sch("images/grafcet48.png","758","316");
quest("Quel est l'état de l'étape 90 après stabilisation des grafcet si au démarrage i0=0, i1=1, i2=0 et i3=1 ?");
rep("( ) l'étape 90 est active");
rep("(o) l'étape 90 n'est pas active");
sch("images/grafcet48.png","758","316");
quest("Quel est l'état de l'étape 100 après stabilisation des grafcet si au démarrage i0=0, i1=1, i2=0 et i3=1 ?");
rep("( ) l'étape 100 est active");
rep("(o) l'étape 100 n'est pas active");
sch("images/grafcet48.png","758","316");
quest("Quel est l'état de l'étape 110 après stabilisation des grafcet si au démarrage i0=0, i1=1, i2=0 et i3=1 ?");
rep("(o) l'étape 110 est active");
rep("( ) l'étape 110 n'est pas active");
sch("images/grafcet48.png","758","316");

Autre variante du programme pour 4 grafcets à 3 étapes et utilisant les 4 premières entrées, soit un fichier de données à 16 lignes :

###################################################
# Programme de génération de questions
# Ce programme utilise le fichier de données généré par l'archivage de données
# d'Automgen pour créer des questions relatives à un grafcet complexe
# Réalisé par Jean-Christophe MICHEL
# www.gecif.net
# Créé le 25 janvier 2015
###################################################

#############################################################
# Cette fonction reçoit 2 paramètres : une variable à 0 ou à 1 (ex : $x20) et le nom de l'étape (ex : "20").
# Elle affiche une case à cochée validée si la variable vaut 1
#############################################################
sub case_a_cocher
{
# Récupère les paramètres dans 2 variables scalaires distinctes :
my ($var,$etape)=@_;
if ($var==1) { print "rep(\"[x] étape ".$etape."\");\n"; }
else { print "rep(\"[ ] étape ".$etape."\");\n"; }
}
#############################################################

#############################################################
# Cette fonction reçoit 2 paramètres : une variable à 0 ou à 1 (ex : $x20) et le nom de l'étape (ex : "20").
# Elle affiche un bouton radio validé si la variable vaut 1
#############################################################
sub bouton_radio
{
# Récupère les paramètres dans 2 variables scalaires distinctes :
my ($var,$etape)=@_;
if ($var==1) { print "rep(\"(o) l'étape ".$etape."\");\n"; }
else { print "rep(\"( ) l'étape ".$etape."\");\n"; }
}
#############################################################

# Paramètres de l'image commune à toutes les questions :
$nom="grafcet50";
$x=758;
$y=316;

# Charge dans le tableau @tab toutes les lignes du fichier de données Automgen passé par l'entrée standard :
while (<STDIN>)
{
push @tab,$_;
}

# Applique le même traitement à toutes les lignes du tableau @tab :
foreach (@tab)
{
# découpe la ligne selon les point-virgules et récupère chacun des champs dans une variable :
($i0,$i1,$i2,$i3,$x0,$x10,$x20,$x30,$x40,$x50,$x60,$x70,$x80,$x90,$x100,$x110)=split /;/;
# supprime le retour à la ligne dans $x110 :
$x110=$x110+0;

# Pose 5 questions pour chacune des lignes du fichier de données :

# Question 1 :
print "quest(\"Cochez les étapes actives après stabilisation des grafcet si au démarrage i0=".$i0.", i1=".$i1.", i2=".$i2." et i3=".$i3." :\");\n";
case_a_cocher($x0,"0");
case_a_cocher($x10,"10");
case_a_cocher($x20,"20");
case_a_cocher($x30,"30");
case_a_cocher($x40,"40");
case_a_cocher($x50,"50");
case_a_cocher($x60,"60");
case_a_cocher($x70,"70");
case_a_cocher($x80,"80");
case_a_cocher($x90,"90");
case_a_cocher($x100,"100");
case_a_cocher($x110,"110");
print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";

# Question 2 :
print "quest(\"Après stabilisation du système quelle sera l'étape active du premier grafcet si au démarrage i0=".$i0.", i1=".$i1.", i2=".$i2." et i3=".$i3." ?\");\n";
bouton_radio($x0,"0");
bouton_radio($x10,"10");
bouton_radio($x20,"20");
print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";

# Question 3 :
print "quest(\"Après stabilisation du système quelle sera l'étape active du deuxième grafcet si au démarrage i0=".$i0.", i1=".$i1.", i2=".$i2." et i3=".$i3." ?\");\n";
bouton_radio($x30,"30");
bouton_radio($x40,"40");
bouton_radio($x50,"50");
print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";

# Question 4 :
print "quest(\"Après stabilisation du système quelle sera l'étape active du troisième grafcet si au démarrage i0=".$i0.", i1=".$i1.", i2=".$i2." et i3=".$i3." ?\");\n";
bouton_radio($x60,"60");
bouton_radio($x70,"70");
bouton_radio($x80,"80");
print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";

# Question 5 :
print "quest(\"Après stabilisation du système quelle sera l'étape active du quatrième grafcet si au démarrage i0=".$i0.", i1=".$i1.", i2=".$i2." et i3=".$i3." ?\");\n";
bouton_radio($x90,"90");
bouton_radio($x100,"100");
bouton_radio($x110,"110");
print "sch(\"images/".$nom.".png\",\"".$x."\",\"".$y."\");\n";


}

Exemple de grafcet utilisé :

Le fichier de données correspondant est :

0;0;0;0;1;0;0;1;0;0;0;1;0;0;1;0
1;0;0;0;0;0;1;0;1;0;0;1;0;0;0;1
0;1;0;0;1;0;0;1;0;0;0;1;0;0;1;0
1;1;0;0;0;0;1;0;1;0;0;1;0;0;0;1
0;0;1;0;0;1;0;0;0;1;0;0;1;1;0;0
1;0;1;0;0;1;0;0;0;1;0;0;1;1;0;0
0;1;1;0;1;0;0;0;1;0;0;1;0;0;0;1
1;1;1;0;0;0;1;0;1;0;0;1;0;0;0;1
0;0;0;1;1;0;0;1;0;0;1;0;0;1;0;0
1;0;0;1;0;1;0;0;0;1;1;0;0;1;0;0
0;1;0;1;1;0;0;1;0;0;1;0;0;1;0;0
1;1;0;1;0;1;0;0;1;0;1;0;0;1;0;0
0;0;1;1;1;0;0;1;0;0;1;0;0;1;0;0
1;0;1;1;0;1;0;0;0;1;1;0;0;1;0;0
0;1;1;1;1;0;0;1;0;0;1;0;0;1;0;0
1;1;1;1;0;1;0;0;1;0;1;0;0;1;0;0

Et voici les questions générées :

quest("Cochez les étapes actives après stabilisation des grafcet si au démarrage i0=1, i1=1, i2=1 et i3=1 :");
rep("[ ] étape 0");
rep("[x] étape 10");
rep("[ ] étape 20");
rep("[ ] étape 30");
rep("[x] étape 40");
rep("[ ] étape 50");
rep("[x] étape 60");
rep("[ ] étape 70");
rep("[ ] étape 80");
rep("[x] étape 90");
rep("[ ] étape 100");
rep("[ ] étape 110");
sch("images/grafcet50.png","758","316");
quest("Après stabilisation du système quelle sera l'étape active du premier grafcet si au démarrage i0=1, i1=1, i2=1 et i3=1 ?");
rep("( ) l'étape 0");
rep("(o) l'étape 10");
rep("( ) l'étape 20");
sch("images/grafcet50.png","758","316");
quest("Après stabilisation du système quelle sera l'étape active du deuxième grafcet si au démarrage i0=1, i1=1, i2=1 et i3=1 ?");
rep("( ) l'étape 30");
rep("(o) l'étape 40");
rep("( ) l'étape 50");
sch("images/grafcet50.png","758","316");
quest("Après stabilisation du système quelle sera l'étape active du troisième grafcet si au démarrage i0=1, i1=1, i2=1 et i3=1 ?");
rep("(o) l'étape 60");
rep("( ) l'étape 70");
rep("( ) l'étape 80");
sch("images/grafcet50.png","758","316");
quest("Après stabilisation du système quelle sera l'étape active du quatrième grafcet si au démarrage i0=1, i1=1, i2=1 et i3=1 ?");
rep("(o) l'étape 90");
rep("( ) l'étape 100");
rep("( ) l'étape 110");
sch("images/grafcet50.png","758","316");

Enfin voici pour terminer l'organisation du projet sous Automgen permettant d'obtenir le fichier de données. Il comprend :

Pour chacune des combinaisons des entrées (soit à répéter 16 fois) il faut :

Quelle pourrait être l'amélioration de ce générateur ? Et bien tout simplement l'automatisation de la création du fichier de données. Pour l'instant il faut cliquer manuellement sur les boutons i0 à i5 avec la séquence décrite précédemment qui doit être répétée 16 fois. Mais on peut imaginer une automatisation de la procédure grâce à un pupitre côté Automgen utilisant des raccourcis clavier pour activer les entrées et une macro SuperMacro automatisant la séquence de touches complexe permettant d'enregistrer l'intégralité du fichier de données.

En cumulant les différentes techniques mises au point ici (génération automatique de folio parfaitement fonctionnels dans Automgen, génération automatique d'un fichier de données par Automgen, et conversion d'un fichier de données en plusieurs centaines de questions grâce à Perl) il est désormais possible de générer plusieurs milliers de questions relatives à des images de projets Automgen plus ou moins complexes.

 

Retour au sommaire

Filtrage d'un fichier binaire en Perl

L'idée est ici de lire un fichier binaire octet par octet, et de modifier certaines chaînes de caractères écrites en clair dans le fichier binaire.

Voici le programme de base qui lit le fichier binaire fic1.DSN et qui génère le nouveau fichier fic2.DNS après avoir remplacé 10k par 47k et 1.5V par 12V (12V plus un espace afin de conserver 4 octets comme dans le fichier original) :

##############################################################################
# Filtrage d'un fichier binaire :
# Ce programme lit un fichier binaire dans lequel il remplace une chaîne de caractères
# Programme réalisé le 30 janvier 2015 par Jean-Christophe MICHEL
# www.gecif.net

##############################################################################

my $nbr_octets_lus,$buff;

# ouvre le fichier fic1 en lecture :
open($fic1,"<","fic1.DSN");
# ouvre le fichier fic2 en écriture :
open($fic2,">","fic2.DSN");

# accède aux fichiers en mode binaire :
binmode $fic1;
binmode $fic2;

# lit le fichier fic1 par paquets de 1024 octets sauf dans la dernière boucle où Perl lira naturellement les octets restants :
while ($lu=read($fic1,$buff,1024))
{

    # traite la variable $buff comme une chaîne de 1024 caractères :
    $buff =~ s/10k/47k/g;
    $buff =~ s/1.5V/12V /g; # remplace 4 octets par 4 octets en complétant le champ texte par un espace
    # écrit la nouvelle variable $buff dans le fichier fic2 en mode biniare :
    print $fic2 $buff;
}

# ferme les fichiers fic1 et fic2 qui ont exactement la même taille à l'octet pré :
# seules les chaînes de caractères modifiées diffèrent entre fic1 et fic2
close $fic1;
close $fic2;

Voici le fichier d'origine fic1.DSN sous ISIS Proteus :

Et voici le nouveau fichier fic2.DSN issue de la modification automatique par Perl du fichier binaire fic1.DSN :

Les fichiers binaires dans lesquels les champs texte sont écrits en clair et qui peuvent donc être facilement modifiés par Perl en utilisant cette technique afin de générer automatiquement une série de fichiers tous issus d'un même fichier modèle sont nombreux. On peut notamment citer :

Logiciel utilisant le fichier
Type de fichier
Extension du fichier
Champs texte modifiables par Perl
Automgen 7.103
zone graphique ou folio
.ZON et .GR7
actions et réceptivités d'un grafcet, équations logiques d'un logigramme
ISIS Proteus
schéma électronique
.DSN
valeur et référence des composants, commentaire en texte ajouté sur le fond de la page
Flowcode
algorigramme
.fcf
tous les champs textuels dans les algorigrammes
Visio
diagramme, croquis et graphiques
.vsd
tous les champs textuels dans les graphiques
Fireworks
image enregistrée au format illustrator 7
.ai
tous les champs textuels dans l'image

Open Office

Microsoft Word

texte enrichi
.rtf
tous les champs textuels dans les graphiques

La modification automatique des champs texte dans des fichiers binaires grâce à Perl est la base des générateurs de 3ème génération : ils génèrent à la fois les questions en texte du QCM mais aussi les images ou fichiers graphiques qui leurs sont associés. De plus l'accès aux fichiers en mode binaire évite les problèmes de retour à la ligne vus ci-dessus lors de la modification des fichiers .ZON d'Automgen (caractères CR LF à la place d'un simple caractère LF dans le fichier généré).

Exemple d'application du filtrage de fichiers binaires : génération de schémas électroniques ISIS Proteus avec un fichier de données associé

On part du circuit de base suivant représentant 3 résistances en dérivation :


circuit.DSN

Grâce au programme Perl suivant on duplique le circuit de base en 48 versions en modifiant les valeurs de chacune des 3 résistances :

##############################################################################################
# Filtrage d'un fichier binaire et génération de circuits ISIS Proteus :
# Ce programme lit un fichier binaire dans lequel il remplace une chaîne de caractères
# Programme resistance.pl réalisé par Jean-Christophe MICHEL le 31 janvier 2015
# www.gecif.net
##############################################################################################

my $nbr_octets_lus,$buff,$r1,$r2,$r3;

$circuit=1;

foreach $r1 (100,200,300)
{
   foreach $r2 (100,300,400,600)
   {
      foreach $r3 (200,300,400,500)
      {
      open($fic2,">","circuit".$circuit.".DSN");
      binmode $fic2;
      open($fic1,"<","circuit.DSN");
      binmode $fic1;
      # lit le fichier d'origine et en modifie la valeur des 3 résistances :
      while ($lu=read($fic1,$buff,1024))
      {
         $buff =~ s/111/$r1/g;
         $buff =~ s/222/$r2/g;
         $buff =~ s/333/$r3/g;
         print $fic2 $buff;
      }
      close $fic1;
      close $fic2;
      # écrit le fichier de données sur la sortie standard :
      print "circuit".$circuit.";".$r1.";".$r2.";".$r3."\n";
      $circuit++;
      }
   }
}

On récupère le fichier de donné sur la sortie standard :

perl resistance.pl > circuit.txt

Voici le fichier de données circuit.txt obtenu. Il contient le nom du circuit en colonne 1 (nom de l'image) puis la valeur de chacune des 3 résistances en colonnes 2 à 4 :

circuit1;100;100;200
circuit2;100;100;300
circuit3;100;100;400
circuit4;100;100;500
circuit5;100;300;200
circuit6;100;300;300
circuit7;100;300;400
circuit8;100;300;500
circuit9;100;400;200
circuit10;100;400;300
circuit11;100;400;400
circuit12;100;400;500
circuit13;100;600;200
circuit14;100;600;300
circuit15;100;600;400
circuit16;100;600;500
circuit17;200;100;200
circuit18;200;100;300
circuit19;200;100;400
circuit20;200;100;500
circuit21;200;300;200
circuit22;200;300;300
circuit23;200;300;400
circuit24;200;300;500
circuit25;200;400;200
circuit26;200;400;300
circuit27;200;400;400
circuit28;200;400;500
circuit29;200;600;200
circuit30;200;600;300
circuit31;200;600;400
circuit32;200;600;500
circuit33;300;100;200
circuit34;300;100;300
circuit35;300;100;400
circuit36;300;100;500
circuit37;300;300;200
circuit38;300;300;300
circuit39;300;300;400
circuit40;300;300;500
circuit41;300;400;200
circuit42;300;400;300
circuit43;300;400;400
circuit44;300;400;500
circuit45;300;600;200
circuit46;300;600;300
circuit47;300;600;400
circuit48;300;600;500

En plus du fichier de données circuit.txt le programme resistance.pl a créé physiquement 48 nouveaux fichiers binaires au format ISIS Proteus sur le disque dur :


quelques fichiers .DSN parmi les 48 générés

Il faut convertir les 48 fichiers .DSN d'ISIS Proteus en 48 images. Pour cela on ouvre un fichier DSN dans ISIS Proteus, on zoome le circuit (F6 F6 avec la souris positionnée sur la résistance centrale) puis on lance la macro suivante avec Fireworks ouvert derrière Proteus (Ctrl-M lance dans Fireworks une macro interne qui recadre en large le circuit avant de réduire le document) :

Voici quelques exemples de circuits parmi les 48 obtenus :


circuit5.DSN

 


circuit12.DSN

 


circuit24.DSN

circuit31.DSN

Il reste à convertir le fichier de données circuit.txt en questions pour QCM.

Voici le programme en Perl qui demande la résistance équivalente du montage en proposant systématiquement 4 valeurs différentes (avec toujours la bonne réponse parmi les 4 et sans la réponse "Aucune de ces propositions"). De plus en cas de valeur entière une seconde question est générée où il est demandé de saisir directement la valeur de la résistance équivalente dans une ligne de saisie :

###################################################
# Programme req.pl de génération de questions
# Ce programme utilise le fichier de données contenant la valeur de 3 résistances en dérivation
# Réalisé par Jean-Christophe MICHEL
# www.gecif.net
# Créé le 01 février 2015
###################################################

#############################################################
# Cette fonction retourne une valeur fausse en la tirant au hasard parmi 11 calculs différents.
#############################################################
sub valeur_fausse
{
# Tire un nombre aléatoire entre 0 et 14 :
$i=int(rand(15));

# Donne à $valeur une valeur fausse selon 11 calculs différents, avec plus de probabilité pour la dernière formule
if ($i==0)
{
   $valeur=100;
}
elsif ($i==1)
{
   $valeur=200;
}
elsif ($i==2)
{
   $valeur=300;
}
elsif ($i==3)
{
   $valeur=1/(1/($r1+100)+1/$r2+1/$r3);
}
elsif ($i==4)
{
   $valeur=1/(1/$r1+1/($r2+100)+1/$r3);
}
elsif ($i==5)
{
   $valeur=1/(1/$r1+1/$r2+1/($r3+100));
}
elsif ($i==6)
{
   if ($r1==100) { $valeur=1/(1/$r2+1/$r3); }
   else { $valeur=1/(1/($r1-100)+1/$r2+1/$r3) }
}
elsif ($i==7)
{
   if ($r2==100) { $valeur=1/(1/$r1+1/$r3); }
   else { $valeur=1/(1/($r2-100)+1/$r1+1/$r3) }
}
elsif ($i==8)
{
   if ($r3==100) { $valeur=1/(1/$r2+1/$r1); }
   else { $valeur=1/(1/($r3-100)+1/$r2+1/$r1) }
}
elsif ($i==9)
{
   $valeur=400;
}
else
{
# Calcule req avec 3 valeurs aléatoires pour chaque résistance prises parmi 100 200 300 ou 400 :
$valeur=1/(1/(100*int(rand(4)+1))+1/(100*int(rand(4)+1))+1/(100*int(rand(4)+1)));
}
# retourne la valeur calculée avec 2 chiffres après la virgule en cas de valeur décimale :
return int($valeur*100)/100;
}
# Fin de la fonction
###############################################################

# Résolution des images communes à toutes les questions :
$x=322;
$y=217;

# Charge dans le tableau @tab toutes les lignes du fichier de données circuit.txt transmis par l'entrée standard :
while (<STDIN>)
{
   push @tab,$_;
}

# Applique le même traitement à toutes les lignes du tableau @tab :
foreach (@tab)
{
   # découpe la ligne selon les point-virgules et récupère chacun des champs dans une variable :
   ($circuit,$r1,$r2,$r3)=split /;/;
   # supprime le retour à la ligne dans $r3 :
   $r3=$r3+0;

   # Calcule la bonne réponse :
   $req=1/(1/$r1+1/$r2+1/$r3);
   # tronque la valeur de $req avec 2 chiffres après la virgule en cas de valeur décimale :
   $req=int($req*100)/100;

   # Recherche 3 mauvaises réponses toutes différentes :
   $faux1=0;
   $faux2=0;
   $faux3=0;

   while ($faux1==$req || $faux2==$req || $faux3==$req || $faux1==$faux2 || $faux1==$faux3 || $faux2==$faux3)
   {
      $faux1=valeur_fausse;
      $faux2=valeur_fausse;
      $faux3=valeur_fausse;
   }

   # Pose la question en proposant 4 réponses :
   print "quest(\"Quelle est la valeur de la résistance équivalente de ce montage ?||Combien vaut la résistance équivalente totale de ce circuit ?//a\");\n";
   print "rep(\"(o) ".$req." &Omega;\");\n";
   print "rep(\"( ) ".$faux1." &Omega;\");\n";
   print "rep(\"( ) ".$faux2." &Omega;\");\n";
   print "rep(\"( ) ".$faux3." &Omega;\");\n";
   print "sch(\"images/".$circuit.".png\",\"".$x."\",\"".$y."\");\n";

   # Demande de saisir la réponse en cas de valeur entière :
   if ($req==int($req))
   {
      print "quest(\"Entrez la valeur entière en ohms de la résistance équivalente totale de ce montage :\");\n";
      print "rep(\"[".$req."] &Omega;\");\n";
      print "sch(\"images/".$circuit.".png\",\"".$x."\",\"".$y."\");\n";
   }

}

On transmet au programme req.pl le fichier de données circuit.txt par l'entrée standard et on récupère les questions du QCM dans le fichier qcm.txt sur la sortie standard :

perl req.pl < circuit.txt > qcm.txt

Exemples de questions générées dans le fichier qcm.txt :

quest("Quelle est la valeur de la résistance équivalente de ce montage ?||Combien vaut la résistance équivalente totale de ce circuit ?//a");
rep("(o) 109.09 &Omega;");
rep("( ) 200 &Omega;");
rep("( ) 92.3 &Omega;");
rep("( ) 100 &Omega;");
sch("images/circuit42.png","322","217");
quest("Quelle est la valeur de la résistance équivalente de ce montage ?||Combien vaut la résistance équivalente totale de ce circuit ?//a");
rep("(o) 120 &Omega;");
rep("( ) 63.15 &Omega;");
rep("( ) 109.09 &Omega;");
rep("( ) 200 &Omega;");
sch("images/circuit43.png","322","217");
quest("Entrez la valeur entière en ohms de la résistance équivalente totale de ce montage :");
rep("[120] &Omega;");
sch("images/circuit43.png","322","217");
quest("Quelle est la valeur de la résistance équivalente de ce montage ?||Combien vaut la résistance équivalente totale de ce circuit ?//a");
rep("(o) 127.65 &Omega;");
rep("( ) 142.85 &Omega;");
rep("( ) 105.26 &Omega;");
rep("( ) 100 &Omega;");
sch("images/circuit44.png","322","217");
quest("Quelle est la valeur de la résistance équivalente de ce montage ?||Combien vaut la résistance équivalente totale de ce circuit ?//a");
rep("(o) 100 &Omega;");
rep("( ) 109.09 &Omega;");
rep("( ) 40 &Omega;");
rep("( ) 300 &Omega;");
sch("images/circuit45.png","322","217");
quest("Entrez la valeur entière en ohms de la résistance équivalente totale de ce montage :");
rep("[100] &Omega;");
sch("images/circuit45.png","322","217");
quest("Quelle est la valeur de la résistance équivalente de ce montage ?||Combien vaut la résistance équivalente totale de ce circuit ?//a");
rep("(o) 120 &Omega;");
rep("( ) 123.52 &Omega;");
rep("( ) 115.38 &Omega;");
rep("( ) 92.3 &Omega;");
sch("images/circuit46.png","322","217");
quest("Entrez la valeur entière en ohms de la résistance équivalente totale de ce montage :");
rep("[120] &Omega;");
sch("images/circuit46.png","322","217");
quest("Quelle est la valeur de la résistance équivalente de ce montage ?||Combien vaut la résistance équivalente totale de ce circuit ?//a");
rep("(o) 133.33 &Omega;");
rep("( ) 400 &Omega;");
rep("( ) 127.65 &Omega;");
rep("( ) 54.54 &Omega;");
sch("images/circuit47.png","322","217");

Cette technique de génération de questions peut bien sûr être étendue à de multiples autres cas et se déroule en 2 étapes :

Dans tous les cas on confit à Perl chacune des 3 tâches principales :

Remarque : 2 programmes distincts en Perl sont obligatoires car à l'instant où on crée le fichier de données on ne connaît pas encore la résolution des images utilisées dans le QCM. En effet, pour ce type de générateur il a fallu convertir manuellement chacun des fichiers binaires générés par Perl en une image au format .PNG (grâce à Fireworks et à une macro Super Macro). De plus l'analyse et l'écriture des fichiers binaires étant une opération bien différente de la transformation de fichiers texte (données vers questions) il est plus simple et plus clair de séparer ces deux tâches de la procédure et de garder deux programmes distincts : le générateur est composé ici des 2 programmes en Perl resistance.pl et req.pl (en plus de l'action semi-automatisée de création des images).

Pour automatiser la conversion d'un lot de fichiers .DSN en images .PNG on peut agir en 3 étapes :

Voici la macro Super Macro qui ouvre dans ISIS Proteus un par un les fichiers circuit1.DSN à circuit27.DSN et qui les exporte dans les images circuit1.BMP à circuit27.BMP grâce à l'exportateur bitmap d'ISIS Proteus :

Dans tous les projets ISIS Proteus la feuille a été réduite pour être juste supérieure au circuit complet : la taille de la feuille définit la taille de l'image .BMP lors de l'exportation et a été configurée manuellement une seule fois sur le fichier modèle avant sa duplication grâce à Perl :

Enfin pour obtenir des images de qualité on configure l'exportateur avec 300 DPI et en couleur "Visu" (24 bits) :

L'exportateur de fichier .BMP de Proteus se configure une fois manuellement (Résolution 300 DPI, Couleurs Visu et répertoire où seront créés tous les fichiers .BMP) puis la macro Super Macro ne fait qu'ouvrir la boîte de dialogue de l'exportateur bitmap et appuyer sur Entrée : le nom du fichier image .BMP est alors automatiquement celui du projet .DSN.

Dans le traitement par lot sous Fireworks la diminution de la résolution des images après leur réduction permet de lisser légèrement les caractères et d'obtenir avec précision une résolution finale pour les images .PNG. Fireworks écrase les anciens .BMP par les nouveaux .BMP.

Quant à la conversion multiple des .BMP en PNG sous XnView elle est entièrement automatisée sans paramètre particulier.

Avec la création automatisée des images en plus du fichier de données et des questions du QCM, ce générateur en Perl est le premier véritable générateur de 3ème génération mis au point et utilisé pour la première fois le 03 février 2015 et réalisant les actions suivantes par un ensemble de scripts ou de macros à adapter à chaque cas particulier :

 

Retour au sommaire

Les structures de données complexes en Perl

J'appelle structures de données complexes :

Déclaration et remplissage d'un tableau à 2 dimensions :

@tab=([1,2],[3,4]);

Syntaxe pour accéder aux éléments du tableau à 2 dimensions :

print "$tab[1][0]"; # Affiche 3

 

Déclaration et remplissage élément par élément d'un hachage de hachage :

$hash{eleve1}{nom}="DUPONT";
$hash{eleve1}{prenom}="Pierre";
$hash{eleve2}{nom}="MARTIN";
$hash{eleve2}{prenom}="Vincent";

Syntaxe pour accéder aux éléments du hachage de hachage :

print "$hash{eleve2}{nom}"; # Affiche MARTIN

On peut aussi déclarer toute la table de hachage en une seule ligne (et non élément par élément) :

Déclaration et remplissage d'un hachage de hachage :

$hash={
eleve1=>{nom=>"DUPONT",prenom=>"Pierre"},
eleve2=>{nom=>"MARTIN",prenom=>"Vincent"},
};

Syntaxe pour accéder aux éléments du hachage de hachage :

print "$hash->{eleve2}->{nom}"; # Affiche MARTIN

Syntaxe de 2 boucles foreach imbriquées pour afficher la totalité des éléments du hachage de hachage :

foreach $cle (keys %hash) {
   foreach $nom (keys %{$hash{$cle}}) {
      print "Élève : $cle    Nom : $nom</br>";
   }
}

Déclaration et remplissage élément par élément d'un tableau de hachage :

$tab[0]{nom}="DUPONT";
$tab[0]{prenom}="Pierre";
$tab[1]{nom}="MARTIN";
$tab[1]{prenom}="Vincent";

Syntaxe pour accéder aux éléments du tableau de hachage :

print "$tab[1]{nom}"; # Affiche MARTIN

Déclaration et remplissage élément par élément d'un hachage de tableau :

$hash{eleve1}[0]="DUPONT";
$hash{eleve1}[1]="Pierre";
$hash{eleve2}[0]="MARTIN";
$hash{eleve2}[1]="Vincent";

Syntaxe pour accéder aux éléments du hachage de tableau :

print "$hash{eleve2}[0]"; # Affiche MARTIN

 

Retour au sommaire

Exécution d'un script bash dans un programme en Perl

Lancer une ligne de commande bash (ou sh) dans un programme en Perl est bien pratique pour exécuter certaines commandes ou tester le système sur un serveur distant hébergeant un site web. Voici comment faire :

#!/usr/bin/perl
print "Content-type: text/html\n\n";
my @args = ( "bash" , "-c", "
#Ligne de commande à lancer :
date
" ); system(@args);

Si on demande l'aide de la commande date l'ensemble des information sera afficher sur une seule ligne dans le navigateur :

#!/usr/bin/perl
print "Content-type: text/html\n\n";
my @args = ( "bash" , "-c", "
#Ligne de commande à lancer :
date --help
" ); system(@args);

Pour ajouter un retour à la ligne (<br> pour le navigateur) à la fin de chaque ligne on peut utiliser sed :

#!/usr/bin/perl
print "Content-type: text/html\n\n";
my @args = ( "bash" , "-c", "
#Ligne de commande à lancer :
date --help | sed -r 's/^(.*)\$/\\1<br>/g'
" ); system(@args);

Cela permet de tester différentes commandes sur le serveur, et surtout de savoir quelles commandes sont autorisées ou pas.

En remplaçant bash par sh on peut aussi utiliser le shell sh :

#!/usr/bin/perl
print "Content-type: text/html\n\n";
my @args = ( "sh" , "-c", "
#Ligne de commande à lancer :
ls -l | sed -r 's/^(.*)\$/\\1<br>/g'
" ); system(@args);

 

Conclusion

Cette page vous a donné tous les éléments de base pour commencer à programmer en Perl sous Linux comme sous Windows.