**RAPPORT DE SOUTENANCE**

**Contributions au système de supervision et au firmware embarqué de la gamme de radios sol-air Telerad 2G+**

***Frontend web de supervision (EMRE-2GPLUS) et serveur web sécurisé embarqué***

***Maxime Point***

***Élève ingénieur — Telerad Expertise***

***Soutenance de projet***

***3 juillet 2026***

# Sommaire

- [1. Introduction](#1-introduction)
  - [1.1. Contexte de la mission](#11-contexte-de-la-mission)
  - [1.2. Objectifs et périmètre](#12-objectifs-et-périmètre)
  - [1.3. Organisation du rapport](#13-organisation-du-rapport)
- [2. Contexte technique et enjeux](#2-contexte-technique-et-enjeux)
  - [2.1. Le produit : une radio de contrôle aérien](#21-le-produit-une-radio-de-contrôle-aérien)
  - [2.2. Une chaîne logicielle à deux étages](#22-une-chaîne-logicielle-à-deux-étages)
  - [2.3. Enjeux transverses](#23-enjeux-transverses)
- [3. Architecture globale du système](#3-architecture-globale-du-système)
  - [3.1. Le serveur web comme pivot d'administration](#31-le-serveur-web-comme-pivot-dadministration)
  - [3.2. Deux paradigmes de programmation](#32-deux-paradigmes-de-programmation)
  - [3.3. Fil conducteur des contributions](#33-fil-conducteur-des-contributions)
- [4. Le module Monitoring & Control : une architecture objet des commandes](#4-le-module-monitoring-control-une-architecture-objet-des-commandes)
  - [4.1. Problématique de départ](#41-problématique-de-départ)
  - [4.2. Une hiérarchie de classes pilotée par une Factory](#42-une-hiérarchie-de-classes-pilotée-par-une-factory)
  - [4.3. Polymorphisme et points d'extension](#43-polymorphisme-et-points-dextension)
  - [4.4. Validation, injection et permissions](#44-validation-injection-et-permissions)
  - [4.5. Cycle de vie et annulation](#45-cycle-de-vie-et-annulation)
- [5. Une interface pilotée par les données](#5-une-interface-pilotée-par-les-données)
  - [5.1. Décrire les écrans plutôt que les coder](#51-décrire-les-écrans-plutôt-que-les-coder)
  - [5.2. TabGenerator, orchestrateur du rendu](#52-tabgenerator-orchestrateur-du-rendu)
  - [5.3. Bénéfice : séparation des données et de la présentation](#53-bénéfice-séparation-des-données-et-de-la-présentation)
- [6. Une couche API adaptative](#6-une-couche-api-adaptative)
  - [6.1. Du wrapper passif au moteur de requêtes](#61-du-wrapper-passif-au-moteur-de-requêtes)
  - [6.2. File d'attente et mise en lot](#62-file-dattente-et-mise-en-lot)
  - [6.3. La course batch-vs-individuel](#63-la-course-batch-vs-individuel)
  - [6.4. Robustesse des échanges](#64-robustesse-des-échanges)
- [7. Internationalisation et composants réutilisables](#7-internationalisation-et-composants-réutilisables)
  - [7.1. Un dictionnaire aligné sur l'arborescence fonctionnelle](#71-un-dictionnaire-aligné-sur-larborescence-fonctionnelle)
  - [7.2. Sélecteur de langue](#72-sélecteur-de-langue)
  - [7.3. Une bibliothèque de composants consolidée](#73-une-bibliothèque-de-composants-consolidée)
- [8. Démarche qualité : une couverture à deux niveaux](#8-démarche-qualité-une-couverture-à-deux-niveaux)
  - [8.1. Tests unitaires (Jest / React Testing Library)](#81-tests-unitaires-jest-react-testing-library)
  - [8.2. Tests de bout en bout (Selenium / Java)](#82-tests-de-bout-en-bout-selenium-java)
  - [8.3. Un contrat DOM comme point d'ancrage](#83-un-contrat-dom-comme-point-dancrage)
  - [8.4. Tests pilotés par les données et traçabilité](#84-tests-pilotés-par-les-données-et-traçabilité)
- [9. Le firmware et le système temps réel INTEGRITY](#9-le-firmware-et-le-système-temps-réel-integrity)
  - [9.1. Un micro-noyau à isolation des tâches](#91-un-micro-noyau-à-isolation-des-tâches)
  - [9.2. Le modèle de communication inter-tâches](#92-le-modèle-de-communication-inter-tâches)
  - [9.3. La tâche Webserver](#93-la-tâche-webserver)
- [10. Fiabilisation du serveur web HTTPS](#10-fiabilisation-du-serveur-web-https)
  - [10.1. Robustesse à l'upload d'un certificat invalide](#101-robustesse-à-lupload-dun-certificat-invalide)
  - [10.2. Réponse HTTPS correcte après upload d'une configuration](#102-réponse-https-correcte-après-upload-dune-configuration)
- [11. Extension du canal de commande Webserver](#11-extension-du-canal-de-commande-webserver)
  - [11.1. Le Webserver comme source de commande de premier rang](#111-le-webserver-comme-source-de-commande-de-premier-rang)
  - [11.2. Une Activity de réception, calquée sur l'existant](#112-une-activity-de-réception-calquée-sur-lexistant)
  - [11.3. Exposition des journaux au serveur web](#113-exposition-des-journaux-au-serveur-web)
  - [11.4. Un correctif de routage révélé en cours de route](#114-un-correctif-de-routage-révélé-en-cours-de-route)
- [12. Outillage, versioning et traçabilité](#12-outillage-versioning-et-traçabilité)
  - [12.1. Un outillage de développement non intrusif](#121-un-outillage-de-développement-non-intrusif)
  - [12.2. Versioning et traçabilité des commandes](#122-versioning-et-traçabilité-des-commandes)
  - [12.3. Le workflow de développement Telerad](#123-le-workflow-de-développement-telerad)
- [13. Bilan et perspectives](#13-bilan-et-perspectives)
  - [13.1. Synthèse des problèmes résolus (firmware)](#131-synthèse-des-problèmes-résolus-firmware)
  - [13.2. Compétences acquises](#132-compétences-acquises)
  - [13.3. Points de discussion et perspectives](#133-points-de-discussion-et-perspectives)
  - [13.4. Conclusion](#134-conclusion)

# 1. Introduction

## 1.1. Contexte de la mission

Ce rapport présente les travaux que j'ai réalisés au sein de Telerad Expertise sur la chaîne logicielle de la radio sol-air 2G+. La mission s'est articulée autour de deux composants complémentaires : d'une part le frontend web de supervision et de configuration (projet EMRE-2GPLUS), développé en React, et d'autre part le firmware embarqué de la radio, écrit en C et exécuté sur le système d'exploitation temps réel Green Hills INTEGRITY. Ces deux briques communiquent via Interface graphique sur un écran LCD et via un terminal SSH ou Telnet. De ce fait la suite était de construire un serveur web d'administration de l'équipement.

## 1.2. Objectifs et périmètre

Le périmètre confié couvrait deux grands axes. Sur le frontend, il s'agissait de concevoir et d'intégrer un module complet de supervision et de commande (Monitoring & Control) reposant sur une architecture logicielle robuste et extensible, accompagné d'une couche réseau optimisée, de l'internationalisation de l'interface et d'une démarche de test complète. Sur le firmware, l'objectif était de fiabiliser le serveur web d'administration : corriger les défauts d'intégration existants et étendre les capacités de pilotage de l'équipement depuis le navigateur.

## 1.3. Organisation du rapport

Après une présentation du contexte technique et des enjeux (chapitre 2) puis de l'architecture globale du système (chapitre 3), le rapport se divise en deux parties. La Partie I détaille les contributions apportées au frontend de supervision : l'architecture orientée objet des commandes, l'interface pilotée par les données, la couche API adaptative et la démarche qualité. La Partie II traite du firmware embarqué : le modèle de communication inter-tâches d'INTEGRITY, la fiabilisation du serveur web HTTPS et l'extension du canal de commande. Un bilan des compétences acquises et des perspectives clôt le document.

# 2. Contexte technique et enjeux

## 2.1. Le produit : une radio de contrôle aérien

La gamme 2G+ regroupe des radios sol VHF/UHF destinées aux communications air-sol du contrôle du trafic aérien. Elle met en œuvre des fonctions critiques pour la sécurité aérienne : voix sur IP selon le standard ED137, enregistrement audio via RTSP, signalisation SELCAL, supervision réseau SNMP. Dans ce contexte, la disponibilité et la fiabilité de l'équipement sont des exigences de premier ordre : une interface d'administration inaccessible ou un comportement erratique ne sont pas de simples désagréments, mais des défauts susceptibles d'affecter l'exploitation opérationnelle.

## 2.2. Une chaîne logicielle à deux étages

Le logiciel de l'équipement se décompose en deux étages complémentaires. Le premier est le firmware embarqué, qui s'exécute directement sur la radio au-dessus du micro-noyau temps réel INTEGRITY de Green Hills. Ce firmware assure les fonctions métier — radio, VoIP, enregistrement, journalisation — et expose un serveur web sécurisé d'administration. Le second étage est le frontend web, une application React servie à l'opérateur, qui offre une interface graphique de supervision et de configuration en dialoguant avec le serveur web du firmware. Le serveur web constitue donc la frontière entre les deux mondes et le point de convergence naturel de mes travaux.

## 2.3. Enjeux transverses

Trois enjeux ont guidé l'ensemble des choix techniques. Le premier est la disponibilité : l'interface d'administration doit rester joignable même après une opération qui échoue, car elle est souvent le seul moyen de rétablir une situation nominale. Le deuxième est l'extensibilité : le catalogue de commandes de la radio évolue régulièrement, et l'ajout d'une commande ou d'un écran ne doit pas imposer une réécriture du code. Le troisième est la robustesse temps réel : sur un système où chaque fonction est isolée et où les échanges sont asynchrones, le séquencement des opérations (réponse réseau, redémarrage, annulation de requêtes) doit être maîtrisé de bout en bout.

# 3. Architecture globale du système

## 3.1. Le serveur web comme pivot d'administration

L'architecture d'ensemble s'organise autour du serveur web sécurisé embarqué dans le firmware. Depuis un navigateur, l'opérateur accède à l'application React de supervision ; celle-ci émet des requêtes HTTPS vers le serveur web de la radio, qui les traduit en commandes internes et les distribue aux tâches métier (radio, enregistrement RTSP, VoIP, stockage Flash). Les valeurs et les états remontent par le chemin inverse et sont restitués à l'écran. Ce schéma en pivot explique pourquoi mes travaux, bien que répartis sur deux bases de code très différentes, forment un tout cohérent : ils renforcent chacun un maillon de la même chaîne administration → commande → équipement.

## 3.2. Deux paradigmes de programmation

Les deux étages relèvent de paradigmes distincts, ce qui a été l'un des aspects les plus formateurs de la mission. Côté frontend, le travail relève du génie logiciel applicatif moderne : programmation orientée objet, patrons de conception, interface déclarative pilotée par les données, tests automatisés. Côté firmware, il relève du logiciel embarqué temps réel : isolation des tâches, communication par messages, gestion fine du séquencement et des ressources, corrections chirurgicales sur une base certifiable. Passer d'un monde à l'autre a nécessité d'adapter en permanence la démarche à la nature du système sous-jacent.

## 3.3. Fil conducteur des contributions

Le tableau ci-dessous résume la cartographie des contributions présentées dans la suite du rapport. Chacune est reliée à l'enjeu qu'elle sert et au maillon de la chaîne qu'elle renforce.

| Étage | Contribution | Enjeu principal servi |
| --- | --- | --- |
| Frontend | Architecture OOP des commandes (Factory + héritage) | Extensibilité |
| Frontend | Interface pilotée par les données (JSON) | Extensibilité |
| Frontend | Couche API adaptative (batch, annulation) | Robustesse / performance |
| Frontend | Tests unitaires + E2E Selenium | Qualité |
| Firmware | Robustesse au certificat invalide | Disponibilité |
| Firmware | Réponse HTTPS après upload de configuration | Robustesse temps réel |
| Firmware | Canal de commande Webserver → RTSP/VoIP | Extensibilité |

**PARTIE I — Frontend de supervision (EMRE-2GPLUS)**

# 4. Le module Monitoring & Control : une architecture objet des commandes

## 4.1. Problématique de départ

Dans la version initiale du frontend, le rendu et l'envoi des commandes reposaient sur du code procédural dispersé : des fonctions ad hoc traitaient chaque type de champ au cas par cas, sans abstraction commune ni validation centralisée. Cette organisation posait deux difficultés. D'une part, l'ajout d'un nouveau type de commande imposait de modifier plusieurs endroits du code, avec un risque élevé de régression. D'autre part, l'absence de contrôle systématique laissait passer des configurations invalides, dont les effets ne se manifestaient qu'à l'exécution. Le module Monitoring & Control (MC) a été conçu pour remédier à ces limites en réintroduisant de la structure là où régnait la duplication.

## 4.2. Une hiérarchie de classes pilotée par une Factory

Le cœur de la refonte est une hiérarchie de classes qui encapsule chaque type de commande. Une classe de base, CommandClass, regroupe le comportement commun — gestion des permissions, cycle de vie, envoi API, rendu générique — tandis que des spécialisations, obtenues par héritage, redéfinissent uniquement ce qui les distingue. La structure obtenue est la suivante :

```
CommandClass           (base : permissions, cycle de vie, envoi API, rendu)
├── CommandBox         (conteneur de sous-commandes)
├── InputCommand       (champ de saisie : pas, unité, min/max)
│   └── IpAddressCommand
│       ├── IpV6Command
│       └── MacAddressCommand
├── SelectCommand      (liste déroulante, formatage de valeur)
├── ButtonCommand      (action appelable)
├── TableCommand       (tableau éditable)
│   └── TableAddCommand (tableau avec ajout de lignes)
├── ListCommand
│   └── ListAddCommand
└── UnderBoxCommand    (encart secondaire)
```

Au total, quatorze classes composent ce modèle : une classe de base et treize spécialisations. La création des objets n'est jamais réalisée directement par le code appelant, mais confiée à un patron Factory : la méthode CommandFactory.create() lit le champ « type » d'une configuration, sélectionne la classe concrète correspondante, puis construit récursivement les sous-commandes et les boutons associés. Les méthodes createFromJSON() et createBatch() instancient directement une arborescence complète à partir des fichiers de configuration. Le code appelant manipule ainsi des CommandClass sans jamais connaître le type concret manipulé.

## 4.3. Polymorphisme et points d'extension

Le polymorphisme est le mécanisme qui rend cette architecture extensible. Chaque sous-classe redéfinit les seules méthodes qui la caractérisent : render() et renderElement() pour le rendu, formatValue() pour la mise en forme des valeurs, getArgsForCommandCall() et getCommandListForApi() pour la construction des appels réseau, buildAddArgs() pour l'ajout de lignes dans un tableau, editArgs() pour l'édition. Concrètement, ajouter un nouveau type de commande revient à écrire une sous-classe et à l'enregistrer dans la Factory, sans toucher à l'existant. On retrouve ici le principe ouvert/fermé : le système est ouvert à l'extension mais fermé à la modification.

## 4.4. Validation, injection et permissions

Trois dispositifs complètent le modèle et illustrent le soin porté à la robustesse. D'abord, la validation à la construction : le constructeur rejette immédiatement, par une exception, un niveau de permission, un type ou un type de radio invalides ; les erreurs de configuration sont ainsi détectées au plus tôt plutôt qu'à l'exécution. Ensuite, l'injection des dépendances d'exécution : la méthode setRuntimeData() injecte après construction le rôle de l'utilisateur, la langue et les callbacks (chargement, changement de valeur, émission d'appels, affichage de pop-ups), en cascade dans les sous-commandes et les boutons. Ce découplage permet aux instances de rester indépendantes de React et de Redux. Enfin, un système de permissions à deux niveaux distingue le droit de lecture (minPerm) du droit d'écriture (minPermModify) ; les niveaux sont évalués via une énumération de rôles ordonnée (GUEST, USER, ADMIN, TELERAD) et propagés jusqu'aux sous-commandes, avec des infobulles explicatives dans l'interface.

## 4.5. Cycle de vie et annulation

Chaque commande gère son propre cycle de vie. Un état de chargement (isLoading) reflète l'activité réseau en cours, et un contrôleur d'annulation (AbortController) permet d'abandonner une requête devenue obsolète — par exemple lorsqu'une nouvelle valeur est demandée avant que la précédente ne soit revenue. Cette maîtrise du cycle de vie au niveau de l'objet lui-même évite les incohérences d'affichage et les fuites de requêtes, et prépare le terrain pour la couche réseau adaptative décrite au chapitre 6.

# 5. Une interface pilotée par les données

## 5.1. Décrire les écrans plutôt que les coder

Le second choix structurant du module MC est de décrire les écrans en JSON plutôt que de les coder. Chaque onglet de supervision est défini par un fichier descripteur (suffixé _Data.json), regroupé par domaine fonctionnel — Main, Maintenance, Network. Le fichier déclare la commande, ses éventuelles sous-commandes, leurs types, leurs contraintes et leurs niveaux de permission. Une vingtaine de ces descripteurs couvrent l'ensemble des onglets. L'extrait ci-dessous, tiré du descripteur SNTP, illustre le contrat :

```json
{
  "Command": "SNTP", 
  "minPerm": "GUEST",
  "minPermModify": "ADMIN",
  "subcommands": [
    { 
      "Command": "SYNCHRONIZED", 
      "type": "select",
      "listSelect": ["YES","NO"]
    },
    {
      "Command": "TIME ZONE", 
      "CommandCall": "TIMEZONE",
      "type": "input", "input": "number",
      "min": -12, "max": 14, "step": 0.5
    }
  ]
}
```

## 5.2. TabGenerator, orchestrateur du rendu

Le composant TabGenerator est le chef d'orchestre qui transforme un descripteur en écran fonctionnel. Son travail se déroule en quatre temps. Il commence par filtrer les commandes et sous-commandes selon le rôle de l'utilisateur et selon la visibilité par type de radio — une commande peut être pertinente en réception, en émission ou dans les deux cas, ce que gère un module dédié. Il instancie ensuite l'arborescence de commandes via CommandFactory.createFromJSON, puis leur injecte le contexte d'exécution par setRuntimeData. Il met en place une interrogation périodique — un rafraîchissement toutes les dix secondes — déléguée à la couche API, qui assure un affichage quasi temps réel des valeurs radio. Il rend enfin l'ensemble en grille à deux colonnes au moyen des composants de présentation dédiés. L'intervalle est proprement nettoyé au démontage du composant pour éviter toute fuite.

## 5.3. Bénéfice : séparation des données et de la présentation

Le gain de cette approche est direct : ajouter un onglet ou une commande consiste à éditer un fichier JSON, sans jamais toucher au code de rendu. Les données et la présentation sont clairement séparées, ce qui accélère et sécurise l'évolution fonctionnelle. Le format du descripteur est documenté dans un tutoriel dédié (TUTO_format_commandes.md), de sorte que l'ajout de commandes reste accessible sans connaissance approfondie du code de rendu. Le compromis assumé est l'existence d'un contrat JSON à respecter, dont la documentation constitue précisément la contrepartie.

# 6. Une couche API adaptative

## 6.1. Du wrapper passif au moteur de requêtes

La couche réseau initiale se limitait à quelques fonctions minces enveloppant l'appel HTTP de base : un appel par commande, sans mise en lot ni annulation. Cette simplicité devenait un handicap dès qu'un écran affichait de nombreuses commandes, chacune générant sa propre requête. La refonte a remplacé ces wrappers passifs par un véritable moteur de requêtes, exposé sous la forme d'un hook useApi, capable de regrouper, d'ordonnancer et d'optimiser les échanges avec le serveur.

## 6.2. File d'attente et mise en lot

Le moteur maintient une file d'attente des commandes à émettre et procède par vidage périodique de cette file. Les commandes en attente sont découpées en lots de taille bornée (cinq au maximum), ce qui limite le nombre de requêtes simultanées et lisse la charge côté serveur comme côté client. Cette mise en lot est le socle sur lequel repose l'optimisation décrite ci-après.

## 6.3. La course batch-vs-individuel

L'élément le plus original de cette couche est une stratégie que l'on peut qualifier de course. Pour un lot donné, deux approches sont lancées en parallèle : d'un côté une requête groupée, contenant l'ensemble des commandes du lot en un seul appel HTTP ; de l'autre, les requêtes individuelles concurrentes. La première réponse complète l'emporte — obtenue au moyen de Promise.any — et l'approche perdante est immédiatement abandonnée via son contrôleur d'annulation. Le système mémorise en outre la stratégie gagnante pour chaque lot, de sorte que les rafraîchissements suivants privilégient d'emblée l'approche la plus rapide. L'optimisation ne repose ainsi sur aucune hypothèse a priori quant au comportement du serveur : elle s'adapte à son comportement réel, mesuré à l'exécution. Le compromis est une logique de concurrence plus fine à maîtriser — annulations, clés de stratégie — que le gain de latence justifie pleinement.

## 6.4. Robustesse des échanges

Deux mécanismes complètent le moteur. Une cartographie des réponses, tolérante au double étiquetage des champs (Command et CommandCall), réassocie chaque valeur renvoyée par le serveur au bon champ de l'interface, y compris lorsque le nom d'appel diffère du nom d'affichage. Par ailleurs, une vérification de connexion précède chaque requête, ce qui évite d'émettre des appels non authentifiés et les erreurs qui en découleraient. Associés à l'annulation systématique des requêtes obsolètes héritée du cycle de vie des commandes, ces dispositifs garantissent la cohérence des données affichées.

# 7. Internationalisation et composants réutilisables

## 7.1. Un dictionnaire aligné sur l'arborescence fonctionnelle

L'interface, initialement monolingue, a été internationalisée en anglais et en français. Le dictionnaire, riche de plusieurs milliers de lignes et d'environ cinq cent soixante-cinq entrées par langue, est structuré de manière imbriquée pour épouser l'arborescence du module MC : titre de section, onglet, sous-onglet, commande, puis libellé et infobulle. Cette organisation hiérarchique n'est pas fortuite : elle fait correspondre directement chaque clé de traduction à un emplacement précis de l'interface, ce qui rend le dictionnaire facile à maintenir et à compléter à mesure que de nouvelles commandes apparaissent.

## 7.2. Sélecteur de langue

Le choix de la langue est offert à l'utilisateur par un sélecteur dédié : un menu déroulant présentant les drapeaux, rendu dans un portail React pour s'affranchir des contraintes de superposition, et adossé à l'état applicatif Redux qui conserve la langue courante. Le changement de langue se répercute instantanément sur l'ensemble de l'interface, y compris les libellés et infobulles des commandes injectés par le contexte d'exécution.

## 7.3. Une bibliothèque de composants consolidée

Au-delà du module MC, plusieurs composants réutilisables ont été créés ou consolidés pour servir de socle transverse : tableaux, listes, pagination, indicateur de chargement, champ de mot de passe, en-têtes de tableau. Une page de synthèse a été ajoutée, ainsi que divers ajustements d'ergonomie — par exemple la fermeture des pop-ups à la touche Entrée. Ces composants partagent des conventions communes et un contrat d'identifiants stable, ce qui, on le verra, facilite considérablement l'automatisation des tests. À l'inverse, du code mort et des données de démonstration ont été supprimés, dans un souci de propreté du dépôt.

# 8. Démarche qualité : une couverture à deux niveaux

## 8.1. Tests unitaires (Jest / React Testing Library)

La première ligne de défense est constituée de trente-quatre fichiers de tests unitaires. Ils couvrent en priorité les classes de commandes — la Factory, la logique de commande, les champs de saisie et leur gestion du pas, le formatage des sélecteurs, l'édition et le rendu des tableaux, la construction des arguments d'ajout de ligne, les listes et les boutons — c'est-à-dire le cœur métier où une régression aurait le plus d'impact. Ils couvrent également les composants réutilisables et les helpers d'API, de stockage et d'outillage. Un utilitaire commun monte chaque composant dans son environnement complet — état Redux et internationalisation — de sorte que les tests s'exécutent dans des conditions proches du réel ; les conventions correspondantes, y compris d'accessibilité, sont consignées dans une documentation dédiée.

## 8.2. Tests de bout en bout (Selenium / Java)

La seconde ligne de défense est une suite de tests de bout en bout comptant vingt-deux fichiers Java, organisée selon le patron Page Object Model. Ce patron isole la description des pages — page de connexion, navigation, onglet, pop-up de résultat — de la logique des scénarios, ce qui rend les tests lisibles et résistants aux évolutions de l'interface. Les scénarios eux-mêmes vérifient le parcours complet : connexion, navigation, envoi de commande, et surtout vérification de l'affichage des valeurs contre le contrat attendu. Une infrastructure dédiée gère la fabrique de driver, la configuration et l'exécution des cas.

## 8.3. Un contrat DOM comme point d'ancrage

Le fil qui relie ces deux niveaux de test est un contrat d'identifiants stable dans le DOM (attributs data-testid normalisés pour les champs, libellés, générateurs d'onglets et lignes). Ce contrat est partagé par le code de rendu et par les tests de bout en bout : le rendu produit ces identifiants, les tests s'y accrochent. On obtient ainsi un couplage explicite et documenté entre l'interface et sa vérification, bien plus robuste qu'une recherche par texte ou par position.

## 8.4. Tests pilotés par les données et traçabilité

Enfin, les cas de test de bout en bout dérivent des jeux de données Postman utilisés pour l'API, ce qui maintient l'alignement entre les tests et le back-end. Cette logique de traçabilité est complétée par un outillage d'audit qui génère l'inventaire des commandes du module MC et une analyse d'écart entre les commandes exposées par le back-end et celles réellement présentes dans le frontend. On dispose ainsi d'une vue objective de la couverture fonctionnelle, précieuse tant pour le pilotage que pour la soutenance.

**PARTIE II — Backend firmware embarqué (gamme 2G+)**

# 9. Le firmware et le système temps réel INTEGRITY

## 9.1. Un micro-noyau à isolation des tâches

Le firmware s'exécute au-dessus du micro-noyau temps réel INTEGRITY de Green Hills, dont le principe directeur est l'isolation : chaque fonction — radio, serveur web, enregistrement RTSP, VoIP, terminal, Flash, journalisation, chien de garde, SNMP — s'exécute dans sa propre tâche et son propre espace d'adressage. Cette ségrégation est une propriété recherchée dans un équipement de contrôle aérien : elle limite la propagation des défauts d'une fonction à l'autre et facilite l'aptitude à la certification. En contrepartie, les tâches ne partagent pas de mémoire par défaut et doivent communiquer exclusivement par des primitives dédiées du noyau.

## 9.2. Le modèle de communication inter-tâches

La compréhension de ce modèle de communication a été un préalable indispensable à mes travaux. Il repose sur un petit nombre de primitives, résumées ci-dessous.

| Primitive INTEGRITY | Rôle |
| --- | --- |
| Task / AddressSpace | Une fonction isolée (Radio, Webserver, RTSP, VoIP, Terminal…) |
| Connection | Canal de communication nommé et typé, déclaré à l'intégration |
| Activity | Point d'attente réveillé par un message, identifié par un activityID |
| Buffer | Zone d'échange associée à une réception / émission |
| Mémoire partagée | Zone commune entre tâches (statistiques, compteurs…) |

## 9.3. La tâche Webserver

Mon travail porte sur la tâche Webserver, le serveur d'administration HTTPS de l'équipement. Depuis un navigateur, cette tâche permet de téléverser des certificats et des fichiers de configuration, de consulter les journaux, de redémarrer l'équipement et de piloter les autres tâches. Les échanges avec ces dernières empruntent des connexions nommées, déclarées dans le fichier d'intégration du système ; la partie applicative ne fait que s'abonner à ces connexions, côté émission ou côté réception. C'est précisément sur ce mécanisme d'abonnement que reposent les contributions décrites dans les chapitres suivants.

# 10. Fiabilisation du serveur web HTTPS

## 10.1. Robustesse à l'upload d'un certificat invalide

Au démarrage, le Webserver charge le certificat et la clé HTTPS de l'utilisateur depuis la mémoire Flash. Dans la version initiale, si ce chargement échouait — certificat téléversé corrompu ou incompatible — la bibliothèque SSL restait dans un état partiel : au redémarrage suivant, la pile HTTPS ne pouvait plus démarrer et l'accès web était perdu. La situation était d'autant plus critique que le seul moyen de re-téléverser un certificat valide est justement le serveur web ; l'équipement se retrouvait donc verrouillé.

La correction consiste à rétablir un état propre en cas d'échec de chargement : au lieu de tenter de démarrer HTTPS avec un certificat corrompu, on décharge la totalité des certificats afin que le service redémarre sur le certificat par défaut.

```c
tvStatusLoadCert = HTTPS_CERT_UNLOAD;
if (tfLoadCredentials() == 0)   /* certificat utilisateur valide */
{
    tvStatusLoadCert = HTTPS_CERT_USER;
}
else                             /* échec de chargement */
{
    ussl_unload_all_certs();    /* on repart d'un état propre */
}
```

Ce choix traduit une priorité assumée : garantir la disponibilité de l'interface d'administration. Mieux vaut un serveur web joignable avec le certificat d'usine qu'un serveur inaccessible verrouillé par un mauvais certificat. Ce compromis entre disponibilité et sécurité stricte du certificat constitue d'ailleurs l'un des points de discussion identifiés pour la soutenance, dans un contexte de sûreté et de certification.

## 10.2. Réponse HTTPS correcte après upload d'une configuration

Le second défaut concernait le téléversement d'un fichier de configuration exigeant un redémarrage. Le Webserver transmet alors la commande à la tâche Radio, qui la traite, en accuse réception, puis redémarre l'équipement. Le code initial attendait une seconde avant de redémarrer, sans distinction de l'origine de la commande. Or cette seconde suffit à un terminal pour afficher « OK », mais pas à la pile HTTPS pour sérialiser et émettre la réponse de succès vers le navigateur avant que le redémarrage ne coupe la connexion TCP. Côté client, l'upload paraissait échouer alors que la configuration avait bien été prise en compte.

La correction rend le délai conditionnel à l'origine de la commande : cinq secondes pour le serveur web sécurisé, le temps que la réponse HTTPS parte réellement sur le réseau ; une seconde sinon, pour ne pas ralentir inutilement les autres flux.

```c
if (resultat == RESULTAT_OK_BOOTIMAGE)
{
    /* Laisser le temps au terminal d'afficher "OK"
       OU d'envoyer la reponse https, puis redemarrer */
    sleep(activityID == ACT_ID_CON_SECUREWEBSERVER ? 5 : 1);
    tfReboot(START_IMAGE);
}
```

Cette solution, minimale et ciblée, ne touche pas au séquencement de redémarrage existant. Il est intéressant de noter qu'une variante intermédiaire, issue du cycle de revue interne, comportait le test inversé (une seconde pour le web, cinq pour le terminal) ; la version finalement retenue, validée en revue de code, rétablit le bon sens. Cet épisode illustre concrètement la valeur du processus de relecture avant intégration.

# 11. Extension du canal de commande Webserver

## 11.1. Le Webserver comme source de commande de premier rang

Dans la version initiale, seules les tâches Terminal (Telnet et SSH) disposaient d'un canal de réception vers les tâches RTSP et VoIP. Le Webserver pouvait commander la Radio, mais les tâches RTSP et VoIP n'écoutaient pas le canal Webserver, alors même que les connexions correspondantes étaient déclarées dans le fichier d'intégration. En pratique, une commande web adressée à l'enregistrement ou à la VoIP restait sans effet. L'objectif de ce chantier était de faire du Webserver une source de commande de premier rang, au même titre que le Terminal.

## 11.2. Une Activity de réception, calquée sur l'existant

La solution ajoute, dans chacune des tâches RTSP et VoIP, une Activity de réception dédiée au Webserver, en reproduisant fidèlement le motif déjà éprouvé pour Telnet et SSH. Concrètement, on déclare l'identité de la nouvelle activité, on la crée, on la met en écoute sur la connexion dédiée, puis on aiguille les messages reçus vers le traitement commun des commandes.

```c
/* tmVoipMain.h : identite de la nouvelle activite */
#define PRIO_ACT_WEBSERVER  127
#define ID_ACT_WEBSERVER    6

/* rtsp.c / voipMain.c : creation, ecoute, dispatch */
CreateActivity(CurrentTask(), PRIO_ACT_..., false,
               (Value)ID_ACT_WEBSERVER, &actReceptionWebserver);
AsynchronousReceive(actReceptionWebserver,
                     (Object)ConRtspVersWebServer, &buf);
...
else if (activityID == ID_ACT_WEBSERVER)
    handleTerminalMessage(ConRtspVersWebServer, ...);
```

L'intérêt de réutiliser le chemin de traitement handleTerminalMessage, déjà validé pour Telnet et SSH, est double : le Webserver devient simplement une source de commandes supplémentaire, sans duplication de la logique métier ni introduction d'un format de message spécifique. On y gagne en cohérence architecturale, en surface de bug réduite et en simplicité de maintenance. Les connexions elles-mêmes sont déclarées dans le fichier d'intégration INTEGRITY ; la partie applicative ne fait que s'y abonner côté réception.

## 11.3. Exposition des journaux au serveur web

Ce chantier a également consisté à initialiser, côté Webserver, la zone de mémoire partagée des « événements de journalisation » (compteurs et statistiques d'événements, par exemple les activations de l'alternat). Sans cette initialisation, le processus Webserver n'attachait pas la zone partagée et ne pouvait donc pas exposer certaines informations de journal. L'ajout de cette étape d'initialisation permet à l'interface web de présenter le paramètre de tampon et le nombre de lignes des journaux, conformément à la demande fonctionnelle à l'origine du ticket.

## 11.4. Un correctif de routage révélé en cours de route

En instrumentant la tâche VoIP pour y ajouter le canal Webserver, j'ai découvert un défaut préexistant : les gestionnaires Telnet et SSH de la VoIP écoutaient et traitaient les connexions du RTSP au lieu de celles de la VoIP. Les commandes terminal destinées à la VoIP transitaient donc par le mauvais objet de connexion, avec un comportement erratique à la clé.

```diff
- handleTerminalMessage(ConRtspVersTerminalTelnet, ...); // mauvaise connexion
+ handleTerminalMessage(ConVoipVersTerminalTelnet, ...); // corrige
- handleTerminalMessage(ConRtspVersSsh, ...);
+ handleTerminalMessage(ConVoipVersSsh, ...);
```

Il s'agissait d'une erreur de copier-coller typique mais à fort impact fonctionnel. Le correctif aligne l'ensemble de la tâche VoIP sur ses propres connexions. Cet épisode illustre une leçon plus générale : l'ajout d'un nouveau canal a été l'occasion d'une relecture attentive qui a permis de résorber une dette latente.

# 12. Outillage, versioning et traçabilité

## 12.1. Un outillage de développement non intrusif

Pour améliorer le confort et la productivité de développement, j'ai introduit une couche d'outillage moderne autour du firmware : un projet CMake qui collecte récursivement les sources et référence la bibliothèque cœur, exploité depuis l'environnement CLion pour l'indexation, la navigation et l'exécution d'outils externes. Un point important a guidé ce choix : cet outillage n'a pas vocation à produire le binaire embarqué. La compilation officielle reste assurée par la chaîne certifiée Green Hills MULTI ; CMake et CLion ne servent qu'à fournir un index de code fiable à l'IDE. On ajoute ainsi de la productivité sans perturber la chaîne de build de référence — un principe de non-intrusion essentiel dans un contexte certifiable.

## 12.2. Versioning et traçabilité des commandes

La version logicielle a été passée à une référence de développement personnel (préfixe M), la compatibilité avec la bibliothèque cœur restant préservée. Par ailleurs, j'ai constitué une table de traçabilité des commandes d'émission : pour chaque commande, elle recense la fonction C associée, son fichier source, ses paramètres, son niveau d'autorisation, son statut d'implémentation et la présence de ses variantes d'aide et web. Ce document d'audit relie les commandes terminal à leur exposition côté serveur web et matérialise l'axe de travail « couverture web des commandes » : s'assurer que chaque commande exposée au terminal dispose de sa variante web et du niveau d'autorisation correct.

## 12.3. Le workflow de développement Telerad

Ces contributions s'inscrivent dans un cycle de développement interne structuré. Chaque correction fait l'objet d'un premier commit, puis d'une version retravaillée et intégrée après revue de code (pull request) ; ce sont ces versions revues qui font foi sur la branche principale. J'ai déjà souligné, à propos du délai de redémarrage, la valeur concrète de ce processus : c'est la revue qui a permis de corriger une inversion de test avant intégration. Certains travaux, cohérents avec la version de développement en cours, figurent encore sur l'arbre de travail au moment de la rédaction et prolongent l'axe de couverture web des commandes.

# 13. Bilan et perspectives

## 13.1. Synthèse des problèmes résolus (firmware)

Le tableau ci-dessous récapitule les défauts corrigés sur le firmware, de leur symptôme à leur résolution.

| Problème | Symptôme | Résolution |
| --- | --- | --- |
| Certificat invalide | Accès web perdu après redémarrage | Déchargement des certificats sur échec |
| Upload de configuration | Upload « en échec » côté navigateur | Délai conditionnel de 5 s pour le web |
| RTSP/VoIP non pilotables | Commandes web sans effet | Nouvelle Activity Webserver + dispatch |
| Journaux web incomplets | Paramètres de journal absents | Initialisation de la mémoire partagée |
| Commandes VoIP mal routées | Comportement erratique | Correction des connexions ConVoipVers* |

## 13.2. Compétences acquises

Cette mission a été particulièrement formatrice par la diversité des registres qu'elle a mobilisés. Sur le plan de la conception logicielle, elle m'a permis de mettre en pratique la programmation orientée objet et les patrons de conception — héritage, polymorphisme, fabrique — au service d'une architecture réellement extensible, ainsi que l'approche déclarative pilotée par les données. Sur le plan de l'embarqué, elle m'a confronté à un système temps réel à isolation stricte, où la maîtrise du séquencement, de la communication inter-tâches et des ressources prime, et où les corrections doivent être chirurgicales. Sur le plan de la qualité, j'ai conçu une démarche de test à deux niveaux reliée par un contrat d'interface explicite. Sur le plan méthodologique, enfin, j'ai pris part à un cycle de développement industriel structuré par la revue de code, dans un contexte exigeant de sûreté.

## 13.3. Points de discussion et perspectives

Plusieurs pistes d'amélioration méritent d'être discutées. Le délai fixe de cinq secondes retenu pour la réponse HTTPS est une valeur empirique ; une solution plus robuste consisterait à attendre l'acquittement de la fin d'émission TCP plutôt qu'un délai fixe. Le compromis entre disponibilité et sécurité stricte du certificat gagnerait à être formalisé au regard des exigences de certification. Du côté frontend, la couverture web des commandes, déjà outillée par l'analyse d'écart, peut être poursuivie jusqu'à une correspondance exhaustive entre commandes terminal et variantes web. Ces perspectives dessinent la suite naturelle des travaux engagés.

## 13.4. Conclusion

Les contributions présentées, bien que réparties sur deux bases de code aux paradigmes opposés, forment un ensemble cohérent orienté par un même fil directeur : renforcer, de bout en bout, la chaîne d'administration web de la gamme 2G+. Le frontend a gagné une architecture extensible, une interface déclarative, une couche réseau optimisée et une couverture de test solide ; le firmware a gagné en disponibilité, en robustesse et en capacité de pilotage. Au-delà des livrables, cette mission m'a appris à adapter en permanence ma démarche à la nature du système — génie logiciel applicatif d'un côté, rigueur de l'embarqué temps réel de l'autre — ce qui constitue sans doute l'acquis le plus durable de ce projet.
