|
Simulateur Ferroviaire
Reconstruction et visualisation d'un réseau ferroviaire à partir de données GeoJSON — Win32 / WebView2 / Leaflet
|
Les éléments interactifs constituent le modèle de domaine ferroviaire. Ils représentent les entités physiques de l'infrastructure (tronçons, aiguillages) avec leurs géométries, leur topologie et leur état opérationnel.
Règle de cycle de vie :
prev/next, root/normal/deviation) sont null jusqu'à Phase9_RepositoryTransfer::resolve() — aucun code amont ne doit y accéder.TopologyRepository est le seul propriétaire des instances (unique_ptr) ; tous les pointeurs résolus sont non-propriétaires.| Classe | Responsabilité unique |
|---|---|
| Element | Interface abstraite commune — identifiant, type, logger partagé |
| ShuntingElement | Étend avec un état opérationnel et les helpers isFree / isOccupied / isInactive |
| StraightBlock | Répresentation d'un bloc de circuit de voie |
| SwitchBlock | Répresentation d'un bloc d'aiguillage |
Copie interdite / déplacement autorisé sur toute la hiérarchie. La copie est supprimée dans Element (risque de slicing). Le déplacement est explicitement déclaré dans ShuntingElement car la présence du destructeur virtuel supprimerait sa génération implicite en C++11/14/17 — ce qui rendrait StraightBlock et SwitchBlock non-déplaçables et incompatibles avec
make_uniqueet le pipeline de construction.
Classe de base abstraite. Définit l'interface commune à tous les éléments ferroviaires.
Element::m_logger est une unique instance statique partagée par toutes les classes dérivées (StraightBlock et SwitchBlock confondus). Cela produit un seul fichier Logs/Elements.log regroupant tous les événements d'infrastructure.
Le mutex interne du Logger garantit la thread-safety des écritures concurrentes.
Couche intermédiaire abstraite. Étend Element avec un état opérationnel (m_state, FREE par défaut) et trois helpers de commodité non-virtuels basés sur getState().
Les helpers isFree() / isOccupied() / isInactive() délèguent à getState() afin que toute sous-classe surchargeant getState() bénéficie automatiquement de la logique correcte.
Modèle d'un tronçon de voie droite. Identifiant au format "s/0", "s/1", … ou "s/0_c1", "s/0_c2", … après découpe par le pipeline.
Chaque bloc maintient deux représentations de sa polyligne, pour deux usages distincts :
| Champ | Système | Usage |
|---|---|---|
m_pointsWGS84 | WGS-84 (lat, lon) | Rendu Leaflet — TopologyRenderer |
m_pointsUTM | UTM (x = est, y = nord, mètres) | Calculs métriques du pipeline |
Les deux polylignes ont la même taille et le même indexage.
Longueur géodésique : m_lengthMeters est calculée par somme Haversine sur m_pointsWGS84 et mise à jour automatiquement par setPointsWGS84(). getLengthUTM() calcule la longueur euclidienne depuis m_pointsUTM à la demande.
m_neighbourIds contient les IDs (chaînes) des blocs adjacents, qu'il s'agisse de StraightBlock ou de SwitchBlock. La liste est maintenue triée lexicographiquement — addNeighbourId() insère en position correcte et rejette les doublons.
| Méthode | Rôle |
|---|---|
| addNeighbourId(id) | Insertion triée sans doublon |
| replaceNeighbourId(oldId, newId) | Substitution lors de l'absorption double switch |
Renseignés par Phase9_RepositoryTransfer::resolve() via setNeighbourPrev() / setNeighbourNext() ou setNeighbourPointers(). Ils restent nullptr jusqu'à cette phase — aucun code antérieur ne doit les lire.
| Méthode | Rôle |
|---|---|
| getNeighbours() | Retourne la struct complète |
| setNeighbourPrev(elem) | Assigne l'extrémité A |
| setNeighbourNext(elem) | Assigne l'extrémité B |
| setNeighbourPointers(n) | Assigne les deux en une opération |
Modèle d'un aiguillage ferroviaire à 3 branches. Identifiant au format "sw/0", "sw/1", …
Un SwitchBlock passe par trois états successifs au fil du pipeline :
Le point de jonction (m_junctionWGS84, m_junctionUTM) est le nœud physique où convergent les trois branches.
Les tips CDC (m_tipOnRoot, m_tipOnNormal, m_tipOnDeviation) sont les extrémités de chaque branche issues du fichier CDC. Ils sont de type std::optional car absents avant l'orientation (Phase8).
La longueur totale de traversée est calculée par computeTotalLength() :
où chaque leg est la distance Haversine entre la jonction et le tip correspondant.
| Requête | Description |
|---|---|
| getJunctionWGS84() | Coordonnée WGS-84 du nœud de jonction |
| getJunctionUTM() | Coordonnée UTM du nœud de jonction |
| getTipOnRoot() | Tip CDC branche root — std::optional |
| getTipOnNormal() | Tip CDC branche normale — std::optional |
| getTipOnDeviation() | Tip CDC branche déviée — std::optional |
| getTotalLengthMeters() | Longueur de traversée — std::optional |
Avant Phase7, un SwitchBlock ne connaît que la liste brute m_branchIds. orient() assigne les rôles sémantiques root / normal / deviation en vérifiant que chaque ID est bien présent dans m_branchIds (lève std::invalid_argument sinon).
isOriented() retourne true dès que m_rootBranchId est renseigné.
swapNormalDeviation() permute symétriquement les rôles, les tips CDC, les polylignes absorbées et les marqueurs de double switch — utilisé par Phase8_SwitchOrientator lors d'une correction de sens.
| Méthode | Rôle |
|---|---|
| orient(rootId, normalId, deviationId) | Assigne les trois rôles |
| swapNormalDeviation() | Permutation normale ↔ deviation |
| isOriented() | True si les rôles sont assignés |
Quand deux aiguillages sont reliés par un court StraightBlock de liaison, Phase7_DoubleSwitchDetector absorbe ce segment dans l'un des deux aiguillages.
absorbLink() effectue en une seule opération :
linkId par partnerId dans m_branchIdsm_normalBranchId ou m_deviationBranchIdm_doubleOnNormal ou m_doubleOnDeviation avec l'ID du partenaireisDouble() retourne true si au moins un côté a absorbé un segment. getPartnerOnNormal() / getPartnerOnDeviation() retournent le SwitchBlock* partenaire via cast statique (valide uniquement si isDouble() est true).
| Requête | Description |
|---|---|
| isDouble() | True si un segment de liaison a été absorbé |
| getDoubleOnNormal() | ID du partenaire côté normal — std::optional |
| getDoubleOnDeviation() | ID du partenaire côté deviation — std::optional |
| getAbsorbedNormalCoordinates() | Polyligne WGS84 absorbée côté normal |
| getAbsorbedDeviationCoordinates() | Polyligne WGS84 absorbée côté deviation |
| getAbsorbedNormalCoordsUTM() | Polyligne UTM absorbée côté normal |
| getAbsorbedDeviationCoordsUTM() | Polyligne UTM absorbée côté deviation |
| getPartnerOnNormal() | SwitchBlock* partenaire côté normal, nullptr sinon |
| getPartnerOnDeviation() | SwitchBlock* partenaire côté deviation, nullptr sinon |
Renseignés par Phase9_RepositoryTransfer::resolve().
| Mutation | Rôle |
|---|---|
| setRootPointer(elem) | Assigne la branche root |
| setNormalPointer(elem) | Assigne la branche normale |
| setDeviationPointer(elem) | Assigne la branche déviée |
| setBranchPointers(branches) | Assigne les trois en une opération |
| replaceBranchPointer(old, new) | Substitution après absorption (Phase8) |
m_activeBranch représente la position physique de l'aiguillage (NORMAL par défaut). Il est modifié en runtime par l'opérateur via l'IHM.
Lors d'une modification, la valeur est propagée automatiquement aux aiguillages partenaires d'un double switch via getPartnerOnNormal() / getPartnerOnDeviation(), sauf si propagate = false est passé explicitement (pour éviter la récursion infinie lors de la réception d'une propagation).
| Méthode | Rôle |
|---|---|
| getActiveBranch() | Branche active courante |
| isDeviationActive() | Raccourci booléen |
| setActiveBranch(branch, propagate=true) | Assigne + propage optionnellement |
| toggleActiveBranch(propagate=true) | Alterne + propage + retourne la nouvelle valeur |
Les deux structs de pointeurs suivent la même convention :
TopologyRepository — ne jamais delete ces pointeurs.Phase9_RepositoryTransfer::resolve().nullptr indique un terminus ou une branche non résolue.StraightBlock::StraightNeighbours
| Champ | Type | Description |
|---|---|---|
prev | ShuntingElement* | Bloc adjacent à l'extrémité A — nullptr si terminus |
next | ShuntingElement* | Bloc adjacent à l'extrémité B — nullptr si terminus |
| Champ | Type | Description |
|---|---|---|
root | ShuntingElement* | Tronc entrant — nullptr si non résolu |
normal | ShuntingElement* | Sortie directe — nullptr si non résolu |
deviation | ShuntingElement* | Sortie déviée — nullptr si non résolu |
| Enum | Valeurs | Usage |
|---|---|---|
| ElementType | SWITCH, STRAIGHT | Typage sans RTTI — dispatching dans PCCGraphBuilder et GeoParser |
| ShuntingState | FREE, OCCUPIED, INACTIVE | État opérationnel de l'infrastructure |
| ActiveBranch | NORMAL, DEVIATION | Position physique de l'aiguillage — modifiée en runtime |