LOCODUINO

Aide
Forum de discussion
Dépôt GIT Locoduino
Flux RSS

mardi 19 mars 2024

Visiteurs connectés : 65

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

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 »

LaBox, Une Centrale DCC polyvalente et abordable (1)

LaBox, Une Centrale DCC polyvalente et abordable (2)

LaBox, Une Centrale DCC polyvalente et abordable (3)

Comment piloter trains et accessoires en DCC avec un Arduino (1)

Comment piloter trains et accessoires en DCC avec un Arduino (2)

Comment piloter trains et accessoires en DCC avec un Arduino (3)

Comment piloter trains et accessoires en DCC avec un Arduino (4)

SGDD : Système de Gestion DD (1)

SGDD : Système de Gestion DD (2)

SGDD : Système de Gestion DD (3)

La PWM : Qu’est-ce que c’est ? (1)

La PWM : Qu’est-ce que c’est ? (2)

La PWM : Qu’est-ce que c’est ? (3)

La PWM : Qu’est-ce que c’est ? (4)

Mise en oeuvre du Bus CAN entre modules Arduino (1)

Mise en oeuvre du Bus CAN entre modules Arduino (2)

Un gestionnaire en C++ pour votre réseau (1)

Un gestionnaire en C++ pour votre réseau (2)

Un gestionnaire en C++ pour votre réseau (3)

Un gestionnaire en C++ pour votre réseau (4)

Réalisation de centrales DCC avec le logiciel libre DCC++ (1)

Réalisation de centrales DCC avec le logiciel libre DCC++ (2)

Réalisation de centrales DCC avec le logiciel libre DCC++ (3)

Contrôleur à télécommande infrarouge pour centrale DCC++

La carte Satellite V1 (1)

La carte Satellite V1 (2)

La carte Satellite V1 (3)

La carte Satellite V1 (4)

La carte Satellite V1 (5)

Chenillard de DEL

Enseigne de magasin

Feux tricolores

Multi-animations lumineuses

L’Arduino et le système de commande numérique DCC

Un décodeur d’accessoire DCC versatile basé sur Arduino

Un moniteur de signaux DCC

Une barrière infrarouge

Un capteur RFID

Un TCO xpressnet

Une animation sonore

L’Arduino au coeur des systèmes de pilotage analogiques ou numériques

Calcul de la vitesse d’un train miniature avec l’Arduino

La génèse d’un réseau 100% Arduino

Une horloge à échelle H0

Simulateur de soudure à arc

Un automatisme de Passage à Niveau

Automatisation du pont FLEISCHMANN 6152 (HO) avec un ESP32 (1)

Identifier et localiser vos trains avec le RFID/NFC et un bus CAN.

Etude d’un passage à niveau multivoies

La rétro-signalisation sur Arduino

Décodeur pour aiguillage à solénoïdes sur Arduino

Un décodeur DCC pour les signaux à deux ou trois feux sur Arduino NANO/UNO

Etude d’un passage à niveau universel

Réalisation pratique d’un système de mesure de vitesse à l’échelle N

Une Passerelle entre le bus S88 et le bus CAN pour la rétro signalisation

Un décodeur DCC pour 16 feux tricolores

Block Automatique Lumineux avec la carte shield "Arduino 4 relays"

Réalisation d’un affichage de gare ARRIVEE DEPART

Ménage à trois (Ordinateur, Arduino, réseau)

Réalisation d’un va-et-vient automatique et réaliste

Souris et centrale sans fil

Communications entre JMRI et Arduino

Annonces en gare avec la RFID

Une croix de pharmacie animée avec Arduino UNO

Réalisation d’un wagon de mesure (distance et vitesse)

Passage à niveau géré par Arduino (1)

Passage à niveau géré par Arduino (2)

Passage à niveau géré par Arduino (3)

Passage à niveau géré par Arduino (4)

Passage à niveau géré par Arduino (5)

Une manette simple et autonome pour LaBox

Éclairer le réseau (1)

Éclairer le réseau (2)

Block Automatique Lumineux à 8 cantons analogiques

Un décodeur DCC pour les plaques tournantes Fleischmann et Roco

Éclairer le réseau (3)

Éclairer le réseau (4)

Éclairer le réseau (5)

JMRI pour Ma première centrale DCC

Rocrail pour Ma première centrale DCC

CDM-Rail pour Ma première centrale DCC (1)

CDM-Rail pour Ma première centrale DCC (2)

Banc de test pour les décodeurs DCC

Ma première manette pour les aiguillages DCC

Mon premier décodeur pour les aiguillages DCC

Boitier 3D pour la station DCC minimale

Va-et-vient pour deux trains

Un programme pour régler facilement les servos moteurs avec un ESP32

Affichage publicitaire avec Arduino (1)

Affichage publicitaire avec Arduino (2)

TCO Web interactif avec des ESP32 et des ESP8266 (1)

TCO Web interactif avec des ESP32 et des ESP8266 (2)

TCO Web interactif avec des ESP32 et des ESP8266 (3)

TCO Web interactif avec des ESP32 et des ESP8266 (4)

TCO Web interactif avec des ESP32 et des ESP8266 (5)

Les derniers articles

LaBox, Une Centrale DCC polyvalente et abordable (3)


Thierry

LaBox, Une Centrale DCC polyvalente et abordable (1)


Thierry

LaBox, Une Centrale DCC polyvalente et abordable (2)


Dominique, msport, Thierry

Un programme pour régler facilement les servos moteurs avec un ESP32


bobyAndCo

TCO Web interactif avec des ESP32 et des ESP8266 (5)


utpeca

TCO Web interactif avec des ESP32 et des ESP8266 (4)


utpeca

TCO Web interactif avec des ESP32 et des ESP8266 (3)


utpeca

TCO Web interactif avec des ESP32 et des ESP8266 (2)


utpeca

TCO Web interactif avec des ESP32 et des ESP8266 (1)


utpeca

Affichage publicitaire avec Arduino (2)


catplus, Christian

Les articles les plus lus

Réalisation de centrales DCC avec le logiciel libre DCC++ (3)

La PWM : Qu’est-ce que c’est ? (1)

Mon premier décodeur pour les aiguillages DCC

La rétro-signalisation sur Arduino

Comment piloter trains et accessoires en DCC avec un Arduino (1)

Réalisation de centrales DCC avec le logiciel libre DCC++ (1)

Chenillard de DEL

Mise en oeuvre du Bus CAN entre modules Arduino (2)

LaBox, Une Centrale DCC polyvalente et abordable (1)

Décodeur pour aiguillage à solénoïdes sur Arduino