Parlons Arduino > Modélisation, Architectures logicielles et matérielles

Modélisation logicielle d'un réseau - le système de Jean-Luc

(1/18) > >>

Jean-Luc:
Bonjour,

Ma petite contribution à la modélisation.

Ayant travaillé sur l'approche objet pour la gestion d'un gare cachée, les articles sont en attente de publication, j'ai fait des classes pour chaque type de voie : aiguille, voie d'extrémité de la gare et voie en gare. Les classes en question sont instanciées et les objets sont connectés pour former un graphe. Une recherche de chemin est ensuite faite pour trouver une voie libre ou pour relier l'entrée à une voie ou bien une voie à la sortie. Le défaut est que le graphe est orienté ce qui fait qu'un aiguillage va connaître les voies en aval mais pas en amont. Donc partant de l'entrée, on trouve une voie en gare, idem partant de la sortie mais on ne trouve pas un chemin de l'entrée vers la sortie par exemple.

Titillé par DDEFF, j'ai généralisé l'idée à un graphe où chaque nœud (bout de voie) connaît ses voisins. Comme exemple, j'ai choisi la gare de DDEFF que j'ai re-dessinnée en PECO code 55 (gare dont on peut déduire deux choses d'ailleurs : 1) Ça va coûté un bras à DDEFF, rien qu'en appareils de voie, il y en a pour 1300€, 2) DDEFF a de la place, la gare fait 4,2m de long. Bref je suis jaloux !  ;D)

Je vous mets le lien vers le PDF : La gare de DDEFF en PDF

J'ai donc défini une classe abstraite de base, Voie :


--- Code: ---class Voie
{
  private:
    byte mIdentifiant;
    byte mDirection;
 
  protected:
    void fixeDirection(byte dir);
   
  public:
    Voie(byte identifiant) { mIdentifiant = identifiant; mDirection = AUCUNE_DIRECTION; }
    byte idVoie() { return mIdentifiant; }
    byte direction() { return mDirection; }
    virtual void connecteDeVoie(Voie *voie, byte connecteur) = 0;
    virtual bool creeCheminVers(byte identifiant, byte dir, Voie *venantDe = NULL) = 0;
};

--- Fin du code ---

Une voie a : 1) un identifiant, 2) une direction. On l'instancie en lui donnant un identifiant et par défaut il n'y a pas de direction fixée. La direction est fixée plus tard lorsque le graphe est construit.

connecteDeVoie permet d'établir une connexion entre deux voies, j'explique ci-dessous. creeCheminVers permet de trouver un chemin entre deux nœuds du graphe (entre deux bout de voies) en suivant une direction.

Ensuite, j'ai 5 classes :

1) les voies de garage qui n'ont qu'un seul connecteur : SORTANT



2) les voies normales qui ont 2 connecteurs : ENTRANT et SORTANT



3) les aiguillages qui ont 3 connecteurs : ENTRANT, SORTANT_DROIT et SORTANT_DEVIE



4) les traversées de jonction double (on peut ajouter également les simples) qui ont 4 connecteurs : ENTRANT, SORTANT, ENTRANT_BIS et SORTANT_BIS
5) les croisement qui ont également 4 connecteurs



Un sens normal de parcours est également défini : de entrant vers sortant.

Thierry:
Effectivement, le réseau est impressionnant...

DDEFF:
Merci Jean-Luc,

Sur le bras que ça va me coûter, c'est sûr. Je m'y suis déjà préparé psychologiquement   :( :(
Tu comprends aussi pourquoi je suis particulièrement motivé pour que ce soit commandé par du tout-Arduino, très bon marché  ;D

Sur la longueur, en employant des éléments Peco classiques, je vais sur les 4,20m, c'est vrai aussi. :( :(

En fait, le problème vient des 3 croisements.
Comme ils sont reliés aux TJD, si on les laisse tels quels, cela définit un écart de voie qui se répercute sur les autres écarts de voie, même sans croisements.
Il faut donc, pour raccourcir la gare, quasiment ne laisser du croisement que le croisement lui même (sans les petits bouts de droite qui l'accompagnent) et faire de même pour les TJD.
Comme je n'ai pas les 4,20 m (on s'en doutait), je pense ainsi réduire la longueur totale de la gare.
Y'a du boulot, du charcutage, mais je tiens à la caser quelque part.
A priori, j'ai une pièce de 3,42 m x 2,57 m et j'aurais bien tort de me plaindre.

Ta démarche m'intéresse au plus haut point, évidemment.
D'autant que je vais pouvoir comparer avec ma solution SGDD qui a été inventée pour cette gare. Et qui fonctionne.
Mais elle utilise une méthode vraiment ... non-conventionnelle (voir SGDD (1) et (2) )
J'ai le programme, pour les curieux.

J'appelle le sens "normal" sens "horaire", mais c'est un détail.
On peut peut-être s'éviter les zones genre "voie 0" en disant que ce n'est jamais que la partie de rails qui fait partie de l'aiquille 0 ? (quand la zone est très courte)

Bon courage !
Et merci pour le plan à l'échelle  ;D ;D

Jean-Luc:
La solution pour caser les 4m dans ta pièce est de courber la gare. Note que j'ai mis des voies de garage très généreuses  :)

Concernant les voies intermédiaires, du point de vu du graphe on peut effectivement les omettre mais je pense étendre le système au delà de la détermination des itinéraires.

Je continue.

Chaque classe dispose de fonction de connexion. Ainsi une voie de garage possède une unique fonction de connexion :


--- Code: ---    void connecteAVoie(Voie &voie, byte connecteur);

--- Fin du code ---


qui va connecter SORTANT à la voie et au connecteur passés en argument.

Une voie normale possède 2 fonctions de connexion :


--- Code: ---    void connecteSortantAVoie(Voie &voie, byte connecteur);
    void connecteEntrantAVoie(Voie &voie, byte connecteur);

--- Fin du code ---

qui vont connecter respectivement SORTANT et ENTRANT à la voie et au connecteur passés en argument. Si on utilise connecteSortantAVoie, une voie normale est connecté dans le sens normal (DIRECTION_AVANT) si on utilise connecteEntrantAVoie, elle est connectée dans le sens inverse (DIRECTION_ARRIERE).

Un aiguillage possède 3 fonctions de connexion :


--- Code: ---    void connecteSortantDroitAVoie(Voie &voie, byte connecteur);
    void connecteSortantDevieAVoie(Voie &voie, byte connecteur);
    void connecteEntrantAVoie(Voie &voie, byte connecteur);

--- Fin du code ---

De la même manière, si on utilise connecteSortantDroitAVoie ou connecteSortantDevieAVoie, l'aiguillage est connecté en DIRECTION_AVANT et si on utilise connecteEntrantAVoie, il est connectée en DIRECTION_ARRIERE.

Idem pour les croisement et TJD :


--- Code: ---    void connecteSortantAVoie(Voie &voie, byte connecteur);
    void connecteSortantBisAVoie(Voie &voie, byte connecteur);
    void connecteEntrantAVoie(Voie &voie, byte connecteur);
    void connecteEntrantBisAVoie(Voie &voie, byte connecteur);

--- Fin du code ---

Les connexions doivent être réalisées de manière compatible. C'est à dire qu'un élément de voie ne peut pas être connecté de telle sorte qu'il soit à la fois en DIRECTION_AVANT et DIRECTION_ARRIERE. Pour cela il faut connecter en parcourant les éléments dans une direction.

Je ne traite donc pas les boucles de retournement pour l'instant.

Les objets sont instanciés simplement. Par exemple, si on prend en bas à gauche VoieLibre0, Aiguille0, Voie0 et Voie1, on a


--- Code: ---VoieNormale voieLibre0(VOIE_LIBRE_0);
Aiguillage aiguille0(AIGUILLE_0);
VoieNormale voie0(VOIE_0);
VoieNormale voie1(VOIE_1);

--- Fin du code ---

Une fois instanciés les objets, on les connecte. Il faut décider d'un direction de connexion, de la gauche vers la droite dans notre cas :


--- Code: ---  voieLibre0.connecteSortantAVoie(aiguille0, ENTRANT);
  aiguille0.connecteSortantDevieAVoie(voie0, ENTRANT);
  aiguille0.connecteSortantDroitAVoie(voie1, ENTRANT);

--- Fin du code ---

Ici tous les objets sont en DIRECTION_AVANT. Mais, par exemple, en connectant de gauche à droite, l'aiguillage Aiguille3 sera en DIRECTION_ARRIERE.

On a donc une ligne par connecteur dans la gare. Pour la gare de DDEFF nous avons 110 connexions :


--- Code: ---  /*
   * Connexion des voies de bas en haut et de gauche à droite
   */
  voieLibre0.connecteSortantAVoie(aiguille0, ENTRANT);
  aiguille0.connecteSortantDevieAVoie(voie0, ENTRANT);
  aiguille0.connecteSortantDroitAVoie(voie1, ENTRANT);
  voie0.connecteSortantAVoie(tjd0, ENTRANT_BIS);
  voie1.connecteSortantAVoie(tjd8, ENTRANT_BIS);
 
  voieLibre1.connecteSortantAVoie(tjd0, ENTRANT);
  tjd0.connecteSortantAVoie(voie3, ENTRANT);
  tjd0.connecteSortantBisAVoie(voie2, ENTRANT);
  voie3.connecteSortantAVoie(tjd8, ENTRANT);
  tjd8.connecteSortantAVoie(voie22, ENTRANT);
  tjd8.connecteSortantBisAVoie(voie21, ENTRANT);
  voie22.connecteSortantAVoie(aiguille12, SORTANT_DEVIE);
  aiguille12.connecteEntrantAVoie(voieGarage2, SORTANT);
  voie32.connecteSortantAVoie(aiguille12, SORTANT_DROIT);

  voieLibre2.connecteSortantAVoie(tjd1, ENTRANT);
  voie2.connecteSortantAVoie(tjd1, ENTRANT_BIS);
  tjd1.connecteSortantAVoie(voie8, ENTRANT);
  tjd1.connecteSortantBisAVoie(voie6, ENTRANT);
  voie8.connecteSortantAVoie(tjd12, ENTRANT);
  voie21.connecteSortantAVoie(tjd12, ENTRANT_BIS);
  tjd12.connecteSortantAVoie(voie27, ENTRANT);
  tjd12.connecteSortantBisAVoie(croisement2, ENTRANT_BIS);
  voie27.connecteSortantAVoie(tjd15, ENTRANT_BIS);
  croisement2.connecteSortantAVoie(tjd15, ENTRANT);
  tjd15.connecteSortantBisAVoie(voie31, ENTRANT);
  tjd15.connecteSortantAVoie(voie32, ENTRANT);
  voie31.connecteSortantAVoie(aiguille11, ENTRANT);
  aiguille11.connecteSortantDevieAVoie(voieGarage3, SORTANT);
  aiguille11.connecteSortantDroitAVoie(voieGarage4, SORTANT);

  voieLibre3.connecteSortantAVoie(tjd2, ENTRANT);
  voie6.connecteSortantAVoie(tjd2, ENTRANT_BIS);
  tjd2.connecteSortantAVoie(voie12, ENTRANT);
  tjd2.connecteSortantBisAVoie(voie11, ENTRANT);
  voie12.connecteSortantAVoie(tjd11, ENTRANT_BIS);
  voie20.connecteSortantAVoie(tjd11, ENTRANT);
  tjd11.connecteSortantBisAVoie(voie26, ENTRANT);
  tjd11.connecteSortantAVoie(croisement2, ENTRANT);
  voie26.connecteSortantAVoie(tjd14, ENTRANT);
  croisement2.connecteSortantBisAVoie(tjd14, ENTRANT_BIS);
  tjd14.connecteSortantAVoie(voie30, ENTRANT);
  tjd14.connecteSortantBisAVoie(voie29, ENTRANT);
  voie30.connecteSortantAVoie(aiguille10, ENTRANT);
  aiguille10.connecteSortantDroitAVoie(voieGarage5, SORTANT);
  aiguille10.connecteSortantDevieAVoie(voie34, ENTRANT);
  voie34.connecteSortantAVoie(aiguille14, SORTANT_DROIT);
  voie33.connecteSortantAVoie(aiguille14, SORTANT_DEVIE);
  aiguille14.connecteEntrantAVoie(voieGarage6, SORTANT);
 
  voieLibre4.connecteSortantAVoie(tjd4, ENTRANT);
  voie11.connecteSortantAVoie(tjd4, ENTRANT_BIS);
  tjd4.connecteSortantAVoie(voie16, ENTRANT);
  tjd4.connecteSortantBisAVoie(croisement0, ENTRANT_BIS);
  voie16.connecteSortantAVoie(tjd7, ENTRANT_BIS);
  croisement0.connecteSortantAVoie(tjd7, ENTRANT);
  tjd7.connecteSortantAVoie(voie20, ENTRANT);
  tjd7.connecteSortantBisAVoie(voie19, ENTRANT);
  voie19.connecteSortantAVoie(aiguille8, SORTANT_DROIT);
  voie25.connecteSortantAVoie(aiguille8, SORTANT_DEVIE);
  aiguille8.connecteEntrantAVoie(voie28, ENTRANT);
  voie28.connecteSortantAVoie(tjd16, ENTRANT);
  voie29.connecteSortantAVoie(tjd16, ENTRANT_BIS);
  tjd16.connecteSortantAVoie(voie33, ENTRANT);
  tjd16.connecteSortantBisAVoie(voieGarage7, SORTANT);

  voieLibre5.connecteSortantAVoie(tjd3, ENTRANT_BIS);
  voie10.connecteSortantAVoie(tjd3, ENTRANT);
  tjd3.connecteSortantAVoie(croisement0, ENTRANT);
  tjd3.connecteSortantBisAVoie(voie15, ENTRANT);
  voie15.connecteSortantAVoie(tjd6, ENTRANT);
  croisement0.connecteSortantBisAVoie(tjd6, ENTRANT_BIS);
  tjd6.connecteSortantAVoie(voie18, ENTRANT);
  tjd6.connecteSortantBisAVoie(croisement1, ENTRANT_BIS);
  voie18.connecteSortantAVoie(tjd10, ENTRANT_BIS);
  croisement1.connecteSortantAVoie(tjd10, ENTRANT);
  tjd10.connecteSortantAVoie(voie25, ENTRANT);
  tjd10.connecteSortantBisAVoie(voieGarage8, SORTANT);

  voieLibre6.connecteSortantAVoie(aiguille3, SORTANT_DROIT);
  voie5.connecteSortantAVoie(aiguille3, SORTANT_DEVIE);
  aiguille3.connecteEntrantAVoie(aiguille4, ENTRANT);
  aiguille4.connecteSortantDroitAVoie(voie9, ENTRANT);
  aiguille4.connecteSortantDevieAVoie(voie10, ENTRANT);
  voie9.connecteSortantAVoie(tjd5, ENTRANT_BIS);
  voie14.connecteSortantAVoie(tjd5, ENTRANT);
  tjd5.connecteSortantAVoie(croisement1, ENTRANT);
  tjd5.connecteSortantBisAVoie(voie17, ENTRANT);
  voie17.connecteSortantAVoie(tjd9, ENTRANT);
  croisement1.connecteSortantBisAVoie(tjd9, ENTRANT_BIS);
  tjd9.connecteSortantAVoie(voie24, ENTRANT);
  tjd9.connecteSortantBisAVoie(voie23, ENTRANT);
  voie24.connecteSortantAVoie(aiguille9, SORTANT_DROIT);
  voie36.connecteSortantAVoie(aiguille9, SORTANT_DEVIE);
  aiguille9.connecteEntrantAVoie(voieGarage9, SORTANT);

  voieGarage0.connecteAVoie(aiguille1, ENTRANT);
  aiguille1.connecteSortantDroitAVoie(voie5, ENTRANT); 
  aiguille1.connecteSortantDevieAVoie(voie4, ENTRANT);

  voieGarage1.connecteAVoie(aiguille2, SORTANT_DEVIE);
  voie4.connecteSortantAVoie(aiguille2, SORTANT_DROIT);
  aiguille2.connecteEntrantAVoie(voie7, ENTRANT);
  voie7.connecteSortantAVoie(aiguille5, ENTRANT);
  aiguille5.connecteSortantDevieAVoie(voieLibre9, ENTRANT);
  aiguille5.connecteSortantDroitAVoie(aiguille6, ENTRANT); 
  aiguille6.connecteSortantDroitAVoie(voieLibre8, ENTRANT); 
  aiguille6.connecteSortantDevieAVoie(aiguille7, ENTRANT);
  aiguille7.connecteSortantDroitAVoie(voie14, ENTRANT);
  aiguille7.connecteSortantDevieAVoie(voie13, ENTRANT);
  voie13.connecteSortantAVoie(tjd13, ENTRANT);
  voie23.connecteSortantAVoie(tjd13, ENTRANT_BIS);
  tjd13.connecteSortantAVoie(voie36, ENTRANT);
  tjd13.connecteSortantBisAVoie(voie35, ENTRANT);
  voie35.connecteSortantAVoie(aiguille13, ENTRANT);
  aiguille13.connecteSortantDroitAVoie(voieGarage10, SORTANT);
  aiguille13.connecteSortantDevieAVoie(voieLibre7, ENTRANT);

--- Fin du code ---

Je vous livre le sketch complet avec la recherche de deux chemins. Je n'ai pas tout testé, je viens de le terminer. Il peut être modifié pour ajouter des requêtes de chemin. Pour l'instant l'algorithme ne cherche qu'un seul chemin. Je vais rajouter la recherche de tous les chemins possibles, le calcul de l'intersection des chemins et donc l'établissement de plusieurs chemins simultanément.

Ça tourne sur un Uno en bouffant 33% de la flash et 52% de la SRAM

DDEFF:
C'est d'une limpidité de cristal !  ;D
Et tu utilises des noms en clair pour les voies, pas des bytes. C'est un progrès aussi.  C'est beaucoup plus parlant ;D

Quelques bugs en lançant, mais c'est un programme non déverminé. Donc, rien de grave.

Pour mes 4 m, je ne suis pas inquiet. Il y a plein de solutions. Mais je vais essayer sans la tordre (j'aime bien les 2 x 2 diagonales bien droites).
Merci de t'en soucier.  ;)

Navigation

[0] Index des messages

[#] Page suivante

Utiliser la version classique