Thème
Superviseur — Supervision d'alarmes incendie Desautel
Logiciel professionnel de supervision en temps réel de centrales de détection incendie via les protocoles Modbus TCP et RTU, compatible avec les centrales INIS-E16 et AVISS.
Documentation :
docs/README.md— index « quel document pour quel besoin ».Notices :
- Utilisation (opérateur / administrateur) — FR :
docs/notices/fr/notice_utilisation.md· EN :docs/notices/en/notice_utilisation.md- Installateur (mise en service, configuration, rôle SuperAdmin) — FR :
docs/notices/fr/notice_installateur.md· EN :docs/notices/en/notice_installateur.md- Technique (intégrateur/exploitant) —
docs/project/NOTICE_TECHNIQUE.md- Dimensionnement stockage & specs du poste —
docs/project/DIMENSIONNEMENT.md
Fonctionnalités clés
- Supervision temps réel multi-centrales — état consolidé de l'ensemble du parc, mises à jour poussées par SignalR.
- Compatibilité INIS-E16 et AVISS — Modbus TCP et RTU, y compris bus série multi-drop (plusieurs centrales partageant la même liaison).
- Acquittement granulaire par type d'incident — feu, dérangement, hors-service, évacuation… traités indépendamment sur chaque zone et chaque point, un acquittement valant pour tout ce que son échelon contient : synthèse → zones → points.
- États d'exploitation supervisés — neutralisation, mode manuel seul, arrêt d'urgence et inhibition remontés par zone et en synthèse générale, signalés dans le chrome de l'application par la couleur qui leur est propre, sans clignotement.
- Plans interactifs — positionnement des zones et points sur le plan du site, statut en temps réel et tooltips détaillés ; chaque synthèse active d'une centrale dessine son anneau autour de son implantation.
- Statistiques et rapports — agrégation par type d'événement, tendances, exports CSV et PDF.
- Alerte sonore — signalisation audio différenciée par type d'incident et par état système, avec reprise après acquittement, doublée de retours sonores d'interface discrets et réglables à part.
- Accès distant — supervision accessible à distance en complément du poste opérateur local.
- Journal d'audit horodaté — chaque détection, acquittement et commande tracé en base PostgreSQL.
- Sécurité — authentification JWT (session liée au processus), contrôle d'accès par rôles (RBAC), secret applicatif unique, fermeture du client protégée par mot de passe administrateur.
Fonctionnement global
Le Superviseur interroge en permanence les centrales incendie via le bus Modbus. Chaque centrale expose des tables de bits : un bit passe de 0 à 1 lorsqu'un événement se produit (alarme feu, dérangement, hors-service, etc.). Le logiciel détecte ces changements, les affiche en temps réel et permet aux opérateurs d'acquitter chaque incident individuellement.
Cycle de surveillance
Chaque centrale est supervisée par sa propre tâche, indépendante des autres : une centrale injoignable ne retarde pas la lecture des suivantes. À chaque cycle, la tâche lit les tables déclarées par le profil et compare l'état lu à l'état précédent.
| Réglage | Valeur | Où |
|---|---|---|
| Intervalle de cycle par défaut | 2 s | SupervisionSettings:PollingIntervalSeconds — appsettings.json |
| Intervalle effectif | pollingInterval de la configuration globale, en millisecondes, quand il est renseigné | Réglages → Paramètres système, stocké en base |
| Plancher | 200 ms, appliqué quel que soit le réglage | SupervisionSettings:MinPollingIntervalMs |
| Cycles de confirmation | 2 | SupervisionSettings:StateConfirmationCycles |
| Recul maximal après échecs consécutifs | 10 s | SupervisionSettings:MaxReconnectBackoffMs |
Tables Modbus — Ce qu'on surveille
Chaque centrale expose plusieurs tables de bits. Chaque table représente un type d'incident et chaque bit dans cette table correspond à un élément (zone, point, ou paramètre système). Les adresses ci-dessous font foi dans dotnet/shared/Profiles/StandardPanelProfiles.cs, lu par le backend comme par le simulateur.
| Table | Portée | INIS-E16 | AVISS |
|---|---|---|---|
| Paramètres système — bit 1 feu général, bit 2 dérangement général, puis défauts | Centrale | 1, 25 bits | 1, 23 bits |
| Hors-service point | Point | 953 | 1001 |
| Feu point | Point | 2001 | 2001 |
| Dérangement point | Point | 3001 | 3001 |
| Hors-service zone | Zone | 4001 | 4001 |
| Feu zone | Zone | 5001 | 5001 |
| Dérangement zone | Zone | 6001 | 6001 |
| Essai zone | Zone | 7001 | 7001 |
| Évacuation zone | Zone | 8001 | 8001 |
| Émission zone | Zone | 8021 | 8021 |
| DM extinction zone | Zone | 8041 | 8041 |
| Neutralisation zone | Zone | 8061 | — |
| Mode manuel seul zone | Zone | 8081 | — |
| Arrêt d'urgence zone | Zone | 8101 | — |
| Inhibition zone | Zone | 8121 | — |
| Écriture de commande | Centrale | 500 | 500 |
La taille lue n'est pas la taille de la table. La fenêtre de lecture s'arrête au plus haut numéro de zone ou de point configuré sur la centrale, l'adressage pouvant être clairsemé — points 1 et 31 sans les intermédiaires. Une AVISS dont aucun point n'est configuré retombe sur une fenêtre de 508 points (PanelAdapterFactory).
Un même élément peut avoir plusieurs incidents simultanés. Par exemple, la Zone 3 peut avoir son bit à 1 dans la table Feu et dans la table Dérangement en même temps. Chaque type d'incident est traité indépendamment.
Changer une adresse
Toutes les valeurs du tableau vivent dans un seul fichier, dotnet/shared/Profiles/StandardPanelProfiles.cs, sous deux cartographies : InisE16 et Aviss. Le backend et le simulateur lisent la même, donc une adresse corrigée là s'applique des deux côtés au prochain build — il n'existe pas de seconde copie à tenir à jour. Les valeurs déclarées sont les adresses fil JBUS brutes, telles que la table d'échange les donne ; AddressOffset porte le décalage propre à la centrale et CommandTargetOffset celui des cibles d'écriture.
Deux positions de tête, à ne pas confondre. L'unité système n'occupe pas la tête de toutes les tables : le cahier dimensionne les tables de zones de base pour 999 zones, où une entrée de tête trouve sa place, et les tables d'exploitation pour les seize zones physiques, où il n'y en a pas.
| Famille | Adresses | Champ du profil | Zone physique 1 |
|---|---|---|---|
| Zones de base — HS, feu, dérangement, essai | 4001 5001 6001 7001 | BaseZoneHeadOffset = 1 | seconde position — feu zone 1 = 5002 |
| Zones d'exploitation — évacuation à inhibition | 8001 … 8121 | OperationZoneHeadOffset = 0 | première position — neutralisation zone 1 = 8061 |
Déplacer une adresse sans regarder à quelle famille elle appartient décale d'un cran tout ce que la table porte.
Le test à mettre à jour dans le même commit : ModbusProfileTests (dotnet/tests/backend/Domain/ValueObjects/)
Le reste — codes de commande, sens de lecture, ajout d'un type de centrale — est dans docs/project/PROFILS-MODBUS.md.
Cycle de vie d'un incident
Un incident naît quand un bit passe à 1 et suit un parcours précis jusqu'à sa disparition complète de l'écran.
Ce que voit l'opérateur à chaque étape
| État | Ce qui s'affiche | Action disponible | Ce qui se passe en coulisse |
|---|---|---|---|
| Actif | Badge rouge vif (ex. « Alarme feu ») | Bouton Acquitter | Bit = 1, AckStatus = None |
| Acquitté | Badge atténué + icône check | — | Bit = 1, AckStatus = SoftwareAcked |
| En attente | Badge vert + icône check | Bouton Effacer | Bit = 0, AckStatus = HardwareCleared |
| Résolu | Disparaît | — | Bit = 0 après acquittement |
Le réarmement est une commande adressée à la centrale, pas à un incident : un seul bouton « Réarmer la centrale » apparaît sur la carte une fois tous ses états acquittés, et il est réservé au SuperAdmin — visible pour les autres rôles, qui savent ainsi qu'une action est attendue, mais inerte.
Acquittement par type d'incident
L'acquittement est granulaire : chaque type d'incident sur chaque élément est traité indépendamment. Acquitter le feu sur la Zone 3 ne touche pas au dérangement sur cette même zone.
Cascade hiérarchique
Un acquittement vaut pour tout ce que son échelon contient, et jamais pour ce qui le contient. Acquitter la synthèse d'une centrale entraîne les zones qu'elle résume, et par elles leurs points ; acquitter une zone entraîne ses points ; acquitter un point n'entraîne rien. L'effacement d'un état rétabli suit la même descente.
Chaque échelon entraîné est consigné au journal sous le nom de l'opérateur : l'acquittement est hérité, non automatique, et l'historique doit pouvoir en rendre compte. Une synthèse qui ne résume aucune zone — alimentation, liaison, communication — n'entraîne rien.
Un acquittement ne remonte jamais : acquitter un point laisse sa zone et la synthèse en l'état, car rien ne dit que les autres points de la zone ont été traités.
Hiérarchie des incidents
L'interface organise les incidents en 3 niveaux, du plus global au plus précis.
Traçabilité et journal d'audit
Chaque action est enregistrée dans la base de données avec horodatage, utilisateur et type d'événement.
| Moment | Ce qui est enregistré | Exemple |
|---|---|---|
| Détection | Type + localisation | Panel-1 — Zone 3 — Fire Alarm Detected |
| Acquittement | Utilisateur + type | Panel-1 — Zone 3 Fire Alarm Acknowledged by john.doe |
| Réarmement hardware | Transition d'état | Panel-1 — Zone 3 — Fire Alarm Restored (Awaiting Ack) |
| Réapparition | Même incident revient | Panel-1 — Zone 3 — Fire Alarm Reappeared |
| Nettoyage | Fin de cycle | Panel-1 — Zone 3 Fire Alarm Reset by john.doe |
Architecture technique
Le code est réparti entre dotnet/ (les projets .NET), vue/ (l'interface web) et docs/ (la documentation) ; deployment/ et scripts/ portent l'outillage. Pour le détail de l'architecture, voir docs/project/ARCHITECTURE.md.
| Composant | Technologie | Rôle |
|---|---|---|
dotnet/backend | C# .NET 9, ASP.NET Core, EF Core, PostgreSQL (Npgsql), SignalR | Service Windows : polling Modbus, API REST, temps réel, journal d'audit |
vue | Vue 3, TypeScript, Tailwind CSS, DaisyUI, Pinia | Interface web bilingue (FR/EN) : tableau de bord, carte SVG, historique |
dotnet/client | WPF .NET 9, WebView2 | Fenêtre native Windows redimensionnable ; interface servie depuis le disque (hôte virtuel), fermeture protégée par mot de passe administrateur |
dotnet/manager | WinForms .NET 9 | Gestionnaire de service (barre des tâches) |
dotnet/simulator | WPF .NET 9 | Simulateur de centrale Modbus (interface graphique) |
dotnet/simulator.core | C# .NET 9 | Cœur du simulateur : serveurs Modbus TCP/RTU, centrales, scénarios |
dotnet/simulator.headless | C# .NET 9 (console) | Simulateur sans interface, piloté par HTTP (tests de bout en bout) |
dotnet/shared | C# .NET 9 | Modèles et configuration partagés |
dotnet/license-generator | WPF .NET 9 | Générateur de licences (outil interne technicien) |
dotnet/tools/TsEnumGenerator | C# .NET 9 (console) | Génère vue/src/config/generated/enums.ts depuis les enums C# ; hors solution |
dotnet/tests/ | xUnit (backend, client, manager, simulator, tools), Vitest (vue/src/), Playwright (vue/e2e/) | Tests unitaires, d'intégration et de bout en bout |
deployment/ | PowerShell + Inno Setup 6 | Compilation, packaging et installateur Windows — deployment/README.md |
scripts/ | PowerShell, Node | Génération des enums TS, analyse SonarQube, plans de démonstration |
docs/ | VitePress + DocFX + TypeDoc | Portail documentaire — docs/README.md |
Chaque module porte son propre README : backend · frontend · client · gestionnaire de service · simulateur · shared · TsEnumGenerator · licences · tests .NET · scripts · déploiement.
Développement
Prérequis
| Outil | Requis pour | Notes |
|---|---|---|
| SDK .NET 9 | Tout | global.json épingle 9.0.100 (rollForward: latestFeature) : un SDK plus récent installé sans son runtime est ignoré |
| Node.js 20.19+ ou 22.12+ | Frontend, docs | Voir engines dans vue/package.json |
| PostgreSQL 17 | Backend | winget install PostgreSQL.PostgreSQL.17. Le backend y puise initdb et pg_ctl pour monter son cluster de développement ; l'installateur y prend les binaires qu'il embarque |
| Runtime WebView2 | Client de bureau | Présent d'origine sur Windows 10 1803+ et Windows 11 ; l'installateur de production le pose si le poste ne le porte pas |
| Inno Setup 6 | Installateur | Uniquement pour deployment\build-all.ps1 — voir deployment/README.md |
| Visual Studio 2022 ou JetBrains Rider | Confort | La ligne de commande dotnet suffit |
dotnet/backend/appsettings.Crypto.dev.json | Codes revendeurs | Copier depuis appsettings.Crypto.dev.exemple.json. Facultatif : un dépôt fraîchement cloné démarre sans lui (voir La base PostgreSQL en développement) |
Secret applicatif unique —
Security:SuperAdminPassword(dansappsettings.Crypto.dev.jsonen développement,appsettings.Crypto.prod.jsonen production) sert à la fois au compte SuperAdmindesautel(haché BCrypt au premier démarrage) et au mot de passe du rôle PostgreSQL (injecté dans la chaîne de connexion au démarrage). Le service refuse de démarrer si ce secret est absent (aucune valeur par défaut). En production Windows, le fichier est scellé au repos via DPAPI dès le premier démarrage, enappsettings.Crypto.prod.protected. Aucun secret n'est embarqué dans l'installateur : l'opérateur le saisit à la première installation, etprovision-admin-secret.ps1l'applique au rôle PostgreSQL puis l'écrit dansappsettings.Crypto.prod.jsonsur le poste. Le fichier crypto livré porte unSuperAdminPasswordvide ; seules les clés revendeurs y subsistent.
Premier lancement après un clone
powershell
git clone https://github.com/RD-Desautel/Superviseur.git
cd Superviseur
dotnet restore # solution .NET
cd vue && npm install && cd .. # dépendances du frontend
dotnet run --project dotnet/backend # crée le cluster PostgreSQL puis la base, API sur :5264Le premier démarrage du backend construit le cluster, la base et le compte SuperAdmin desautel ; il prend quelques dizaines de secondes, les suivants sont immédiats. L'interface se lance à part, dans un second terminal (cd vue && npm run dev) : le proxy Vite dirige /api et /modbusHub vers :5264.
Lancement en mode développement
bash
# Backend normal (service + API sur :5264 en développement, :5000 en production)
dotnet run --project dotnet/backend
# Backend Demo (Fixtures à cumuler avec le mode charge du simulateur situé dans l'ui pour créer et lancer toutes les centrales de test)
dotnet run --project dotnet/backend --launch-profile "Superviseur.Api (démo)"
# Frontend (dans un autre terminal, HMR sur :5173)
cd vue && npm install && npm run dev
# Sous une marque revendeur, pour l'éprouver sans poste installé (compilation DEBUG seule)
dotnet run --project dotnet/backend -- --brand <identifiant>Le front reçoit son habillage du service : l'argument suffit à voir l'interface entière sous la marque demandée. Le client de bureau et le gestionnaire lisent le même réglage par la variable d'environnement SUPERVISEUR_BRAND. Détail dans la marque blanche.
Les autres exécutables se lancent de la même façon, chacun dans son terminal :
| Exécutable | Commande | Ce qu'il faut avoir démarré |
|---|---|---|
| Client de bureau (WPF) | dotnet run --project dotnet/client | Vite sur :5173 — en DEBUG le client charge le serveur de développement et vise l'API sur :5264 |
| Simulateur de centrales (WPF) | dotnet run --project dotnet/simulator | Rien : il sert lui-même les centrales que le backend interroge |
| Simulateur sans interface | dotnet run --project dotnet/simulator.headless | Rien : centrales de demo-fixtures.json, pilotage HTTP sur :7000 |
| Gestionnaire de service | dotnet run --project dotnet/manager | Le service Windows installé : le gestionnaire pilote Superviseur-Service, pas un backend lancé depuis le dépôt |
En production, le client WPF charge l'interface depuis le disque via un hôte virtuel WebView2 (https://superviseur.local/) et appelle l'API du backend sur https://localhost:5000 ; en développement, il pointe sur le serveur Vite http://localhost:5173 (API proxifiée vers :5264).
La base PostgreSQL en développement
Le poste de développement monte son propre serveur PostgreSQL, indépendant de toute installation du Superviseur : au premier dotnet run, le backend initialise un cluster dans le profil de l'utilisateur et le démarre.
| Cluster | %LOCALAPPDATA%\Superviseur_Dev\pgdata |
| Port | 5433 — un service installé occupe le 5432 |
| Base / rôle | firesupervisor / firesupervisor_app, créés au démarrage du backend |
| Secret | Security:SuperAdminPassword, lu dans appsettings.Development.json |
| Journal | %LOCALAPPDATA%\Superviseur_Dev\postgres.log |
Les exécutables viennent d'une installation de PostgreSQL 17, déjà nécessaire pour construire l'installateur — qui y puise les binaires qu'il embarque (winget install PostgreSQL.PostgreSQL.17). Le serveur tourne sous le compte de l'utilisateur, sans service Windows.
Les deux emplacements connus sont ceux de l'installateur officiel, C:\Program Files\PostgreSQL\17\bin et son prédécesseur en 16. Un poste qui installe ailleurs — autre disque, version portable, droits refusés sur Program Files — désigne le sien par la variable d'environnement SUPERVISEUR_PG_BIN, qui passe devant :
powershell
$env:SUPERVISEUR_PG_BIN = "D:\pgsql\bin"Le secret de développement est versionné dans appsettings.Development.json : il n'ouvre qu'un cluster local et n'a aucune valeur ailleurs. appsettings.Crypto.dev.json reste hors du dépôt, puisqu'il porte les clés revendeurs ; chargé après, il remplace ce secret dès qu'il existe. Un dépôt fraîchement cloné démarre donc sans ce fichier, qui n'est requis que pour éprouver les codes revendeurs.
Pour inspecter la base (extension PostgreSQL de VS Code, psql, DBeaver…) :
| Paramètre | Valeur |
|---|---|
| Hôte / Server | localhost |
| Port | 5433 — dans l'extension VS Code, il se règle sous Advanced |
| Base / Database | firesupervisor |
| Utilisateur | firesupervisor_app |
| Mot de passe | Security:SuperAdminPassword de votre configuration de développement |
Le mot de passe du rôle est le secret applicatif : le backend l'applique à PostgreSQL au démarrage. Lancez-le donc au moins une fois avant de vous connecter avec un outil externe.
Repartir d'une base vierge revient à supprimer le cluster ; le démarrage suivant le reconstruit :
powershell
& "C:\Program Files\PostgreSQL\17\bin\pg_ctl.exe" -D "$env:LOCALAPPDATA\Superviseur_Dev\pgdata" -m fast stop
Remove-Item -Recurse -Force "$env:LOCALAPPDATA\Superviseur_Dev"Elle emporte tout le dossier : la base, mais aussi les plans téléversés, les sons remplacés et la clé de session. Supprimer le seul pgdata laisse ces derniers en place.
Si le démarrage échoue sur could not locate a valid checkpoint record, le journal des transactions du cluster a disparu — ses fichiers de données, eux, sont intacts. Le backend le constate au démarrage, repose un journal neuf avec pg_resetwal et réessaie : il n’y a rien à faire. Il ne s’y risque que sur un cluster arrêté proprement ; sinon il s’arrête en le disant, et forcer la réinitialisation — au risque d’une base incohérente — se fait à la main :
powershell
& "C:\Program Files\PostgreSQL\17\bin\pg_resetwal.exe" -f -D "$env:LOCALAPPDATA\Superviseur_Dev\pgdata"Tests
bash
# Backend (xUnit)
dotnet test
# Frontend (Vitest)
cd vue && npm run test:run
# Bout en bout dans un navigateur (Playwright, local)
cd vue && npm run test:e2eLes tests de bout en bout démarrent eux-mêmes le simulateur sans interface, le backend et Vite. Ils requièrent un PostgreSQL local et dotnet/backend/appsettings.Crypto.dev.json (prérequis de développement).
Le simulateur sans interface sert les centrales de demo-fixtures.json — le fichier dont le backend tire aussi son jeu de démonstration — et se lance seul pour développer sans l'interface graphique :
bash
# Toutes les centrales de démonstration, canal de pilotage sur :7000
dotnet run --project dotnet/simulator.headless
# Un sous-ensemble choisi
dotnet run --project dotnet/simulator.headless -- --panels demo-inis-01,demo-aviss-01 --control-port 7000
curl http://127.0.0.1:7000/sim/health # centrales servies
curl -X POST -d '' http://127.0.0.1:7000/sim/demo-inis-01/zone/3/fire # poser une alarme
curl http://127.0.0.1:7000/sim/demo-inis-01/zone/3 # état réel de la centraleDocumentation
powershell
# Génération complète + serveur local sur http://localhost:3008/
.\docs\generate-docs.ps1 -Serve
# Sans l'API C# (plus rapide)
.\docs\generate-docs.ps1 -SkipBackend -Serve
# Développement avec hot-reload (guides + API TypeScript) — outillage docs sous docs/
cd docs && npm run docs:dev| Section | URL | Outil |
|---|---|---|
| Portail et guides | http://localhost:3008/ | VitePress |
| API TypeScript | http://localhost:3008/api/ | TypeDoc |
| Référence C# | http://localhost:3008/backend/ | DocFX |
Build et déploiement
Superviseur
powershell
cd deployment
.\build-all.ps1Le script compile tout en Release, télécharge le runtime WebView2 si nécessaire, embarque les binaires PostgreSQL 17 et génère Installer/Superviseur-Setup-<version>.exe via Inno Setup 6. Voir deployment/README.md pour les détails.
Le poste à équiper n'a besoin d'aucun prérequis. Les quatre exécutables sont publiés autonomes (runtime .NET inclus), PostgreSQL est embarqué avec ses bibliothèques — dont le runtime C de Visual C++, dont tous ses exécutables dépendent sans que sa distribution ne le livre —, et le runtime WebView2 n'est posé que si Windows ne le porte pas déjà. Un Windows 11 fraîchement installé suffit.
Outil de licences
Outil interne Desautel, publié par build-all.ps1 dans staging/license-tool/ et volontairement absent de l'installateur : les logiciels livrés ne contiennent que la clé publique de vérification.
powershell
dotnet publish dotnet/license-generator/LicenseGenerator.csproj -c Release -r win-x64 `
--self-contained true `
-p:PublishSingleFile=true `
-p:IncludeNativeLibrariesForSelfExtract=true `
-p:EnableCompressionInSingleFile=true `
-p:DebugType=none `
-p:GenerateDocumentationFile=false `
-o staging/license-toolLes quatre dernières options sont nécessaires au fichier unique : PublishSingleFile seul laisse à côté de l'exécutable les cinq bibliothèques natives de WPF, les symboles de débogage et la documentation XML.
Le poste à équiper n'a besoin d'aucun outil : le client de supervision et le gestionnaire de service produisent la demande d'activation et appliquent la licence reçue. Circuit complet, format et rotation des clés dans dotnet/license-generator/README-licences.md.
Sécurité et rôles
Le système implémente un contrôle d'accès strict (RBAC) avec authentification JWT. Trois rôles : le SuperAdmin (installateur/configurateur du site), l'Admin (exploitant avancé) et l'Utilisateur (opérateur). Le tableau suivant fait autorité — l'application applique ces droits côté frontend (visibilité) et côté backend (autorisation des endpoints).
| Fonction | SuperAdmin | Admin | Utilisateur |
|---|---|---|---|
| Carte live, supervision, incidents, acquittements | ✅ | ✅ | ✅ |
| Historique / rapports / statistiques (consultation) | ✅ | ✅ | ✅ |
| Réseau INIS — consultation | ✅ | ✅ | ✅ |
| Réseau INIS — édition (réseaux, matrices, assignations) | ✅ | ✅ | ❌ |
| Commandes centrale hors réarmement (silence, hors/en service, essai) | ✅ | ✅ | ✅ |
| Réarmement d'une centrale (reset, tout type) | ✅ | ❌ | ❌ |
| Gestion des comptes (hors SuperAdmin) | ✅ | ✅ | ❌ |
| Options générales (nom du site, apparence, sons, logo) | ✅ | ✅ | ❌ |
| Paramètres systèmes, maintenance, réinitialisation d'usine | ✅ | ❌ | ❌ |
| Configuration des centrales | ✅ | ❌ | ❌ |
| Sauvegarde / restauration | ✅ | ❌ | ❌ |
| Éditeur de plans | ✅ | ❌ | ❌ |
| Export intégral de la base / suppression d'archives | ✅ | ✅ | ❌ |
| Scanner Modbus & console de diagnostic | ✅ | ❌ | ❌ |
Le bouton « Réarmer » proposé après un acquittement reste visible pour tous (il signale que la centrale doit être réarmée) mais n'est cliquable que par le SuperAdmin ; pour les autres rôles il est inerte, sans curseur cliquable, afin d'être compris comme une instruction et non comme un dysfonctionnement.
Protections supplémentaires :
- Chiffrement des fichiers de configuration INIS (algorithme SimpleCrypt Desautel)
- Clés revendeurs et mots de passe sensibles dans
appsettings.Crypto.{dev,prod}.json(non versionnés) - Fermeture du client protégée par mot de passe administrateur (autorisée sans mot de passe si le backend est injoignable)
- Session liée au processus : déconnexion à la fermeture de la fenêtre, au redémarrage du poste, à la désinstallation/mise à jour ; maintien de la session en veille
- Mot de passe SuperAdmin (compte
desautel, également mot de passe du rôle PostgreSQL) saisi par l'installateur à la première installation ; voir la notice installateur. - Journal d'audit réglementaire inaltérable (traçabilité des actions sensibles, conservation cinq ans).
Sauvegarde et restauration
Le SuperAdmin exporte une archive ZIP qui porte la configuration du poste et les fichiers téléversés — logos, plans, sons. Elle contient les comptes, les centrales avec leurs zones et points, les plans, la configuration générale, les profils Modbus, les réseaux INIS, les modèles de rapport et les notes d'exploitation ; le journal des événements n'y entre que si on le demande.
En sont écartés, chacun pour une raison tenue à jour dans BackupCoverageTests : le journal d'audit réglementaire, qui retrace ce qui s'est produit sur cette machine et qu'une restauration ne réécrit pas ; l'état lu sur les centrales, reconstruit à la relecture de la configuration ; les mesures de qualité de liaison ; les rapports déjà produits, archivés à part et regénérables.
Une entité ajoutée à la base n'entre pas d'elle-même dans l'archive, et son absence ne se voit pas : l'export réussit, l'archive paraît complète, et la perte n'apparaît qu'à la restauration. Un contrôle porté sur le contexte de persistance échoue donc dès qu'une table n'est ni sauvegardée ni écartée avec son motif — voir les profils Modbus pour la marche à suivre quand on en ajoute une.
À la restauration, les profils livrés que l'exploitant n'a pas modifiés sont réalignés sur la cartographie du code. Une archive les fige tels qu'ils étaient le jour de son écriture, adresses comprises : sans ce réalignement, une correction d'adressage venue depuis serait défaite et le poste interrogerait les anciennes adresses jusqu'au redémarrage suivant du service. Un profil livré modifié sur site garde ce qu'on en a fait, exactement comme au démarrage.
Documentation Modbus
Les adresses de registres, les codes de commande et la carte GlobalBits — quels bits de synthèse chaque centrale sert — des centrales standard font foi dans une source unique en code, dotnet/shared/Profiles/StandardPanelProfiles.cs, partagée par le backend et le simulateur ; le vocabulaire des états système vit à côté, dans dotnet/shared/Profiles/SystemFlagKeys.cs.
Ce qu'il faut retenir
Les tables d'échange INIS-E16 et AVISS documentent des adresses fil JBUS brutes : l'adresse logique est envoyée telle quelle, sans décalage d'adressage. Les deux centrales partagent le même cahier, écrit pour l'AVISS ; l'INIS-E16 s'en écarte bloc par bloc, et ces écarts sont relevés sur centrales réelles, non déduits. Elle apporte par ailleurs quatre tables de zones et deux synthèses générales qui lui sont propres — neutralisation, mode manuel seul, arrêt d'urgence, inhibition — que le profil AVISS laisse nulles, aucune lecture n'étant alors émise.
Un état système n'est pas identifié par son adresse mais par sa clé : deux centrales portent le même état à des bits différents, ou ne le portent pas. Une centrale dont le firmware ne sert que les bobines de synthèse de base reste connectée en mode dégradé au lieu d'être déclarée en panne : ses états de zone demeurent fiables, seules les synthèses d'exploitation deviennent indisponibles.
Où trouver le détail
| Besoin | Document |
|---|---|
| Adresses de chaque table, divergences firmware, codes de commande | docs/project/MODBUS.md |
| Configurer une adresse, ajouter un type de centrale | Profils Modbus |
| Ajouter un état supervisé, campagne de validation JBUS | docs/project/PLAN-ETATS-EXPLOITATION.md |
| Faiblesses de l'interface des centrales et parades — interne | docs/project/COMPENSATIONS-MODBUS.md |
Workflow et releases
main: branche de développement, CI (tests + build) à chaque push.prod: branche de production, mise à jour par Pull Request uniquement, déclenche le CD (release GitHub).
Procédure de release
- Sur
main: valider les tests, mettre à jour la version dansapp.config.jsonet leCHANGELOG.md. - Ouvrir une Pull Request
main→prod, vérifier la CI. - Au merge : le CD crée automatiquement une release GitHub en brouillon (tag
vX.Y.Z+ notes extraites du changelog). Il ne compile pas l'application. - Compiler l'installateur localement :
cd deployment && .\build-all.ps1. - Attacher
deployment/Installer/Superviseur-Setup-<version>.exeau brouillon de release, puis publier.
Le build de l'installateur (lourd) est volontairement réalisé en local, pas sur GitHub Actions.
Conventions
| Objet | Règle | Vérifiable par |
|---|---|---|
| Message de commit | Une seule ligne de sujet, Conventional Commits, en français. Types employés : fix, feat, docs, refactor, chore, test, style, ci, build, perf | git log --oneline |
| Corps de commit | Absent, sauf demande explicite. Le détail va au changelog | git log --format=%b |
| Version | AppVersion de app.config.json — source unique, estampillée sur les binaires par build-all.ps1 | Le CD y lit le tag et le nom de la release |
| Entrée de changelog | ## [X.Y.Z] - JJ-MM-AAAA, puis des sections ### Added, ### Changed, ### Fixed, ### Security, ### Removed, ### Tests, ### Performance | Le CD extrait la première entrée jusqu'au --- suivant |
Le format de date JJ-MM-AAAA et le séparateur --- en fin d'entrée sont exigés par l'expression régulière d'extraction du CD (cd.yml) : une entrée mal formée produit une release sans notes, sans erreur.
Intégration continue
Quatre workflows GitHub Actions, tous décrits dans .github/workflows/.
| Workflow | Déclencheur | Ce qu'il fait | Artefacts |
|---|---|---|---|
ci.yml | push et PR sur main | Onze jobs : frontend (build → tests → lint), backend (build → tests), desktop (build → tests), contrôle de build de la documentation, scan de vulnérabilités .NET et npm, SBOM | sbom-cyclonedx (30 j) |
docs.yml | push sur main touchant docs/**, un README.md, CHANGELOG.md ou lui-même ; ou manuel | TypeDoc → VitePress → DocFX, puis déploiement Cloudflare Pages (projet superviseur-docs, branche main) | superviseur-documentation (7 j) |
semgrep.yml | push et PR sur main, lundi 03:00 UTC, ou manuel | Semgrep OSS (p/default, p/security-audit, p/secrets) ; échoue sur les findings de sévérité ERROR | semgrep-sarif (14 j), seulement en cas d'échec |
cd.yml | push sur prod | Crée une release GitHub en brouillon : tag v<AppVersion>, notes extraites du changelog. Ne compile rien | Aucun |
Points qui surprennent :
| Comportement | Raison portée par le workflow |
|---|---|
| La CI ne compile jamais l'installateur | Build long et coûteux, réalisé en local |
| Les PR Dependabot n'exécutent qu'une partie des jobs | Le préfixe de branche (dependabot/nuget/, dependabot/npm_and_yarn/) sert de discriminant ; un bump front ne relance pas les builds Windows |
Backend-Build échoue si vue/src/config/generated/enums.ts diffère | Le job régénère le fichier et compare avec git diff --exit-code — régénérer par scripts/generate-ts-enums.ps1 et commiter |
npm audit bloque sur les dépendances de production seulement | Les dépendances de développement ne sont pas livrées ; l'audit complet reste informatif |
| Les tests backend tournent en Release | Certains types du backend sont sous #if DEBUG ; un test réflexif ne voit pas le même assembly qu'en local |
| La doc n'est pas reconstruite à chaque push de code | La référence d'API ne se rafraîchit que sur workflow_dispatch ou sur un changement de docs/** |
Les téléversements d'artefacts sont en continue-on-error | Un quota de stockage saturé ne doit pas faire échouer un job qui a abouti |
Contraintes actives du dépôt
| Contrainte | Effet si elle est ignorée | Où |
|---|---|---|
global.json épingle le SDK .NET sur 9.0.100 | Un SDK plus récent installé sans son runtime fait échouer toute commande dotnet sur « You must install or update .NET » | global.json |
appsettings.Crypto.{dev,prod}.json ne sont jamais versionnés ni affichés | Ils portent les clés revendeurs et le secret applicatif | .gitignore |
deployment/setup.iss est généré et non versionné | Toute édition à la main est écrasée au build suivant | generate-setup.ps1 |
vue/src/config/generated/enums.ts est généré | Une édition à la main est perdue et fait rougir la CI | TsEnumGenerator |
docs/site/api/ est généré par TypeDoc et ignoré par git | Vide sur un dépôt fraîchement cloné, ce qui est normal | vue/typedoc.json |
cleanOutDir de VitePress reste à false | À true, la construction du portail efface dist-docs/backend/ produit par DocFX | docs/site/.vitepress/config.ts |
| DocFX passe après VitePress | L'ordre inverse laisse la référence C# vide | generate-docs.ps1 |
Les guides VitePress incluent les sources par <!--@include: --> | Éditer la page du portail au lieu de la source est sans effet durable | docs/site/guide/ |
| Les scripts PowerShell sont écrits en UTF-8 avec BOM | Sans BOM, PowerShell 5.1 les lit en ANSI, corrompt les accents et casse le parsing | scripts/, deployment/ |
Le frontend est publié dans le wwwroot du backend et dans celui du client | Hors configuration Debug, la publication du backend déclenche npm run build et recopie vue/dist/ ; build-all.ps1 en dépose une seconde copie dans staging/client/wwwroot/. Un poste installé sans cette seconde copie affiche une fenêtre vide | backend.csproj, build-all.ps1 |
Aucune URL /api ou /uploads relative dans le front | Elles échouent sur poste installé, où le front est servi par un hôte virtuel distinct du backend | vue/src/api/apiBase.ts, verrouillé par relative-api-calls.spec.ts |
| Une entité ajoutée à la base doit être sauvegardée ou écartée avec son motif | Sinon l'export réussit et la perte n'apparaît qu'à la restauration | BackupCoverageTests |