LOCODUINO

Un gestionnaire en C++ pour votre réseau

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

Aiguilles et Zones

.
Par : Pierre59

DIFFICULTÉ :

On va, dans cette série d’articles, développer pas à pas le noyau d’un programme de gestion de réseau en utilisant la programmation orientée objet en C++.

La programmation orientée objet peut se résumer en trois mots : encapsulation, héritage, polymorphisme. Il faut faire les trois choses pour vraiment faire de la programmation objet.

L’accent sera essentiellement mis sur la conception objet, les exemples seront donc écrits dans un C++ simplifié. Cela reste du vrai C++, mais pour pouvoir le compiler, il faudra ajouter quelques trucs techniques qui polluent un peu le programme et qui nuisent un peu à sa compréhension. On trouvera en fin d’article le programme C++ complet compilable avec aussi plus d’exemples.

Pour réaliser un programme de gestion de réseau à partir de ce noyau, il faut écrire des classes (en suivant les modèles joints), déclarer des objets, choisir les options désirées et écrire toute la partie d’interface avec le matériel : commande des aiguilles, des alimentations (analogiques ou digitales), ou des signaux, rétro-signalisation, gestion du TCO, gestion des bus, …

Pour bénéficier de toutes les possibilités du noyau, toutes les actions de l’utilisateur (commande d’aiguilles, d’itinéraires, de sens et vitesse des trains, ...) doivent êtres contrôlées par le programme avant leur application.

Ce noyau objet est très puissant et très facilement modifiable. On peut modifier le réseau facilement, sans tout remettre en cause dans le programme. On peut ajouter progressivement, suivant ses envies, des "gadgets" comme le suivi des trains (par exemple, afficher le nom du train sur le TCO, mais aussi le contrôle des sons pour les machines sonores, ..), la poursuite en canton (appelée aussi conduite sélective, elle concerne principalement le fonctionnement en analogique, un train étant contrôlé sur tout le réseau avec la même souris par commutation automatique des alimentations sur les zones), le cabsignal (l’affichage des signaux en cabine de conduite des machines, par exemple sur la souris qui affiche le signal réel que voit le train), etc …

Ce noyau objet est plutôt orienté vers la gestion de réseaux avec commande par itinéraires, signalisation, rétro-signalisation, circulation manuelle et automatique, .. ceci quelque soit leur taille. Mais il peut être aussi utilisé sur des réseaux simples en laissant tomber les parties optionnelles.

Comme le noyau est écrit en C++, il peut être utilisé avec un Arduino, un mini PC (genre PCduino) ou un vrai PC. Un Arduino UNO sera très vite insuffisant au niveau mémoire vive voire au niveau mémoire flash, un méga ou mieux un due est plus réaliste.

Première étape : l’encapsulation

La première chose à faire en programmation objet, c’est d’identifier les objets potentiels.
Dans notre cas, ceux ci ne manquent pas : aiguilles, zones, cantons, signaux, trains, TCOs, souris, …

Ensuite il faut écrire des classes pour chacun des objets retenus. Dans ce premier article on va s’intéresser aux objets aiguille et zone.

Les classes sont des modèles pour fabriquer des objets, à partir d’une classe on peut fabriquer autant d’objets que l’on veut, ces objets sont appelés instances de la classe. Par exemple si on a 10 aiguilles on fera 10 instances de la classe ’aiguille’ pour avoir 10 objets, un pour chaque aiguille.

On va commencer avec la classe aiguille, pour cela on écrit une classe en C++ :

class Aiguille {};

Dans une classe les variables sont souvent appelées "attributs" et les fonctions "méthodes".

Il faut maintenant remplir la classe. Pour cela il faut se demander quelles sont les informations que l’on a besoin de savoir sur une aiguille et quelles sont les actions que l’on veut effectuer sur une aiguille.

Pour les informations, on a besoin de savoir si l’aiguille est en position directe ou en position déviée.
On peut faire cela avec deux méthodes à résultat booléen : directe() et deviee(), l’état de l’aiguille étant mémorisé dans une variable booléenne : état.

Pour les actions, on a besoin de manœuvrer l’aiguille, cela peut se faire avec deux méthodes : directer() et devier(). Ces méthodes ne font la manœuvre que si nécessaire, elles renvoient un booléen pour savoir comment cela s’est passé (true=OK). Bien évidemment ces méthodes changent l’état. La méthode : manoeuvrer() fera la commande effective (voir plus loin).

Cela donne :

class Aiguille {
	booleen etat; // directe (true) ou deviee (false)

	boolean directe()  { return etat; }  // méthode d'acces
	boolean deviee()  { return !etat; } // méthode d'acces

	boolean directer() { 
		if (directe())  return true; 
		etat=true;  
		return manoeuvrer(true); 
	}
	boolean devier()   { 
		if (deviee())  return true; 
		etat=false; 
		return manoeuvrer(false); 
	}
};

Pour rendre la classe utilisable, il faut une méthode : manoeuvrer() qui va faire l’interface avec le matériel. Cette méthode a besoin d’un numéro pour pouvoir commander le matériel (directement ou par un bus). On va donc rajouter à la classe un entier (ou plusieurs si besoin) qui est une sorte de numéro d’aiguille. La méthode : manoeuvrer() utilisera ce numéro pour manœuvrer effectivement l’aiguille. Cette méthode devra être complétée par l’utilisateur. Ce numéro d’aiguille va être donné en utilisant un constructeur.

Reste un problème d’initialisation de l’état. Deux cas se présentent, si on peut connaitre la position des aiguilles à l’initialisation du programme de gestion (dans le setup() ) alors on appelle la méthode : init1(), sinon il faut appeler la méthode : init2() qui force la position de l’aiguille en la manœuvrant.

On va profiter de la présence du constructeur pour ajouter une information optionnelle qui est la zone dans laquelle est l’aiguille. Cela peut être utile pour réaliser un PRS (Poste à Relais à transit Souple) où on a besoin de savoir si l’aiguille est manœuvrable (si la zone correspondante n’est pas occupée), la méthode : manoeuvrable() est prévue pour cela.

De façon générale les objets seront manipulés par le biais de pointeurs. Cela permet de profiter des mécanismes objets que nous utiliserons par la suite. Manipuler les objets (ou les structures) avec des pointeurs c’est normal en C++, il existe d’ailleurs un opérateur( -> ) prévu pour cela.

Ce qui donne :

class Aiguille {
	boolean etat; // directe (true) ou déviée (false)
	int no;       // numéro de l'aiguille
	Zone* zone;   // la Zone ou est l'aiguille (optionnel)

        Aiguille(int n) { no=n; } // constructeur mini
	Aiguille(int n,Zone* z) { no=n; zone=z; } // constructeur maxi

	boolean directe()  { return etat; }  // méthode d’accès
	boolean deviee()  { return !etat; } // méthode d’accès

	boolean directer() { 
		if (directe())  return true; 
		etat=true;  
		return manoeuvrer(true); 
	}  

	boolean devier()   { 
		if (deviee())  return true; 
		etat=false; 
		return manoeuvrer(false); 
	}

	boolean manoeuvrable()  { return zone->libre(); } // (optionnel)

	boolean manoeuvrer(boolean e) { // commande de l'aiguille avec le numéro  
		return true; // return false si cela c'est mal passe
 	}

	void init1(boolean e) { etat=e; }
	void init2(boolean e) { if (e) commander(e); else commander(!e);  etat=e; }
};

Voici deux exemples d’aiguilles (instanciations) :

	Aiguille* a1=new Aiguille(1);  // aiguille N°1 
	Aiguille* a2=new Aiguille(2, z9);  // aiguille N°2 dans la zone z9

Maintenant passons à la classe Zone.

Comme pour Aiguille, la zone a deux états : libre et occupé, un booléen convient donc.

A l’instar d’Aiguille, on va avoir deux méthodes, libre() et occupee() pour tester l’état. Deux méthodes occuper() et liberer(), pour modifier l’état, ces deux méthodes doivent êtres appelées par la rétro-signalisation, une table de pointeurs sur les zones facilitera la tâche.
Ces deux méthodes appellent les méthodes actions() et desactions() qui sont destinées à faire les actions spécifiques à une zone particulière à l’occupation de la zone particulière et à sa libération.

class Zone { 
	boolean etat; // libre (false) ou occupé (true)
   
	boolean occupee() { return etat; }  // méthode d’accès
	boolean libre()   { return !etat; } // méthode d’accès

	void occuper() { // appelée par la rétro-signalisation
   	// fait tout ce qu'il y a à faire en cas d'occupation (actions communes à toutes les zones)
   	actions(); // fait les actions spécifiques à une zone
	}

 	void liberer() { // appelée par la rétro-signalisation
  		// fait tout ce qu'il y a à faire en cas de libération (actions communes à toutes les zones)
   	desactions(); // fait les actions spécifiques à une zone
	}

	void actions() {} // les actions spécifiques à faire en cas d'occupation
	void desactions() {} // les actions spécifiques à faire en cas de libération
 
        void init(boolean e) { etat=e; }
};

Les deux méthodes : occuper() et liberer(), font tout ce qu’il y a faire lors de l’occupation d’une zone et de sa libération. Pour l’instant c’est presque vide mais il faudra faire la mise à jour du TCO, le suivi des trains, le cabsignal, etc …

Ces deux méthodes font tout ce qu’il y a de commun à toutes les zones. Mais pour chaque zone il y a des choses particulières à faire : aubiner les signaux, gérer le BAL, gérer les itinéraires. … .
Cela va être le rôle de deux méthodes, la méthode : actions() qui fait les actions à faire lors de l’occupation et : desactions() lors de la libération.

Comme pour aiguille il reste un problème d’initialisation de l’état. A l’initialisation du programme de gestion (dans le setup() ) la rétro-signalisation doit appeler, pour chaque zone, la méthode : init() pour indiquer l’état initial de la zone.

Deuxième étape : l’héritage

Bien que complète la classe zone n’est pas utilisable, d’autant que les méthodes : actions() et : desactions() ne font rien.
Lors de l’occupation et de la libération d’une zone, outre des actions communes à toutes les zones, en pratique il y a des actions spécifiques à chaque zone à faire : aubiner les signaux, gérer le BAL, gérer les itinéraires. …
C’est le rôle des deux méthodes : actions() et : desactions(). Pour cela on pourrait utiliser deux pointeurs sur des fonctions passés en paramètres du constructeur, et utilisés dans ces méthodes mais il y a une façon plus naturelle en programmation objet, c’est l’héritage.

Pour chaque zone du réseau on va écrire une classe spécifique qui héritera de la classe de base Zone et qui va redéfinir les méthodes : actions() et : desactions() pour leur faire faire les actions propres à chaque zone.

class Z8 : Zone { // héritage de Zone
        void actions() { … } // les actions spécifiques à faire en cas d'occupation
        void desactions() { … } // les actions spécifiques à faire en cas de libération
};

class Z9 : Zone { // héritage de Zone
        void actions() { … } // les actions spécifiques à faire en cas d'occupation
        void desactions() { … } // les actions spécifiques à faire en cas de libération
 };

Pour faire des choses plus intéressantes il faut enrichir la classe. Deux choses sont bien utiles, d’une part les deux signaux éventuels implantés sur la zone. D’autre part l’accès à la zone suivante et à la zone précédente.

Pour les signaux c’est facile, on ajoute deux variables de type pointeur de signal (on verra les signaux dans un prochain article), un constructeur à deux paramètres (pointeur de signal) et deux méthodes d’accès, les sens sont notés comme à la SNCF (pair et impair), en l’absence d’un signal on met NULL (le pointeur vide) :

class Zone { //  les ajouts a la classe zone
	…
	Signal* signalPair; 	// les signaux éventuels de la zone
	Signal* signalImpair; 

	Zone(Signal* sp,Signal* si) { // constructeur
		signalPair=sp; 
		signalImpair=si; 
	} 
 
	Signal* signalPair()     { return signalPair; } // méthode d'accès
	Signal* signalImpair() { return signalImpair; }  // méthode d'accès
	…
};

Pour les zones suivantes et précédentes on pourrait aussi envisager de les passer au constructeur mais dans notre cas deux problèmes empêchent d’avoir recours à cette technique.

  • Le premier problème est que les zones s’appellent mutuellement : Par exemple, si on a deux zones z1 et z2 la suivante paire de z1 peut être z2 et la suivante impaire de z2 être z1, c’est impossible à déclarer, car il faudrait déclarer z1 avant z2 et z2 avant z1 !
  • Le deuxième problème vient du fait que les zones suivantes dépendent éventuellement de la position d’une (ou plusieurs) aiguille. Cela reflète l’aspect dynamique du réseau et cet aspect est essentiel pour la modélisation.

On ne peut donc pas passer par le constructeur directement. On pourrait envisager de passer des pointeurs sur des fonctions, mais il est plus pratique d’utiliser l’héritage d’autant plus que l’on a déjà commencé.

On va donc pour chaque zone réelle compléter la classe spécifique déjà écrite en lui ajoutant deux méthodes : suivantePaire() et : suivanteImpaire(). Ces deux noms de méthode sont préférés aux noms suivante() et precedente() car il ne dépendent pas du sens de circulation.

Pour que l’héritage et les mécanismes objets se passent bien en C++ (on en parlera dans le prochain article), il faut ajouter aussi ces méthodes à la classe de base Zone. Il faut aussi, pour toutes les méthodes qui sont redéfinies ainsi, mettre le mot clé virtual devant les méthodes (actions(), desactions(), suivantePaire(), suivanteImpaire() ).

Il ne reste plus qu’a tout réunir, la classe de base zone :

class Zone { 
	boolean etat; // libre (false) ou occupe (true)
	Signal* signalPair; 	// les signaux éventuels de la zone
	Signal* signalmpair; 

	Zone(Signal* sp,Signal* si) { // constructeur
		signalPair=sp; 
		signalImpair=si; 
	}
   
	boolean occupee() { return etat; }  // méthode d’accès
	boolean libre()   { return !etat; } // méthode d’accès

	void occuper() { // appelée par la rétrosignalisation
		// fait tout ce qu'il y a à faire en cas d'occupation 
		// (actions communes à toutes les zones)
		actions(); // fait les actions spécifiques à une zone
	}

	void liberer() { // appelée par la rétrosignalisation
		// fait tout ce qu'il y a à faire en cas de libération 
		// (actions communes à toutes les zones)
		desactions(); // fait les actions spécifiques à une zone
	}

	virtual void actions() {} // les actions spécifiques à faire en cas d'occupation
	virtual void desactions() {} // les actions spécifiques à faire en cas de libération

 	Signal* signalPair()     { return signalPair; } // méthode d'acces
	Signal* signalImpair() { return signalImpair; } // méthode d'acces

	virtual Zone* suivantePaire()   { return NULL; } // la zone suivante paire (éventuellement vide)
	virtual Zone* suivanteImpaire() { return NULL; } // la zone suivante impaire (éventuellement vide)

	void init1(boolean e) { etat=e; } // initialisation

	Zone* selonAiguille(Aiguille* a,Zone* z1,Zone* z2) { // méthode utilitaire
		return a->directe()?z1:z2; 
		}
};

La méthode utilitaire selonAiguille() de la classe zone permet de prendre en compte l’aspect dynamique du réseau en fonction de la position réelle des aiguilles, cet aspect est essentiel au fonctionnement du gestionnaire de réseau.

Exemples de zones réelles (les exemples sont là pour montrer tous les cas caractéristiques d’utilisation, mais ils ne correspondent à aucun réseau concret) :

class Z8 : Zone { // héritage de Zone
	Z8(Signal* sp,Signal* si):Zone(sp,si) {} // constructeur

	virtual void actions() { c2.aubiner();  } 
		// les actions spécifiques à faire en cas d'occupation
	virtual  void desactions() { } 
		// les actions spécifiques à faire en cas de libération
	virtual Zone* suivantePaire()   { return NULL; } 
		//pas de zone suivante paire 
	virtual Zone* suivanteImpaire() { return z9; } 
		// la zone suivante impaire 
} ;

class Z9 : Zone { // héritage de Zone
	Z9(Signal* sp,Signal* si):Zone(sp,si) {}  // constructeur

	virtual  void actions() {s2.aubiner();  } 
		// les actions spécifiques à faire en cas d'occupation
	virtual void desactions() { … } 
		// les actions spécifiques à faire en cas de libération
	virtual Zone* suivantePaire()   { return selonAiguille( a1,z8,z9 } 
		// la zone suivante paire depends de a1
	virtual Zone* suivanteImpaire() { return selonAiguille( a1,selonAiguille( a2,z8,z9),z9; } 
		// la zone suivante impaire depend de a1 et a2
};

Exemples de déclarations d’objets signaux, zones, aiguilles :

	Signal* c2=new C2(); // signaux
	Signal* s2=new S2();

	Zone* z8=new Z8(s2,c2); // zones
	Zone* z9=new Z9(s2,NULL);

	Aiguille* a1=new Aiguille(1,z8); // aiguilles
	Aiguille* a2=new Aiguille(2,z9);

	Zone* tableZones[]  {z8,z9} // la table des zones pour la retrosignalisation

L’exemple précédent n’est pas directement compilable, il faut mettre des protections (mots clés : public, protected, …), il faut respecter un ordre très précis des déclarations de classes et de variables car les objets s’utilisent les uns les autres.

Les méthodes qui sont écrites complètement dans la classe sont réputées "inline", c’est à dire que le compilateur ne fait pas un vrai appel de méthode mais met les instructions de la méthode directement dans le programme. C’est très bien pour les méthodes courtes comme les méthodes d’accès ou certaines méthodes utilitaires. Pour les autres il faut juste les spécifier dans la classe et les écrire en dehors de la classe en rappelant le nom de la classe. Dans notre cas cela facilite la prise en compte des inextricables problèmes avec l’ordre des déclarations.

On trouvera ci dessous un exemple compilable plus complet, on a rajouté tout ce qu’il faut pour que cela soit compilable et ajusté l’ordre des déclarations qu’il faut scrupuleusement respecter pour que cela reste compilable.

On a aussi diversifié les exemples, ajouté la possibilité de donner des noms et des types aux objets avec les constructeurs nécessaires. Il y a aussi des choses (variables et méthodes) qui seront utilisées dans les prochains articles.

Noyau de base Gestionnaire C++

Dans les articles suivant on verra les classes Signal, Train, Itinéraire ainsi que des gadgets comme le suivi des trains, la poursuite en canton, le cabsignal, …

Si vous souhaitez tester cette première partie avec une modélisation réelle de votre propre réseau, vous pouvez suivre le fil « modélisation logicielle d’un réseau, ici : http://forum.locoduino.org/index.ph...

et aussi : ici dans le forum

17 Messages

  • Un gestionnaire en C++ pour votre réseau 10 mars 2016 14:55, par Christian

    Bonjour Pierre59,

    Excellent travail ! En lisant l’article et en allant faire un tour sur le forum, je me rends compte que j’ai du TAF sur la planche pour rattraper le niveau !

    Pourrais-tu expliquer le terme "aubiner des signaux" qui doit être un terme SNCF mais dont on ne dit rien dans le dicco. (C’est dans la deuxième étape : l’héritage)

    De plus, je dois dire que j’ai été un peu gêné dans la lecture de cet article par l’absence de figure, sur des choses qui sont certainement simples et pourtant pas toujours clairement établies. Prenons un exemple, celui du canton en analogique. Tout le monde a sa propre définition mais dans la réalité, comment le reconstituer sur le réseau ? Doit-il avoir une zone de pleine voie (ou pleine vitesse) et une zone d’arrêt, ou bien une zone de pleine vitesse, une zone de ralentissement et une zone d’arrêt. Si la voie est à double sens, combien de zones différentes à prévoir, quelle longueur optimale. A partir de là, on peut alors mieux imaginer en quoi consisterait les méthodes liberer() ou occuper().

    Je ne sais pas si j’ai été très clair, mais pour moi, le software est fortement lié au hardware, (même si on cherche à obtenir un modèle universel), et la question que je me pose est : Est-ce que ce que tu développes peut fonctionner pour une solution matérielle un peu différente ? Je pense que oui, mais avec un exemple concret, j’en serais sûr.

    Répondre

    • Un gestionnaire en C++ pour votre réseau 10 mars 2016 16:04, par Pierre59

      Bonjour

      Quelques éléments de réponse :

      L’aubinage est la fermeture automatique d’un signal lors de son franchissement par un train ( le nom vient de l’invention, par Mr Aubine, de la pédale mécanique qui fermait les signaux mécaniques au passage d’un train, et le nom est resté pour les signaux lumineux ).

      Un "canton" est constitué de une ou plusieurs zones et peut comporter une ou plusieurs aiguilles. Ici on travaille au niveau des zones. Il n’y a pas formellement de "cantons" car ceux ci peuvent être absents et/ou éventuellement volatiles.

      Dans la réalité il n’y a des "cantons" que quand il y a du cantonnement c’est essentiellement avec des blocks (BMU, BAL, BAPR, …). Certaines parties des réseaux ne sont pas cantonnées : garages, dépots, petites lignes, petites gares, …

      Chaque utilisateur découpe ses "cantons" en zones comme il veut, ici les méthodes liberer() et occuper() font référence à des zones (pas des cantons). L’arrêt des trains sera géré par les signaux (à venir). Ceci pour pouvoir gérer toutes les parties d’un réseau.

      Le gestionnaire se veut au maximum indépendant du hardware, et beaucoup de solutions matérielles peuvent être prises en compte. Ce qui est proposé ici, n’est que le noyau d’un gestionnaire de réseau. C’est à l’utilisateur d’écrire du code pour interfacer à son hardware.

      Des exemples concrèts sont prévus.

      Répondre

  • Un gestionnaire en C++ pour votre réseau 10 mars 2016 16:45, par Christian

    Bonjour Pierre,

    Merci pour cette réponse rapide, notamment sur l’origine du mot aubinage.

    Pour les cantons de ton réseau, quel découpage en zones (pleine voie, arrêt, etc) t’a paru meilleur et pour quelles raisons ?

    Si liberer() et occuper() font référence à des zones, que signifie ce terme (par rapport à cantons que je visualise mieux). Quelle différence ?

    Tu dis que c’est à l’utilisateur d’écrire son code en fonction de son hardware, n’y a-t-il pas d’incompatibilité, c’est-à-dire des cas de morphologie de réseau qui ne serait pas pris en compte par ce que tu as développé ?

    J’attends avec impatience des cas concrets pour mieux me rendre compte, mais je vois déjà la puissance d’une telle approche...

    Répondre

    • Un gestionnaire en C++ pour votre réseau 10 mars 2016 17:20, par Pierre59

      Sur mon réseau les "cantons" de pleine voie n’ont qu’une zone, il n’y a pas de zone d’arrêt ni de ralentissement. L’arrêt des trains fait par une détection ponctuelle (cellules photo pour l’instant), le ralentissement se fait dès qu’un signal (avertissement ou ralentissement) fermé est franchi en jouant sur la PWM en analogique ou les crans DCC, les machines sont étalonnées (vitesse versus PWM ou cran) les "cantons" sont mesurés. C’est le gestionnaire qui fait tout cela.

      Certains "cantons", ceux qui comportent des aiguilles (j’en ai beaucoup), ont plusieurs zones (mais ce ne sont pas des zones d’arrêt ni de ralentissement), c’est nécessaire pour assurer la sécurité comme dans un vrai poste d’aiguillage.

      Une zone sert à gérer la présence d’un train sur une (éventuellement petite) portion de voie. Le gestionnaire fonctionne essentiellement avec l’occupation et la libération des zones (rétrosignalisation).

      Les "cantons" ne servent qu’au contrôle de l’espacement des trains.

      L’intérêt de la programmation objet est de pouvoir isoler des concepts ne dépendant pas du matériel. Les classes que je fourni sont complètement indépendantes du matériel et du réseau. C’est des briques de base dont le fonctionnement est a peu près le même pour tous les réseaux.

      Répondre

  • Un gestionnaire en C++ pour votre réseau 10 mars 2016 17:48, par Christian

    Un grand merci pour toutes ces précisions qui me permettent de mieux comprendre.

    Je suis d’autant plus intéressé que mon réseau aussi peut être divisé en cantons courts sans zone d’arrêt car je ne pense pas avoir la place pour des cantons longs avec pleine voie et arrêt. J’attends donc la suite avec impatience mais ne te presse pas, je dois déjà bien digérer cela.

    Amicalement.

    Christian

    Répondre

  • Un gestionnaire en C++ pour votre réseau 14 mars 2016 09:21, par Dominique

    J’ai ajouté l’exemple du Locodrome, ici, dans le Forum

    Répondre

  • Un gestionnaire en C++ pour votre réseau 6 avril 2016 23:05, par Dominique

    Suite à un remaniement du fil du Forum sur la modélisation, le lien ci-dessus était devenu faux. Maintenant c’est corrigé, il est bon :)

    Voir en ligne : http://forum.locoduino.org/index.ph...

    Répondre

  • Un gestionnaire en C++ pour votre réseau 4 janvier 2017 11:10, par Christian

    Bonjour Pierre,

    N’étant pas moi-même très habitué à la POO, voici quelques questions que je me pose :

    • Pourquoi deux méthodes pour savoir si une aiguille est en position directe ou déviée ? Cela me parait redondant ; une seule méthode positionAiguille() renverrait TRUE si directe et FALSE si déviée, non ?
    • Par contre, je suis d’accord qu’il faut 2 méthodes directer() et devier() pour manœuvrer l’aiguille, alors pourquoi la méthode manœuvrer() ? Qu’apporte-t-elle de plus ?
    • Je me pose les mêmes questions pour la class Zones.
    • Enfin, n’y a-t-il pas redondance entre occuper() et actions() ainsi que liberer() et desactions() ? Je ne vois pas l’intérêt d’autant de méthodes pour faire des choses similaires et je suis un peu perdu...
      Point positif : je commence à entrevoir la puissance de tout cela sauf que j’aurais envie de simplifier. Mais est-ce possible ?

    Répondre

    • Un gestionnaire en C++ pour votre réseau 4 janvier 2017 17:19, par Pierre59

      Bonjour

      Quelques éléments de réponse :

      • pour les méthodes directe() et deviee() c’est juste pour la facilité d’utilisation, avec une méthode positionAiguille() il faut réfléchir sur le résultat attendu (true ou false) et se souvenir du "codage"
      • pour les méthodes directer() et devier() on aurait pu avoir une seule méthode avec un paramètre booléen
      • pour la méthode manoeuvrer() cela évite la duplication de code elle est appelée dans directer() et devier() et puis elle isole bien la commande physique de l’aiguille
      • pour la classe zone c’est en gros les mêmes réponses
      • pour la redondance entre les méthodes occuper() et actions() c’est plus subtil c’est des problèmes de POO, la méthode occuper() traite ce qui est général à toutes les zones, la méthode actions() traite ce qui est particulier à une zone particulière, elle doit être redéfinie (par héritage) dans les zones particulières. De même pour liberer() et desactions().
      • pour la simplification oui et non, sur la méthodes directe(), deviee() … pourquoi pas, sur les méthodes occuper() , actions(), … c’est plus difficile

      Concrètement ce n’est qu’une écriture possible, beaucoup d’autres sont possibles comme pour beaucoup de programmes.

      Pierre

      Répondre

      • Un gestionnaire en C++ pour votre réseau 4 janvier 2017 17:51, par Christian

        Merci Pierre pour tes réponses.

        Peut-être qu’il va falloir que je m’imprègne encore plus de ce que font les méthodes. Une chose est sûre, tes articles ont éveillé ma curiosité. On a bien compris un sujet quand on est capable de l’adapter à ses propres besoins ; cela me donne donc envie de modéliser mon petit réseau. Si j’arrive à produire un TCO virtuel comme celui que tu nous a fourni pour le locodrome, mais adapté à mon réseau, c’est que j’aurais bien assimilé ce que tu enseignes. J’ai du boulot pour rattraper mon retard.

        Répondre

  • Un gestionnaire en C++ pour votre réseau 4 janvier 2017 22:02, par Dominique

    Moi, j’ai modélisé mon réseau avec le système de Pierre dès l’article 1 ou 2, bien avant le TCO en Processing.
    J’ai d’abord créé les objets zones et aiguilles comme je l’ai expliqué dans le Forum (fil du système de Pierre), puis j’ai réalisé un premier écran graphique pour le représenter dynamiquement et, enfin, je les ai raccordé au réseau réel par les messages CAN.
    J’utilise effectivement les méthodes que tu mentionnes et c’est important d’avoir des noms en français conformes à ceux de la SNCF : j’ai appris et compris plein de choses de cette façon et c’est bien plus pédagogique.
    Je vais décrire pas à pas la mise en œuvre du gestionnaire de Pierre dans mon réseau avec l’interface CAN qui n’est pas très éloignée de l’interface série.
    Je vais partir cette fois-ci de la version 4.

    Je crois qu’on peut voir le gestionnaire de Pierre comme une énorme boîte à outils qui couvre tous les besoins d’un réseau complexe. On peut s’en servir de différentes manières comme dans l’exemple du Locodrome. Mais le mieux est de multiplier les exemples.

    Répondre

  • Un gestionnaire en C++ pour votre réseau (1) 19 février 2020 10:32, par Roberto Zuliani

    Bonjour
    très intéressant ! Si je comprends bien, une telle structure logicielle vous permettrait de gérer les trains automatiquement et simultanément manuellement en toute sécurité.

    Répondre

    • Un gestionnaire en C++ pour votre réseau (1) 19 février 2020 11:15, par Dominique

      Bonjour,
      Vous avez raison, et comme je l’ai écrit plus haut, c’est une boite à outils qui permet de faire tout ce que l’on veut à condition de bien l’étudier et d’avancer pas à pas. Nous avons fait une démonstration assez simple à Orléans en 2018 (le Locoduinodrome) et, chez moi cela progresse bien avec un réseau bien plus complexe et des échanges de messages Can entre les modules (gestionnaire avec écran graphique sur Arduino Due, commandes d’aiguilles sur Mega, TCO physique sur Mega et commandes de traction sur Mega aussi).

      Répondre

  • Un gestionnaire en C++ pour votre réseau (1) 20 décembre 2022 13:10, par bernard semhoun

    Bonjour plus je zappe divers page plus je me rend compte que le site est biens achalander

    Répondre

  • Un gestionnaire en C++ pour votre réseau (1) 20 décembre 2022 17:02, par msport

    Merci du compliment.

    Répondre

  • Un gestionnaire en C++ pour votre réseau (1) 18 avril 2023 22:48, par Dominique

    Bonjour,
    Je découvre ce sujet un peu tard par rapport à sa parution, mais je me pose une question concernant l’état de l’aiguille.
    Il n’y a ici que deux états, direct ou dévié. Mais qu’en est-il si l’état est indéterminé, ce qui peut arriver avec une aiguille mal positionnée ou un défaut de rétro-signalisation de la position de l’aiguille ?
    Cordialement,
    Dominique

    Répondre

    • Un gestionnaire en C++ pour votre réseau (1) 19 avril 2023 09:10, par Pierre59

      Bonjour

      Si on veut gérer un état indéterminé pour les aiguilles, il suffit de remplacer le booléen d’état par un entier en utilisant trois valeurs (directe, déviée, entrebaillée), adapter toutes les utilisations du booléen et adapter le dessin de l’aiguille (deux voies noires par exemple pour indéterminé).

      Cordialement

      Pierre

      Répondre

Réagissez à « Un gestionnaire en C++ pour votre réseau (1) »

Qui êtes-vous ?
Votre message

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

Lien hypertexte

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

Rubrique « Projets »

Les derniers articles

Les articles les plus lus