Voir les contributions

Cette section vous permet de consulter les contributions (messages, sujets et fichiers joints) d'un utilisateur. Vous ne pourrez voir que les contributions des zones auxquelles vous avez accès.


Sujets - Pierre59

Pages: [1]
1
Discussions ouvertes / Devinette
« le: mai 31, 2020, 04:13:31 pm »

Bonjour

Il existe en C/C++ quatre opérateurs booléens :  !    &    |    ^   pour les opérations booléennes   non   et   ou   ou-exclusif   .

Par exemple  :    boolean a,b,c;    c=!a;   c=a&b;    c=a|b;    c=a^b;   

Trois de ces opérateurs peuvent êtres aussi utilisés sur des entiers, ils effectuent dans ce cas des   et   ou   ou-exclusif   bit à bit.

Par exemple :   int a,b,c;   c=a&b;    c=a|b;    c=a^b; 

Il existe aussi deux autres opérateurs booléens qui effectuent une optimisation   &&   ||   pour les opérations booléennes   et   ou    .

Pour ces deux opérateurs si on peux déterminer le résultat avec seulement le premier opérande, le deuxième n'est pas pris en compte. Pour le    et    si le premier opérande est faux le résultat est faux, pour le   ou   si le premier opérande est vrai le résultat est vrai.

Mais attention, les deux opérateurs ne sont pas équivalents dans certains cas, cela se produit quand le deuxième opérateur fait ce que l'on appelle "des effets de bord", cet effet de bord sera toujours fait avec l'opérateur normal, mais pas systématiquement avec l'opérateur optimisé.

Il existe aussi des opérateurs booléens combinés avec l'affectation  (comme pour beaucoup d'opérateurs) :       .

Par exemple :    boolean a,b;   a&=b;   a|=b;

Devinette, il existe des opérateurs optimisés pour le   et   et le   ou  , pourquoi il n'y en n'a pas pour le ou-exclusif ?

Pierre



2
Discussions ouvertes / Patron de conception
« le: février 07, 2019, 03:43:54 pm »
Bonjour

Je vais vous parler ici des "Patrons de Conception" (Design Pattern) en langage orienté objet et tout particulièrement du patron "observateur". Un patron de conception est une solution standard, indépendante du langage, pour résoudre un problème de conception. L'idée, pour le patron observateur, est qu'un objet puisse observer un autre objet pour savoir quand son état change. Il y a deux approches possibles :

- soit l'objet observateur teste périodiquement l'objet observé
- soit l'objet observé notifie son changement à l'objet observateur

Pour la première solution ce qui peut venir à l'esprit est de questionner périodiquement l'objet observé, c'est de l'attente active, elle peut être éventuellement déléguée à un thread, mais outre la consommation de ressources elle présente un temps de latence dû au questionnement périodique. Ce n'est pas la bonne solution.

La deuxième solution est plus intéressante mais plus technique à mettre en oeuvre, elle a l'avantage de ne pas gaspiller de ressources et de ne pas avoir de temps de latence.L'objet observé notifie directement son changement d'état.

Tous les exemples seront donnés en C++ (pas en Arduino) et Processing (Java), juste pour pouvoir faire facilement des affichages.

Patron de conception observateur

Le patron de conception observateur (Design pattern Observer) donne une solution très souple pour réaliser ces échanges d'information entre deux objets et plus généralement entre des objets hétérogènes.

Partons sur deux objets pour commencer, l'objet qui observe l'autre est l'observateur, l'autre est l'observé. Il va falloir des nouvelles classe pour préciser les méthodes standard utilisée et donner des types à nos deux objets (pour le polymorphisme).

Commençons par l'observateur, celui ci a juste besoin d'une méthode qui sera appelée par l'observé quand son état change, l'observé n'a pas à savoir ce que l'observateur fera de l'information, c'est à l'observateur d'implémenter l'utilisation de cette information. Pour rendre le mécanisme général on a besoin d'une classe abstraite :

class Observateur { // observe les changements d'etat d'un objet
public:
virtual void etatChange(Observable* o)=0; // methode appelee sur l'observateur lors de changement d'etat
};

----------

interface Observateur { // observe les changements d'etat d'un objet
  void etatChange(Observable o); // methode appelee sur l'Observateur lors de changement d'etat
}

En C++ une classe abstraite se fait avec une méthode virtuelle dite pure ( =0; ). La méthode abstraite etatChange() a un paramètre  pointeur sur un objet observé (avec le polymorphisme, n'importe quel objet pourra être utilisé). En Java qui n'a pas d'héritage multiple, une classe n'ayant que des méthodes abstraites est un interface (permettant ainsi un ersatz d' héritage multiple indispensable ici).

Passons à l'observé, il a besoin d'une méthode pour notifier un changement à l'observateur, il faut aussi une méthode pour permettre à l'observateur de s'enregistrer auprès de lui. Plus délicat il faut une structure de données pour mémoriser les différents observateurs (il peut en avoir un nombre quelconque et de type quelconque). Un simple tableau ne convient pas, il faut un tableau extensible, fortuitement on trouve de tels objets en bibliothèque, "vecteur" en C++, "ArrayList" en Java. Cela donne :

class Observable {
vector<Observateur*> observateurs; // liste des observateurs

public:
void ajouterObservateur(Observateur* e) { observateurs.push_back(e); } // ajouter un observateur
    void notifier(bool b) { for (auto & e : observateurs) e->etatChange(this); } // notifier aux observateurs
};

----------

class Observable {
  ArrayList<Observateur> Observateurs=new ArrayList<Observateur>(); // liste des Observateurs
 
  void ajouterObservateur(Observateur e) { Observateurs.add(e); } // ajouter un Observateur
  void notifier(boolean b) { for (Observateur e: Observateurs) e.etatChange(this); } // notifier aux Observateurs
}

La méthode ajouterObservateur() rajoute le pointeur sur l'observateur au tableau extensible, la méthode notifier() appelle la méthode etatChange() sur tous les observateurs (noter le paramètre qui fourni à l'observateur un pointeur sur l'objet à l'initiative du changement d'état). En Java c'est très semblable à la version en C++, sauf pour la gestion des pointeurs et de l'écriture du "forall".

Utilisation

Voyons quelques exemples d'utilisation (inspirés du gestionnaire du Locoduinodrome), commençons par le dialogue entre une balise et une zone, pour cela il faut deux classes une pour les balises et une pour les zones :

class Balise : public Observateur {
bool etat=false;
public:
Balise(Zone& zone) { zone.ajouterObservateur(this); } // constructeur

void etatChange(Observable* o) { cout<<"balise : la zone observee a change\n"; }
};

class Zone : public Observable {
bool etat=false;
public:
Zone() {} // constructeur

void occuper() { etat=true;}
void liberer() { etat=false; notifier(etat); }
};

----------

class Balise implements Observateur {
  boolean etat=false;
  Balise(Zone z) { z.ajouterObservateur(this); } // constructeur
 
  void etatChange(Observable o) { println("balise : la zone observee a change"); } 
}

class Zone extends Observable {
  boolean etat=false;
 
  Zone() {} // constructeur
 
  void occuper() { etat=true;}
  void liberer() { etat=false; notifier(etat); }
}

La classe balise hérite de la classe abstraite Observateur et implémente la méthode abstraite etatChange(), méthode qui fait ici juste un affichage. Le constructeur reçoit une zone en paramètre et s'enregistre auprès de cette zone comme observateur.

La classe Zone hérite de la classe Observable qui lui fournit la gestion toute faite de la liste des observateurs. La méthode liberer() notifie l'information à tous les observateurs.

Reste plus que à faire un essai, pour cela il faut au moins une instance de Balise et une instance de Zone :

Zone zone;
Balise balise(zone);

----------

Zone zone=new Zone();
Balise balise=new Balise(zone);

on obtient à l'exécution dans la console :

balise : la zone observee a change
Bien évidemment on pourrait afficher aussi le nom des zones si elles avaient un nom. On pourrait aussi enregistrer d'autres balises sur d'autres zones (ou la même).

Autre utilisation

Les itinéraires ont aussi besoin de la libération des zones pour se détruire (libération de la dernière zone de l'itinéraire). On écrit facilement une classe Itineraire (sur le modèle de Balise) :

class Itineraire : public Observateur {
public:
Itineraire(Zone& zone) { zone.ajouterObservateur(this); } // constructeur

void etatChange(Observable* o) { cout<<"itineraire : detruit\n"; }
};

----------

class Itineraire implements Observateur {
 
  Itineraire(Zone zone) { zone.ajouterObservateur(this); } // constructeur

  void etatChange(Observable o) { println("itineraire : detruit"); }
}

il faut une instance :

Itineraire itineraire;

----------

Itineraire itineraire=new Itineraire();

et cela donne :

balise : la zone a change
itineraire : detruit

On a mis dans la liste des observés deux types différents, des balises et des itinéraires, c'est tout à fait normal car les balises et les itinéraires ont tous un type commun Observateur (on joue sur le polymorphisme). Ceci est rendu possible par le subtil jeu des types des classes Observateur et Observable et l'héritage multiple (ceci est un bon exemple de son utilité).

Dernière utilisation

Supposons que les zones aient besoin de savoir quand une aiguille est bougée. Il faut une classe Aiguille, sur le modèle de Zone :

class Aiguille : public Observable {
bool etat;
public:
void manoeuvrer() { etat=!etat; notifier(etat); }
};

----------

class Aiguille extends Observable {
  boolean etat;
 
  void manoeuvrer() { etat=!etat; notifier(etat); }
}

Maintenant zone devient aussi un observateur tout en continuant d'être observable, cela donne :

class Zone : public Observable,Observateur {
bool etat=false;
public:
Zone(Aiguille& aiguille) { aiguille.ajouterObservateur(this); } // constructeur

void etatChange(Observable* o) { cout<<"zone : l'aiguille a change\n"; }

void occuper() { etat=true;}
void liberer() { etat=false; notifier(etat); }
};

----------

class Zone extends Observable implements Observateur {
  boolean etat=false;
 
  Zone(Aiguille aiguille) {aiguille.ajouterObservateur(this); } // constructeur
 
  void occuper() { etat=true;}
  void liberer() { etat=false; notifier(etat); }

  void etatChange(Observable o) { println("zone : l'aiguille a change"); } 
}

zone hérite de deux classe (héritage multiple) donc a trois types pour l'objet : Zone,  Observable et Observateur

Bilan

Ce patron est très souple mais très puissant, il permet des évolutions faciles à mettre en oeuvre. Dans la classe Observable on pourrait avoir plus de méthodes pour gérer la liste des observateurs (suppression par exemple …). On peut aussi spécialiser par héritage les classes Observateur et Observable.

Ce patron est utilisé dans les interfaces graphiques pour gérer les évènements, de souris par exemple. Il pourrait être aussi utilisé dans un gestionnaire de réseau.

Pierre

3
Composants / M5stack
« le: août 04, 2018, 10:16:58 am »
Bonjour

Un ESP32 joliment emballé (M5stack), compatible Arduino, qui pourrait supporter facilement un gestionnaire de réseau et autres.

On doit même pouvoir trouver des versions avec le CAN.

Amicalement

Pierre

4
Bonjour

Je voudrais monter ici une approche objet de la modélisation logicielle d'un réseau.

Mon réseau est constitué d'un ovale à double voie sur lequel il y a une coulisse et une gare moyenne. Cette gare comporte plusieurs voies à quai, une partie marchandises et un dépot, de la gare part aussi une voie unique finissant à une petite gare terminus.

Le réseau comporte une trentaine d'aiguilles, une trentaine de zones et une dizaine de cantons.

Le réseau est géré par un très gros programme sur PC. Une dizaine de cartes électroniques sur deux bus I2C font l'interface entre le PC et le réseau.

Le programme sur PC comporte plusieurs parties :

- une partie de gestion des cartes électroniques (rétrosignalisation, souris, commandes des alimentations, des aiguilles, des signaux …)

- une partie de gestion de TCOs sur l'écran du PC (il pourrait avoir un TCO réel)

- une grosse partie de gestion du fonctionnement du réseau, cette partie reçoit toutes les infos du réseau (rétrosignalisation …) et toutes les commandes des utilisateurs (souris, itinéraires, …), elle sait ainsi toujours ce qui se passe sur le réseau. Cette partie est réalisée en programmation objet et ne comporte pas de tables, la description du réseau se fait par les liens entre les objets et les méthodes (fonctions) de ces objets.

Je vais essayer de décrire les principales classes et leur fonctionnement en détaillant un peu plus la classe Zone. Mes classes sont en Java mais je vais essayer de les présenter à la C++ (longtemps que je n'ai pas fait de C++ objet).

La classe essentielle est la classe Zone :

class Zone {
   String nom;
   boolean etat; // libre ou occupé
   Train train; // le train dans la zone
   Signal signalPair,signalImpair; // les signaux éventuels de la zone

   Zone(…) {…} // constructeur

   virtual Zone suivantePaire(); // la zone suivante paire (éventuellement vide)
   virtual Zone suvanteImpaire(); // la zone suivante impaire (éventuellement vide)

   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

   boolean occupee() { return etat; } // méthode utilitaire
   boolean libre() { return !etat; } // méthode utilitaire

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

chaque zone réelle fait l'objet d'une classe héritant de la classe Zone, voici un exemple :

class Z9 : Zone { // héritage de Zone
   
   Z9(…) {…} // constructeur

   void actions() { aubiner(c3,c5); } // fermeture de signaux (occupation)
   void desactions() { detruire(XA,XB,XC); } // destruction d'itinéraires (liberation)

   Zone suivantePaire() { return z11; } // la zone paire suivante (toujours la meme)
   Zone suivantePaire() { if (a3.directe()) return z9; else return z7; } // la zone impaire suivante (dépends de la position de l'aiguille a3)
}

Ces classes zones décrivent la géométrie du réseau et les interactions entre les différentes parties. Elles précisent les liens entre les zones (de façon dynamique en fonction de la position réelle des aiguilles), l'emplacement des signaux, … Tout le réseau est ainsi décrit.

Les autres classes principales s'articulent autour des zones :

- la classe signal gère les signaux (lumineux et mécaniques), elle calcule les feux en fonction des occupations de zones, des signaux adjacents et de la position des aiguilles, elle fait le cantonnement pour les parties du réseau qui en sont équipées) et commandes les signaux.

- la classe Aiguille garde la position d'une l'aiguille et fait l'interface avec l'électronique pour les commandes

- la classe Train mémorise les informations sur un train ( sens, vitesse, nom de l'engin moteur, adresse DCC, …)

- la classe Itineraire réalise les itinéraires (une centaine), elle fait les enclanchements entre itinéraires, la mémorisation/création/destruction des itinéraires. Elle est organisée comme la classe Zone, la classe Itineraire contient tout ce qui est commun à tous les itinéraires, des classes (héritant de la classe Itineraire) contenant  les parties spécifiques.


Cette programmation objet bien que assez technique est très puissante, elle peut gérer des petits ou grands réseaux,, elle évite les tables (avec leur problèmes de construction/mise à jour, …), elle facilite grandement des modification ponctuelles du réseau (seules quelques classes sont à modifier ou à créer) et facilite la mise au point au début. Elle nécessite un processeur assez puissant. Du point de vue allocation mémoire les objets sont tous crées à l'initialisation du programme, l'accroissement des variables est linéaire avec l'accroissement du réseau.

Cette programmation n'évite pas (comme avec d'autres types de  programmations) les problèmes de synchronisation dans le cas de fonctionnement multi-tâches et/ou de programmation évenementielle (sur interruptions).

Voila, n'hésitez pas à poser des questions, et franchissez le pas de la programmation objet (voir les cours sur les objets et l'héritage dans une autre partie du site)

Pierre



Pages: [1]