Fonctions Lua (2ème partie)

Dans le premier article consacré à la découverte des fonctions Lua pour EEP, nous avons appris comment déclarer nos propres variables, écrire et faire appel aux fonctions utilisateur, comment structurer le code, etc.

Dans ce deuxième article tout en continuant notre découverte des fonctions Lua pour EEP, nous allons monter progressivement en puissance et découvrir de nouveaux mots-clés du langage Lua.

Fonctions Lua pour EEP17.1

Fonctions relatives aux aiguillages

Les fonctions d'aiguillages agissent directement sur les appareils de voies. Pour construire un aiguillage, vous pouvez consulter les articles de l'éditeur des voies ferroviaires et Les appareils de voies dans EEP.

EEPSetSwitch()

Syntaxe

Paramètres

Valeur(s) retournée(s)

A partir de

Objectif

EEPSetSwitch(ID, Position, Callback)

Deux ou trois

Une

EEP 10.2 plug-in 2 / évolution avec EEP14.1 plug-in 1.

Actionne un aiguillage

Remarque

  • Le premier argument est une valeur numérique représentant l'ID de l'aiguillage.
  • Le deuxième argument est la position que l'on souhaite faire adopter à l'aiguille. Les valeurs à partir de 1 et suivants déplacent aussitôt l'aiguille dans la position correspondante. La valeur 0 déplace l’aiguille à le position suivante. Avec la valeur -1 l'aiguille revient sur sa position précédente.
  • Depuis EEP 14.1 un troisième argument facultatif avec la valeur 1 permet de lancer l'exécution de la fonction de rappel EEPOnSwitch_x(). Ce troisième paramètre n'a d'effet sur l'aiguillage que si celui-ci est enregistré et qu'une action EEPOnSwitch_x() a été définie. A utiliser avec précaution ! L'aiguillage doit être enregistré via la fonction EEPRegisterSwitch(). Une utilisation incorrecte de cette fonction peut entraîner des boucles infinies.
  • La valeur renvoyée est égale à 1 lorsque l'aiguillage ainsi que la position souhaitée existent ou égale à 0 si l'un des deux n'a pu être trouvé.

Exemple :

Cliquez sur le bouton à droite pour copier ce code dans le presse-papier

clearlog()

CONST_EMBRANCHEMENT = 2

function EEPMain()
    return 1
end

-- Bouge l'aiguille de l'aiguillage n° 1 sur la position n° 2.
-- Par défaut, il s'agit de la position Embranchement.
EEPSetSwitch(1, CONST_EMBRANCHEMENT)

Avant de passer en mode 3D pour exécuter le script, Quelques explications s'imposent !

Nous trouvons notre fonction EEPSetSwitch(1, CONST_EMBRANCHEMENT) à la ligne n° 12. Le premier argument est l'ID de l'aiguillage. Ce numéro est affiché dans la fenêtre des propriétés de la voie et aussi dans la fenêtre 2D en chiffre noir à côté d'un petit triangle vert. Il s'agit d'un numéro unique attribué automatiquement pour un aiguillage lors de la pose et il ne pourra jamais y avoir deux aiguillages avec le même numéro.

Important : Une section de voie utilisée comme aiguillage possède deux ID. Le premier ID est celui de la voie alors que le deuxième ID indique celui de l'aiguillage !
Image montrant la différence entre l'ID de la voie et l'aiguillage dans EEP
2 ID différents (voie et aiguillage)

L'ID de la section de voie est entouré en violet. Ce qui nous intéresse ici est l'ID de l'aiguillage entouré en rouge. Dans notre exemple il s'agit du n° 1. Dans la fenêtre 2D, le n° de l'aiguillage est affiché en noir à côté d'un petit triangle vert :

Image numéro aiguillage dans EEP

Comme vous le savez déjà, la pointe du triangle vert indique la position de l'aiguille.

Ici nous avons un aiguillage à deux directions :

  • Position 1 = Branche principale
  • Position 2 = Embranchement

Pour reprendre l'exemple de notre fonction EEPSetSwitch(1, CONST_EMBRANCHEMENT) à la ligne n° 12, le premier chiffre correspond à l'ID de l'aiguillage et le numéro 2 correspond à la position Embranchement dans la liste des positions de l'aiguillage. Ainsi lors de l'appel de cette fonction, l'aiguille bougera sur la position n° 2 quelque soit sa position antérieure.

Contrairement aux signaux où il était permis de commuter la position du signal via ses propriétés, bouger directement l'aiguille dans les propriétés de l'aiguillage n'est pas possible car cette opération s'effectue par l'intermédiaire d'un contact pour... aiguillage.

Pour créer et poser un contact pour aiguillage, regardez cette courte vidéo ci-dessous :

Une fois notre contact pour aiguillage posé, nous pouvons désormais accéder aux propriétés de l'aiguillage concerné pour la position de l'aiguille :

Propriétés contact aiguillage dans EEP
Aiguillage à deux positions

Dans cet exemple, nous retrouvons bien nos deux positions pour cet aiguillage :

  • Position 1 = Branche principale
  • Position 2 = Embranchement

Petite particularité : nous pouvons voir une troisième position intitulée Position suivante. Lorsque vous utilisez la fonction EEPSetSwitch, l'option Position suivante n'est pas la n° 3 mais la n° 0. Regardons le script ci-dessous et plus particulièrement les constantes :

Cliquez sur le bouton à droite pour copier ce code dans le presse-papier

clearlog()

CONST_POSITION_PRECEDENTE = -1
CONST_POSITION_SUIVANTE = 0
CONST_BRANCHE_PRINCIPALE = 1
CONST_EMBRANCHEMENT = 2

-- Sans effet
-- CONST_POSITION_SUIVANTE_PROPRIETE_CONTACT = 3


function EEPMain()
    return 1
end

function fnc_MaFonction()

	-- Bouger l'aiguille sur Branche principale
	EEPSetSwitch(1, CONST_BRANCHE_PRINCIPALE)

	print("Aiguillage commutée sur : ", CONST_BRANCHE_PRINCIPALE)

end

L'option Position suivante incluse dans le contact pour aiguillage peut être utilisée pour remplacer EEPSetSwitch avec son deuxième argument égal à 0.

Dans l'exemple du script ci-dessus, nous avons écrit notre propre fonction utilisateur fnc_MaFonction() pour commuter l'aiguillage sur Branche principale chaque fois qu'une locomotive franchira le contact. Pour rappel, afin d'éviter un message d'erreur, n'insérez pas les parenthèses après le nom de la fonction dans les propriétés du contact véhicule :

Nom de la fonction dans un contact pour véhicule
Pas de parenthèses après le nom

Question : Mais pourquoi utiliser un contact véhicule alors que nous travaillons avec un aiguillage ?

Réponse : Si vous utilisez un contact pour aiguillage et entrez le nom de la fonction fnc_MaFonction comme dans l'image ci-dessous :

Image contact Aiguillage dans EEP

... lorsque le véhicule franchira le contact, le script sera bien exécuté mais la position retenue et validée sera toujours celle de l'option sélectionnée dans la propriété Position du contact (ici entourée en rouge). Voilà pourquoi avec EEPSetSwitch, il faut appeler la fonction utilisateur fnc_MaFonction à partir d'un contact véhicule.

En fait, tous les contacts peuvent être utilisés pour appeler une fonction utilisateur sauf le contact pour aiguillage si celui-ci est lié avec l'aiguillage. En soi, cela n'est pas illogique vu que le contact pour aiguillage permet de modifier directement les positions sans avoir besoin de recourir à l'écriture d'une fonction Lua. Par exemple, si vous utilisez un contact pour Animation et entrez le nom de la fonction fnc_MaFonction, l'aiguille sera commutée sans problème.

Dans l'exemple du script ci-dessus à la ligne n° 20, la fonction EEPSetSwitch(1, CONST_BRANCHE_PRINCIPALE) a commuté la position de l'aiguille sur Branche principale, nous allons maintenant utiliser la constante CONST_POSITION_SUIVANTE = 0 pour commuter la position de l'aiguille par ordre croissant. Voici l'exemple en vidéo :

Dans tous les cas si vous voulez vérifier la bonne exécution de la fonction, il suffit de tester la valeur en retour comme le permet notre fonction EEPSetSwitch() et enregistrer le résultat dans une variable. Regardez le script ci-dessous :

Cliquez sur le bouton à droite pour copier ce code dans le presse-papier

clearlog()

-- Déclare une variable nulle car on ne connait pas encore le résultat de la fonction en retour
g_nbrResultat_Aiguillage = nil
-- Déclare la constante Embranchement
CONST_EMBRANCHEMENT = 2

function EEPMain()
    return 1
end

-- Exécute la fonction et récupère le résultat en retour pour l'aiguillage n° 1 et la position n° 2
g_nbrResultat_Aiguillage = EEPSetSwitch(1, CONST_EMBRANCHEMENT)

-- Affiche la valeur du résultat
print("La valeur retournée pour l'exécution de la fonction est : ", g_nbrResultat_Aiguillage )

Activons la fenêtre 3D pour exécuter le script et vérifier le résultat dans la fenêtre d'évènements :

Valeur retournée pour un signal dans EEP

La valeur renvoyée est égale à 1 lorsque l'aiguillage et la position souhaitée existent. Dans le cas contraire, la valeur serait égale à 0 si l'un des deux n'avait pas été trouvé.

Dans les deux cas, valeur retournée ou pas, l'une ou l'autre de ces deux lignes de code (n° 13 et 16) bougeront l'aiguille quoi qu'il arrive :

Cliquez sur le bouton à droite pour copier ce code dans le presse-papier

-- Déclare une variable nulle car on ne connait pas encore le résultat de la fonction en retour
g_nbrResultat_Aiguillage = nil
-- Déclare la constante Embranchement
CONST_EMBRANCHEMENT = 2

function EEPMain()
    return 1
end

-- Exécuter soit la ligne 13 ou 16 mais pas les deux en même temps
-- Exécute la fonction pour l'aiguillage n° 1 et la position n° 2
EEPSetSwitch(1, CONST_EMBRANCHEMENT)

-- Exécute la fonction et récupère la valeur en retour pour l'aiguillage n° 1 et la position n° 2
g_nbrResultat_Aiguillage = EEPSetSwitch(1, CONST_EMBRANCHEMENT)
Important : Récupérer une valeur en retour n’empêche pas l'exécution d'une fonction !

Nous aborderons le 3ème argument facultatif de notre fonction EEPSetSwitch(1, x, 1) dans le chapitre consacrée à la fonction EEPRegisterSwitch() car les deux sont liés.

EEPGetSwitch()

Syntaxe

Paramètres

Valeur(s) renvoyée(s)

A partir de

Objectif

EEPGetSwitch(ID)

Un

Une

EEP 10.2 plug-in 2

Renvoie le positionnement de l'aiguillage.

Remarque

  • L'unique argument est une valeur numérique représentant l'ID de l'aiguillage.
  • La valeur retournée correspond à la position de l'aiguille.
  • Le numéro renvoie à la ligne correspondante de la liste des différentes positions possibles pour l'aiguille. Lorsque l'aiguillage interrogée n'existe pas, alors la valeur 0 est renvoyée.
  • Attention : La fonction EEPGetSwitch() ne renseigne sur la nouvelle position définie par un EEPSetSwitch() qu'à partir du moment où l‘on se retrouve dans un nouveau cycle EEPMain()

Exemple :


-- Déclare une variable nulle car on ne connait pas encore le résultat de la fonction en retour
g_nbrPosition_Aiguille = nil

function EEPMain()
    return 1
end

g_nbrPosition_Aiguille = EEPGetSwitch(1)

-- Exemple avec un aiguillage à deux positions
if g_nbrPosition_Aiguille == 0 then -- Si g_nbrPosition_Aiguille est = à 0 alors
    print("L'aiguillage n'existe pas") 
  elseif g_nbrPosition_Aiguille == 1 then -- Sinon si g_nbrPosition_Aiguille est = à 1 alors
    print("L'aiguille est positionnée sur Branche principale") 
  elseif g_nbrPosition_Aiguille == 2 then -- Sinon si g_nbrPosition_Aiguille est = à 2 alors
    print("L'aiguille est positionnée sur Embranchement")
  else  -- Sinon g_nbrPosition_Aiguille est différent de 0, 1 ou 2 alors...
    print("La position est incorrecte ou cet aiguillage n'existe pas")
end  -- Fin du test

Dans le script, nous retrouvons les tests en utilisant les mots clés if - elseif - end que l'on peut traduire par : si - sinon si - fin. Le principe est simple : tester si une valeur correspond à une autre valeur. La comparaison s'effectue généralement entre une variable et un nombre, une valeur booléenne ou une chaine de caractère mais peut également comprendre le résultat d'une fonction. Si une comparaison s'avère exacte le test se termine immédiatement et toutes les autres comparaisons sont purement et simplement ignorées.

Dans notre exemple aux lignes 12, 14 et 16, nous comparons si la variable g_nbrPosition_Aiguille est égale à une valeur numérique. Dans Lua, la comparaison d'égalité s'écrit avec 2 signes =. Si vous écrivez if variable = nombre then avec un seul signe =, une erreur se produira à l'exécution du script. La ligne n° 18 offre une "porte de sortie" au cas où la valeur stockée dans g_nbrPosition_Aiguille serait différente de 0, 1 ou 2. Il s'agit de la condition par défaut si au moins une des autres conditions n'est pas remplie.

Nous allons reprendre notre exemple dérivé de la fonction EEPSetSwitch() avec la locomotive mais cette fois-ci nous allons ajouter la fonction EEPGetSwitch() pour récupérer le numéro de position et ainsi vérifier si EEPSetSwitch() fait bien son travail !

Ci-dessous le script en question :

Cliquez sur le bouton à droite pour copier ce code dans le presse-papier

clearlog()

-- On déclare une variable pour récupérer le numéro de la position de l'aiguille
g_nbrPosition_Aiguille = nil

-- On déclare une constante pour le 2ème argument de la fonction EEPSetSwitch()
CONST_POSITION_SUIVANTE = 0

function EEPMain()
    return 1
end

-- 1er contact
function fnc_ChangePosition()

  -- Commute la position de l'aiguillage n° 1 sur la position suivante
  EEPSetSwitch(1, CONST_POSITION_SUIVANTE)
	
  print("\nLa locomotive vient de passer sur le contact fnc_ChangePosition")

end

-- 2ème contact
function fnc_AffichePosition()

  -- Récupère la position de l'aiguillage n° 1 
  g_nbrPosition_Aiguille = EEPGetSwitch(1)

  -- Affiche la position de l'aiguillage n° 1
  print("L'aiguillage est commutée sur la position n° : ", g_nbrPosition_Aiguille)
	
end

Regardons tout ça en vidéo :

Notre aiguillage ne possède que 2 directions. Si nous avions eu un aiguillage à 3 directions avec une position supplémentaire intitulée 2ème branche, la position n° 3 lui serait attribuée.

Propriétés contact aiguillage à 3 directions dans EEP
Aiguillage à 3 directions possibles
EEPRegisterSwitch()

Syntaxe

Paramètres

Valeur(s) renvoyée(s)

A partir de

Objectif

EEPRegisterSwitch(ID)

Un

Une

EEP 10.2 plug-in 2

Enregistre un aiguillage pour la fonction de rappel EEPOnSwitch_x()

Remarque

  • L'enregistrement d'un aiguillage est obligatoire pour que celui-ci puisse exécuter automatiquement la fonction de rappel EEPOnSwitch_x() lors des opérations de commutation.
  • L'unique argument est une valeur numérique représentant l'ID de l'aiguillage.
  • La valeur retournée est égale à 1 si l'aiguillage existe ou 0 si l'aiguillage n'existe pas.

Exemple :


function EEPMain()
    return 1
end

-- On enregistre l'aiguillage n° 2 pour activer la fonction de rappel EEPOnSwitch_2
EEPRegisterSwitch(2)

fonction EEPOnSwitch_2(nouvelle_Position) 

  if nouvelle_Position == 1 then
      print("L'aiguillage n° 2 est définie sur Branche principale") 
    elseif nouvelle_Position == 2 then 
    print("L'aiguillage n° 2 est définie sur Embranchement")
  end
 
end

Expliquons simplement la logique de cette fonction liée avec les fonctions EEPOnSwitch_x() ou le troisième argument facultatif de la fonction EEPSetSwitch(). Supposons que nous voulons être informés du changement de position de l'aiguillage n° 2. Cette commutation peut s'effectuer à différents moments : soit au détours d'un script, soit une action de l'utilisateur directement sur la lanterne de l'aiguillage ou encore à partir d'une liaison avec un autre signal ou aiguillage. Comment le prévoir pour intervenir sur la position de l'aiguille afin de reprendre le contrôle à des fins de tests ou tout bonnement modifier la position ? C'est ici qu'intervient l'enregistrement de l'aiguillage avec la fonction EEPRegisterSwitch(2).

Avec cette fonction, nous demandons à EEP : "Tu enregistres l'aiguillage n° 2 et tu m'informes à chaque changement de position pour cet aiguillage". Une fois celui-ci enregistré, EEP va surveiller chaque changement de position et va déclencher en retour la fonction EEPOnSwitch_x() pour nous informer que la position vient d'être commutée. Vu qu'on lui demande, il nous informe !

Dans cette fonction EEPOnSwitch_x() nous allons pouvoir écrire du code pour agir sur l'aiguillage et aussi sur d'autres éléments selon les besoins des situations qui pourraient se présenter.

Pour le moment retenez seulement ceci : pour déclencher la fonction de rappel EEPOnSwitch_x(), vous avez besoin d'enregistrer l'aiguillage concerné avec la fonction EEPRegistrerSwitch(). Un exemple de cette fonctionnalité sera développé dans le chapitre suivant avec la fonction EEPOnSwitch_x().

EEPOnSwitch_x()

Syntaxe

Paramètres

Valeur(s) renvoyée(s)

A partir de

Objectif

EEPOnSwitch_x(Nouvelle_Position)

Un

Aucune

EEP 10.2 plug-in 2

Les aiguillages enregistrés appellent automatiquement cette fonction lorsque leur position change si l'aiguillage a été enregistré préalablement avec la fonction de rappel EEPRegisterSwitch(ID).
Dans le script, vous définissez et liez la fonction correspondante et déterminez ainsi ce qu'il faut faire lorsque la position de l'aiguille change.

Important : Afin d’appeler la fonction de rappel EEPOnSwitch_x() au sein de la fonction EEPSetSwitch() il est nécessaire de donner la valeur 1 au 3ème paramètre de la fonction EEPSetSwitch().

Remarque

  • Le nom de la fonction ne doit pas se terminer par _x mais par l'ID de l'aiguillage. Pour l'aiguillage 0002 par exemple, le nom correct de la fonction serait EEPOnSwitch_2().
  • Veuillez noter : Les zéros en tête doivent être omis dans le nom de la fonction !
  • L'argument entre parenthèses est la nouvelle position sous la forme d'un numéro. Ce numéro fait référence à la place qu'occupe cette nouvelle position dans la liste des propriétés de l'aiguillage. Utilisez une variable de votre choix afin de stocker cette valeur pour une utilisation ultérieure.
  • EEP n'attend aucune valeur en retour lors de l'utilisation de cette fonction.

Exemple :

Dans cet exemple, nous allons utiliser un aiguillage à trois directions avec les positions par défaut :

  1. Branche principale : le train prendra la voie du milieu,
  2. Embranchement : le train prendra la voie à gauche,
  3. 2ème branche : le train prendra la voie à droite.

Pour rappel (voir l'article sur Les appareils de voies dans EEP), la position Branche principale n'est pas réservée exclusivement dans le même plan continuité d'alignement de la voie avant l'aiguillage. Dans EEP, vous pouvez permuter les directions entre-elles. Un clic droit sur l'aiguillage ouvre un menu contextuel dans lequel toutes les combinaisons possibles peuvent être sélectionnées. Regardons l'image ci-dessous :

Image menu contextuel permutation aiguillage dans EEP

Mais dans notre exemple, nous allons nous en tenir aux positions par défaut énumérées ci-dessus.

L'idée est de faire entrer trois trains dans la gare chacun sur une voie. Pour réaliser ce scénario, plusieurs méthodes sont possibles. Nous allons en détailler quelques-unes afin de démontrer tout le potentiel offert par EEP dans l'automatisation des opérations de circulation. Dans tous les cas, la position de départ de l'aiguillage est commutée sur Branche principale.

Méthode n° 1 :

EEPOnSwitch (1er exemple)

Projet de démonstration avec la fonction EEPOnSwitch (1er exemple)

Cette première méthode fait appel à la fonction utilisateur fnc_ChangePosition enregistrée dans les trois contacts véhicules et commute l'aiguillage sur la position suivante lorsque le dernier wagon du train franchira le contact après l'aiguillage. Pour détecter la fin du train, nous allons activer la propriété Fin véhicule dans les trois contacts :

Image contacts véhicules dans fonction EEPOnSwitch()
Les trois contacts concernés
Image propriété Fin véhicule contact véhicule dans EEP
Propriété Fin véhicule activée

Lorsqu'un train entre dans la gare, le suivant démarre pour entrer à son tour sur la voie suivante et ainsi de suite. Pour le moment nous allons démarrer les trains séquentiellement via la fenêtre de contrôle, mais lorsque nous serons arrivés à un stade plus avancés dans les fonctions Lua, nous pourrons automatiser toutes ces opérations. Il nous restera juste à contempler le résultat sous nos yeux.

Cliquez sur le bouton à droite pour copier ce code dans le presse-papier

clearlog()

-- On déclare une variable pour créer des espaces sur le bord gauche de la
-- fenêtre d'évènements pour l'affichage des phrases
g_strEspace = string.rep ("    ", 3)

-- On déclare une constante pour le 2ème argument de la fonction EEPSetSwitch()
CONST_POSITION_SUIVANTE = 0

-- Constantes pour l'aiguillage
CONST_BRANCHE_PRINCIPALE = 1
CONST_EMBRANCHEMENT = 2
CONST_2EME_BRANCHE = 3

function EEPMain()
    return 1
end

-- On enregistre l'aiguillage n° 2
EEPRegisterSwitch(2)

-- Fonction appelée par un contact
function fnc_ChangePosition()

  -- Commute l'aiguillage n° 2 sur la position suivante
-- Le troisième argument prend la valeur 1 pour déclencher l'appel de la fonction EEPOnSwitch_2
  EEPSetSwitch(2, CONST_POSITION_SUIVANTE, 1)
	
  print("\n      Le dernier wagon vient de passer sur le contact Lua fnc_ChangePosition")

end

function EEPOnSwitch_2(new_Position) 

  print(g_strEspace.."la position de l'aiguille est le n° : ", new_Position)

  if new_Position == CONST_BRANCHE_PRINCIPALE then
      print(g_strEspace.."Le train suivant sera dirigé sur la voie du milieu (Branche principale)") 
    elseif new_Position == CONST_EMBRANCHEMENT then 
      print(g_strEspace.."Le train suivant sera dirigé sur la voie de gauche (Embranchement)")
    elseif new_Position == CONST_2EME_BRANCHE then 
      print(g_strEspace.."Le train suivant sera dirigé sur la voie de droite (2ème branche)")
  end
 
end

Avant de démarrer le projet procédons à l'analyse de ce script. La ligne n° 6 affiche la fonction Lua dénommée string.rep :

Image fonction lua string.rep dans fonction EEPOnSwitch

Cette fonction est constituée de deux arguments : le premier concerne la chaine de caractères à répéter et le second le nombre de fois pour la répétition. Dans notre exemple, la chaine avec des espaces sera répétée 3 fois. Lorsque le script va s'afficher dans la fenêtre d'évènements, un décalage vers la droite se produira ce qui nous permettra une mise en forme et une lecture plus aisée. Si par hasard vous voulez diminuer ou augmenter le retrait, il suffit juste de modifier le deuxième argument.

Les constantes sont déclarées aux lignes n° 9 à 14 :

Image des constantes dans fonction EEPOnSwitch

A la ligne n° 21 se trouve la fonction EEPRegisterSwitch(2) afin d'enregistrer l'aiguillage n° 2. La déclaration de cette fonction est impérative si on veut déclencher l'évènement EEPOnSwitch_2. Si vous n'enregistrez pas au préalable l'aiguillage et utilisez la fonction EEPSetSwitch avec le troisième argument égal à 1, EEP finira par rentrer dans une boucle infinie car il cherchera un aiguillage non enregistré.

A la ligne n° 24 se trouve la fonction fnc_ChangePosition() :

Image fonction utilisateur dans EEPOnSwitch

Elle fait appel à la fonction EEPSetSwitch(2, CONST_POSITION_SUIVANTE, 1) située à la ligne 28. Le troisième argument facultatif est égal à 1 et c'est lui qui déclenche l'appel à la fonction EEPOnSwitch_2. Si vous omettez cet argument, la fonction EEPOnSwitch_2 ne sera jamais appelée.

Ensuite vient la fonction EEPOnSwitch_2 appelée à chaque changement de position de l'aiguille :

Fonction Lua EEPOnSwitch

L'argument new_Position renvoie la position en cours de l'aiguillage. Nous retrouvons la variable g_strEspace dans les fonctions print. Les 2 points après la variable ajoute la phrase entre guillemets. Il s'agit de la concaténation de deux phrases pour en former qu'une seule. Si la syntaxe Lua l'avait autorisée, les 2 points auraient pu être remplacés par le signe +.

Les tests permettent d'afficher le numéro de la position courante de l'aiguillage. Pour rappel avec Lua, le test d'égalité s'écrit avec 2 signes == !

Lorsque le dernier wagon du train passe sur le contact, la fonction EEPOnSwitch_2 est appelée et commute l'aiguillage sur la position suivante. Ci-dessous, un aperçu de la fenêtre d'évènements une fois l'opération terminée :

Image fenêtre d'évènements fonction EEPOnSwitch

Comme la position de l'aiguille avant le démarrage du projet était Branche principale, il est logique de revenir sur cette position lorsque tous les trains sont entrés dans la gare.

Regardons en vidéo le déroulement de l'action :

Méthode n° 2 :

EEPOnSwitch (2ème exemple)

Projet de démonstration avec la fonction EEPOnSwitch (2ème exemple)

Nous allons maintenant employer une autre méthode pour arriver au même résultat sauf que les commutations des aiguilles seront activées avant le passage du train sur l'aiguillage.

Voici le script modifié en conséquence :

Cliquez sur le bouton à droite pour copier ce code dans le presse-papier

clearlog()

-- On déclare une variable pour créer des espaces sur le bord gauche de la
-- fenêtre d'évènements pour l'affichage des phrases
g_strEspace = string.rep ("    ", 3)

-- On déclare une constante pour le 2ème argument de la fonction EEPSetSwitch()
CONST_POSITION_SUIVANTE = 0

-- Constantes pour l'aiguillage
CONST_BRANCHE_PRINCIPALE = 1
CONST_EMBRANCHEMENT = 2
CONST_2EME_BRANCHE = 3

function EEPMain()
    return 1
end

-- On enregistre l'aiguillage n° 2
EEPRegisterSwitch(2)

-- Fonction appelée par un contact véhicule
function fnc_ChangePosition()

  -- Commute l'aiguillage n° 2 sur la position suivante
-- Le troisième argument prend la valeur 1 pour déclencher l'appel de la fonction EEPOnSwitch_2
  EEPSetSwitch(2, CONST_POSITION_SUIVANTE, 1)
	
  print("\n      Le train vient de passer sur le contact Lua fnc_ChangePosition")

end

function EEPOnSwitch_2(new_Position) 

  print(g_strEspace.."la position de l'aiguille est le n° : ", new_Position)

  if new_Position == CONST_BRANCHE_PRINCIPALE then
      print(g_strEspace.."Le train va entrer sur la voie du milieu (Branche principale)") 
    elseif new_Position == CONST_EMBRANCHEMENT then 
      print(g_strEspace.."Le train va entrer sur la voie de gauche (Embranchement)")
    elseif new_Position == CONST_2EME_BRANCHE then 
      print(g_strEspace.."Le train va entrer sur la voie de droite (2ème branche)")
  end
 
end

Un seul contact véhicule est placé devant l'aiguillage et fait appel à la fonction utilisateur fnc_ChangePosition :

Image contact véhicules dans fonction EEPOnSwitch()
Un seul contact
Image propriété Fin véhicule contact véhicule dans EEP
Propriété Fin véhicule désactivée

Avant de démarrer le projet procédons à l'analyse du déroulement des opérations :

  1. L'aiguillage est défini par défaut sur Branche principale (position n° 1),
  2. Le premier train va appeler la fonction fnc_ChangePosition avant de franchir l'aiguillage pour commuter l'aiguille sur la position suivante et la position suivante dans la liste est Embranchement (position n° 2) sur la voie de gauche, ce qui explique pourquoi le premier train va aller à gauche contrairement au premier exemple ou le train est entré sur la voie du milieu,
  3. Le deuxième train va commuter l'aiguillage sur la position suivante 2ème branche (position n° 3) et entrer sur la voie de droite,
  4. Le troisième et dernier train va commuter l'aiguillage sur Branche principale pour revenir au début sur la position n° 1,
  5. Le cycle est terminé et nous pouvons remarquer le résultat de la séquence identique à la première méthode, seul l'emplacement des trains sur les voies est modifié.

Ci-dessous, un aperçu de la fenêtre d'évènements une fois l'opération terminée.

Image fenêtre d'évènements fonction EEPOnSwitch

Comme la position de l'aiguille avant le démarrage du projet était Branche principale, il est logique de revenir sur cette position lorsque tous les trains sont entrés dans la gare.

Regardons en vidéo le déroulement de l'action :

Cette deuxième méthode à l'avantage d'utiliser un seul contact au lieu des trois dans la première, ce qui fait moins de contacts à gérer pour EEP et aussi pour vous ! La première méthode est à privilégier si vous avez des opérations à effectuer après le franchissement de l'aiguillage. A l'inverse, retenez la deuxième méthode si vous avez des opérations à effectuer avant le franchissement de l'aiguillage.

Méthodes n° 3 et 4:

Deux autres méthodes sont également disponibles mais font appel exclusivement à des contacts ce qui dépasse le cadre de cet article. Si vous voulez les découvrir et en savoir plus, reportez vous au chapitre des contacts pour aiguillages.

Fonctions d'enregistrement

EEP possède deux fonctions spécifiquement dédiées pour l'enregistrement et la restitution de données. Pendant l'exploitation d'un réseau, il peut être nécessaire d'enregistrer des valeurs temporairement pour les récupérer ultérieurement selon les besoins du script. Bien-sûr, nous pourrions enregistrer toutes ces valeurs dans des variables ou des tables mais cela alourdirait inutilement le script.

Vu qu'EEP offre la possibilité d'enregistrer et de récupérer des données via les fonctions EEPSaveData et EEPSaveData, nous allons les utiliser.

EEPSaveData()

Syntaxe

Paramètres

Valeur(s) renvoyée(s)

A partir de

Objectif

EEPSaveData(Emplacement, Nombre|"Chaine"|Booléan|nil)

Deux

Une

EEP 11

Enregistre quelque chose dans un emplacement de stockage spécifique. Est automatiquement enregistré avec le projet. Les données enregistrées sont stockées dans le fichier Lua de l'utilisateur (*.lua), mais ne sont pas visibles dans l'éditeur Lua. Cette fonction évite la perte de données lorsque le script est rechargé.

Remarque

  • Cette fonction fournit un mécanisme permettant au code Lua de stocker les données dans des emplacements numérotés de 1 à 1000.
  • Chaque emplacement peut contenir une valeur numérique, chaîne ou booléenne mais pas de table. Les chaînes de caractères ne doivent pas contenir de caractères de formatage (guillemets par exemple).
  • Le premier argument est le numéro de l'emplacement de stockage.
  • Le deuxième argument concerne les données à enregistrer. Effacez le contenu de l'emplacement en lui attribuant la valeur nil.
  • La valeur retournée est vraie (true) lorsque l'enregistrement s'est déroulé avec succès (c'est-à-dire l'emplacement cible trouvé), sinon la valeur retournée est fausse (false).
  • Chaque emplacement est écrit en utilisant la fonction EEPSaveData() et lu grâce à la fonction EEPLoadData(). La valeur actuelle de chaque emplacement sera également sauvegardée dans le script Lua lors de la sauvegarde du projet. Ceci maintiendra la synchronisation entre la disposition du projet et les données.
  • La section du script contenant ces données n'est pas visible dans l'éditeur Lua d'EEP.

Exemple :

Pour illustrer les fonctions EEPSaveData() et EEPLoadData(), nous allons utiliser le projet EURO_Vmax_v7.anl3 fourni en standard avec EEP. Vous le trouverez dans le dossier \TREND\EEP17\Resourcen\Anlagen. Ce projet regorge de signaux, d'aiguillages... Bref, de quoi enregistrer pas mal de données ! Ce terrain de jeu sera parfait pour notre exemple et pour étendre la diversité des données possibles à sauvegarder, nous allons ajouter notre petite touche personnelle !

Avant de démarrer, sauvegardez le projet original dans un autre dossier ou un support amovible de votre choix car nous allons apporter des modifications et ajouter notre propre script.

Maintenant si ce n'est pas déjà fait, ouvrez EEP et chargez le projet. Une fois le chargement terminé, ouvrons la fenêtre de l'éditeur Lua et regardons le contenu du script :

Image fonction Lua d'origine EURO_Vmax_V7 dans EEP
Script Lua d'origine du projet EURO_Vmax_V7

Comme nous pouvons le constater le script est vierge de toute fonction (à part la fonction EEPMain). Ce projet fait donc appel à des automatismes basés à 100 % sur des contacts. Nous allons déjà commencer à épurer le script comme ceci et partir d'une nouvelle base :

Cliquez sur le bouton à droite pour copier ce code dans le presse-papier
clearlog()

function EEPMain()
    return 1
end

Maintenant, nous allons rechercher dans ce projet des éléments susceptibles d'être enregistrés comme des signaux par exemple. Pour enregistrer des données, nous pourrions nous tourner vers les fonctions Get car ce sont elles qui retournent des valeurs mais fouiller dans le projet pour rechercher des modèles précis afin de récupérer les numéros ID pour les passer ensuite en paramètres dans les fonctions Get peut être assez long et fastidieux.

Non, l'idéal serait de pouvoir récupérer tous les numéros ID des signaux et des aiguillages pour les enregistrer directement via la fonction EEPSaveData().

Pour les utilisateurs EEP17 uniquement (pour les versions antérieures, patientez un peu !), une nouvelle fonctionnalité a fait son apparition et s'appelle Modèles utilisés. Vous la trouverez dans le menu Outils :

Image menu Outils modèles utilisés dans EEP

Une fois affichée, la fenêtre des modèles utilisés liste tous les objets présents dans un projet :

Image liste des modèles utilisés dans EEP17

Et cerise sur le gâteau, la liste peut être exportée dans un fichier au format CSV pour être affichée dans un tableur. Après avoir cliqué sur le bouton Générer CSV, un fichier nommé EURO_Vmax_v7.csv sera créé dans le dossier du projet. Dans notre exemple il s'agit de \TREND\EEP17\Resourcen\Anlagen.

A la lecture de ce fichier et malgré l’exhaustivité des informations contenues à l'intérieur, il n'est malheureusement pas encore possible pour le moment de récupérer les n° ID des signaux et des aiguillages. Alors comment faire ?

Si EEP nous offre pas la possibilité de récupérer nos informations alors nous allons le faire nous-même directement avec Lua.

Idées du script :

Si vous n'êtes pas familiarisés avec Lua, ce qui va suivre peut vous paraître complexe mais rassurez-vous, je vais faire le maximum pour rester le plus simple possible.

Avant de commencer la saisie du code, analysons nos besoins pour ce script :

  1. Ouvrir le fichier du projet et "fouiller" dans le code pour trouver tous les signaux et tous les aiguillages,
  2. Récupérer les noms, id's et positions de tous les signaux et aiguillages inclus dans le projet,
  3. Une fois les informations trouvées, les stocker temporairement dans des tables,
  4. Et pour terminer, enregistrer ces données via la fonction EEPSaveData().
Note : Au point n° 3, nous allons découvrir les tables. Pour faire simple, une table n'est ni plus ni moins qu'un espace pour regrouper plusieurs variables.
Ecriture du script pas-à-pas :

Nous allons commencer à partir de notre script de base :

Script de base
clearlog()

function EEPMain()
    return 1
end

Maintenant la première chose à faire est de déclarer nos variables globales :

Déclaration des variables globales
clearlog()

-- Variables pour les deux tables (signalisation et aiguillages)
g_tblSignal = {}
g_tblSwitch = {}

-- Variables numériques pour compter le nombre d'éléments dans chaque classe d'objets
g_nbrSignal = 0
g_nbrSwitch = 0

function EEPMain()
	return 1
end

Les déclarations des variables pour les tables se situent aux lignes 4 et 5. Pour déclarer une table, il faut utiliser les deux accolades {} pour indiquer à Lua qu'il s'agit bien d'une table dans laquelle on pourra stocker tout type de valeur.

Ensuite nous trouvons deux variables numériques pour compter le nombre de signaux et d'aiguillages. Tant qu'à faire, vu que nous allons analyser le fichier entier, autant connaître le nombre total d'objets pour ces deux classes. Ces deux variables ne sont pas obligatoires mais existent uniquement pas souci d'apprentissage. Vous verrez que l'on peut obtenir ces valeurs autrement.

Nos variables globales déclarées, rentrons dans le vif du sujet. Notre premier travail consiste à ouvrir le fichier projet. Le nom d'un fichier projet EEP se termine par l'extension .anl3. Pour se faire, nous allons créer la fonction fnc_Lire_Fichier() qui va s'occuper d'ouvrir le fichier concerné. Cette fonction est appelée tout à la fin du script par ce petit bout de code :

Appel de la fonction fnc_Lire_Fichier()

-- Point de démarrage du script
-- Appel de la fonction fnc_Lire_Fichier()
fnc_Lire_Fichier()
Fonction fnc_Lire_Fichier() pour ouvrir le fichier projet EEP
-- Cette fonction ouvre le fichier projet et appelle les fonctions
-- pour scanner les signaux et les aiguillages
function fnc_Lire_Fichier()

	-- Variable pour stocker l'objet fichier
	local l_file = ""	
	-- Variable pour stocker le texte du fichier
	local l_strTexte = ""	
	-- Variable la plus courte possible pour les boucles
	local i = nil

	-- Ouvre le fichier en lecture seule par mesure de sécurité
	l_file = io.open("D:\\TREND\\EEP17\\Resourcen\\Anlagen\\EURO_Vmax_v7.anl3", "r")

	-- Récupère le texte du fichier
	l_strTexte = l_file:read("*a")
	
	-- Fonction pour scanner les signaux
	fnc_Extraire_Signaux(l_strTexte)
	-- Fonction pour scanner les aiguillages
	fnc_Extraire_Switch(l_strTexte)

	-- Affiche le nombre total de signalisation et d'aiguillages
	print("\nNombre total de signalisation : ", g_nbrSignal)
	print("Nombre total d'aiguillages : ", g_nbrSwitch)

	-- Code désactivé pour le moment
	--[[
		
		print("nombre de ligne table : ", #g_tblSwitch, "\n")
		print("nombre de ligne table : ", #g_tblSignal, "\n")

		for i = 1, #g_tblSwitch do
 
			print("Table Nom du modèle : ", g_tblSwitch[i][1])
			print("Table ID du modèle : ", g_tblSwitch[i][2])
			print("Table Position de l'aiguillage : ", g_tblSwitch[i][3], "\n")

		end
	]]--
	
	-- Fonction pour préparer et enregistrer les données
	-- via la fonction EEPSaveData()
	fnc_Enregistre_Info()

end

Dans le cadre de cet article, nous ne nous attarderons pas sur les deux fonctions du langage Lua io.open("Chemin_Complet_Du_Fichier) et l_file:read("*a") aux lignes n° 13 et 16. Vous pouvez vous reporter à la documentation Lua disponible sur internet pour en savoir plus sur ces fonctions.

Ce qui nous intéresse ici est la déclaration des deux variables locales l_file et l_strTexte aux lignes n° 6 et 8. La première variable récupère l'objet fichier alors que la seconde récupère tout le texte du fichier projet. C'est cette variable que nous allons passer en argument dans les deux fonctions fnc_Extraire_Signaux(l_strTexte) et fnc_Extraire_Switch(l_strTexte) pour scanner d'une part les signaux et d'autre part les aiguillages.

Commençons par l'appel à la ligne n° 19 de la fonction fnc_Extraire_Signaux(l_strTexte) avec comme argument entre parenthèses le texte du fichier projet contenu dans la variable l_strTexte :

Dans cette fonction, les lignes n° 6 à 15 contiennent la déclaration des variables locales. Nous en avons besoin pour extraire l'endroit où sont enregistrées les signalisations dont le nom, l'ID et la position de la signalisation. Les deux dernières variables l_Debut et l_Fin sont utilisées comme délimiteurs pour récupérer le début et la fin de la position de la phrase concernant la signalisation dans le fichier :

Extrait de la fonction fnc_Extraire_Signaux(arg_Code) pour scanner toutes les signalisations du projet
-- Cette fonction récupère le nom, l'id et la position des signaux
function fnc_Extraire_Signaux(arg_Code)

	-- Déclaration des variables locales
	-- Variable pour les données partielles extraites du fichier projet
	local l_strExtrait = ""
	-- Variable pour récupérer le nom de la signalisation
	local l_Name = ""
	-- Variable pour récupérer le n° ID de la signalisation
	local l_Id = ""
	-- Variable pour récupérer la position courante de la signalisation
	local l_Position = nil
	-- Variables pour délimiter le début et la fin des données partielles extraites
	local l_Debut = 0
	local l_fin = 0

Pour comprendre, voici à quoi ressemble une phrase partielle pour une signalisation dans un fichier projet EEP :

Phrase contenant une signalisation dans un fichier projet EEP
<Meldung Position="4190" ParaOderAnti="1" fireOnPara="1" fireOnAnti="0" clsid="D180F669-DCCC-42E9-960C-B5305B29F69D" name="BS1_SBB_Korb_506_li_6" Key_Id="1" Hofs="300" Vofs="300" Show="1" LockEd="0">
<KontaktZiel>0</KontaktZiel>
<Signal stellung="1" wirkungsdistanz="1000" StopAt="0" Geschwindigkeit="0" Delay="0" ActDelay="0"/>
</Meldung>

Par rapport à notre recherche, nous avons ici toutes les informations dont nous avons besoin à savoir :

  1. Le nom de la signalisation avec le mot clé : name
  2. L'ID de la signalisation avec le mot clé : Key_Id
  3. Et la position de la signalisation avec le mot clé : Signal stellung

Une fois les mots clés repérés, rien de plus facile pour isoler ces informations contenues dans la phrase. Mais avant, nous devons délimiter le début et la fin de la phrase pour l'extraire à partir du texte entier du fichier projet. Pour rappel ce texte est fourni par l'argument arg_Code (ligne n° 2).

Extrait de la fonction fnc_Extraire_Signaux(arg_Code) pour scanner toutes les signalisations du projet

	print("\n #   Signaux   #\n")

	-- Boucle tant que la condition est vraie
	while true do

		-- Trouve le début de la position dans le fichier de la
		-- phrase concernant une signalisation
		l_Debut = string.find(arg_Code, "<Meldung", 0)
		-- Trouve la fin de la position dans le fichier de la
		-- phrase concernant une signalisation
		l_fin = string.find(arg_Code, "</Meldung>", l_Debut)
		
		-- print ("l_Debut : ", l_Debut, "   Fin : ", l_fin )

		-- Si plus aucune phrase concernant une signalisation alors
		-- on sort de la boucle while
		if l_Debut == nil then
			break
		end
		
		-- Extraire la phrase partielle concernant une signalisation quelconque
		l_strExtrait = string.sub(arg_Code, l_Debut, l_fin)

		-- print("variable l_strExtrait : ", l_strExtrait)

La variable l_Debut à la ligne n° 24 enregistre la position du début de la phrase commençant par le mot clé <Meldung et la variable l_Fin à la ligne n° 27 enregistre la position fin de la phrase se terminant par </Meldung>. Le 3ème argument de la fonction Lua string.find indique le début de la position où chercher en l'occurrence au numéro 0, c'est-à-dire au début du texte entier. Ensuite on teste (ligne n° 33) la validité de la variable l_Debut car si elle retourne la valeur nil cela signifie que toutes les phrases concernant les signalisations ont été analysées et par conséquent il nous faut absolument une porte de sortie pour terminer la boucle. Cette porte de sortie est possible avec le mot clé break sinon le programme rentrerait dans une boucle infinie.

Ensuite il nous reste à extraire et isoler la phrase concernant la signalisation trouvée et la stocker dans la variable l_strExtrait. C'est le rôle du code situé à la ligne n° 38. L'instruction Lua string.sub extrait le texte du projet compris entre les positions du début et de fin. Ce qui revient à extraire la phrase partielle contenant une signalisation dans un fichier projet EEP comme indiqué ci-dessus.

Maintenant une fois la phrase de la signalisation isolée, il suffit de rechercher les valeurs des mots clés qui nous intéressent. Expliquer en profondeur ces lignes de codes ci-dessous (lignes n° 43, 47 et 51) dépasserait le cadre de cet article. Les trois fonctions string.find ont trois arguments :

  1. La phrase extraite précédemment contenue dans la variable l_strExtrait,
  2. Les mots clés suivis d'un motif (pattern). Un pattern est un bout de code utilisé dans les expressions régulières ou Regex.
  3. La position ou commencer la recherche dans la phrase (par défaut = 0 pour rechercher à partir du début).

Les expressions régulières sont composées de motifs qui permettent de faire des recherches sur des données. Ici dans notre cas nous les utilisons pour extraire le nom complet, les numéros ID et la position du signal en question :

Extrait de la fonction fnc_Extraire_Signaux(arg_Code) pour scanner toutes les signalisations du projet
		
		-- Récupère le nom de la signalisation
		_, _, l_Name = string.find(l_strExtrait, "name=\"(.[^\"]+)", 0)
		print("Nom du signal : ", l_Name)

		-- Récupère le n° ID
		_, _, l_Id = string.find(l_strExtrait, "Key_Id=\"(%d+)", 0)
		print("id : ", l_Id)

		-- Récupère la position de la signalisation
		_, _, l_Position = string.find(l_strExtrait, "Signal stellung=\"(%d+)", 0)
		print("Position du signal : ", l_Position, "\n")
Note : Dans un autre article dédié pour ce script, nous reviendrons en profondeur sur les pattern dans Lua en apportant des améliorations comme la sauvegarde des données dans un fichier csv et bien d'autres choses encore ! En attendant si vous voulez en savoir plus sur les expressions régulières, vous pouvez trouver des ressources traitant ce sujet sur internet. Entrez expression régulière ou regex dans un moteur de recherche.

Les trois variables l_Name, l_Id, l_Position récupèrent respectivement le nom, l'Id et la position de la signalisation. La position fait référence ici à celle commutée par exemple sur Arrêt ou Voie libre et non pas à la position physique sur le plan.

Une fois les trois variables renseignées, nous pouvons supprimer la phrase partielle du texte complet du projet dans l'argument arg_Code via la fonction Lua string.sub située à la ligne n° 56. Cette fonction contient deux arguments :

  1. La phrase du texte complet du projet enregistrée dans l'argument arg_Code
  2. La position fin de la phrase partielle

La fonction string.sub fonctionne comme ceci :

  1. Dans un premier temps elle exécute toute la partie située à droite du signe = pour couper le texte complet du projet contenu dans l'argument arg_Code à partir de la position fin de la phrase partielle,
  2. Dans un deuxième temps, elle affecte le nouveau texte complet dans l'argument arg_Code amputé ainsi de la phrase partielle de l'ancienne signalisation.

Pour faire simple, imaginez le texte complet d'un projet EEP avant analyse d'une longueur de 10000 caractères et la phrase partielle extraite concernant la signalisation d'une longueur 500 caractères. Après utilisation de la fonction string.sub, le résultat serait un nouveau texte d'une longueur de 9500 caractères.

A la ligne n° 59 nous incrémentons de +1 le nombre total de signalisations du projet.

Fin de la fonction fnc_Extraire_Signaux(arg_Code) pour scanner toutes les signalisations du projet
	
		-- La signalisation vient d'être analysée alors on peut
		-- supprimer la phrase partielle de l'argument arg_Code.
		arg_Code = string.sub(arg_Code, l_fin)		

		-- Un signal de plus à comptabiliser
		g_nbrSignal = g_nbrSignal + 1

		-- Stocker dans la table les trois valeurs : Nom, ID, Position
		g_tblSignal[g_nbrSignal] = {l_Name, l_Id, l_Position}	

	end	-- end de la boucle while

end	-- end fin de la fonction

A la ligne n° 62 nous voici avec notre table g_tblSignal. Comment fonctionne une table ? Très rapidement, il existe deux types de tables avec Lua :

  1. La table avec une clé numérique comme c'est le cas ici avec la variable g_nbrSignal placée entre crochets,
  2. La table avec une clé basée sur une valeur (nous y reviendrons dans d'autres articles).

Pour comprendre comment fonctionne notre table, il suffit de se représenter un tableau avec des lignes et des colonnes. Les lignes sont formées par la clé numérique (ici notre variable g_nbrSignal) et les colonnes par nos trois variables l_Name, l_Id, l_Position. Si nous devions les afficher dans un tableau, il ressemblerait à ceci :

Image table Lua fonction EEPSaveData()

Comme la variable g_nbrSignal est incrémentée d'une unité à chaque itération de la boucle while, elle est parfaite pour créer autant de lignes qu'il y a de signalisations. Pour résumer voici la syntaxe pour notre table :

  1. A gauche du signe =, on créé une nouvelle ligne avec la variable g_nbrSignal placée entre crochets comme ceci g_tblSignal[g_nbrSignal ],
  2. A droite du signe =, on place les trois variables l_Name, l_Id, l_Position entre accolades comme ceci : {l_Name, l_Id, l_Position}.
  3. Une seule ligne de code ajoute une ligne et trois variables en une seule opération !

Ici nous avons une table à trois dimensions car chaque variable correspond à une dimension.

Si nous avions écrit g_tblSignal[g_nbrSignal] = 0 ou g_tblSignal[g_nbrSignal] = "une phrase quelconque", on parlerait d'une table à une dimension.

Si nous passons en mode 3D voici les informations affichées dans la fenêtre d'évènements :

Image affichage script signalisation EEP

Pour l'exemple la variable l_strExtrait est affichée (ligne n° 40 dans la fonction fnc_Extraire_Signaux), nous pouvons déjà voir 2 des 3 mots clés recherchés à savoir le nom = name et l'id = Key_Id, le mot clé pour la position est Signal stellung mais n'apparait pas dans cette capture d'écran.

Sous cette phrase nous avons bien le nom du signal, l'id et la position du signal. Déjà évoquée plus haut, la position fait référence ici à celle commutée sur Arrêt ou Voie libre et non pas à la position physique du signal sur le plan.

Parallèlement à l'affichage, toutes ces informations sont bien sûr enregistrées dans la table g_tblSignal. Nous verrons après comment les récupérer pour les afficher dans la fenêtre d'évènements.

Vous remarquerez tout en bas de la fenêtre deux lignes totalisant le nombre de signalisations et d'aiguillages (pour le moment ce dernier est égal à 0). Concernant le nombre de signalisations, nous avons deux possibilités pour connaitre le total. Retournons un instant dans la fonction fnc_Lire_Fichier() :

Extrait de la fonction fnc_Lire_Fichier() pour ouvrir le fichier projet EEP

	-- Affiche le nombre total de signalisation et d'aiguillages
	print("\nNombre total de signalisation : ", g_nbrSignal)
	print("Nombre total d'aiguillages : ", g_nbrSwitch)

	-- Code désactivé pour le moment
	--[[
		
		print("Nombre de ligne table : ", #g_tblSwitch, "\n")
		print("Nombre de ligne table : ", #g_tblSignal, "\n")

		for i = 1, #g_tblSwitch do
 
			print("Table Nom du modèle : ", g_tblSwitch[i][1])
			print("Table ID du modèle : ", g_tblSwitch[i][2])
			print("Table Position de l'aiguillage : ", g_tblSwitch[i][3], "\n")

		end
	]]--

end  -- end fin de la fonction
  1. La ligne n° 24 affiche simplement la valeur de la variable g_nbrSignal, car cette variable est incrémentée de +1 à chaque itération de la boucle while,
  2. On peut aussi obtenir le même résultat en interrogeant le nombre de ligne dans la table g_tblSignal. Pour obtenir ce résultat, il faut juste utiliser la syntaxe suivante : #g_tblSignal (ligne n° 31). Pour les besoins de l'article, ce code est temporairement désactivé.

Nous allons maintenant nous intéresser à la fonction fnc_Extraire_Switch(l_strTexte) appelée à la ligne n° 21 de la fonction fnc_Lire_Fichier() avec toujours comme argument entre parenthèses le texte du fichier projet contenu dans la variable l_strTexte :

Extrait de la fonction fnc_Lire_Fichier() pour ouvrir le fichier projet EEP

	-- Fonction pour scanner les signaux
	fnc_Extraire_Signaux(l_strTexte)
	-- Fonction pour scanner les aiguillages
	fnc_Extraire_Switch(l_strTexte)

Cette fonction est basée exactement sur le même principe des signalisations. Seuls changent les noms des variables et les patterns. Les mots-clés à rechercher pour les aiguillages sont les suivants :

  1. Le nom de l'aiguillage avec le mot clé : gsbname
  2. L'ID de l'aiguillage avec le mot clé : Key_Id
  3. Et la position de l'aiguillage avec le mot clé : weichenstellung

Ci-dessous la fonction dans son intégralité :

Fonction fnc_Extraire_Switch(arg_Code) pour scanner tous les aiguillages du projet

-- Cette fonction récupère le nom, l'id et la position des aiguillages
function fnc_Extraire_Switch(arg_Code)

	-- Déclaration des variables locales
	local l_strExtrait = ""
	local l_Name = ""
	local l_Id = ""
	local l_Position = nil
	local l_Debut = 0
	local l_fin = 0

	print("\n #   Aiguillages   #\n")

	-- Boucle tant que la condition est vraie
	while true do

		l_Debut = string.find(arg_Code, "weichenstellung=", 0)
		l_fin = string.find(arg_Code, "LockEd=", l_Debut)

		if l_Debut == nil then
			break
		end
		
		-- print ("l_Debut : ", l_Debut, "   Fin : ", l_fin )

		l_strExtrait = string.sub(arg_Code, l_Debut, l_fin)

		-- print("variable l_strExtrait : ", l_strExtrait)

		_, _, l_Name = string.find(l_strExtrait, "gsbname=\"(.[^\"]+)", 0)
		print("Nom de l'aiguillage : ", l_Name)

		_, _, l_Id = string.find(l_strExtrait, "Key_Id=\"(%d+)", 0)
		print("id : ", l_Id)

		_, _, l_Position = string.find(l_strExtrait, "weichenstellung=\"(%d+)", 0)
		print("Numéro de la position : ", l_Position, "\n")

		arg_Code = string.sub(arg_Code, l_fin)

		g_nbrSwitch = g_nbrSwitch + 1		

		g_tblSwitch[g_nbrSwitch] = {l_Name, l_Id, l_Position}	

	end	-- end de la boucle while

end	-- end fin de la fonction

Comme vous pouvez le constater, rien de bien compliqué. Les deux fonctions pour les signalisations et les aiguillages reprennent exactement la même structure.

Après l'exécution de ces deux fonctions, nous allons maintenant vérifier si nos tables ont bien enregistrées toutes les informations capturées. Pour ce faire, retournez dans la fonction fnc_Lire_Fichier() :

Extrait de la fonction fnc_Lire_Fichier() pour ouvrir le fichier projet EEP
	
	-- Code désactivé pour le moment
	--[[	
	print("Nombre de ligne table : ", #g_tblSwitch, "\n")
	print("Nombre de ligne table : ", #g_tblSignal, "\n")

	for i = 1, #g_tblSwitch do
 
		print("Table Nom du modèle : ", g_tblSwitch[i][1])
		print("Table ID du modèle : ", g_tblSwitch[i][2])
		print("Table Position de l'aiguillage : ", g_tblSwitch[i][3], "\n")

	end
	]]

Avant de démarrer le script, il faut retirer les marques des commentaires aux lignes n° 28 et 39. Ainsi les lignes n° 29 à 38 deviendront opérationnelles.

La limitation d'affichage imposée à la fenêtre d'évènements nous permet d'afficher qu'une seule table à la fois. Une partie des informations sera toutefois tronquée à l'affichage due à cette limitation. Nous allons afficher uniquement la table g_tblSwitch des aiguillages. Bien entendu le principe reste strictement le même pour la table des signaux.

Tout d'abord revenons un instant sur le nombre total des aiguillages. Ici nous allons interroger directement la table avec la syntaxe #g_tblSwitch et l'afficher avec une fonction print (voir ligne n° 29). Après nous allons boucler sur la table à partir de la première ligne jusqu'à la dernière qui correspond forcément à la valeur contenue dans #g_tblSwitch.

La boucle commence à la ligne n° 32 et va de 1 au nombre total de lignes. A chaque itération, nous allons rechercher les informations Nom du modèle, ID du modèle et la position de l'aiguillage. Comment retrouver ces informations ? C'est très simple, il suffit d'indiquer l'ordre dans lequel ces variables ont été placées pour l'enregistrement à la ligne n° 44 dans la fonction fnc_Extraire_Switch(arg_Code) :

Extrait de la fonction fnc_Extraire_Switch(arg_Code) pour scanner tous les aiguillages du projet

		g_tblSwitch[g_nbrSwitch] = {l_Name, l_Id, l_Position}	

Le nom de l'aiguillage est en premier, l'ID en deuxième et la position de l'aiguillage en troisième. Analysons notre boucle à partir de la ligne n° 32 :

Extrait de la fonction fnc_Lire_Fichier() pour ouvrir le fichier projet EEP
	
	for i = 1, #g_tblSwitch do
 
		print("Table Nom du modèle : ", g_tblSwitch[i][1])
		print("Table ID du modèle : ", g_tblSwitch[i][2])
		print("Table Position de l'aiguillage : ", g_tblSwitch[i][3], "\n")

	end
	

Les lignes n° 34 à 36 affichent les informations. La syntaxe est la suivante :

  1. La variable i contient le numéro de ligne (de 1 à n...),
  2. Avant tout il faut se positionner sur la ligne correspondante comme ceci : g_tblSwitch[i],
  3. Ensuite pour aller chercher les informations, il suffit d'indiquer à la suite le numéro d'ordre utilisé lors de l'enregistrement comme ceci : g_tblSwitch[i][1] pour le nom de l'aiguillage, g_tblSwitch[i][2] pour l'ID de l'aiguillage et g_tblSwitch[i][3] pour la position de l'aiguillage.

Les numéros entre crochets se nomment indices. Ainsi à l'indice n° 2, nous récupérons l'ID de l'aiguillage.

Et une fois que i est égal à #g_tblSwitch, la boucle s'arrête et l'exécution passe à la ligne suivante. Dans notre exemple, c'est la fin de notre fonction.

Regardons l'affichage dans la fenêtre d'évènements :

Image affichage script table des aiguillages dans EEP

Nous retrouvons bien le nom de l'aiguillage, l'id et la position de l'aiguillage.

Bon tout ceci est bien beau mais lorsque nous fermerons le projet, toutes ces informations seront perdues ce qui serait un peu dommage. Heureusement, c'est ici qu'intervient notre fonction EEPSaveData().

Rappelez-vous, cette fonction ne peut contenir que 1000 enregistrements maximum et n'accepte pas les lignes des tables mais uniquement une valeur unique par enregistrement. Qu'importe, nous allons ruser avec une astuce pour faire entrer toutes nos informations.

Alors de quoi avons-nous besoin tout de suite ?

  1. Un emplacement pour enregistrer nos informations,
  2. Nos informations contenues dans les tables g_tblSwitch et g_tblSignal,
  3. Et c'est tout !

L'emplacement est facile à trouver car nous allons créer une boucle pour incrémenter un compteur. Il suffira de récupérer la valeur de ce compteur pour assigner un nouvel emplacement automatiquement dans la fonction EEPSaveData().

Pour les informations nous allons utiliser l'astuce suivante : puisqu'il n'est pas possible de sauvegarder plusieurs valeurs dans un seul enregistrement nous allons les regrouper en une seule phrase séparé par un caractère délimiteur comme une virgule par exemple.

Avant :

  1. Nom du modèle : g_tblSwitch[i][1] = Gleisstile\Strassen\FELDWEG.3dm
  2. ID de l'aiguillage : g_tblSwitch[i][2] = 222
  3. Position de l'aiguille : g_tblSwitch[i][3] = 1

Après :

  1. Phrase unique : "Gleisstile\Strassen\FELDWEG.3dm,222,1"

C'est le rôle de notre dernière fonction fnc_Enregistre_Info() de convertir les informations contenues dans nos tables en phrases uniques et d'enregistrer le tout via la fonction EEPSaveData(). Ci-dessous la fonction en question :

Fonction fnc_Enregistre_Info() pour sauvegarder les données du projet EEP

-- Cette fonction prépare les données et enregistre toutes les
-- informations contenues dans les tables g_tblSwitch et g_tblSignal
function fnc_Enregistre_Info()

	-- Variable pour déclarer une table locale afin de regrouper
	-- dans un seul emplacement toutes les phrases uniques
	local l_tblUnique = {}
	-- Variable pour recevoir la phrase unique
	local l_strUnique = ""
	-- Variable la plus courte possible pour les boucles
	local i = nil

	-- On scanne la table g_tblSwitch des aiguillages
	for i = 1, #g_tblSwitch do

	    -- On utilise la virgule comme séparateur entre les indices de la table g_tblSwitch
	    -- et concaténation des trois indices de la table
	    l_strUnique = g_tblSwitch[i][1]..","..g_tblSwitch[i][2]..","..g_tblSwitch[i][3]
 
            -- print("l_strUnique : ", l_strUnique)

	    -- Ajoute la phrase unique dans la table l_tblUnique
	    l_tblUnique[#l_tblUnique + 1] = l_strUnique	

	end

	-- On scanne la table g_tblSignal des signaux
	for i = 1, #g_tblSignal do

	    -- On utilise la virgule comme séparateur entre les indices de la table g_tblSignal
	    -- et concaténation des trois indices de la table
	    l_strUnique = g_tblSignal[i][1]..","..g_tblSignal[i][2]..","..g_tblSignal[i][3]
 
	    -- print("l_strUnique : ", l_strUnique)

	    -- Ajoute la phrase unique dans la table l_tblUnique
	    l_tblUnique[#l_tblUnique + 1] = l_strUnique	

	end
	
	-- On ajoute une simple chaine de caractères pour la fonction EEPLoadData()
	l_tblUnique[#l_tblUnique + 1] = "Confirmation : toutes les données ont bien été récupérées !"

	print("Nombre total d'enregistrement des deux tables : ", #l_tblUnique, "\n")

	if #l_tblUnique <= 1000 then

	    -- On enregistre toutes les informations via la fonction EEPSaveData()
            -- Début de la boucle pour parcourir la table l_tblUnique
	    for i = 1, #l_tblUnique do

	      EEPSaveData(i, l_tblUnique[i])	-- Enregistre les phrases uniques

	      -- print("l_tblUnique[i] : ", l_tblUnique[i])
	      
	    end

	    print("\nLes données des signalisations et des aiguillages sont désormais enregistrées !")

	  else

	    print("\nLe nombre de données est supérieur à 1000 ! Impossible de continuer !")

      end

end  -- end fin de la fonction

Ici rien de bien compliqué, les lignes n° 24 et 38 ajoutent les informations des deux tables g_tblSwitch et g_tblSignal dans la table locale l_tblUnique. L'incrémentation des lignes (emplacements) de cette dernière ne se fait pas avec la variable i de la boucle for mais en additionnant le nombre total de lignes déjà présentes dans la table via la déclaration entre crochets #l_tblUnique à laquelle on rajoute +1. Ainsi nous avons la certitude d'avoir des enregistrements séquentiels.

Les lignes n° 19 et 33 rassemblent les données pour constituer une chaine unique. On pourrait très bien enregistrer les données directement dans la fonction EEPSaveData() mais cela impliquerait de tenir à jour un compteur du nombre total d'enregistrements entre le scan des deux tables g_tblSwitch et g_tblSignal. Ici le choix d'utiliser une table locale pour regrouper toutes les données est pertinent dans la mesure où cela permet une parfaite synchronisation entre le nombre d'enregistrements de la table et de la fonction EEPSaveData().

La ligne n° 43 ajoute une simple chaine de caractère pour enregistrer une confirmation lorsque nous étudierons la fonction suivante EEPLoadData().

Les deux boucles des tables g_tblSwitch et g_tblSignal pourraient aussi être optimisées dans une seule et même fonction mais le jeu n'en vaut pas la chandelle pour le moment. Dans le futur article réservé à l'amélioration de ce script, nous allons enregistrer beaucoup plus d'informations concernant les objets immobiliers, éléments de paysage, etc... A ce moment là, l'optimisation prendra tout son sens.

Pour terminer, on test le nombre de lignes contenues dans la table l_tblUnique (ligne n° 47). Si le nombre dépasse la limite autorisée de 1000 emplacements, l'opération est annulée. Une autre solution serait d'additionner directement le nombre de lignes des deux tables avec les instructions #g_tblSwitch et #g_tblSignal juste au début de la fonction. Si le nombre est supérieur à 1000, l'opération est annulée et provoque la sortie immédiate de la fonction.

Voici le résultat dans la fenêtre d'évènements après exécution du script :

Image affichage script enregistrement EEPSaveData terminé dans EEP

Pour vérifier si les informations sont bien enregistrées, rien de plus simple. Il suffit d'ouvrir le fichier Lua du projet avec notepad++ :

Image résultat dans Notepad++ après EEPSaveData() dans EEP

Nous constatons l'enregistrement des informations à la suite du script. Seulement si vous affichez le script dans EEP tout ce qui suit après la section [EEPLuaData] ne sera pas affichée mais les données sont bien là !

Important : Les données doivent impérativement être enregistrées séquentiellement. Vous ne pouvez pas les enregistrer à l'emplacement n° 1 et passer directement à l'emplacement n° 10 en laissant les emplacements n° 2 à 9 vides. Dans ce cas de figure seul l'emplacement n° 1 sera réellement enregistré et l'emplacement n° 10 sera simplement ignoré.

Maintenant imaginons : vous voulez supprimer un ou plusieurs enregistrements. Comment faire ? c'est très simple, il suffit d'attribuer la valeur nil au numéro d'enregistrement concerné. Ci-dessous le code pour effacer toutes les données enregistrées de l'exemple précédent :

Supprime tous les enregistrements de la table l_tblUnique

for i = 1, #l_tblUnique do

  -- Supprime tous les enregistrements
  EEPSaveData(i, nil)

end

Pour les besoins de ce tutoriel, la fonction EEPSaveData() est appelée via la fonction utilisateur fnc_Lire_Fichier() dès le mode 3D activé. Concrètement chez vous, pour vos futurs réseaux ce n'est pas forcément la meilleure méthode. Plusieurs solutions s'offrent à vous pour démarrer le processus à la demande comme :

  1. Utiliser une signalisation réservée uniquement pour l'enregistrement des données en commutant cette signalisation manuellement. Il suffit de l'enregistrer via la fonction EEPRegisterSignal(numéro_de_la_signalisation) pour appeler notre fonction utilisateur fnc_Lire_Fichier() via la fonction EEPOnSignal(numéro_de_la_signalisation).
  2. Utiliser une signalisation déjà existante et paramétrer un de ses contacts pour appeler directement notre fonction utilisateur fnc_Lire_Fichier(). Par exemple, après l'entrée dans un dépôt, dans une gare, une opération de chargement/déchargement, etc...
  3. Utiliser n'importe quel contact pour appeler directement notre fonction utilisateur fnc_Lire_Fichier().

Voilà ! nous en avons terminé avec la fonction EEPSaveData(). Maintenant nous allons passer à la fonction inverse EEPLoadData() pour récupérer nos données.

Note : Il est bon de rappeler la nécessité pour le moment de rester dans une démarche strictement pédagogique pour appréhender et se familiariser avec certaines fonctions Lua. Les exemples ci-dessus existent uniquement pour aider les nouveaux utilisateurs à progresser pas-à-pas. Par la suite, nous reviendrons sur cet exemple afin de le rendre plus souple et efficace pour retrouver les informations désirées à la demande selon les besoins.
EEPLoadData()

Syntaxe

Paramètres

Valeur(s) renvoyée(s)

A partir de

Objectif

EEPLoadData(Emplacement)

Un

Deux

EEP 11

Charge des données depuis un emplacement de stockage spécifique. Cette fonction est utilisée pour restaurer les données enregistrées par la fonction EEPSaveData().

Remarque

  • Fournit un moyen pour le code Lua de lire les données des emplacements numérotés de 1 à 1000.
  • L'argument est le numéro de l'emplacement de stockage.
  • La première valeur retournée est true lorsque l'emplacement de stockage en question contient des données ou false lorsque ce dernier est vide.
  • La deuxième valeur récupère le contenu de cet enregistrement.
  • Lorsque le projet se charge, EEP récupère les contenus des emplacements de stockage mentionnés dans le script. En faisant appel à EEPLoadData() il est possible d'interroger ces contenus et de les attribuer à des variables en cas de besoin.

Exemple :

Pour illustrer la fonction EEPLoadData(), nous allons recharger le projet EURO_Vmax_v7.anl3 utilisé avec la fonction EEPSaveData() et nous allons voir comment récupérer les données précédemment enregistrées.

Pour récupérer les données c'est très simple, il suffit juste d'indiquer le numéro de l'enregistrement. Exemple :

Exemple de code pour récupérer des données

-- Récupère les données enregistrées dans l'emplacement n° 100
Result, Donnee = EEPLoadData(100)
		

Dans le cadre de notre tutoriel, nous avons ajouté dans notre projet EURO_Vmax_v7 une section de voie et une signalisation. Pour la retrouver facilement, utilisez la fonction Rechercher une signalisation ou un aiguillage dans le menu Outils et entrez le n° 400.

Pour commencer nous allons enregistrer cette signalisation au début du script comme ceci :

Enregistrement de la signalisation n° 400

clearlog()

-- Variables pour les deux tables (signalisation et aiguillages)
g_tblSignal = {}
g_tblSwitch = {}

-- Variables numériques pour compter le nombre d'éléments dans chaque classe d'objets
g_nbrSignal = 0
g_nbrSwitch = 0

-- Enregistrer le signal pour récupérer les données
EEPRegisterSignal(400)

function EEPMain()
  return 1
end

Ainsi lorsque nous commuterons manuellement le signal, la fonction EEPOnSignal_400(Position) va être appelée et exécutera le code à l'intérieur pour extraire nos données.

Mais avant nous allons tester le comportement de la fonction EEPLoadData() avec des numéros de lignes au hasard :

Fonction EEPOnSignal_400

-- Fonction appelée à chaque fois que l'état de la signalisation n° 400 change
function EEPOnSignal_400(Position)

	-- Nettoyer la fenêtre d'évènements
	clearlog()

	-- On extrait les données dans l'emplacement n° 100 quelque soit la position du signal
	Result, Donnee = EEPLoadData(100)

	if Result == true then
	    -- Result est vrai (true)
	    print("\nla donnée de l'emplacement n° 100 est : ", Donnee)
	  else
	    -- Result est faux (false)
	    print("\nLa donnée de l'emplacement n° 100 n'existe pas")
	end
	
end

Et le résultat dans la fenêtre d'évènements :

Image résultat fonction EEPLoadData

Nous avons bien récupéré nos données situées à l'emplacement (ou ligne) n° 100 !

Maintenant nous allons tenter d'extraire un emplacement qui n'existe pas :

Fonction EEPOnSignal_400

-- Fonction appelée à chaque fois que l'état de la signalisation n° 400 change
function EEPOnSignal_400(Position)

	-- Nettoyer la fenêtre d'évènements
	clearlog()

	-- On extrait les données dans l'emplacement n° 500 quelque soit la position du signal
	Result, Donnee = EEPLoadData(500)

	if Result == true then
	    -- Result est vrai (true)
	    print("\nla donnée de l'emplacement n° 500 est : ", Donnee)
	  else
	    -- Result est faux (false)
	    print("\nLa donnée de l'emplacement n° 500 n'existe pas")
	end
	
end

Et le résultat dans la fenêtre d'évènements :

Image résultat false fonction EEPLoadData

Ici la valeur retournée dans la variable Result est égale à false car l'enregistrement n° 500 n'existe pas.

Après ce petit préambule, comment réaliser l'opération inverse pour récupérer toutes nos données enregistrées via la fonction EEPSaveData() ? Simplement en découpant notre chaine simple pour reconstituer nos trois tables. Nous allons modifier le code de la fonction EEPOnSignal_400(Position) comme ceci :

Fonction EEPOnSignal_400
-- Fonction appelée à chaque fois que l'état de la signalisation n° 400 change
function EEPOnSignal_400(Position)

  -- Variable la plus courte possible pour les boucles
  local i = 0
  -- Variable pour récupérer les données partielles
  local s = nil

  -- Table pour récupérer toutes les données
  local l_tblToutExtraire = {}
  -- Table temporaire pour extraire les données partielles
  local l_tblTemp = {}
  
  -- Récupération du résultat pour la fonction EEPLoadData()
  local bool_Result = nil
  local s_Donnee = ""
  
  -- Nettoyer la fenêtre d'évènements
  clearlog()
  
  -- Boucle while pour parcourir toutes les données précédemment
  -- enregistrées par la fonction EEPSaveData()
  while true do
  
    -- Compteur pour la table l_tblToutExtraire
    i = i + 1

    -- Extraire les données
    bool_Result, s_Donnee = EEPLoadData(i) -- extraire les phrases uniques
    
    -- Si une ligne de données a été trouvée alors
    if bool_Result == true then
    
        -- Extraire les trois données partielles séparées par les virgules...
        for s in string.gmatch(s_Donnee, "[%a%d%séèê:_\\%.]+") do
        		
          -- ... et les affecter dans la table temporaire l_tblTemp
          l_tblTemp[#l_tblTemp + 1] = s
        				
        end
  
        -- Transférer les trois données partielles dans la table l_tblToutExtraire
        l_tblToutExtraire[i] = l_tblTemp
        
        -- Réinitialiser la table temporaire l_tblTemp				
        l_tblTemp = {}
        
      else
  
        -- Juste pour l'exemple, afficher les données extraites
        for i = 1, #l_tblToutExtraire do
         
          print("Table Nom du modèle : ", l_tblToutExtraire[i][1])
          print("Table ID du modèle : ", l_tblToutExtraire[i][2])
          print("Table Position du signal ou de l'aiguillage : ", l_tblToutExtraire[i][3], "\n")
          
        end
  				
        print("\nNombre total d'enregistrements trouvés : ", #l_tblToutExtraire)
        print("\nToutes les données ont été parcourues, sortir de la boucle.\n")
        break
        
    end
  
  end	-- end fin de la boucle while

end  -- end fin de la fonction

Comme d'habitude, nous commençons par déclarer nos variables aux lignes n° 5 à 16. Ensuite pour parcourir toutes les données précédemment enregistrées nous allons utiliser une boucle while. Celle-ci début à la ligne n° 23. Nous incrémentons un compteur représenté par la variable i (ligne n° 26). En effet, ici on ne peut pas utiliser une boucle for i = 1, ... car on ne connait pas à l'avance le nombre de lignes à retourner. Il faudra juste veiller à prévoir une porte de sortie avec l'instruction break pour terminer la boucle. La ligne n° 29 extrait les données via la fonction EEPLoadData(i) vu que la variable i en argument contient le numéro séquentiel d'emplacement (ou ligne). La fonction retourne deux valeurs stockées dans les variables bool_Result et s_Donnee.

La valeur booléenne contenu dans la variable bool_Result nous indique si la fonction a trouvé avec succès une ligne de données. La ligne n° 32 teste si la variable contient la valeur true (vraie). Si c'est le cas, l'exécution du script continue à la ligne n° 35. Dans le cas contraire si la valeur est égale à false (fausse), l'exécution du script passe directement à la ligne n° 51.

Analysons la boucle utilisée pour extraire les données à partir de la ligne n° 35 :

Image données partielles fonction Lua EEPLoadData

Jusqu'à présent, nous avons toujours employé des boucles avec un compteur numérique. Exemple :

Boucle numérique pour compter de 1 à 10
for i = 1, 10 do
  -- Faire quelque chose...
end

Dans notre exercice à la ligne n° 35, nous allons maintenant découvrir et utiliser une boucle alphanumérique. Sa mise en œuvre est un peu plus complexe. C'est pour cela que nous allons décortiquer son fonctionnement pas-à-pas.

  1. On commence toujours une boucle 'for' par le mot clé lua for peu importe qu'elle soit numérique ou alphanumérique,
  2. Ensuite la variable s est utilisée non pas comme un compteur mais pour récupérer une chaine de caractères contenue dans la variable s_Donnee,
  3. La chaine de caractères est retournée grâce à l'instruction string.gmatch dans laquelle un motif (ou pattern) a été passé en 2ème argument.

Arrêtons-nous sur le motif en question. A première vue, les caractères entre guillemets ne ressemblent à rien et pourtant !

Motif de la fonction string.gmatch
for s in string.gmatch(s_Donnee, "[%a%d%séèê:_\\%.]+") do

Le motif commence par un crochet ouvert et se termine par un crochet fermé. L'emploi de crochets signifie que la chaine de caractères extraite de la variable s_Donnee doit correspondre au motif compris entre les crochets. Analysons facilement et séquentiellement le motif :

  1. %a = n'importe quelles lettres majuscules ou minuscules,
  2. %d = n'importe quels chiffres,
  3. %s = n'importe quels caractères d'espace,
  4. é = si un ou plusieurs é accents aigus sont présents,
  5. è = si un ou plusieurs è accent grave sont présents,
  6. ê = si un ou plusieurs ê accent circonflexe sont présents,
  7. : = si les deux points sont présents,
  8. _ = si un ou plusieurs caractère de soulignement sont présents,
  9. \\ = si un ou plusieurs signes anti-slash sont présents (pour l'anti-slash, il faut le doubler),
  10. %. = si un ou plusieurs point sont présents

Le signe + après le dernier crochet signifie que la recherche continue tant que un ou plusieurs caractères trouvés dans la variable s_Donnee correspondent au motif.

Reprenons notre exemple avec l'affichage de la variable s_Donnee situé à la ligne n° 100 :

Image résultat fonction EEPLoadData

Son contenu est : \Gleisstile\Strassen\FELDWEG.3dm,160,2. A votre avis, dans cette chaine de caractères quel est le seul caractère non inclus dans le motif ? On dirait bien la virgule ! Rappelez-vous, dans la fonction EEPSaveData() nous avions regroupé dans une phrase unique les trois variables séparées par des virgules comme caractères délimiteurs.

Ainsi dans notre boucle dès qu'une virgule est présente, celle-ci est exclue ainsi la fonction string.gmatch retourne dans la variable s la partie comprise soit à gauche ou à droite de la virgule et la boucle continue de découper la chaine contenue dans la variable s_Donnee pour passer à la virgule suivante. Ici en l’occurrence dans notre exemple il s'agit de :

  1. \Gleisstile\Strassen\FELDWEG.3dm
  2. 160
  3. 2

Ensuite le stockage temporaire de ces valeurs séparées s'effectue dans la table l_tblTemp. A la ligne n° 38, on enregistre dans la table l_tblTemp[#l_tblTemp + 1] la chaine partielle stockée dans la variable s. #l_tblTemp entre crochets retourne le nombre d'enregistrement(s) déjà présent(s) dans la table. Ainsi avec #l_tblTemp + 1, on ajoute automatiquement à chaque itération de la boucle une ligne dans la table.

Image données partielles fonction Lua EEPLoadData

Une fois que la fonction string.gmatch a trouvé l'emplacement de toutes les virgules alors la fin de la recherche dans la variable s_Donnee est atteinte et la boucle se termine.

A la ligne n° 42 il nous reste à transférer le contenu de la table l_tblTemp avec ses trois enregistrements dans la table l_tblToutExtraire[i] à l'indice i incrémenté à la ligne n° 26. Une fois le transfert terminé, la ligne n° 46 réinitialise la table l_tblTemp pour la prochaine itération de la boucle while :

Image transfert de données partielles fonction Lua EEPLoadData

Et le cycle recommence tant que la variable bool_Result retourne la valeur true (vraie).

Par contre si la valeur false (fausse) est retournée alors l'exécution du script passe à la ligne n° 50 pour afficher tous les enregistrements contenus dans la table l_tblToutExtraire. Il s'agit du même code déjà vu dans la fonction fnc_Lire_Fichier :

Image condition fausse dans fonction Lua EEPLoadData
  1. La variable i contient le numéro d'enregistrement (de 1 à n...),
  2. Avant tout il faut se positionner sur l'enregistrement correspondant comme ceci : l_tblToutExtraire[i],
  3. Ensuite il suffit d'indiquer à la suite le numéro d'ordre utilisé lors de l'enregistrement comme ceci : l_tblToutExtraire[i][1] pour le nom du modèle, l_tblToutExtraire[i][2] pour l'ID du modèle et l_tblToutExtraire[i][3] pour la position du signal ou de l'aiguillage.

Une fois la boucle terminée, la ligne n° 59 affiche le nombre d'enregistrements trouvés dans la table l_tblToutExtraire. La sortie de la boucle while s'effectue à la ligne n° 61 grâce à l'instruction break.

Attention : Omettre l'instruction break entrainera le script dans une boucle infinie et bloquera l'exécution d'EEP.

Ci-dessous le résultat dans la fenêtre d'évènements après avoir cliqué (en maintenant une des deux touches Shift) sur le signal n° 400 dans la fenêtre 3D :

Image fin de la fonction EEPLoadData()

Volontairement dans la fonction utilisateur fnc_Enregistre_Info(), nous avions ajouté à la fin une phrase simple pour confirmer la récupération des données :

Fonction fnc_Enregistre_Info() pour sauvegarder les données du projet EEP
	
	-- On ajoute une simple chaine de caractères pour la fonction EEPLoadData()
	l_tblUnique[#l_tblUnique + 1] = "Confirmation : toutes les données ont bien été récupérées !"

Comme nous pouvons le constater dans la fenêtre d'évènements cette phrase a bien été retournée à la fin de la boucle. Seulement comme il n'y a pas de virgule dans cette phrase, l'ID du modèle et la position du signal ou de l'aiguillage contiennent la valeur nil. Ainsi la phrase a été affectée au premier emplacement c'est-à-dire le nom du modèle.

Ceci explique aussi pourquoi le motif contenait les lettres accentuées, le trait de soulignement, les deux points.... afin de retourner cette phrase en entier.

Conclusion

L'intégration d'un langage de script comme Lua dans EEP est un atout indiscutable et indispensable pour automatiser des tâches plus ou moins complexes dans vos réseaux. Lua pousse beaucoup plus loin les possibilités offertes par l'utilisation des contacts et paradoxalement simplifie la construction des automatismes en évitant de trop surcharger les réseaux par des dizaines de contacts inutiles. Les fonctions Lua pour EEP offre aussi la possibilité d'accéder à certaines fonctions internes du programme et la gestion des objets.

Continuons notre découverte des fonctions Lua pour EEP avec l'article suivant consacré aux aiguillages...

Merci d'avoir lu cet article. Si vous avez des questions ou des suggestions, n’hésitez pas à nous en faire part en bas de cette page en commentaires.

Amusez-vous à lire un autre article. A bientôt !

0 0 votes
Évaluation de l'article
S’abonner
Notification pour
guest
0 Commentaires
Le plus ancien
Le plus récent Le plus populaire
Commentaires en ligne
Afficher tous les commentaires
Retour en haut