Thème
Superviseur — Simulateur de centrales
Émule des centrales de détection incendie INIS-E16 et AVISS afin d'exercer le Superviseur de bout en bout, sans matériel physique.
Le simulateur se présente sous deux formes, bâties sur un cœur commun :
| Projet | Forme | Usage |
|---|---|---|
dotnet/simulator.core | Bibliothèque (.NET 9, sans WPF) | Serveurs Modbus TCP/RTU, centrales, profils, scénarios. Consommé par les deux hôtes ci-dessous et par dotnet/tests/simulator. |
dotnet/simulator | Application WPF | Interface : ajout de centrales, bascule des états à la souris, modes automatiques. |
dotnet/simulator.headless | Console | Sans interface, piloté par HTTP. Démarre automatiquement sous les tests de bout en bout. |
La logique Modbus vivant dans le cœur, les deux hôtes se comportent identiquement sur le fil : même profils, mêmes adresses, même réaction aux commandes.
| Technologie | C# .NET 9 ; WPF pour l'interface, console pour l'hôte sans interface |
| Points d'entrée | simulator/MainWindow.xaml.cs, simulator.headless/Program.cs |
| Bibliothèque Modbus | NModbus |
| Référence projet | dotnet/shared — pour les cartographies de registres |
| Tests | dotnet/tests/simulator |
Structure du cœur
| Fichier | Rôle |
|---|---|
PanelViewModel.cs | Une centrale émulée : serveur Modbus, état des zones et des points, pilotage des états système |
PanelModels.cs | Zones et points, une zone suivant ses points pour tenir ses états à jour |
RtuBus.cs | Bus RTU multi-drop : un transport physique, N esclaves, chacun avec son identifiant et son magasin de données |
PanelDispatcher.cs | Sérialise les mutations d'état d'une centrale sur un fil unique |
ObservableDataStore.cs | Notifie les écritures venues du superviseur et sérialise tous les accès au tableau interne |
ProfileConfig.cs | Profil Modbus d'une centrale simulée, dérivé des cartographies partagées |
SlaveFaultModel.cs | Défaut de communication d'un esclave voisin — adresses JBUS 9 à 16 de la table AVISS |
ScenarioPreset.cs | Scénarios prêts à jouer |
DemoFixtureLoader.cs | Lecture de demo-fixtures.json |
CleanupHelper.cs | Seul endroit où une exception est délibérément avalée : la libération d'un transport déjà fermé ne doit pas interrompre le nettoyage |
Fonctionnalités
- Multi-centrales : simule plusieurs centrales simultanément, chacune avec son type, son profil Modbus et son identifiant esclave.
- Modbus TCP : chaque centrale TCP écoute sur son propre port (ex.
502,503). - Modbus RTU multi-drop : plusieurs centrales partagent un même bus série émulé via Named Pipe (ou port série réel), comme un vrai bus RS-485 multi-points.
- Profils alignés : adresses de registres dérivées de la source unique partagée
dotnet/shared/Profiles/StandardPanelProfiles.cs(INIS-E16 et AVISS), identiques à celles du backend. - Injection d'états : forçage des états de zones (Feu, Évacuation, Émission, DM, Dérangement, Test, Hors-Service, Neutralisation, Mode manuel seul, Arrêt d'urgence, Inhibition) et de points (Feu, Dérangement, Hors-Service). Les quatre derniers états de zone n'existent que sur les centrales dont le profil déclare les tables correspondantes (INIS-E16).
- Injection des états système : chaque bit de la table des paramètres généraux servi par la centrale se force indépendamment de l'état des zones — synthèses feu et dérangement, points hors service, zone en essai, synthèses de neutralisation et de mode manuel seul, défauts de communication des esclaves 01 à 08 (AVISS). Chaque bit porte son adresse JBUS en infobulle, et les tables qu'une centrale ne sert pas n'apparaissent pas.
- Génération de firmware : une bascule fait servir la table de synthèse d'origine, plus courte, la centrale répondant « adresse illégale » au-delà. Reproduit le parc antérieur aux synthèses d'exploitation, pour vérifier que la supervision reste connectée en mode dégradé.
- Modes automatiques : Cascade (propagation progressive d'incidents) et Chaos (événements aléatoires) pour les tests de charge et de robustesse. Réservés à l'interface graphique.
- Réception des commandes : interprète les commandes écrites par le backend (réarmement, mise hors/en service, essai).
- Compteur de lectures temps réel pour visualiser l'activité de polling du backend.
Prérequis
- .NET 9 SDK
- Windows pour l'interface graphique (WPF). Le cœur et l'hôte sans interface ne visent aucune plateforme en particulier.
Interface graphique
bash
dotnet run --project dotnet/simulatorAu lancement, un parc de démonstration démarre : 2 centrales INIS-E16 (Modbus TCP, ports 502 et 503) et 3 centrales AVISS sur un bus RTU multi-drop partagé (Named Pipe). L'interface permet d'ajouter ou retirer des centrales, de basculer les états et d'activer les modes automatiques. Côté Superviseur, configurer les centrales avec les mêmes paramètres de connexion (port TCP ou bus RTU).
Simulateur sans interface
Sert les centrales décrites dans demo-fixtures.json — le fichier dont le backend tire aussi son jeu de démonstration, ce qui garantit que ports, tubes nommés et identifiants d'esclave concordent des deux côtés.
bash
# Toutes les centrales du fichier ; canal de pilotage sur :7000
dotnet run --project dotnet/simulator.headless
# Un sous-ensemble, et un autre port de pilotage
dotnet run --project dotnet/simulator.headless -- --panels demo-inis-01,demo-aviss-01 --control-port 7000Une clé de centrale inconnue interrompt le démarrage et liste les clés disponibles, plutôt que de laisser une centrale silencieusement absente.
Canal de pilotage
Réservé au développement et aux tests : il permet de provoquer un changement d'état et de lire l'état réel de la centrale simulée — par exemple pour vérifier qu'une commande émise depuis l'interface l'a bien atteinte.
| Route | Effet |
|---|---|
GET /sim/health | Sonde de démarrage ; liste les centrales servies. |
POST /sim/{centrale}/zone/{n}/{état} | Pose un état : fire, fault, test, disabled, neutralized, manualonly, emergencystop, inhibited, ou normal qui remet la zone au repos. |
GET /sim/{centrale}/zone/{n} | État courant de la zone, tel que la centrale le détient. |
POST /sim/{centrale}/firmware/{legacy|current} | Bascule la génération de firmware servie : legacy limite la table de synthèse à sa taille d'origine, la centrale répondant « adresse illégale » au-delà. |
bash
curl http://127.0.0.1:7000/sim/health
curl -X POST -d '' http://127.0.0.1:7000/sim/demo-inis-01/zone/3/fire
curl http://127.0.0.1:7000/sim/demo-inis-01/zone/3Une centrale, une zone ou un état inconnus sont refusés par un code d'erreur explicite : une faute de frappe échoue immédiatement au lieu de laisser croire à un test réussi.
Tests
dotnet/tests/simulator vérifie que le cœur réagit aux trames de commande hors de toute interface graphique, sur les deux profils et sous commandes concurrentes.
bash
dotnet test dotnet/tests/simulator/simulator.tests.csprojLes tests de bout en bout du frontend (cd vue && npm run test:e2e) démarrent l'hôte sans interface automatiquement ; il n'y a rien à lancer à la main.
Mécanismes non évidents
Le cœur ne référence pas le backend
DemoFixtureLoader et ProfileConfig sont des répliques autonomes : même contrat JSON, mêmes adresses — puisées dans dotnet/shared/Profiles — mais aucune référence croisée. Le simulateur peut donc tourner sans le backend, et une divergence d'adressage reste impossible, les deux côtés lisant la même cartographie.
Toute mutation d'état passe par le répartiteur
PanelDispatcher sérialise les changements sur un fil unique. Le canal de pilotage HTTP poste sa mutation puis attend qu'elle soit réellement appliquée, dans la limite de cinq secondes : un test qui pose une alarme et lit aussitôt la centrale voit l'état posé, pas l'état précédent.
Une clé de centrale inconnue arrête le démarrage
L'hôte sans interface interrompt son démarrage et liste les clés disponibles plutôt que de laisser une centrale silencieusement absente. Le canal de pilotage refuse de même une centrale, une zone ou un état inconnus par un code d'erreur explicite.
Le bus RTU est un vrai multi-drop
Un seul transport — port série ou tube nommé — héberge N esclaves, chacun avec son identifiant et son magasin de données. Le superviseur les interroge par identifiant d'esclave, comme sur un RS-485 AVISS.
Contraintes actives
| Contrainte | Conséquence si elle est ignorée |
|---|---|
simulator.core et simulator.headless ne dépendent d'aucune interface graphique | L'intégration continue les compile et les éprouve sur Ubuntu |
Les adresses viennent de dotnet/shared/Profiles | Les déclarer localement ferait diverger simulateur et backend |
| Les modes automatiques restent propres à l'interface graphique | Cascade et Chaos n'existent pas dans l'hôte sans interface |
demo-fixtures.json est partagé avec le jeu de démonstration du backend | Ports, tubes nommés et identifiants d'esclave concordent des deux côtés par construction |