Skip to content

Architecture & guide de prise en main — Superviseur ​

Superviseur est le logiciel de supervision d'alarmes incendie Desautel. Il interroge des centrales incendie via Modbus (TCP ou RTU), centralise leur état et l'expose en temps réel dans une interface web. Ce document décrit la structure du dépôt, le rôle des composants et les procédures techniques courantes.

Sommaire ​

  1. Vue d'ensemble & solution
  2. Démarrage rapide (dev)
  3. Flux de données de bout en bout
  4. Backend
  5. Frontend
  6. Configuration & fichiers clés
  7. How-to : ajouter un type de centrale + profil Modbus
  8. Autres procédures courantes
  9. Tests, qualité, branches

1. Vue d'ensemble & solution ​

La solution regroupe plusieurs projets :

ProjetTechnoRôle
dotnet/backend/C# .NET 9, ASP.NET Core, EF Core, PostgreSQL, SignalR, JWTService de supervision : polling Modbus, API REST, temps réel, rapports. Sert aussi le frontend buildé.
vue/Vue 3, TypeScript, Vite, Tailwind + DaisyUI, PiniaSPA web (l'interface opérateur). Se build dans dotnet/backend/wwwroot/.
dotnet/client/WPF .NET 9, WebView2Coquille bureau qui affiche le frontend (pointe :5000 en prod, :5173 en dev).
dotnet/manager/WinForms .NET 9Icône barre des tâches (gestion du service).
dotnet/simulator.core/C# .NET 9 (bibliothèque)Cœur du simulateur : serveurs Modbus TCP/RTU, centrales, profils, scénarios. Sans dépendance graphique, donc testable et réutilisable.
dotnet/simulator/WPF .NET 9Interface du simulateur. Consomme simulator.core et lui fournit son dispatcher.
dotnet/simulator.headless/C# .NET 9 (console)Simulateur sans interface : sert les centrales de demo-fixtures.json et expose un canal de pilotage HTTP local. Démarre automatiquement sous les tests de bout en bout.
dotnet/shared/C# .NET 9Modèles partagés entre projets .NET.
dotnet/license-generator/C# .NET 9 (WPF)Outil interne Desautel : émission et signature RSA des licences, registre des émissions. Hors installateur.
dotnet/tools/C# .NET 9 (console)TsEnumGenerator — génère les enums TypeScript depuis les sources C#. Hors solution : consommé par dotnet/tests/tools.
dotnet/tests/xUnit (backend, client, manager, simulator, tools) + Vitest (co-localisé dans vue/src/) + Playwright (vue/e2e/)Tests automatisés.

Le backend est le cœur : il tourne comme service Windows, interroge les centrales en continu, et pousse les changements au frontend via SignalR.


2. Démarrage rapide (dev) ​

powershell
# Backend (API) — écoute :5264 en dev (:5000 en prod)
dotnet run --project dotnet/backend

# Frontend (Vite HMR sur :5173, proxy /api et /modbusHub vers :5264)
cd vue && npm run dev

# Simulateur de centrale (pour avoir des données sans matériel réel)
dotnet run --project dotnet/simulator

# Tests
dotnet test                       # tous les projets .NET
cd vue && npm run test:run   # Vitest

Le dev a besoin d'un PostgreSQL local et du fichier dotnet/backend/appsettings.Crypto.dev.json (non versionné, à copier depuis appsettings.Crypto.dev.exemple.json) contenant le secret applicatif — voir §6.


3. Flux de données de bout en bout ​

Centrale incendie (INIS-E16 / AVISS …)
      │  Modbus TCP/RTU (registres/coils)
      ▼
SupervisionWorker (boucle de polling, 1 tâche/centrale)
      │  via PanelAdapterFactory → IFirePanelAdapter (par type) → IModbusClient
      ▼
PanelStateService / PanelStateManager  (état en mémoire, verrou par centrale)
      │  écrit aussi SystemLog (événements) + métriques Modbus
      ▼
IPanelStateNotifier → SignalRPanelStateNotifier (Hub /modbusHub)
      │  event "ReceivePanelUpdate" (PanelSnapshot)
      ▼
Frontend : signalr.service.ts → panel.store.ts (liveStates[serial])
      │  systemStatus (computed, par priorité) → chrome (trait, compteur, menus), vues, sons
      ▼
Opérateur

Côté requêtes ponctuelles (config, commandes, rapports), le frontend appelle l'API REST (apiService.ts → contrôleurs Presentation/Controllers/).


4. Backend ​

4.1 Démarrage ​

  • dotnet/backend/Program.cs — point d'entrée. Charge AppConfig, crée le dossier de données applicatif, construit le logger Serilog, configure le host en service Windows, charge la configuration crypto de l'environnement via CryptoConfigProtector, puis délègue l'assemblage à StartupExtensions. Gère le crash de démarrage proprement (et repropage HostAbortedException pour les tests d'intégration).
  • dotnet/backend/StartupExtensions.cs — assemblage du host, extrait de Program.cs pour réduire la complexité :
    1. BuildLogger (Serilog : console + fichiers journaliers logs/log-*.txt, rétention 90 j).
    2. ConfigureKestrel — HTTPS sur le port 5000 (prod), HTTP sur 5264 (dev). Certificat approvisionné (Https:CertificatePath) ou auto-signé de repli.
    3. BuildConnectionString — injecte le mot de passe PostgreSQL dérivé du secret unique (SecurityDefaults.Resolve).
    4. Clé JWT via JwtKeyProvider (persistée dans {AppData}/jwt.key, chiffrée au repos via DPAPI en production Windows ; un redémarrage du backend n'invalide pas les jetons d'un client encore ouvert).
    5. AddApplicationServices — enregistre EF Core, contrôleurs, SignalR, auth JWT, CORS, rate limiting, et tous les services métier (DI).
    6. Migrations EF + DbInitializer.Initialize (seed).
    7. Pipeline middleware.

4.2 Découpage en couches ​

Le backend suit Domain → Application → Infrastructure → Presentation (+ Configuration).

Domain (dotnet/backend/Domain/) ​

Modèle métier pur, sans dépendance technique.

  • Models/FirePanel.cs — la centrale (état runtime + config Modbus). Champs clés : Type (PanelType), ProfileId (string), Profile (ModbusProfile), ConnectionType, IP/Port ou série, SlaveId, Zones.
  • Models/SystemFlagState.cs — état lu d'un état système d'une centrale, indexé par clé.
  • Entities/ — entités EF : User, SystemLog, Zone, GlobalConfiguration, GeneratedReport, ReportTemplate, ModbusMetricSnapshot, InisNetwork, InisNetworkMember, PanelZoneConfig, PanelPointConfig.
  • ValueObjects/ — immuables : IpAddress, Port, ModbusConnectionSettings, ModbusProfile (mappage des registres Modbus — central pour le how-to §7), IncidentDefinition et SystemFlagDefinition (description des incidents zonés et des états système du profil).
  • Enums/ — PanelType (InisE16, Aviss), ConnectionType (Tcp, Rtu), ZoneStatus, EventType, LogSeverity, AckStatus, PanelConnectionState. Les clés d'états système font exception : ce sont des constantes de chaîne dans dotnet/shared/Profiles/SystemFlagKeys.cs, une même clé étant servie à des bits différents selon la centrale.
  • Interfaces/Repositories/ — contrats de repository (IFirePanelRepository, ISystemLogRepository, …).

Application (dotnet/backend/Application/) ​

Orchestration métier, DTO, interfaces de service.

  • Services/PanelStateService.cs — orchestrateur principal des états de centrales (lecture, mise à jour, commandes, acquittements). S'appuie sur des services spécialisés ZoneManagementService, PointManagementService, SystemManagementService.
  • Services/PanelStateManager.cs — cache mémoire des FirePanel avec verrou par centrale (ExecuteWithPanelLockAsync).
  • Services/AuthService.cs — login/refresh/revoke JWT (BCrypt).
  • Services/StatisticsService.cs (+ StatisticsCalculator, StatisticsCacheService) — agrégats de supervision.
  • Services/ReportService.cs + Reports/ — génération de rapports modulaires (voir §4.7).
  • Services/Backup*Service.cs — export/import de configuration.
  • Interfaces/ — IPanelStateService, IPanelStateNotifier (push SignalR), IPanelStateManager, IStatisticsService, etc.
  • DTOs/ — objets de transfert API.

Infrastructure (dotnet/backend/Infrastructure/) ​

Détails techniques : données, Modbus, jobs, télémétrie.

  • Persistence/FireSupervisorDbContext.cs — DbContext EF (Users, FirePanels, Zones, SystemLogs, ModbusProfiles, ModbusMetricSnapshots, GeneratedReports, ReportTemplates, InisNetworks…).
  • Persistence/DbInitializer.cs — seed au boot (SuperAdmin desautel, config globale, profils Modbus, réseau INIS par défaut) + réalignement (RepairPanelProfiles).
  • Migrations/ (racine du projet backend) — migrations EF (PostgreSQL).
  • Repositories/ — implémentations (Repository<> générique + spécialisés).
  • BackgroundJobs/ — SupervisionWorker, ReportSchedulerService, ModbusMetricsFlushService (voir §4.4).
  • Communication/ — transport Modbus : IModbusClient, ModbusTcpClient, ModbusRtuClient, ModbusConnectionManager (cache de clients + partage RTU multi-drop), adaptateurs série / pipe nommé (simulateur).
  • Adapters/ — stratégies de lecture par type de centrale : IFirePanelAdapter, ModbusFirePanelAdapterBase, InisAdapter, AvissAdapter, GenericModbusAdapter, GlobalCoilReader (lecture de la table de paramètres généraux tolérant l'absence des bobines d'extension), Strategies/ (voir §4.5).
  • Factory/PanelAdapterFactory.cs — fabrique l'adaptateur selon le PanelType + applique les contraintes de timing par type.
  • Telemetry/ — collecte des métriques Modbus (IModbusMetricsCollector).
  • Crypto/InisConfigCrypto.cs — déchiffrement des configs INIS (format propriétaire).

Presentation (dotnet/backend/Presentation/) ​

  • Controllers/ — ~18 contrôleurs [Authorize] : AuthController, PanelsController (CRUD centrales, commandes, diagnostic), ReportsController, StatisticsController, SystemLogsController, UsersController, GlobalParametersController, InisNetworkController, ReportTemplatesController, etc. Règle : pas de logique métier dans les contrôleurs → déléguer aux services.
  • Hubs/ModbusHub.cs — Hub SignalR (/modbusHub), groupe SuperAdminConsole.
  • Services/SignalRPanelStateNotifier.cs — implémente IPanelStateNotifier, pousse les events au frontend.

Configuration (dotnet/backend/Configuration/) ​

  • AppConfig.cs — config applicative singleton (identité app/service, ports, chemins ; chargée depuis app.config.json). SupervisionOptions.cs — paramètres de supervision typés, liés à appsettings.json et validés au démarrage.
  • SecurityDefaults.cs — résout le secret applicatif unique (Security:SuperAdminPassword) ; exception fatale si absent (aucun défaut codé en dur).
  • JwtKeyProvider.cs — résolution/persistance de la clé de signature JWT.

4.3 Persistance & migrations ​

  • DbContext : Infrastructure/Persistence/FireSupervisorDbContext.cs. Les profils Modbus sont seedés via HasData(ModbusProfile.CreateInisE16(), ModbusProfile.CreateAviss()). Les incidents, commandes et définitions d'états système du profil y sont sérialisés en colonnes JSON : toute modification de leur description en code demande une migration de reseed, faute de quoi le modèle EF diverge du snapshot. Les bases en service sont, elles, réalignées au démarrage par DbInitializer.ApplyProfileMigration.
  • Créer une migration :
    powershell
    dotnet ef migrations add <Nom> --project dotnet/backend/backend.csproj
    dotnet ef database update --project dotnet/backend/backend.csproj
  • DbInitializer rejoue au démarrage : crée le SuperAdmin (desautel, hashé BCrypt à partir du secret), la config globale, sème et valide les profils standard depuis PanelProfileRegistry, et le réseau INIS par défaut.

Service en cours = bin/ verrouillé. Quand le service Windows tourne, il verrouille les binaires : arrêter Superviseur-Service.exe avant dotnet test/migration, ou compiler vers un dossier de sortie alternatif.

4.4 Jobs de fond (Infrastructure/BackgroundJobs/) ​

  • SupervisionWorker — le cœur. Au démarrage, charge les centrales, et lance une tâche async par centrale :
    • crée l'adaptateur via PanelAdapterFactory.CreateAdapterAsync ;
    • boucle de polling (intervalle SupervisionSettings:PollingIntervalSeconds, défaut 2 s) → adapter.ReadStatusAsync() ;
    • met à jour l'état (PanelStateManager) et notifie (IPanelStateNotifier) ;
    • collecte la télémétrie Modbus ; gère reconnexion auto.
    • expose PausePanelSupervisionAsync / ResumePanelSupervisionAsync (bascule de supervision par centrale).
  • ReportSchedulerService — toutes les heures, vérifie la planification (GlobalConfiguration.SystemParamsJson → ReportSchedule) et génère + archive un rapport si une fenêtre est due.
  • ModbusMetricsFlushService — toutes les ~5 min (MetricsFlushIntervalMinutes), vide les compteurs Modbus en base (ModbusMetricSnapshot, 1 ligne/centrale/fenêtre) et purge au-delà de la rétention (MetricsRetentionDays, défaut 90 j).

4.5 Supervision Modbus (le chemin centrale → état) ​

FirePanel.Type (PanelType) + FirePanel.Profile (ModbusProfile)
        │
PanelAdapterFactory.CreateAdapterAsync()
   • sélectionne une PanelTypePolicy dans le dictionnaire _policies (clé = PanelType)
   • résout le profil, applique les minima de timing du type
   • crée le IModbusClient (TCP/RTU) via ModbusConnectionManager
   • policy.BuildAdapter(ctx) → adaptateur concret
        │
IFirePanelAdapter (InisAdapter / AvissAdapter / GenericModbusAdapter)
   • ReadStatusAsync() : lit l'état global + points + zones selon les adresses du ModbusProfile,
     applique AddressOffset, décode les bits → ZoneStatus selon le PriorityOrder des définitions
     d'incidents du profil (émission, arrêt d'urgence et évacuation en tête, états d'exploitation
     sous le dérangement ; à rang égal, le premier drapeau apparu garde la main)
   • renvoie un PanelSnapshot
        │
IModbusClient.ReadCoilsAsync / ReadRegisters / Write…

Points importants :

  • ModbusProfile (Domain/ValueObjects/ModbusProfile.cs) porte toutes les adresses de registres (par point : PointFireAddress, PointFaultAddress, PointDisableAddress ; par zone : ZoneFireAddress, ZoneFaultAddress, Evacuation/Emission/DM/ZoneTest/ZoneDisable, et sur INIS-E16 Neutralization/ManualOnly/EmergencyStop/Inhibition ; globaux : GlobalFireAddress, GlobalFaultAddress), le ZoneCount, l'AddressOffset (décalage appliqué à toutes les adresses), les codes de commande (CmdReset, CmdSilence, …) et la collection SystemFlags — les définitions d'états système, dont le décalage de bit provient de la carte GlobalBits de la centrale.
  • PanelAdapterFactory._policies (dictionnaire PanelType → PanelTypePolicy) encapsule, pour chaque type : comment résoudre le profil, les minima de timing I/O, et comment construire l'adaptateur.
  • ConnectionType gère TCP (IP:port) et RTU (port série, baud…). Le simulateur expose une centrale via TCP / pipe nommé.

4.6 Temps réel (SignalR) ​

  • Hub : Presentation/Hubs/ModbusHub.cs (/modbusHub), avec le groupe SuperAdminConsole.
  • Contrat serveur → client — la liste ci-dessous est exhaustive ; tout événement écouté par le frontend et absent d'ici n'est jamais émis :
ÉvénementÉmis parCharge utile
ReceivePanelUpdateSignalRPanelStateNotifierLe PanelDto complet, à chaque changement d'état
ReceiveReconnectionSignalRPanelStateNotifierIdentifiant et nom de la centrale reconnectée
SystemErrorSignalRPanelStateNotifierSource, message, horodatage
ExportProgressService d'exportIdentifiant de tâche et pourcentage
ResourceChangedSignalRResourceChangeNotifierLe seul nom d'une famille de ResourceKind
ReceiveModbusLogHubModbusLoggerTrame Modbus — groupe SuperAdminConsole uniquement
  • Ajouter un événement : l'émettre depuis un service de Presentation/Services/, l'ajouter à ce tableau, puis l'écouter dans vue/src/api/signalr.service.ts. Les deux côtés n'ayant aucun contrat partagé compilé, seule cette table les tient alignés.

Données partagées : ResourceChanged ​

Une donnée modifiée sur un poste s'affiche sur les autres sans rechargement de page. Le mécanisme tient en trois pièces :

  1. [NotifiesResource(ResourceKind.Maps)] sur le contrôleur — ou sur une action, qui l'emporte alors.
  2. ResourceChangeFilter émet après toute réponse réussie à un POST/PUT/PATCH/DELETE portant l'attribut : une seule notification par requête, quel que soit le nombre d'entités touchées.
  3. Le poste incrémente un compteur dans resource-sync.store.ts ; useResourceSync(famille, recharger) observe ce compteur et recharge en silence.

L'événement ne transporte jamais de donnée. Chaque poste recharge par l'API, donc avec ses propres droits — le filtrage par rôle reste celui de l'API, en un seul endroit.

Familles (Domain/Enums/ResourceKind.cs) : Panels, Config, IncidentProfiles, Maps, InisNetworks, Notes, Users, ReportTemplates. Une famille correspond à ce qu'un poste recharge d'un seul appel, non à une table — sons et paramètres généraux relèvent tous deux de Config.

ResourceChangeCoverageTests échoue si une action écrivant une donnée partagée n'est ni annotée ni inscrite dans la liste motivée des contrôleurs exclus (session, sauvegarde, rapports, commandes Modbus, outillage, journal, jeu de démonstration).

4.7 Rapports modulaires (Application/Reports/) ​

Architecture par sections cochables : chaque section implémente IReportSectionComposer (méthodes LoadAsync, ComposePdf, AppendCsv, propriété Order). ReportService collecte les composeurs injectés, filtre selon les sections demandées (ReportRequestDto.Sections), et génère PDF (PDFsharp/MigraDoc) et CSV. Composeurs actuels : CentralesSectionComposer, EvenementsSectionComposer, StatsModbusSectionComposer, UtilisateursSectionComposer (dans Reports/Sections/). Ajouter une section = §8.

4.8 Sécurité ​

  • Secret applicatif unique Security:SuperAdminPassword (appsettings.Crypto.dev.json en développement, appsettings.Crypto.prod.json en production ; non versionnés). SecurityDefaults.Resolve le résout ; le service refuse de démarrer s'il est absent (aucune valeur par défaut). En production Windows, le fichier est scellé au premier démarrage en appsettings.Crypto.prod.protected (DPAPI, portée machine) puis supprimé. Il sert à la fois au compte SuperAdmin desautel (BCrypt) et au mot de passe du rôle PostgreSQL applicatif.
  • JWT : AuthService émet l'access token (claim Role) + un refresh token, tous deux en cookies de session (HttpOnly, Secure, sans expiration persistante). La clé de signature est persistée (JwtKeyProvider) : un redémarrage du backend n'invalide pas les jetons d'un client encore ouvert.
  • Session liée au processus : les cookies de session vivent en mémoire du client tant que l'application tourne — la veille du poste ne déconnecte pas — et sont effacés à l'arrêt du processus (fermeture de la fenêtre, redémarrage du poste, désinstallation/mise à jour), imposant alors une nouvelle authentification. Côté frontend, l'utilisateur courant est conservé en sessionStorage (même cycle de vie) et l'intercepteur 401 (vue/src/api/httpClient.ts) rafraîchit le token de façon transparente ; le retour à l'écran de connexion n'intervient qu'après plusieurs refus d'authentification consécutifs du serveur, jamais sur une simple coupure réseau (poste 24/7). Auth:RefreshTokenDays (défaut 30 j) borne la validité serveur du refresh token, distincte de la durée de session côté poste.
  • Rôles : SuperAdmin > Admin > User.

4.9 Accès réseau local ​

L'API (et l'interface pour un accès navigateur depuis le LAN) est servie par le backend en HTTPS sur le port 5000 (Kestrel + UseHttps, écoute sur toutes les interfaces), avec redirection HTTP→HTTPS, HSTS et en-tête CSP. Le client de bureau, lui, charge l'interface depuis le disque (hôte virtuel WebView2 superviseur.local) et n'appelle le backend que pour l'API et le temps réel. Accès depuis tout appareil du LAN via https://<ip-du-poste>:5000. Le filtrage d'hôtes reste ouvert (AllowedHosts: "*") car l'accès par IP arbitraire du poste est une fonctionnalité ; la surface est bornée par la règle de pare-feu (port 5000, profil réseau privé) et la politique CORS AddLanCors (localhost + plages RFC 1918). L'endpoint GET /api/config/network-info (dotnet/backend/Presentation/Controllers/ConfigController.cs) retourne les IPv4 de la machine + le port ; le frontend les affiche dans Paramètres → Profil (carte « Accès réseau local », visible par un utilisateur simple). Déploiement en réseau local fermé, sans Internet. Le certificat auto-signé (CN=localhost) est installé dans le magasin racine du poste à l'installation (pour un accès navigateur local) ; le client de bureau lui accorde en outre sa confiance directement pour la boucle locale dans WebView2, sans dépendre du magasin racine.


5. Frontend ​

SPA Vue 3 organisée par domaine (features/), avec couches transverses (components/, composables/, services/, stores/, api/, layouts/, router/, i18n/, config/).

5.1 Structure vue/src/ ​

DossierRôle
features/<domaine>/Un domaine = sa vue racine + composants + store/types co-localisés. Domaines : dashboard (accueil/KPI), status (état détaillé zones/points), history (journal), statistics (KPI/graphes, consultation ouverte à tous), reports (génération, consultation ouverte à tous ; suppression d'archive et export réservés Admin+), settings (params, sections filtrées par rôle), inis-network (matrice INIS, édition Admin, lecture User), map (plans), panel (store central, pas de vue), tools (scanner Modbus, SuperAdmin), auth (login).
components/ui/Primitives réutilisables : BaseIcon, KpiCard, StatusBadge, SearchableSelect, SystemKpiGrid, NotificationStack, ConfirmDialog, EmptyState…
composables/Logique réutilisable : useAutoZoom (mise à l'échelle dynamique), useAudioAlerts, useIncidentStyling, useStatusLabel (point de passage de tout libellé de statut : acquittement, puis profil d'incident, puis repli i18n), useNotify.
stores/Stores Pinia transverses : global-config, sidebar, connection, modbus-console. (Les stores panel et auth sont co-localisés dans leurs features.)
api/apiService.ts (toutes les requêtes REST + DTO), httpClient.ts (Axios + intercepteur 401 + file d'attente de refresh), signalr.service.ts (client temps réel), map.service.ts, modbus.service.ts.
services/Services applicatifs non-HTTP : notification.service.ts (toasts, dédup), confirm.service.ts, et les deux services audio — sound.service.ts diffuse les sons d'alarme (fichiers de public/sounds/, remplaçables par site), ui-sound.service.ts synthétise les retours d'interface en WebAudio (aucun fichier audio).
layouts/MainLayout.vue (shell) + TheSidebar, TheHeader, TheFooter, BottomNav.
router/index.tsRoutes + guards (requiresAuth, requiresAdmin, requiresSuperAdmin).
i18n/locales/{fr,en}.jsonTraductions (vue-i18n).
config/constants.tsÉnums & mappings : ZONE_STATUSES, SYSTEM_STATUSES, PANEL_TYPES, API_ENDPOINTS, utilitaires couleurs.
config/ui-sounds.tsRecettes des retours sonores d'interface : hauteur, forme d'onde, durée et gain de chaque ton, jouées par ui-sound.service.ts.
assets/main.cssTailwind v4 (@theme), @font-face IBM Plex Sans (embarquée, hors-ligne), tailles/poids de police, tokens de marque (--color-brand) et palette de l'écran d'authentification (--color-auth-*), point de rupture split (900 px).

5.2 App shell ​

  • main.ts — crée l'app, enregistre Pinia/router/i18n, initialise l'auth, monte sur #app.

  • App.vue — monte NotificationStack/ConfirmDialog/ModbusConsole, initialise SignalR + useAutoZoom.

  • Signalisation d'incident dans le chrome — pilotée par config/app-status-line.ts, qui donne les couleurs de l'échelle de sévérité : rouge pour émission, alarme et évacuation, ambre pour dérangement, arrêt d'urgence et neutralisation, violet pour un déclenchement manuel, ciel pour inhibition et mode manuel seul, ardoise pour une liaison perdue. Quatre porteurs, tous permanents et sans clignotement :

    • layouts/components/AppStatusLine.vue — trait de 3 px sous l'en-tête, traversant la barre latérale et le contenu, posé dans MainLayout. Il ne coûte aucune hauteur utile et se distingue des traits de séparation du chrome, qui s'arrêtent à leur panneau.
    • components/ui/IncidentJumpButton.vue — compteur d'incidents de l'en-tête, visible dès qu'un incident est actif ; un clic ouvre le tableau de bord, où le flux en direct les détaille.
    • TheSidebar / BottomNav — l'élément de menu sélectionné prend la couleur de l'état.
    • TheHeader — le nom du site prend la couleur de l'état.

    Aucun de ces porteurs ne considère l'acquittement : acquitter dit « j'ai vu », pas « c'est fini ». Ils ne retombent qu'avec l'état.

  • MainLayout.vue — sidebar repliable + header sticky + <router-view> (transitions GSAP) + footer.

  • features/auth/LoginView.vue — écran de connexion hors shell : split-panel (panneau de marque + parcours en deux étapes), choix du profil (liste triée par dernière connexion locale, filtrable, repli « Autre compte » vers saisie manuelle) puis saisie du mot de passe, sélecteur de langue FR/EN. Colonne unique sous le point de rupture split (900 px). Exclu de useAutoZoom (échelle propre, en pixels).

  • components/ui/SplashScreen.vue — voile plein écran aux couleurs de la marque, affiché pendant l'authentification (splash.store).

5.3 État & temps réel ​

  • features/panel/services/alert-processor.service.ts — consolide l'état des centrales en incidents : une entrée par nature d'événement et par centrale, réunissant la synthèse générale (states) et les cibles touchées (targets : zones et leurs détecteurs). Les trois échelons sont signalés ensemble : l'opérateur acquitte ce qu'il voit.
  • features/panel/panel.store.ts — le cœur : configurations (config statique des centrales), profiles (profils Modbus), liveStates[serial] (état temps réel par numéro de série), apiConnected/modbusConnected. Les états système des snapshots y sont ingérés par clé, sans liste codée en dur. Computeds clés : systemStatus (texte + couleur, par priorité : API down > centrale déconnectée > évacuation > émission > DM > alarme feu > dérangement > exploitation dégradée > normal), activeAlerts, systemIssues.
    • La cascade d'exploitation dégradée (DEGRADED_OPERATION_STATUSES) passe sous le dérangement, du plus grave au moins grave : arrêt d'urgence, neutralisation, inhibition, mode manuel seul. Chaque état se lit sur le statut de zone, sur les drapeaux de zone (deux états peuvent se cumuler sur une même zone) et sur la synthèse générale, qui peut être active sans zone porteuse visible.
  • api/signalr.service.ts — connexion au hub /modbusHub, reconnexion auto, listeners : ReceivePanelUpdate → panelStore.updatePanelState(snapshot), ResourceChanged → resourceSyncStore.markChanged(famille), SystemError, ExportProgress.

5.4 Build ​

  • npm run dev (Vite :5173, proxy /api et /modbusHub → :5264), npm run build (sort dans dotnet/backend/wwwroot/ pour être embarqué dans le service), npm run type-check, npm run lint, npm run test:run.
  • vite.config.ts — alias @ → src, __APP_VERSION__ injecté, proxies dev, chunks (vendor, signalr).

6. Configuration & fichiers clés ​

FichierRôle
app.config.json (racine)Identité de l'app/service, ports, chemins, config installateur.
dotnet/backend/appsettings.jsonSupervisionSettings (polling, timeouts, métriques), Jwt, Auth (RefreshTokenDays), Https (CertificatePath/Password), ConnectionStrings.
dotnet/backend/appsettings.Crypto.dev.jsonNon versionné. Secret Security:SuperAdminPassword + clés revendeur, en clair, pour le développement et hors Windows. Gabarit : appsettings.Crypto.dev.exemple.json.
dotnet/backend/appsettings.Crypto.prod.jsonNon versionné. Même contenu pour la production : scellé au premier démarrage en appsettings.Crypto.prod.protected (DPAPI, portée machine) puis supprimé. Gabarit : appsettings.Crypto.prod.exemple.json.
dotnet/backend/appsettings.Crypto.jsonAncien nom unique, encore lu en repli. Sous l'environnement Testing, c'est le seul fichier chargé : le secret factice versionné dans dotnet/tests/backend/ ne doit pas être écrasé par un fichier de développement présent dans la sortie de build.
CHANGELOG.mdSource de vérité du CD (format ## [X.Y.Z] - DD-MM-YYYY).
deployment/build-all.ps1Build complet + installateur Inno Setup.
deployment/setup.issAuto-généré, non versionné (porte le secret PostgreSQL).

Ne jamais afficher ni modifier les fichiers appsettings.Crypto.*.json dans le code.


7. How-to : ajouter un type de centrale / profil Modbus ​

Le support d'une centrale repose sur deux éléments découplés :

  • un ModbusProfile (Domain/ValueObjects/ModbusProfile.cs) — le mappage des registres : adresses des états par point et par zone, états globaux, décalage d'adresse, nombre de zones, codes de commande ;
  • un adaptateur (Infrastructure/Adapters/) — la logique de lecture/décodage qui transforme ces registres en PanelSnapshot.

Selon le protocole de la centrale, deux cas se présentent :

  • Cas 1 — Modbus proche d'un type déjà supporté : un nouveau profil suffit. L'adaptateur générique (GenericModbusAdapter) lit en s'appuyant uniquement sur les adresses du profil ; aucun code de lecture spécifique n'est requis.
  • Cas 2 — logique de lecture spécifique (longueurs/plages de lecture différentes, regroupement des requêtes, décodage particulier des bits, commandes propres) : un adaptateur dédié est nécessaire, en plus du profil.

Points d'extension ​

Chaque objet s'ajoute depuis un seul endroit ; le typage et la validation au démarrage font le reste.

Ajouter…Où
Type de centrale (enum)Domain/Enums/PanelType.cs
Adresses & codes de commande d'un profil standarddotnet/shared/Profiles/StandardPanelProfiles.cs (source unique partagée backend + simulateur)
Profil standardDomain/Profiles/PanelProfileRegistry.cs (fabriques ModbusProfile.Create*), validé au démarrage
Comportement / timing d'un typeInfrastructure/Factory/PanelAdapterFactory.cs (_policies) + un adaptateur héritant de ModbusFirePanelAdapterBase si le décodage diffère
Transport / protocoleimplémentation d'IModbusClient (Infrastructure/Communication/)
Incident / commande / son d'alarmel'enum de clé (Domain/Enums/) + l'entrée dans les BuildStandard* + une migration de reseed (voir ci-dessous)
Retour sonore d'interfaceune entrée d'UiSoundType et sa recette dans vue/src/config/ui-sounds.ts — purement frontend, sans base ni migration
Réglage global de présentationune clé de SystemParamsJson (GlobalConfiguration), lue par global-config.store — persistée en base, donc valable pour tous les postes
État systèmela clé dans dotnet/shared/Profiles/SystemFlagKeys.cs, le bit dans la carte GlobalBits des centrales qui le servent, la description dans ModbusProfile.BuildStandardSystemFlags, puis une migration de reseed (voir États système et états d'exploitation)
RôleDomain/Enums/UserRole.cs
Famille de données partagées à tenir à jour sur tous les postesla valeur dans Domain/Enums/ResourceKind.cs, l'attribut [NotifiesResource] sur le contrôleur, le type dans vue/src/stores/resource-sync.store.ts, puis useResourceSync(famille, recharger) chez le consommateur (voir §4.6)
Exposer un enum au frontendliste exported de dotnet/tools/TsEnumGenerator, puis scripts/generate-ts-enums.ps1
Simuler un type de centrale (optionnel)dotnet/simulator.core/ (ProfileLoader dérive de StandardPanelProfiles)

Toute modification d'un BuildStandard* demande une migration de reseed. Les profils standard sont semés par HasData(CreateInisE16(), CreateAviss()) : incidents, commandes et états système y sont sérialisés en colonnes JSON. Sans migration, le modèle EF diverge du snapshot. Les bases déjà en service sont, elles, réalignées au démarrage par DbInitializer.ApplyProfileMigration.

Détail et exemples : « Profils Modbus & objets métier ». Le backend (ModbusProfile.Create*) et le cœur du simulateur (ProfileLoader, dans dotnet/simulator.core/) dérivent leurs adresses de la même cartographie StandardPanelProfiles : une seule édition met à jour les deux côtés, et les trois consommateurs du simulateur (interface graphique, hôte sans interface, tests) héritent du même mappage.

Squelettes ​

Fabrique de profil (dans ModbusProfile.cs, sur le modèle des fabriques existantes — les adresses, l'offset et le nombre de zones proviennent de la documentation Modbus de la centrale) :

csharp
public static ModbusProfile CreateXxx() => new()
{
    Id = "xxx",
    Name = "...",
    TargetSystem = PanelType.Xxx,
    IsReadOnly = true,
    AddressOffset = …,                         // décalage appliqué à toutes les adresses
    PointFireAddress = …, PointFaultAddress = …, PointDisableAddress = …,
    ZoneCount = …,
    ZoneFireAddress = …, ZoneFaultAddress = …, // … autres états par zone
    GlobalFireAddress = …, GlobalFaultAddress = …,
    // codes de commande (CmdReset, CmdSilence, …) si la centrale accepte des écritures
};

Politique de type (dans PanelAdapterFactory._policies) :

csharp
[PanelType.Xxx] = new(
    ResolveProfile: panel => panel.Profile ?? ModbusProfile.CreateXxx(),
    MinIoTimeoutMs: …,                          // minima de timing imposés par la centrale
    MinInterRequestDelayMs: …,
    BuildAdapter: c => new XxxAdapter(/* client, profil, nb zones/points, délais … */)),

Pour le cas 1, BuildAdapter construit un GenericModbusAdapter. Pour le cas 2, une classe d'adaptateur dédiée porte la logique de lecture particulière (implémentation de IFirePanelAdapter via ModbusFirePanelAdapterBase).


8. Autres procédures courantes ​

Ajouter une section de rapport : créer une classe XxxSectionComposer : IReportSectionComposer dans dotnet/backend/Application/Reports/Sections/, l'enregistrer en DI dans StartupExtensions.AddApplicationServices (AddScoped<IReportSectionComposer, XxxSectionComposer>()), et ajouter la clé correspondante dans l'enum ReportSectionKey (Application/DTOs/ReportRequestDto.cs) + l'UI (vue/src/features/reports/).

Ajouter une page frontend : créer features/<domaine>/XxxView.vue, déclarer la route dans router/index.ts (avec le bon guard), ajouter l'entrée de navigation dans layouts/components/TheSidebar.vue, et les clés i18n.

Ajouter un endpoint API : créer/étendre un contrôleur dans Presentation/Controllers/ (logique déléguée à un service Application/Services/), exposer la méthode côté front dans api/apiService.ts.

Migration EF : dotnet ef migrations add <Nom> --project dotnet/backend/backend.csproj puis dotnet ef database update. Arrêter le service Windows si bin/ est verrouillé.


9. Tests, qualité, branches ​

  • Tests : dotnet test (xUnit) ; cd vue && npm run test:run (Vitest) ; cd vue && npm run test:e2e (Playwright, navigateur réel). Avant commit front : npm run lint + npm run format.
  • Tests de bout en bout : joués en local uniquement (l'intégration continue n'a ni PostgreSQL ni appsettings.Crypto.dev.json). La suite démarre elle-même le simulateur sans interface, le backend et Vite. Deux centrales sont réellement servies en Modbus : vue/e2e/specs/supervision.spec.ts exerce une centrale en ligne, une alarme jusqu'à l'écran et une commande qui atteint la centrale ; incidents.spec.ts exerce le cycle de vie d'un incident groupé à l'écran, acquittement compris.
  • Rendre la flotte entre deux parcours : un état retombé sans acquittement reste exposé, et la réinitialisation globale ne couvre ni les synthèses de centrale. Les helpers settlePanel et resetSimulatedFleet (vue/e2e/support/api.ts) attendent le délai de confirmation du backend puis effacent les états rétablis, avant comme après chaque série.
  • Qualité : SonarQube via scripts/sonar-scan.ps1 (un projet superviseur couvrant back + front). Voir CLAUDE.md.
  • Branches : main = dev (CI : tests + build à chaque push) ; prod = production (PR uniquement, déclenche CD + GitHub Release). Avant PR vers prod : monter la version dans app.config.json + CHANGELOG.md.
  • Commits : signés par l'humain uniquement, sans mention d'IA. Commentaires de code en état absolu (décrire ce qui est, pas justifier un changement).

Pour les détails de conventions (namespaces, couches, secrets, docs VitePress/DocFX), voir CLAUDE.md à la racine.

Documentation Technique