LOCODUINO

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

samedi 22 juillet 2017

22 visiteurs en ce moment

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

Troisième partie : Itineraires et exemple

. Par : Pierre59

Avec les classes aiguille, zone et signaux, on dispose des outils de base pour faire des choses intéressantes, notamment des itinéraires. Ceci sous la forme de poste(s) d’aiguillages à itinéraires ou de gestionnaire de réseau à itinéraires. Un exemple complet de gestionnaire de réseau sera aussi présenté avec un réseau minimaliste, le Locodrome. Ce gestionnaire de réseau complet est destiné à fonctionner sur un Arduino Uno, il pourra être testé complètement par le biais d’un TCO virtuel avec circulation effective de trains (tout aussi virtuels). Ce TCO virtuel est un programme écrit en Processing qui communique avec le gestionnaire par une liaison USB/série.

Sur le réseau SNCF il y a de nombreux postes d’aiguillage, dans les gares, aux bifurcations, … . Ces postes communiquent entre eux pour faire circuler les trains en assurant la sécurité. A la SNCF il y a une grande variété de types de postes d’aiguillage, en voici quelque uns :

  • des postes électromécaniques à leviers individuels
  • des postes électromécaniques à itinéraires
  • des postes électriques à relais à itinéraires
  • des postes à commande informatique
  • des postes entièrement informatisés

La plupart des ces postes sont équipés de TCO (Tableau de Contrôle Optique), affichant le tracé des voies, l’état des zones, l’état des signaux, … .

Sur nos réseaux on peut avoir plusieurs postes d’aiguillage avec chacun son TCO, un peu comme à la SNCF, le gestionnaire de réseau pouvant être centralisé ou réparti, mais souvent aussi on a un "poste" unique avec affichage sur le TCO de tout le tracé du réseau, c’est le gestionnaire de réseaux centralisé, c’est moins réaliste mais plus agréable à utiliser.

A l’usage les postes à itinéraires sont plus pratiques à exploiter sur nos réseaux, l’accent sera donc mis ici sur ce type de poste, donc sur un gestionnaire avec itinéraires.

Un itinéraire c’est un tracé de voie pour qu’un train puisse aller d’un point du réseau à un autre. Pour qu’un train puisse parcourir un itinéraire en toute sécurité, il faut positionner des aiguilles et ouvrir des signaux, mais aussi contrôler les enclenchements.

On distingue deux types de fonctionnement dans les itinéraires, le transit rigide et le transit souple. Avec le transit rigide, les ressources de l’itinéraire (aiguilles, zones, …) ne sont libérées que quand le train a parcouru tout l’itinéraire (à la libération de la dernière zone). Le transit souple libère les ressources au fur et a mesure du passage du train, les ressources libérées peuvent êtres réutilisées tout de suite par un autre itinéraire en attente.

Enclenchement

Une notion importante avec les postes d’aiguillage est l’enclenchement, il y a plusieurs types d’enclenchements :

  • des enclenchements entre itinéraires, des itinéraires qui utilisent des parties de voies communes ne peuvent bien évidemment pas êtres formés en même temps
  • des enclenchements avec les aiguilles, il faut que les aiguilles utilisées par l’itinéraire soient dans le bon sens
  • des enclenchements avec les signaux, on ne peut ouvrir le signal que si les aiguilles sont dans la bonne position
  • des enclenchements avec les zones, l’occupation d’une zone, consécutive à la présence d’un train, empêche la manoeuvre des aiguilles ou la formation d’itinéraires
  • des enclenchements dits d’approche, quand un itinéraire est formé et le signal ouvert (donc l’avertissement aussi), il n’est plus question de pouvoir fermer le signal dès que le train franchit le signal d’avertissement (si le train a vu l’avertissement ouvert et l’a dépassé c’est trop tard pour fermer le carré qui le suit).
  • des enclenchements par autorisations, quand plusieurs postes d’aiguillage peuvent établir des itinéraires utilisant des parties de voies communes (essentiellement des voies uniques) on utilise des autorisations, seul celui qui a l’autorisation peut établir l’itinéraire.

Ces enclenchements sont essentiels pour la sécurité des trains, pour éviter les prises en écharpe, les tamponnements frontaux (nez à nez), les déraillements sur les aiguilles mal positionnées, … . La prévention du rattrapage des trains reste réalisée par le "cantonnement" ou le "block" (BAL, BAPR, BMU, …).

Dans les postes d’aiguillages électromécaniques (à leviers individuels ou à leviers d’itinéraires) certains enclenchements sont réalisés par des moyens mécaniques (tables d’enclenchements ou serrures et clés). D’autres enclenchements sont réalisés par des moyens électriques. Dans les postes plus modernes tous les enclenchements sont réalisés par des moyens électriques, voire informatiques. Sur nos réseaux les enclenchements vont êtres réalisés par des moyens informatiques (gestionnaire de réseau) en collaboration avec des moyens électriques (rétrosignalisation).

Itinéraires

Les classes vues dans les articles précédents et la classe itinéraire que l’on va voir ici, ont tout ce qu’il faut pour réaliser tous ces enclenchements. Pour commencer on va s’intéresser aux itinéraires à transit rigide. Mais il ne faut pas oublier que les trains doivent respecter les signaux, condition indispensable pour avoir une sécurité totale.

Pour les itinéraires il faut une classe Itinéraire. Comme pour les zones on va avoir une classe de base Itinéraire et des classes dérivées pour chaque itinéraire particulier.

Un itinéraire peut avoir plusieurs états : libre, formé, en-attente, …
Les états de base sont libre et formé, l’état en-attente est utilisé pour mémoriser des itinéraires qui ne sont pas formables mais qui seront formés dès que possible (mémorisation).

Pour réaliser les enclenchements et gérer les itinéraires on va utiliser essentiellement quatre méthodes :

  • une méthode formable() qui teste si l’itinéraire peut être formé (test des enclenchements entre itinéraires, avec les zones, avec les autorisations, …), en bref que rien ne s’oppose à la formation de l’itinéraire.
  • une méthode former() qui établit effectivement l’itinéraire sans contrôles (ils doivent être fait avant avec la méthode formable() ), il faut manoeuvrer les aiguilles et ouvrir les signaux, voire gérer les autorisations, et éventuellement le traçage l’itinéraire sur le TCO.
  • une méthode deformable() qui teste si l’itinéraire peut être libéré (pas de train sur l’itinéraire, pas d’enclenchement d’approche).
  • une méthode deformer() qui libère effectivement l’itinéraire sans contrôles (ils doivent être fait avant avec la méthode deformable() ), cela se limite généralement à des fermetures de signaux, voire à des libérations d’autorisations, et éventuellement effacement de l’itinéraire sur le TCO.
  1. enum { ITINERAIRE_LIBRE,ITINERAIRE_FORME,ITINERAIRE_EN_ATTENTE, /* ... */ }; // etats possibles
  2.  
  3. class Itineraire { // classe de base des itineraires
  4. byte etat= ITINERAIRE_LIBRE; // l'etat de l'itineraire (libre, en attente, forme, ... )
  5. Itineraire() {} // constructeur vide
  6.  
  7. boolean libre() { return etat== ITINERAIRE_LIBRE; }
  8. boolean forme() { return etat==ITINERAIRE_FORME; }
  9. boolean enAttente() { return etat==ITINERAIRE_EN_ATTENTE; } // OPTION
  10.  
  11. virtual boolean formable() { return false; } // teste si l'itinéraire est formable
  12. virtual void former() {} // forme l'itineraire
  13.  
  14. virtual boolean deformable() { return false; } // teste si l'itinéraire peut etre detruit
  15. virtual void deformer() {} // detruit l'itineraire
  16.  
  17. boolean formation() { // essai de formation manuelle
  18. if (formable()) { former(); etat=ITINERAIRE_FORME; return true; }
  19. return false;
  20. }
  21. boolean destruction() { // essai de destruction manuelle
  22. if (deformable()) { deformer(); etat=ITINERAIRE_LIBRE; return true; }
  23. return false;
  24. }
  25.  
  26. void detruire() { // destruction automatique (appelle par les methodes desactions() de zone)
  27. if (etat==ITINERAIRE_LIBRE) return;
  28. if (etat==ITINERAIRE_EN_ATTENTE) return; // OPTION
  29. deformer();
  30. tracer(false); // OPTION
  31. etat=ITINERAIRE_LIBRE;
  32. }
  33.  
  34. virtual void tracer(boolean b) {} // trace de l'tineraire OPTION
  35.  
  36. void effacer(); // effacement d'un itineraire memorise OPTION
  37. };

On retrouve la variable d’état avec des méthodes de test et les quatre méthodes dont on a parlé avant. Ces méthodes seront redéfinies dans les classes dérivées.

La méthode formation() essaie de former l’itinéraire, elle teste si l’itinéraire est formable, si oui elle le forme, le résultat de la méthode rends compte de la formation effective ou non de l’itinéraire. Il est impératif que pendant l’exécution de cette méthode l’état global du réseau ne change pas. Dans le cas où l’itinéraire n’est pas formable on peut le mettre en mémoire (on en reparlera plus tard). Cette méthode est appelée par des commandes manuelles d’itinéraires.

La méthode destruction() fait la même chose pour la destruction. Cette méthode est appelée par des commandes manuelles d’itinéraires.

La méthode détruire() est utilisée pour la destruction automatique des itinéraires, elle est appelée par la rétrosignalisation par le biais des méthodes desactions() de zone, aucun test préalable n’est nécessaire.

Les méthodes optionnelles tracer() et effacer() permettent de tracer et d’effacer un itinéraire sur un TCO.

Il y a aussi des méthodes utilitaires pour les enclenchements (voir plus loin et dans le fichier" Fonctions.h"). Pour des postes d’aiguillage avec beaucoup d’itinéraires le contrôle des enclenchements peut être assez lourd et compliqué, il ne faut pas hésiter à rajouter toutes les méthodes utilitaires nécessaires pour faciliter la prise en compte.

C’est tout, mais il reste pas mal à faire pour l’utilisation effective.

Il faut écrire une classe par itinéraire, cet classe héritant de la classe de base Itinéraire, exemple :

  1. class TC: Itineraire { // un itineraire particulier de T vers C
  2.  
  3. virtual boolean formable() { return libres(iTC,iCT) && // test d'enclenchement sur les itineraires
  4. libres(z8,z9); } // test d'enclenchement sur les zones
  5.  
  6. virtual void former() { aig1->devier(); aig2->directer(); // manoeuvre des aiguilles
  7. c2->ouvrir(); } // ouverture du signal
  8.  
  9. virtual boolean deformable() { return libres(z8,z9,z10); } // test d'enclenchement sur les zones (dont zones d'approche)
  10.  
  11. virtual void deformer() { c2->fermer(); } // fermeture du signal
  12.  
  13. virtual void tracer(boolean b) { z8->tracer(b); z9->tracer(b); } // OPTION
  14. };

Une méthode tracer() est rajoutée à la classe Zone pour mettre en oeuvre le tracé de l’itinéraire sur une zone particulière.

Autorisations

Avec la classe Itinéraire il peut être difficile de réaliser les enclenchements de nez à nez dans des cas un peu compliqués. Pour faciliter leur prise en compte on va introduire un outil annexe, l’autorisation.

Quand deux postes d’aiguillages sont reliés par une voie unique, il faut faire en sorte que les postes ne puissent envoyer deux trains sur la voie en même temps, cela ferait une catastrophe (nez à nez). A la SNCF pour se prémunir de telles situations on utilise un enclenchement appelé autorisation, le poste qui détient l’autorisation peut envoyer un train, l’autre pas.

Sur nos réseaux qui comportent souvent un ovale en voie unique, c’est souvent le même poste à chaque bout de la voie unique, mais on utilisera aussi une autorisation pour simplifier.

Si la voie unique est "cantonnée" on peut envoyer plusieurs trains, dans le même sens, à la suite (fonction du nombre de "cantons"), cela complique un peu la gestion de l’autorisation.

Une autorisation est prise pour un sens de circulation donné (pair ou impair), pour établir un itinéraire. Une fois l’autorisation prise elle doit être verrouillée jusqu’à la destruction de l’itinéraire, elle peut être alors rendue.

Dans le cas des voies doubles (sans IPCS -Installations Permanentes de Contre-Sens- et sans banalisation), on n’a pas besoin d’autorisation car il n’y a pas de risque de nez à nez.

Comme d’habitude on écrit une classe Autorisation. Cette classe comporte deux booléens un pour le sens et un pour le verrouillage. Il faut une méthode booléenne testant si l’autorisation est prenable, un méthode pour prendre l’autorisation et une méthode pour rendre l’autorisation :

  1. class Autorisation { // classe de base des autorisations
  2. boolean sens=false; // pair=false impair=true
  3. boolean prise=false; // verrou
  4.  
  5. Autorisation() {} // constructeur vide
  6.  
  7. boolean prenable(boolean s) { // s : sens pair ou impair
  8. if (sens==s) return !prise; // meme sens
  9. return !prise && aucunTrain(); // pas meme sens
  10. }
  11.  
  12. void prendre(boolean s) { sens=s; prise=true; } // s : sens pair ou impair
  13. void rendre() { prise=false; }
  14.  
  15. virtual boolean aucunTrain() { return false; }
  16. };

Une autorisation est prenable pour un sens donné si elle est déjà dans le bon sens et pas prise (pas verrouillée) sinon il faut qu’il n’y ait aucun train (sur la voie unique) pour pouvoir changer le sens et qu’elle ne soit pas prise (pas verrouillée).

Les méthodes prendre() et rendre() gèrent les deux booléens.

On a aussi besoin d’une méthode virtuelle aucunTrain() qui teste s’il n’y a aucun train sur la voie unique.

Pour utiliser une autorisation, il faut écrire une classe qui hérite de la classe autorisation et qui redéfinit la méthode aucunTrain(), exemple :

  1. class Aut:Autorisation {
  2. virtual boolean aucunTrain() { return libres(z1,z3,z4); }
  3. };

Fonctions utilitaires

Quand on écrit un gestionnaire, on est très souvent amené à écrire des séries d’appels à une même méthode du genre :

  1. objet1->methode(); objet2->methode(); objet3->methode();

Dans de tels cas il est pratique de pouvoir écrire :

  1. methode(objet1,objet2,objet3,);

Cela nécessite alors d’écrire des fonctions utilitaires avec un nombre variable d’arguments.

En C++ on peut avoir des fonctions avec un nombre variable d’arguments, mais ce n’est pas très pratique, il faut un premier argument pour préciser le nombre et il n’y a pas de contrôle ni de nombre ni de type des arguments (cela utilise des macros).

Aussi il est plus pratique d’avoir une fonction utilitaire pour chaque nombre d’arguments (on peut en rajouter si ce n’est pas suffisant). Ce mécanisme a déja été utilisé dans les articles précédents. Toutes ces fonctions utilitaires sont regroupées dans un fichier Fonctions.h.

Le Locodrome est un réseau minimaliste, qui convient parfaitement ici comme exemple de gestionnaire. Ce réseau comporte juste un ovale avec une voie d’évitement :

On trouvera aussi ce dessin dans les fichiers joints pour faciliter la lecture de l’article. On a :

  • deux aiguilles a0 et a1
  • six zones z0 à z5 dont deux "cantons" de BAL (z3 et z4)
  • six carrés C1 à C6
  • deux sémaphores de BAL S1 et S2
  • huit itinéraires XA, XB, AX, BX, YA, YB, AY et BY

il faudra aussi une autorisation pour la gestion des enclenchements de la voie unique (X à Y et inversement).

Les séparations des zones sont matérialisées part des petites croix (x).

On va écrire le gestionnaire le plus simple possible (destiné à tenir dans un Arduino Uno), pour cela on choisit d’utiliser des carrés sans BAL et juste deux sémaphores de BAL, ainsi qu’un transit rigide (le transit souple n’apporte ici pas beaucoup plus de possibilités). On pourrait simplifier encore en utilisant deux signaux d’avertissement à la place des sémaphores, mais cela limite encore plus les possibilités de circulation, et montre moins le potentiel du gestionnaire.

Pour rendre le gestionnaire fonctionnel, il sera interfacé avec un TCO virtuel en Processing, communiquant avec le gestionnaire par un "bus" USB/série. On pourra aussi y faire circuler des trains virtuels en conduite manuelle. Ce TCO sera décrit sur le forum.

Les possibilités de circulation sont limitées, on ne peut avoir raisonnablement que deux trains.

Conformément à la SNCF :

  • C3,C4,C5 et C6 sont des carrés avec avertissement, feux : C, A ou Vl. L’avertissement est pour les sémaphores suivants (S1 ou S2).
  • C1 et C2 sont des carrés avec rappel de ralentissement et avertissement, feux : C, A, Vl et RR. L’avertissement est pour les carrés suivants (C3 ou C5 et C4 ou C6). Le rappel de ralentissement est présenté quand l’aiguille protégée est déviée.
  • S1 et S2 sont des sémaphores de BAL avec ralentissement, feux : S, A, Vl et R. L’avertissement est pour les carrés suivants (C1 ou C2). Le ralentissement est présenté quand l’aiguille protégée par le carré suivant est déviée.

On va donc devoir écrire huit classes pour les signaux, six classes pour les zones, huit classes pour les itinéraires et une classe pour l’autorisation. Il faut aussi déclarer des variables pour les objets : deux aiguilles, huit signaux, six zones, huit itinéraires et une autorisation.

Objets

Il faut déclarer tous les objets dont on a besoin. Commençons par les objets aiguille :

  1. Aiguille* a0=new Aiguille(0);
  2. Aiguille* a1=new Aiguille(1);

on utilise le plus petit constructeur, juste un n° pour commander effectivement les aiguilles.

Les signaux :

  1. SignalBAL* s1=new S1(0); // la classe S1 hérite de SemaphoreBAL
  2. SignalBAL* s2=new S2(1); // la classe S2 hérite de SemaphoreBAL
  3.  
  4. Signal* c1=new C1(2); // la classe C1 hérite de CarreAvertissementRappelRalentissement
  5. Signal* c2=new C2(3); // la classe C2 hérite de CarreAvertissementRappelRalentissement
  6.  
  7. Signal* c3=new C3(4); // la classe C3 hérite de CarreAvertissement
  8. Signal* c5=new C5(5); // la classe C4 hérite de CarreAvertissement
  9. Signal* c4=new C4(6); // la classe C5 hérite de CarreAvertissement
  10. Signal* c6=new C6(7); // la classe C6 hérite de CarreAvertissement

On utilise le plus petit constructeur, juste un n° pour commander effectivement les signaux.

Les zones :

  1. Zone* z0=new Z0(0); // Zone* z0=new Z0(0,c4,c3);
  2. Zone* z1=new Z1(1); // Zone* z1=new Z1(1,c6,c5);
  3. Zone* z2=new Z2(2); // Zone* z2=new Z2(2,NULL,NULL);
  4. Zone* z3=new Z3(3); // Zone* z3=new Z3(3,s2,c1);
  5. Zone* z4=new Z4(4); // Zone* z4=new Z4(4,c2,s2);
  6. Zone* z5=new Z5(5); // Zone* z5=new Z5(5,NULL,NULL);

On utilise le constructeur avec un n° pour commander le TCO. Les signaux attachés à la zone ne sont pas nécessaires pour le gestionnaire minimum, ils sont présents en commentaires pour montrer leur utilisation (ils pourront servir par exemple pour des extensions).

Les itinéraires :

  1. Itineraire* xa=new XA(0);
  2. Itineraire* xb=new XB(1);
  3. Itineraire* ax=new AX(2);
  4. Itineraire* bx=new BX(3);
  5. Itineraire* ya=new YA(4);
  6. Itineraire* yb=new YB(5);
  7. Itineraire* ay=new AY(6);
  8. Itineraire* by=new BY(7);

On a besoin d’un n° d’itinéraire pour gérer le TCO. Une variable entière est ajoutée à la classe Itinéraire ainsi qu’un constructeur avec un paramètre entier.

Et finalement l’autorisation :

  1. Autorisation* aut=new Aut;

On aura aussi besoin de quelques tables pour faire l’interface avec le TCO et pour la rétrosignalisation :

  1. Signal* tableSignaux[] {s1,s2, c1,c2,c3,c4,c5,c6}; // la table des signaux pour le TCO
  2.  
  3. Zone* tableZones[] {z0, z1, z2, z3, z4, z5}; // la table des zones pour la retrosignalisation
  4.  
  5. Itineraire* tableItineraires[] {xa,xb,ax,bx,ya,yb,ay,by}; // la table des itinéraires pour TCO

Signaux

Il faut maintenant écrire les huit classes pour les signaux, comme le réseau est symétrique on va associer les classes semblables.

Classes S1 et S2

  1. class S1:SemaphoreBALRalentissement {
  2.  
  3. S1(int n):SemaphoreBALRalentissement(n) {} // constructeur mini
  4.  
  5. virtual Signal* suivant() { return c1; }
  6. virtual Signal* precedent() { return selonAiguille(a1,c4,c6); }
  7. virtual boolean cantonOccupe() { return z3->occupee(); }
  8. };

Le suivant de s1 est le carré c1, le précédent est c4 si l’aiguille a1 est directe, c6 si elle dest déviée. Le "canton" est réduit à une seule zone z3. La classe S2 est symétrique de S1 :

  1. class S2:SemaphoreBALRalentissement {
  2. public:
  3. S2(int n):SemaphoreBALRalentissement(n) {} // constructeur mini
  4. virtual Signal* suivant() { return c2; }
  5. virtual Signal* precedent() { return selonAiguille(a0,c3,c5); }
  6. virtual boolean cantonOccupe() { return z4->occupee(); }
  7. };

Classes C1 et C2

  1. class C1:CarreAvertissementRappelRalentissement {
  2.  
  3. C1(int n):CarreAvertissementRappelRalentissement(n) {} // constructeur mini
  4.  
  5. virtual Signal* suivant() { return selonAiguille(a0,c3,c5); }
  6. virtual Signal* precedent() { return s1; }
  7. virtual boolean ralentissement30() { return a0->deviee(); }
  8. };

Le signal précédent est s1, le signal suivant dépends de la position de l’aiguille a0. Si a0 est directe le suivant est c3, si elle est déviée c’est c5. Si l’aiguille a0 est déviée on a un ralentissement à 30.

La classe C2 est la symétrique de C1 :

  1. class C2:CarreAvertissementRappelRalentissement {
  2.  
  3. C2(int n):CarreAvertissementRappelRalentissement(n) {} // constructeur mini
  4.  
  5. virtual Signal* suivant() { return selonAiguille(a1,c4,c6); }
  6. virtual Signal* precedent() { return s2; }
  7. virtual boolean ralentissement30() { return a1->deviee(); }
  8. };

Classes C3 C5 C4 et C6

  1. class C3:public CarreAvertissement {
  2.  
  3. C3(int n):CarreAvertissement(n) {} // constructeur mini
  4.  
  5. virtual Signal* suivant() { return s1; }
  6. virtual Signal* precedent() { return c1; }
  7. };

Le signal suivant est s1, le précédent est c1.

Les trois autres classes sont semblables :

  1. class C5:public CarreAvertissement {
  2.  
  3. C5(int n):CarreAvertissement(n) {} // constructeur mini
  4.  
  5. virtual Signal* suivant() { return s1; }
  6. virtual Signal* precedent() { return c1; }
  7. };
  8.  
  9. class C4:public CarreAvertissement {
  10.  
  11. C4(int n):CarreAvertissement(n) {} // constructeur mini
  12.  
  13. virtual Signal* suivant() { return s2; }
  14. virtual Signal* precedent() { return c2; }
  15. };
  16.  
  17. class C6:public CarreAvertissement {
  18.  
  19. C6(int n):CarreAvertissement(n) {} // constructeur mini
  20.  
  21. virtual Signal* suivant() { return s2; }
  22. virtual Signal* precedent() { return c2; }
  23. };

Zones

Ici aussi on aussi a une symétrie, on va donc associer les classes semblables.

classes Z0 et Z1

  1. class Z0 : public Zone {
  2.  
  3. Z0(int n):Zone(n) {} // constructeur mini
  4. // Z0(int n,Signal* sp,Signal* si):Zone(n,sp,si) {} // constructeur
  5.  
  6. // virtual Zone* suivantePaire() { return selonAiguille(a0,z2,NULL); }
  7. // virtual Zone* suivanteImpaire() { return selonAiguille(a1,z5,NULL); }
  8. };

Le constructeur n’a qu’un argument, le n° de la zone (pour le TCO). Le constructeur acceptant en plus deux signaux n’est pas nécessaire pour le Locodrome, il est ici en commentaire.

La zone paire suivante dépend de la position le l’aiguille a0. Si l’aiguille est directe c’est z2, si l’aiguille est déviée c’est NULL, il n’y a pas de zone suivante, si on fait passer un train il va dérailler. Cet aspect est essentiel pour le bon fonctionnement de la méthode provenance() de la classe Zone (cette méthode servira dans les extensions).

Même chose pour la suivante impaire. Pour la version minimale du Locodrome on en a pas besoin de ces méthodes, c’est pour cela qu’elles sont en commentaires, mais si on veut faire évoluer le Locodrome on en aura besoin.

La classe Z1 est très semblable (juste une inversion pour les aiguilles) :

  1. class Z1 : public Zone {
  2.  
  3. Z1(int n):Zone(n) {} // constructeur mini
  4. // Z1(int n,Signal* sp,Signal* si):Zone(n,sp,si) {} // constructeur
  5.  
  6. // virtual Zone* suivantePaire() { return selonAiguille(a0,NULL,z2); }
  7. // virtual Zone* suivanteImpaire() { return selonAiguille(a1,NULL,z5); }
  8. };

Classes Z2 et Z5

Les classes Z2 et Z5 sont concernées par la rétrosignalisation :

  1. class Z2 : public Zone {
  2.  
  3. Z2(int n):Zone(n) {} // constructeur mini
  4. // Z2(int n,Signal* sp,Signal* si):Zone(n,sp,si) {} // constructeur
  5.  
  6. virtual void actions() { aubiner(c1,c4,c6); }
  7. virtual void desactions() { detruire(xa,xb,ax,bx); }
  8.  
  9. // virtual Zone* suivantePaire() { return z3; }
  10. // virtual Zone* suivanteImpaire() { return selonAiguille(a0,z0,z1); }
  11. };

Lors de l’occupation de la zone z2 la méthode actions() est appelée/ Il faut aubiner (fermer) un des signaux c1,c4 ou c6, un seul peut être ouvert. Comme on rechigne à tester lequel on les aubine tous. Pour ceux qui sont déjà fermés cela ne fera rien et celui qui est ouvert sera fermé. En pratique ou devrait écrire :

  1. c1->aubiner(); c4->aubiner(); c6->aubiner();

Mais des fonctions utilitaires permettent de faciliter l’écriture (surtout sur de grands réseaux).

Lors de la libération de la zone z2 la méthode desactions() est appelée. Il faut détruire (on est en transit rigide) un des itinéraires xa, xb, ax ou bx/ Comme pour les signaux un seul peut être formé et ici aussi une fonction utilitaire est utilisée.

La classe Z5 est symétrique :

  1. class Z5 : public Zone {
  2.  
  3. Z5(int n):Zone(n) {} // constructeur mini
  4. // Z5(int n,Signal* sp,Signal* si):Zone(n,sp,si) {} // constructeur
  5.  
  6. virtual void actions() { aubiner(c2,c3,c5); }
  7. virtual void desactions() { detruire(ya,yb,ay,by); }
  8.  
  9. // virtual Zone* suivantePaire() { return selonAiguille(a1,z0,z1); }
  10. // virtual Zone* suivanteImpaire() { return z4; }
  11. };

Classes Z3 et Z4

Les deux zones z3 et z4 constituent les "cantons" pour les deux sémaphores de BAL s1 et s2, elles sont donc concernées aussi par la rétrosignalisation :

  1. class Z3 : public Zone {
  2.  
  3. Z3(int n):Zone(n) {} // constructeur mini
  4. // Z3(int n,Signal* sp,Signal* si):Zone(n,sp,si) {} // constructeur
  5.  
  6. virtual void actions() { s1->aubiner(); }
  7. virtual void desactions() { s1->desaubiner(); }
  8.  
  9. // virtual Zone* suivantePaire() { return z4; }
  10. // virtual Zone* suivanteImpaire() { return z2; }
  11. };

Lors de l’occupation de la zone z3 il faut aubiner (fermer) le sémaphore s1. Lors de la libération de la zone z3 il faut désaubiner (ouvrir) le même sémaphore.

La classe Z4 est symétrique :

  1. class Z4 : public Zone {
  2.  
  3. Z4(int n):Zone(n) {} // constructeur mini
  4. // Z4(int n,Signal* sp,Signal* si):Zone(n,sp,si) {} // constructeur
  5.  
  6. virtual void actions() { s2->aubiner(); }
  7. virtual void desactions() { s2->desaubiner(); }
  8.  
  9. // virtual Zone* suivantePaire() { return z5; }
  10. // virtual Zone* suivanteImpaire() { return z3; }
  11. };

Il faut noter que si un train circule en sens pair (de X vers Y) le sémaphore de sens impair s1 va être aubiné (par occupation de z3), puis désaubiné (par libération de z3), c’est normal.

Ces classes zones montrent bien l’utilisation de la rétrosignalisation pour gérer les signaux et les itinéraires. Elles montrent aussi que les zones sont au coeur du gestionnaire de réseau, beaucoup de choses passent par elles (et ce n’est pas fini).

Itinéraires

Comme pour les zones on a une symétrie gauche/droite, on va donc aussi les regrouper.

Classes XA, XB, YA et YB
Commençons par l’itinéraire XA :

  1. class XA:public Itineraire {
  2.  
  3. XA():Itineraire(0) {} // constructeur
  4.  
  5. virtual boolean formable() { return libres(xa,xb,ax,bx, ya) && libres(z2,z0); }
  6. virtual void former() { a0->directer(); c1->ouvrir(); }
  7. virtual boolean deformable() { return libres(z3,z2); }
  8. virtual void deformer() { c1->fermer(); }
  9. virtual void tracer(boolean b) { trace(b,z3,z2,z0); } // OPTION
  10. };

Pour pouvoir former l’itinéraire XA il faut que tous les itinéraires utilisant la zone z2 soient libres soit XA,XB,AX et BX. Il faut aussi que l’itinéraire YA soit libre pour éviter les nez à nez. Il faut aussi que les zones z2 et z0 soient libres.

Pour former l’itinéraire il faut juste mettre l’aiguille a0 en position directe et ouvrir le carré c1.

Pour détruire l’itinéraire (manuellement) il faut que les zones utilisées par l’itinéraire soient libres, ici z2. Il faut aussi qu’il n’y ait pas d’enclenchement d’approche, donc que z3 soit libre. L’enclenchement d’approche devrait aussi inclure une partie de la zone z4 (juste devant le signal d’avertissement), mais c’est un peu plus difficile à mettre en oeuvre.

La destruction automatique des itinéraires est faite par la rétrosignalisation via les méthodes desaction() des zones z2 et z5.

La méthode optionnelle tracer() permet un tracé de l’itinéraire sur un TCO, elle liste les zones qui doivent êtres marquées (b=true) ou démarquées (b=false), en utilisant une fonction utilitaire.

Il faut remarquer que l’on ne peut envoyer un train sur la voie A que si celle-ci n’est pas occupée.
Envoyer un "train" sur une voie occupée est nécessaire, par exemple, pour mettre une locomotive en tête d’une rame, dans ce cas normalement on a un itinéraire en mode manoeuvre (avec le signal qui s’ouvre avec un feu manoeuvre (blanc lunaire)). Cet aspect n’est pas pris en compte ici, mais il est tout à fait réalisable.

L’itinéraire XB est très semblable :

  1. class XB:public Itineraire {
  2. XB():Itineraire(1) {} // constructeur
  3. virtual boolean formable() { return libres(xa,xb,ax,bx,yb) && libres(z2,z1); }
  4. virtual void former() { a0->devier(); c1->ouvrir(); }
  5. virtual boolean deformable() { return libres(z3,z2); }
  6. virtual void deformer() { c1->fermer(); }
  7. virtual void tracer(boolean b) { trace(b,z3,z2,z1); }
  8. };

De même que les itinéraires YA et YB (symétriques) :

  1. class YA:public Itineraire {
  2. YA():Itineraire(4) {} // constructeur
  3. virtual boolean formable() { return libres(ya,yb,ay,by,xa) && libres(z5,z0); }
  4. virtual void former() { a1->directer(); c2->ouvrir(); }
  5. virtual boolean deformable() { return libres(z4,z5); }
  6. virtual void deformer() { c2->fermer(); }
  7. virtual void tracer(boolean b) { trace(b,z4,z5,z0); }
  8. };
  9.  
  10. class YB:public Itineraire {
  11. YB():Itineraire(5) {} // constructeur
  12. virtual boolean formable() { return libres(ya,yb,ay,by,xb) && libres(z5,z1); }
  13. virtual void former() {a1->devier(); c2->ouvrir(); }
  14. virtual boolean deformable()
  15. virtual void deformer() { libres(z4,z5); }
  16. /virtual void tracer(boolean b) { trace(b,z4,z5,z1); }
  17. };

Classes AX BX et AY BY

Ces quatre itinéraires peuvent envoyer des trains sur la voie unique, comme cette voie unique comporte deux "cantons" la réalisation des enclenchements est plus délicate. Pour faciliter la chose on utilise une autorisation (aut).

Commençons par la classe AX :

  1. class AX:public Itineraire { // sens pair
  2. AX():Itineraire(2) {} // constructeur
  3. virtual boolean formable() { return libres(xa,xb,ax,bx,ay,by) && libres(z2,z3,z4) && aut->prenable(SENS_PAIR); }
  4. virtual void former() { a0->directer(); c4->ouvrir(); aut->prendre(SENS_PAIR); }
  5. virtual boolean deformable() { return libres(z0,z2); } !
  6. virtual void deformer() { c4->fermer(); aut->rendre(); }
  7. virtual void tracer(boolean b) { trace(b,z0,z2,z3); }
  8. };

Pour que l’itinéraire soit formable il faut pouvoir prendre l’autorisation. Quand on forme l’itinéraire il faut prendre l’autorisation et la rendre quand on le détruit.

Dans la méthode deformable() l’enclenchement d’approche devrait aussi porter sur les zones z5 et une partie de z4 (celle devant le signal d’avertissement), c’est c2 qui fait l’avertissement de c4, mais ce test doit être conditionnel, il dépend de la position de l’aiguille a1 et de l’ouverture du signal c2, pour simplifier on ne fera pas ce test.

il faut remarquer ici la complexité des enclenchements pour un si petit réseau (et on a simplifié les enclenchements d’approche), mais avec la programmation objet tout est réalisable (ou presque !).

La classe BX est très semblable :

  1. class BX:public Itineraire {
  2. public:
  3. BX():Itineraire(3) {}
  4. virtual boolean formable() { return libres(xa,xb,ax,bx,ay,by) && libres(z2,z3,z4) && aut->prenable(SENS_PAIR); }
  5. virtual void former() { a0->devier(); c6->ouvrir(); aut->prendre(SENS_PAIR); }
  6. virtual boolean deformable() { return libres(z1,z2); }
  7. virtual void deformer() { c6->fermer(); aut->rendre(); }
  8. virtual void tracer(boolean b) { trace(b,z1,z2,z3); }
  9. };

Les deux itinéraires de la partie droite :

  1. class AY:public Itineraire {
  2. AY():Itineraire(6) {} // constructeur
  3. virtual boolean formable() { return libres(ya,yb,ay,by,ax,bx) && libres(z5,z4,z3) && aut->prenable(SENS_IMPAIR); }
  4. virtual void former() { a1->directer(); c3->ouvrir(); aut->prendre(SENS_IMPAIR); }
  5. virtual boolean deformable() { return libres(z0,z5); }
  6. virtual void deformer() { c3->fermer(); aut->rendre(); }
  7. virtual void tracer(boolean b) { trace(b,z0,z5,z4); }
  8. };
  9.  
  10. class BY:public Itineraire {
  11. BY():Itineraire(7) {} // constructeur
  12. virtual boolean formable() { return libres(ya,yb,ay,by,ax,bx) && libres(z5,z4,z3) && aut->prenable(SENS_IMPAIR); }
  13. virtual void former() { a1->devier(); c5->ouvrir(); aut->prendre(SENS_IMPAIR); }
  14. virtual boolean deformable() { return libres(z1,z5); }
  15. virtual void deformer() { c5->fermer(); aut->rendre(); }
  16. virtual void tracer(boolean b) { trace(b,z1,z5,z4); }
  17. };

Mise en oeuvre

Voila, le programme de gestion du Locodrome est quasiment complet, il manque la réception des informations de rétrosignalisation et des commandes d’itinéraires ainsi que l’envoi des commandes au réseau (aiguilles, signaux, … ). Pour pouvoir le tester (et plus généralement le mettre au point) en l’absence d’un vrai réseau, on va communicer avec le gestionnaire par le "bus USB/série".

Deux modes de fonctionnement sont possibles, soit en dialoguant directement avec le gestionnaire via le "Moniteur série", soit en utilisant un TCO virtuel (toujours avec le "bus USB/série"). Le mode de fonctionnement sera déterminé par la variable booléenne (TCO), les deux modes sont très proches.

Dans les fichiers joints, le gestionnaire à été modifié en ce sens. La réception d’information se fait dans la fonction loop(), chaque information se fait sous la forme d’UN caractère imprimable :

  • A à F pour occuper une zone, respectivement Z0 à Z5 (rétrosignalisation), fait l’appel de la méthode occuper() sur le bon objet zone.
  • a à f pour libérer une zone, respectivement Z0 à Z5 (rétrosignalisation), fait l’appel de la méthode liberer() sur le bon objet zone.
  • 0 à 7 pour commander un itinéraire, respectivement XA,XB,AX,BX, YA,YB,AY et BY, fait l’appel aux méthodes sur le bon objet itinéraire.

La fonction loop() teste aussi les itinéraires en attente pour essayer de les former. Il faut noter que toutes les informations reçues par le gestionnaire doivent êtres prises en compte uniquement dans la fonction loop(), si des informations sont issues d’interruptions il faut positionner une variable qui sera testée dans la fonction loop(), ceci pour garantir que quand une méthode du gestionnaire est appelée elle ira jusqu’au bout (sans que l’état du gestionnaire change) garantissant un état cohérent du gestionnaire.

Inversement le gestionnaire émet des commandes vers le réseau, ces commandes comportent trois parties, un code de commande, une information (couleur de signal ou de zone, position d’une aiguille, … ) et un numéro d’objet.

C’est la fonction tco() (voir le fichier"TCO.h") qui réalise l’émission des commandes sur le "bus USB/série", plusieurs objets appellent cette fonction :

  • les aiguille pour affichage sur le TCO
  • les signaux (sémaphores ou carrés) pour affichage sur le TCO
  • les zones pour marquer l’état (libre ou occupé) sur le TCO et pour tracer les itinéraires sur le TCO
  • les itinéraires pour gérer les couleurs de boutons du TCO (avec ou sans clignotement)

Quand le gestionnaire est en mode dialogue avec le "Moniteur série" (variable TCO=false) les commandes sont mises en forme pour les rendre lisibles dans le "Moniteur série". Quand le gestionnaire est en mode dialogue avec le TCO (variable TCO=true) les commandes sont codées sur deux octets (4 bits+4 bits+8 bits) qui seront décodés par le TCO virtuel.

Le "Moniteur série" permet des test basiques sur le gestionnaire puis
le TCO virtuel permet de tester de façon agréable et plus approfondie le gestionnaire, ceci avant de passer au réseau réel qui nécessitera aussi d’autres tests. Aperçu du TCO virtuel :

Le TCO virtuel permet aussi de faire circuler un ou deux trains tout aussi virtuels, donnant beaucoup de possibilités de test. Il faut remarquer que dans le gestionnaire rien n’est fait pour que les trains respectent les signaux, le gestionnaire est pour l’instant prévu pour une "marche à vue" des trains, on parlera de ce délicat problème dans un prochain article, par contre les train virtuels sont prévus pour respecter les signaux.

Bilan

Bien que minimaliste le Locodrome amène déjà à un gestionnaire assez compliqué, mais le programme tient facilement dans un Arduino uno en utilisant moins d’un millier d’octets de RAM et moins de 10 000 octets de mémoire FLASH, laissant de la place pour des extensions (arrêt des trains aux signaux par exemple).

La prise en compte de réseaux plus importants, n’est pas beaucoup plus compliquée, certes il y aura plus de classes à écrire et plus de contraintes sur les enclenchements, mais c’est tout à fait réalisable.

Comme dans les articles précédent on trouvera dans le fichier ci-dessous le programme de gestion du Locodrome bien écrit en C++, ainsi que le TCO virtuel écrit en Processing :

Dans les différents fichiers, du programme de gestion du Locodrome, tout ce qui est spécifique à l’interface avec le TCO virtuel est marqué "TCO". Cette version devient pour l’instant la version de référence, elle peut être utilisée pour d’autres réseaux en réécrivant le fichier "LocodromeBAL".

Pour essayer le programme de gestion du Locodrome avec le TCO virtuel consulter le fichier "Notice".

La discussion est ouverte sur le Forum : Modélisation logicielle d’un réseau - le système de Pierre59.

8 Messages

Réagissez à « Un gestionnaire en C++ pour votre réseau (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 »

Un 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

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

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)

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++

Gestion d’une gare cachée (1)

Gestion d’une gare cachée (2)

Gestion d’une gare cachée (3)

Les derniers articles

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


Jean-Luc

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


Christian Bézanger

Réalisation d’un affichage de gare ARRIVEE DEPART


Gilbert

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


Daniel

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


Jean-Luc

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


bobyAndCo

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


Pierre59

Un décodeur DCC pour 16 feux tricolores


Dominique, JPClaude, Thierry

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


bobyAndCo

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


Dominique

Les articles les plus lus

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

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

Une barrière infrarouge

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

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

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

Un chenillard de DEL

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

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

La rétro-signalisation sur Arduino