LOCODUINO

Gestion d’une gare cachée

Gestion d’une gare cachée (3)

Remplissage et vidange

.
Par : Jean-Luc

DIFFICULTÉ :

Dans « Gestion d’une gare cachée (2) » nous avons posé les bases d’une conception objet pour notre gare cachée. Cette conception est fondée sur un graphe où chaque nœud est un élément de voie. Nous avons développé un algorithme de parcours exhaustif de ce graphe pour trouver, tout en déterminant la position des aiguilles, une voie de la gare cachée. Toutefois, il nous manque un algorithme pour trouver une voie libre et pour trouver une voie occupée. Nous allons maintenant ajouter ces deux algorithmes.

Le premier algorithme que nous allons ajouter est très proche de celui que nous avons développé dans le précédent article. Seul le critère de sélection de la voie en gare change. Au lieu de chercher une voie possédant l’identifiant voulu, nous allons chercher une voie libre. Le second algorithme recherchera aléatoirement une voie occupée dans la gare. Il nous faut donc ajouter une variable membre booléenne dans la classe VoieGare que nous appellerons libre. Cette variable sera initialisée à true car nous supposons qu’au démarrage du système la gare cachée est entièrement vide. La classe VoieGare devient donc :

/*
 * Classe pour les voies de gare
 */
class VoieGare : public Voie
{
  private:
    bool mLibre;
    
  public:
    VoieGare(byte identifiant) : Voie(identifiant) { mLibre = true; }
    virtual bool creeCheminVers(byte identifiant);
};

Il nous reste maintenant à écrire nos deux algorithmes.

Trouver une voie libre

Nous allons non seulement la trouver mais établir au passage les positions des aiguilles qui permettent d’y amener un train. Tout d’abord, il faut ajouter une fonction membre virtuelle dans la classe de base. Cette fonction membre, creeCheminVersLibre retourne un identifiant de voie, donc un byte [1]. Elle retournera un identifiant de voie si une voie libre est trouvée et son chemin établi, et l’identifiant spécial AUCUNE_VOIE si toutes les voies sont occupées.

/*
 * Classe de base des éléments de voie
 * 
 * Fonctions virtuelles de recherche de chemin et identifiant
 */
class Voie
{
  private:
    byte mIdentifiant;
 
  public:
    Voie(byte identifiant) { mIdentifiant = identifiant; }
    byte idVoie() { return mIdentifiant; }
    virtual bool creeCheminVers(byte identifiant) = 0;
    virtual byte creeCheminVersLibre() = 0;
};

Pour une voie de la gare, creeCheminVersLibre va simplement retourner l’identifiant de la voie si cette voie est libre, ou AUCUNE_VOIE si cette voie est occupée, mais nous allons également en profiter pour afficher l’identifiant de la voie si elle est trouvée libre afin de tester le système et également de passer mLibre à false puisqu’il s’agit maintenant d’occuper cette voie.

byte VoieGare::creeCheminVersLibre()
{
  if (mLibre) {
    Serial.print(F("Occupation de : "));
    afficheVoie(idVoie());
    Serial.println();
    mLibre = false;
    return idVoie();
  }
  else {
    return AUCUNE_VOIE;
  }
}

Un aiguillage demandera aux deux voies qui lui sont connectées, droite et déviée, si elle est libre. On commencera par la connexion droite. Si une voie libre est trouvée, les aiguilles sont positionnées en conséquence. C’est très similaire à la fonction creeCheminVers.

byte Aiguillage::creeCheminVersLibre()
{
  byte voie;
  if (mVoieDroite != NULL && mVoieDeviee != NULL) {
    /* Uniquement si correctement connecte     */
    /* Cherche la destination cote voie droite */
    voie = mVoieDroite->creeCheminVersLibre();
    if (voie != AUCUNE_VOIE) {
      /* Destination trouvee cote droit */
      afficheVoie(idVoie());
      Serial.println(F(": Droit"));
      return voie;
    }
    else {
      /* Cherche la destination cote voie deviee */
      voie = mVoieDeviee->creeCheminVersLibre();
      if (voie != AUCUNE_VOIE) {
        /* destination trouvee cote devie */
        afficheVoie(idVoie());
        Serial.println(F(": Devie"));
        return voie;
      }
      else {
        /* destination non trouvee */
        return AUCUNE_VOIE;
      }
    }
  }
}

Enfin, la voie d’entrée se contente de demander à la voie qui lui est connectée si elle elle libre, la demande se propageant le long du graphe jusqu’à une voie de gare.

byte VoieExtreme::creeCheminVersLibre()
{
  if (mVoie != NULL) {
    return mVoie->creeCheminVersLibre();
  }
}

Nous pouvons maintenant tester notre programme. Le test est simple, on ajoute des trains dans la gare jusqu’à ce qu’elle soit pleine. Pour cela nous ajoutons une fonction de test qui affiche en clair si une voie libre est trouvée.

void testLibre()
{
  if (voieEntree.creeCheminVersLibre() != AUCUNE_VOIE)
    Serial.println(F("TROUVE"));
  else
    Serial.println(F("NON TROUVE"));
}

Il faut également compléter la fonction d’affichage du nom d’une voie en clair. Nous n’y avions mis que les aiguillages, il faut maintenant afficher le nom des voies de la gare.

    case VOIE_GARE_0:
       Serial.print("Voie gare 0"); break;
    case VOIE_GARE_1:
       Serial.print("Voie gare 1"); break;
    case VOIE_GARE_2:
       Serial.print("Voie gare 2"); break;
    case VOIE_GARE_3:
       Serial.print("Voie gare 3"); break;

Enfin nous ajoutons la séquence de test à la fin de setup

  Serial.println("***** TEST de chemin vers libre");
  testLibre();    
  testLibre();    
  testLibre();    
  testLibre();    
  testLibre();    

Comme notre gare a quatre voies, les quatre premiers appels à testLibre trouveront une voie et le dernier échouera. On a bien le résultat escompté :

***** TEST de chemin vers libre
Occupation de : Voie gare 0
Aiguillage E0: Droit
TROUVE
Occupation de : Voie gare 3
Aiguillage E2: Droit
Aiguillage E1: Droit
Aiguillage E0: Devie
TROUVE
Occupation de : Voie gare 2
Aiguillage E2: Devie
Aiguillage E1: Droit
Aiguillage E0: Devie
TROUVE
Occupation de : Voie gare 1
Aiguillage E1: Devie
Aiguillage E0: Devie
TROUVE
NON TROUVE

Sélectionner un train

Pour extraire un train de la gare, nous pourrions procéder de la même manière en inversant le critère de sélection. Au lieu de chercher une voie libre, nous chercherions une voie occupée. Mais, l’ordre de recherche d’une voie étant toujours le même, cette façon de faire conduirait à une circulation uniforme.

Au lieu de cela, nous allons sélectionner une voie de gare occupée aléatoirement. Il nous manque pour cela un moyen de savoir si une voie est occupée. En effet, la variable membre libre est privée. Nous ajoutons une fonction membre occupee à la classe VoieGare qui retourne true si la voie est occupée.

bool occupee() { return ! mLibre; }

Et nous écrivons une fonction qui sélectionne aléatoirement une voie occupée. Il faut tout d’abord recenser les voies occupées. Le plus simple est de parcourir chacune des 4 voies de la gare et, si elle est occupée, ajouter son identifiant dans un tableau. Appelons ce tableau voiesOccupees. Ensuite, connaissant le nombre de voies occupées, nous tirons un nombre aléatoire entre 0 et ce nombre - 1 afin de sélectionner la voie occupée. Pour en savoir plus sur l’aléatoire, vous pouvez lire l’article « Comment gérer l’aléatoire ? » [2]. Le plus simple est également que les voies de la gare soient dans un tableau, nous en profitons pour mettre le nombre de voies dans une constante :

const int TAILLE_GARE = 4;

VoieGare voieGare[TAILLE_GARE] = {
   VoieGare(VOIE_GARE_0),
   VoieGare(VOIE_GARE_1),
   VoieGare(VOIE_GARE_2),
   VoieGare(VOIE_GARE_3)
};

Il faut également faire attention au fait que la gare peut être entièrement vide. Dans ce cas, le tableau des voies occupées sera vide et nous serons dans l’incapacité de retourner un identifiant de voie. Pour cela, nous ajoutons un identifiant spécial nomme AUCUNE_VOIE. Nous retournerons cet identifiant si aucune voie n’est occupée. Voici cette fonction que nous appellerons selectionneTrain :

/*
 * Sélectionne aléatoirement un des trains des voies occupées
 */
byte selectionneTrain()
{
  byte voiesOccupees[TAILLE_GARE];
  byte indexVoieOccupee = 0;
  /* Parcoure les voies de la gare */
  for (byte indexVoieGare = 0; indexVoieGare < TAILLE_GARE; indexVoieGare++) {
    if (voieGare[indexVoieGare].occupee()) {
      /* Une voie occupee est trouvée, on ajoute son identifiant à notre tableau */
      voiesOccupees[indexVoieOccupee++] = voieGare[indexVoieGare].idVoie();
    }
  }
  if (indexVoieOccupee > 0) {
    /* Au moins une voie occupée a été trouvée */
    /* Tire au hasard une voie occupée */
    byte voie = random(0, indexVoieOccupee);
    /* et retourne la voie */
    return voiesOccupees[voie];   
  }
  else {
    /* la gare est vide ! */
    return AUCUNE_VOIE;
  }
}

Cette fonction retournant l’identifiant de la voie de gare sur laquelle le train stationne, il suffit ensuite d’appeler creeCheminVers(...) avec comme argument cet identifiant pour manœuvrer les aiguilles et extraire ce train de la gare. Il manque seulement une fonction membre de la classe VoieGare permettant de libérer une voie de la gare une fois que le train est parti. Ajoutons la.

void libere()  { mLibre = true; }

Et, enfin, une fonction permettant de parcourir les voies de la gare et de libérer celle dont l’identifiant est passé en argument. Appelons cette fonction libereVoieGare.

void libereVoieGare(byte identifiant)
{
  for (byte indexVoieGare = 0; indexVoieGare < TAILLE_GARE; indexVoieGare++) {
    if (voieGare[indexVoieGare].idVoie() == identifiant) {
      /* La voie d'indentifiant correspondant est trouvée */
      voieGare[indexVoieGare].libere();
      /* Comme on a trouvé, on sort de la boucle */
      break;
    }
  } 
}

Il reste à tester tout cela. À la suite du test de remplissage que nous avons fait ci-dessous, ajoutons un test de vidage aléatoire.

  randomSeed(getSeed());
  Serial.println("***** TEST de vidange de la gare");
  while (true)
  {
    byte identifiant = selectionneTrain();
    if (identifiant != AUCUNE_VOIE) {
      Serial.print("Selectionne le train de : ");
      afficheVoie(identifiant);
      Serial.println();
      voieSortie.creeCheminVers(identifiant);
      libereVoieGare(identifiant);
    }
    else {
      break;    
    }
  }
  Serial.println("TERMINE");

Voici une des sorties possibles de ce test, une des sorties car la vidange étant aléatoire, une autre séquence peut être obtenue.

Selectionne le train de : Voie gare 1
Aiguillage S2: Devie
Aiguillage S1: Droit
Aiguillage S0: Devie
Selectionne le train de : Voie gare 2
Aiguillage S1: Devie
Aiguillage S0: Devie
Selectionne le train de : Voie gare 0
Aiguillage S2: Droit
Aiguillage S1: Droit
Aiguillage S0: Devie
Selectionne le train de : Voie gare 3
Aiguillage S0: Droit
TERMINE

Voici le sketch complet avec tous les tests :

Sketch de gare cachée
Tous les objets et fonctions ainsi que le test.

Nous avons maintenant toutes les structures de données et toutes les fonctions pour gérer notre gare cachée. Notez que c’est directement adaptable à n’importe quelle taille de gare, il suffit de créer les objets correspondant aux éléments de voies, de les connecter et de mettre à jour la constante TAILLE_GARE ainsi que les tests.

Le prochain article traitera des capteurs et des actionneurs que nous allons utiliser.

[1Ceci nous limite à 256 identifiants de voie ce qui est suffisant pour une gare cachée.

[2D’ailleurs, nous intégrons la fonction getSeed() décrite à la fin de cet article.

10 Messages

  • Gestion d’une gare cachée (3) 12 mars 2017 16:51, par Patrice

    Bonjour,
    je viens de lire votre article sur la gestion d’une gare cachée et j’avoue
    avoir décroché dans la lecture de votre programme(je pense que vous avez
    une maitrise du langage Arduino très poussée ; félicitation).

    Je suis moi même en cours d’essai sur la gestion d’une gare avec un arduino
    méga et je voulais vous demander si votre système de détection par ILS
    fonctionne bien car je suis à l’échelle N et j’ai de mauvais souvenir de l’utilisation d’ILS pour des détections ; j’ai fini par les abandonner au profit d’une détection par infra-rouge. Cela fonctionne relativement bien
    mais j’ai un autre problème à l’arrêt des convois si le détecteur (verticale) se trouve au niveau d’un attelage ; la détection ne se fait plus.

    Sur mon système à l’entré d’un convoi dans la gare, j’effectue une détection de tous les capteurs pour chercher une vois libre.

    Sur votre système (Elle retournera un identifiant de voie si une voie libre est trouvée et son chemin établi,)comment avec l’ILS peut-il renvoyer un signal si la loco ne se trouve pas exactement au dessus de l’ILS.

    En attendant votre réponse ,merci.

    Répondre

    • Gestion d’une gare cachée (3) 12 mars 2017 22:34, par Dominique

      Bonsoir,

      Votre problème semble plutôt se situer sur la fiabilité des détecteurs IR que sur la gestion de la gare cachée.

      J’ai déjà réalisé des barrières infrarouges qui évitent de rater un convoi à cause du trou entre 2 wagons. Il suffit de les installer en biais et à l’horizontal ce qui supprime ce trou. Si vous les installez verticalement c’est surement moins commode mais néanmoins possible.

      Pour détecter l’absence d’un convoi, je vous recommande toutefois les détecteurs de consommation.

      Mais si vous ne pouvez pas changer vos détecteurs infra-rouge, il doit être possible de réaliser un algorithme de détection qui distingue la présence et l’absence de trains (en vérifiant notamment qu’un train est bien sorti d’une zone)

      Répondre

      • Gestion d’une gare cachée (3) 13 mars 2017 01:41, par Patrice

        Bonsoir,
        merci pour votre réponse très rapide.
        Je retiens votre idée pour l’installation des détecteurs IR en diagonale
        je n’y avais pas pensé et je pense que cela va résoudre effectivement les problèmes de trou entre les wagons.
        Pour la détection par consommation de courant c’est impossible car je coupe
        le courant sur la voie pour arrêter les convois ; j’utilise une centrale
        Roco Z21 en DCC et je n’ai pas encore trouvé le moyen d’arrêter un convoi
        sans couper le courant (sauf si je passe par une commande par ordinateur).
        Merci encore une fois.

        Répondre

    • Gestion d’une gare cachée (3) 13 mars 2017 08:41, par Jean-Luc

      Bonjour,

      Concernant la détection, vous avez deux manière de vous y prendre dans le programme :

      • On utilise la valeur brute renvoyée par l’entrée raccordée à l’ILS, c’est à dire son état. Dans ce cas le programme n’a pas la mémoire des états antérieurs et, si l’ILS ne se trouve plus en face de l’aimant, on oublie qu’une rame est présente ;
      • On mémorise dans la programme la valeur passée de l’ILS et on détecte les changements. En ne s’intéressant qu’aux changement non-présent—présent, on détecte la présence de la rame même si l’aimant n’est plus en face de l’ILS.

      Répondre

      • Gestion d’une gare cachée (3) 13 mars 2017 11:34, par Patrice

        Bonjour,
        merci de votre réponse rapide, je comprends mieux
        maintenant la facon d’utilliser le signal de ILS ;
        parfaitement expliqué.
        cordialement.

        Répondre

  • Gestion d’une gare cachée (3) 16 mars 2017 23:16, par Patrice

    bonsoir,
    en suivant vos conseils pour l’installation des capteurs IR, cela a résolu les problèmes de détections.
    Pour mon programme je suis parti sur le principe de fonctionnement suivant :
    étant dans un club, on a pas toujours assez de locos
    pour utiliser la gare cachée, donc à l’initialisation
    on sélectionne le nombres de voies que l’on veut utiliser.
    1- analyse de chaque voies ; si il y a plus de voies occupés que le nombre sélectionné au départ, le système vide les voies en surplus.
    2- l’opérateur decide de faire entrer un convoi dans la gare (c’est la seule action manuel).
    3- ouverture de l’aiguille d’entrée
    4- recherche de la 1 voie libre (à partir de la voie 0)
    5 mise en place des aiguilles pour atteindre la voie
    sélectionnée
    6- alimentation de la voie en courant DCC
    7- témoin visuel (clignotant)en attendant l’arrivée du convoi sous le capteur
    8- activation du capteur
    9- coupure du courant dans la voie
    10- fermeture de l’aiguille d’entrée
    11- allumage témoin de voie occupée
    12- comptage des convois si nombre supérieur au nombre sélectionné
    13- recherche 1 voie occupée
    14- activation du courant dans la voie sélectionnée
    15- protection de la sortie (canton au rouge)
    16- attente de validation du capteur de sortie (témoin)
    17- validation de la sortie extinction du témoin de voie et coupure du courant.
    etc...
    Depuis l’installation des capteurs IR tout fonctionne bien reste juste à tester sur un longue période.
    je voulais avoir votre avis sur le déroulement du fonctionnement du logiciel.
    Je pense que votre analyse peu m’aider à améliorer encore le logiciel.
    Merci.
    Matériels utilisés : Arduino méga, bloc relais et capteurs IR

    Répondre

  • Gestion d’une gare cachée (3) 30 mars 2019 18:10, par Jean-Pierre

    Bonjour, essayant de passer à 6 voie pour permettre d’avoir plus trains différents et après pas mal de plantage dans les tests résolu maintenant, j’aurais voulu savoir quand l’article sur les capteurs et actionneurs sera placé sur le site, pour me permettre de finir le tout, la détection des trains est en s88 sur arduino nano suivant l’article de Jean-Claude et cela fonctionnent très bien.

    Jean-Pierre

    Répondre

  • Gestion d’une gare cachée (3) 6 février 2021 13:15, par Ardin

    Bonjour,

    Avec grand intérêt j’ai lu vos articles sur une gare cachée pilotée par un Arduino et maintenant j’aimerais experimenter avec ceci.

    A la fin de l’article 3 sur une gare cachée c’est mentionnée : "Le prochain article traitera des capteurs et des actionneurs que nous allons utiliser."

    Ou pourrais-je trouver cette article ?

    Merci d’avance

    Répondre

  • Gestion d’une gare cachée (3) 29 septembre 2021 17:30, par Olivier

    Bonjour,

    Tout d’abord un grand merci pour la communication et le partage de ces 3 chapitres "Gestion d’une gare cachée".

    A la fin du 3ème et dernier chapitre, vous indiquez que le prochain article traitera des capteurs et des actionneurs que nous pouvons utiliser pour mener à bien la gestion d’une gare cachée.
    Or, je ne trouve malheureusement pas cet article complémentaire.

    Pourriez-vous m’indiquer son emplacement SVP ?

    Un grand merci par avance !

    Voir en ligne : Gestion d’une gare cachée (1) (2) (3)

    Répondre

  • Gestion d’une gare cachée (3) 18 septembre 2023 20:35, par THOLEY MICHEL

    A la fin du 3ème et dernier chapitre, vous indiquez que le prochain article traitera des capteurs et des actionneurs que nous pouvons utiliser pour mener à bien la gestion d’une gare cachée.
    Or, je ne trouve malheureusement pas cet article complémentaire.

    Pourriez-vous m’indiquer son emplacement SVP ?

    Un grand merci par avance !

    Voir en ligne : Gestion d’une gare cachée (1) (2) (3)

    Répondre

Réagissez à « Gestion d’une gare cachée (3) »

Qui êtes-vous ?
Votre message

Pour créer des paragraphes, laissez simplement des lignes vides.

Lien hypertexte

(Si votre message se réfère à un article publié sur le Web, ou à une page fournissant plus d’informations, vous pouvez indiquer ci-après le titre de la page et son adresse.)

Rubrique « Projets »

Les derniers articles

Les articles les plus lus