LOCODUINO

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

mardi 19 mars 2024

Visiteurs connectés : 19

Gestion d’une gare cachée (2)

« Show me the data »

.
Par : Jean-Luc

DIFFICULTÉ :

Avant de se lancer à programmer, il faut déterminer la façon dont les objets du réseau sont représentés dans le programme. Une bonne représentation permettra d’écrire un programme clair et un programme clair a de bien meilleures chances de fonctionner du premier coup.

<

Cet article va mettre en œuvre des notions avancées de programmation objet. Je vous engage à lire les articles de Thierry à ce sujet :

Dans un premier temps nous allons nous occuper de faire fonctionner cette gare indépendamment des capteurs et des actionneurs. Nous ajouterons ces derniers plus tard. La première chose à faire est d’exploiter le graphe de la gare pour y trouver une voie. Cet article traite de ce problème d’exploitation du graphe. Ensuite, faire fonctionner la gare consistera à trouver une voie en gare libre pour y stocker un train, d’une part, et sélectionner un train de la gare pour le mettre en circulation d’autre part. Ce sont deux problèmes voisins qui seront traités dans le prochain article.

À la fin de « Gestion d’une gare cachée (1) », nous avons vu la représentation de notre gare sous forme de graphe.

PNG - 131.2 kio

Nous allons traduire cette représentation en objets.

Tout d’abord, il nous faut définir des classes. Un nœud du graphe est un élément de voie. Déclarons donc une classe pour représenter un élément de voie. Il n’y a pas besoin de grand chose, juste un identifiant permettant de nommer les éléments de voie afin de les retrouver dans le graphe ainsi qu’un constructeur pour initialiser cet identifiant avec l’argument qui lui est passé et une fonction membre permettant de lire cet identifiant. Et puis nous avons besoin d’une fonction membre permettant de créer un chemin entre l’entrée de la gare et une voie de la gare, appelons cette fonction membre creeCheminVers, et entre la sortie de gare et une voie de la gare. Cette fonction membre prend pour argument l’identifiant de la voie vers laquelle on souhaite établir un chemin. Si la destination est trouvée, elle retourne true sinon elle retourne false. Il s’agit d’une fonction membre virtuelle car elle sera redéfinie concrètement pour un aiguillage, pour les voies de gare et pour les voies d’entrée et de sortie. Il s’agit même d’une fonction virtuelle pure, ce qui est dénoté par le = 0 à la fin de la déclaration. Non seulement elle est virtuelle mais, comme elle ne peut rien faire avec un objet de la classe Voie qui n’est connecté à rien, elle n’existera concrètement que dans les classes dérivées de Voie. Cela fait de Voie une classe abstraite, c’est à dire qu’on ne peut pas créer (instancier) des objets de cette classe.

class Voie
{
  private:
    byte mIdentifiant;

  public:
    Voie(byte identifiant) { mIdentifiant = identifiant; }
    byte idVoie() { return mIdentifiant; }
    virtual bool creeCheminVers(byte identifiant) = 0;
};

Cette classe de base est maintenant employée pour définir nos trois autres classes qui vont donc en hériter.

Ces voies concrètes définissent concrètement la fonction membre creeCheminVers comme nous allons le voir. Elle définissent également des fonctions membres permettant de connecter les voies entre elles. On est ici proche de ce que nous réalisons en construisant un réseau, nous connectons des voies entre elles. Ici, comme nous traitons le cas spécifique d’une gare cachée avec un graphe dédié à cette réalisation, les connexions sont simples à traiter. Une voie d’entrée connait, est connectée à, une seule autre voie, une voie en gare n’en connait aucune et un aiguillage connait deux autres voies.

Les voies d’entrée et de sortie

Commençons par la classe des voies d’entrée et de sortie. Ces voies sont particulières et différentes des voies de gare car elles ne sont connectées qu’à une seule autre voie. Nous allons appeler cette classe VoieExtreme. Cette classe possède une donnée membre, mVoie, qui est un pointeur vers une autre Voie, la fonction membre connecte qui permet de réaliser la connexion à cette autre voie et creeCheminVers.

Notez qu’on ne préjuge pas du type de voie connecté puisque mVoie est un pointeur vers un objet de type Voie. De fait, toute voie dérivée de Voie peut être connectée, la différence viendra de la fonction membre creeCheminVers qui sera différente selon la voie effectivement connectée. Comme cette fonction membre est virtuelle, c’est le creeCheminVers de la voie effectivement connectée qui sera appelé même si le pointeur est un pointeur de Voie.

Pour une VoieExtreme, la fonction creeCheminVers va comparer l’identifiant qui lui est passé en argument à l’identifiant de la voie d’entrée ou de sortie. Si il sont identiques, cela signifie que l’on est arrivé à destination, sinon on demande à la voie à laquelle elle est connectée de créer le chemin. Notez que cela permet de chercher un chemin vers une voie d’entrée, ce qui n’est guère utile car c’est le point de départ de la recherche mais c’est une généralisation du principe. Notez également l’initialisation de mVoie à NULL. Elle va permettre de savoir si le graphe a été correctement initialisé ou si certaines voies ne sont connectées à aucune autre et d’éviter de suivre des chemins qui n’existent pas.

class VoieExtreme : public Voie
{
  private:
    Voie *mVoie;

  public:
    VoieExtreme(byte identifiant) : Voie(identifiant) { mVoie = NULL; }
    void connecte(Voie *voie) { mVoie = voie; }
    virtual bool creeCheminVers(byte identifiant);
};

bool VoieExtreme::creeCheminVers(byte identifiant)
{
  if (identifiant == idVoie()) {
    return true;
  }
  else {
    if (mVoie != NULL) {
      return mVoie->creeCheminVers(identifiant);
    }
    else {
      return false;
    }
  }
}

Les voies de gare

Les voies de gare n’étant connectées à aucunes autres, rappelez vous que le graphe est orienté, elles sont très simples à représenter. Le constructeur se contente d’appeler le constructeur de Voie et creeCheminVers se contente de retourner true ou false selon que la voie de gare est la destination souhaitée ou non.

class VoieGare : public Voie
{
  public:
    VoieGare(byte identifiant) : Voie(identifiant) {}
    virtual bool creeCheminVers(byte identifiant);
};

bool VoieGare::creeCheminVers(byte identifiant)
{
  if (identifiant == idVoie()) {
    return true;
  }
  else {
    return false;
  }
}

Les aiguillages

Les aiguillages sont un petit peu plus compliqués car ils sont connectés à 2 éléments de voie. Ils vont donc posséder deux données membres, mVoieDroite et mVoieDeviee, des pointeurs vers ces deux éléments de voie et deux fonctions membres, connecteDroite et connecteDeviee vont permettre de les connecter. creeCheminVers cherche côté droit et côté dévié. Normalement un seul chemin est valable, ou aucun. On cherche d’abord côté droit et si on ne trouve pas, on cherche côté dévié. On va en profiter pour afficher, via la ligne série, le chemin qui a été trouvé afin de tester le programme.

class Aiguillage : public Voie
{
  private:
    Voie *mVoieDroite;
    Voie *mVoieDeviee;
  
  public:
    Aiguillage(byte identifiant) : Voie(identifiant) { mVoieDroite = mVoieDeviee = NULL; }
    void connecteDroite(Voie *voie) { mVoieDroite = voie; }
    void connecteDeviee(Voie *voie) { mVoieDeviee = voie; }
    virtual bool creeCheminVers(byte identifiant);
};

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

Mise en œuvre de ces objets

Nous avons notre base, construisons maintenant notre graphe. Si il apparaît que d’autre fonctions sont nécessaires nous les ajouterons par la suite.

Tout d’abord, il nous faut créer des identifiants pour nos éléments de voie. Le meilleur moyen de faire cela est de créer un enum. Voir à ce propos « Trois façons de déclarer des constantes ».

enum {
  VOIE_ENTREE,
  VOIE_SORTIE,
  AIG_ENTREE_0,
  AIG_ENTREE_1,
  AIG_ENTREE_2,
  AIG_SORTIE_0,
  AIG_SORTIE_1,
  AIG_SORTIE_2,
  VOIE_GARE_0,
  VOIE_GARE_1,
  VOIE_GARE_2,
  VOIE_GARE_3,
  AUCUNE_VOIE
};

Notez le dernier identifiant, AUCUNE_VOIE qui va nous servir plus tard. En effet, lors de la recherche d’une voie libre, si cette voie est trouvée nous retournerons son identifiant. Si aucune voie libre n’est trouvée, nous retournerons AUCUNE_VOIE.

Notez également que ces identifiants sont des nombres et que si on veut les afficher pour tester notre système, la lecture risque d’être difficile. Nous allons donc créer une fonction afficheVoie qui à partir de cet identifiant va nous afficher en clair de quel élément de voie il s’agit. Seuls les aiguillages nous intéressent à priori.

void afficheVoie(byte identifiant)
{
  switch (identifiant) {
    case AIG_ENTREE_0:
      Serial.print("Aiguillage E0"); break;
    case AIG_ENTREE_1:
      Serial.print("Aiguillage E1"); break;
    case AIG_ENTREE_2:
      Serial.print("Aiguillage E2"); break;
    case AIG_SORTIE_0:
      Serial.print("Aiguillage S0"); break;
    case AIG_SORTIE_1:
      Serial.print("Aiguillage S1"); break;
    case AIG_SORTIE_2:
      Serial.print("Aiguillage S2"); break;
    default:
      Serial.print("** INCONNU **"); break;
  }
}

Et par conséquent nous allons modifier la fonction creeCheminVers de la classe Aiguillage en remplaçant les Serial.print(idVoie()) par afficheVoie(idVoie()).

Nous allons ensuite créer nos objets.

VoieExtreme voieEntree(VOIE_ENTREE);
VoieExtreme voieSortie(VOIE_SORTIE);
Aiguillage aiguillageE0(AIG_ENTREE_0);
Aiguillage aiguillageE1(AIG_ENTREE_1);
Aiguillage aiguillageE2(AIG_ENTREE_2);
Aiguillage aiguillageS0(AIG_SORTIE_0);
Aiguillage aiguillageS1(AIG_SORTIE_1);
Aiguillage aiguillageS2(AIG_SORTIE_2);
VoieGare voieGare0(VOIE_GARE_0);
VoieGare voieGare1(VOIE_GARE_1);
VoieGare voieGare2(VOIE_GARE_2);
VoieGare voieGare3(VOIE_GARE_3);

Pour l’instant, ils ne sont pas connectés entre eux. Pour réaliser cette connexion, nous allons utiliser les fonctions connecte, connecteDroite et connecteDeviee dans setup.

  /* Connexion des elements de voie du grill d'entree */
  voieEntree.connecte(&aiguillageE0);
  aiguillageE0.connecteDroite(&voieGare0);
  aiguillageE0.connecteDeviee(&aiguillageE1);
  aiguillageE1.connecteDroite(&aiguillageE2);
  aiguillageE1.connecteDeviee(&voieGare1);
  aiguillageE2.connecteDroite(&voieGare3);
  aiguillageE2.connecteDeviee(&voieGare2);
  /* Connexion des elements de voie du grill de sortie */
  voieSortie.connecte(&aiguillageS0);
  aiguillageS0.connecteDroite(&voieGare3);
  aiguillageS0.connecteDeviee(&aiguillageS1);
  aiguillageS1.connecteDroite(&aiguillageS2);
  aiguillageS1.connecteDeviee(&voieGare2);
  aiguillageS2.connecteDroite(&voieGare0);
  aiguillageS2.connecteDeviee(&voieGare1);

  ...

Ajoutons maintenant dans setup le test des chemins vers toutes les voies de la gare que ce soit depuis l’entrée ou depuis la sortie.

  Serial.begin(9600);
  Serial.println("***** TEST");
  /* Test des chemins */
  Serial.println("===== Test de l'entree");
  Serial.println("Voie gare 0");
  voieEntree.creeCheminVers(VOIE_GARE_0);
  Serial.println("Voie gare 1");
  voieEntree.creeCheminVers(VOIE_GARE_1);
  Serial.println("Voie gare 2");
  voieEntree.creeCheminVers(VOIE_GARE_2);
  Serial.println("Voie gare 3");
  voieEntree.creeCheminVers(VOIE_GARE_3);
  
  Serial.println("===== Test de la sortie");
  Serial.println("Voie gare 0");
  voieSortie.creeCheminVers(VOIE_GARE_0);
  Serial.println("Voie gare 1");
  voieSortie.creeCheminVers(VOIE_GARE_1);
  Serial.println("Voie gare 2");
  voieSortie.creeCheminVers(VOIE_GARE_2);
  Serial.println("Voie gare 3");
  voieSortie.creeCheminVers(VOIE_GARE_3);

L’exécution conduit au résultat attendu comme vous pouvez le constater ci-dessous.

***** TEST
===== Test de l'entree
Voie gare 0
Aiguillage E0: Droit
Voie gare 1
Aiguillage E1: Devie
Aiguillage E0: Devie
Voie gare 2
Aiguillage E2: Devie
Aiguillage E1: Droit
Aiguillage E0: Devie
Voie gare 3
Aiguillage E2: Droit
Aiguillage E1: Droit
Aiguillage E0: Devie
===== Test de la sortie
Voie gare 0
Aiguillage S2: Droit
Aiguillage S1: Droit
Aiguillage S0: Devie
Voie gare 1
Aiguillage S2: Devie
Aiguillage S1: Droit
Aiguillage S0: Devie
Voie gare 2
Aiguillage S1: Devie
Aiguillage S0: Devie
Voie gare 3
Aiguillage S0: Droit

Comment cela fonctionne-il ?

Tous les chemins possibles sont explorés jusqu’à ce que la destination soit trouvée. Si nous prenons, par exemple, le chemin de la voieEntree vers la voieGare1, l’exécution du programme est la suivante :

  • voieEntree demande à aiguillageE0
    • aiguillageE0 demande à voieGare0 (droite) qui répond false
    • aiguillageE0 demande à aiguillageE1 (gauche)
      • aiguillageE1 demande à aiguillageE2 (droite)
        • aiguillageE2 demande à voieGare3 (droite) qui répond false
        • aiguillageE2 demande à voieGare2 (droite) qui répond false
      • aiguillageE1 demande à voieGare1 (gauche) qui répond true TROUVÉ !
    • TROUVÉ !
  • TROUVÉ !

Lors de l’exploration, la fonction membre creeCheminVers des aiguillages affiche la position que doit prendre l’aiguillage pour conduire à la voie recherchée, le résultat est l’affichage que l’on voit ci-dessus.

Sketch complet
Contient les classes et le test de la gare cachée

Nous avons maintenant notre base pour automatiser notre gare cachée. Il nous manque la recherche d’une voie libre, ce qui n’est pas très compliqué à faire, la recherche d’une voie occupée, même chose, et l’intégration avec les capteurs et les actionneurs. Nous verrons tout ceci dans les articles qui suivent.

2 Messages

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

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)

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

Mon premier décodeur pour les aiguillages DCC

La rétro-signalisation sur Arduino

Chenillard de DEL

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

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

Un TCO xpressnet

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