T1 Architecture & flux
| Les quatre flux de données | Ce qui circule, et dans quel ordre |
| Anatomie d'une trame | Ce que devient une mesure, de la radio à la plateforme |
| Composants du flux | Les 187 nodes, par rôle |
| Stockage local | Ce que la passerelle écrit, et ce qu'elle relit |
| Résilience | Ce qui résiste à une coupure, et ce qui ne résiste pas |
Vue d'ensemble
La CloudGate CG8102 est à la fois la gateway LoRa (packet forwarder), le LoRaWAN Network Server et l'Application Server. Elle remplace une combinaison gateway + LNS cloud classique. Tout le code applicatif est implanté dans le canvas LuvitRED (port Lua/Luvit de Node-RED, embarqué dans le firmware CG).
FPort 64/70 downlink
Class A LoRaWAN
LoRa NS + AS · LTE/Ethernet
Stockage local 15 j
uplinks-YYYY-MM-DD.jsonl
rétention 15 j
POST /v1/payloads
POST /gateway/health
POST /lorawan (uplinks)
GET /getPendingMsgs (dl)
Les quatre flux de données
/v1/payloads avec Bearer DAP. Body au format spec v1.0 : equipment_model, payload (hex), site, datasource, metrics[], timestamp ISO 8601. Headers X-CloudGate-Id + X-Device-Id. Voir Chap. 03./lorawan avec Basic auth + header X-Network. Body Loriot-like : endDevice.devEui, fPort, fCntUp, payload, recvTime, dataRate, gwInfo[]. Voir Chap. 04./getPendingMsgs?client=<id>. Si la réponse contient des device_list/payload/cmd, la chaîne parse_asystom_dl émet un msg.app = { queue = { [DevEUI] = { data, fport, confirmed=false } } } vers le node lora-app qui en fait un downlink LoRa Class A./gateway/health. Body : life_counter, uptime, lora_uplinks_count, lora_status (active/idle/stale), gsm_rssi, gsm_operator, gsm_technology, gsm_rfband, modem_model, last_iobase_ok_ts, last_asystom_ok_ts.Anatomie d'une trame uplink
Une trame LoRa reçue est fan-out en 3 destinations parallèles depuis la CloudGate : IoBase (cloud), Asystom Advisor (cloud), et un audit local sur disque. Cliquer Lancer pour visualiser le timing.
uplinks-YYYY-MM-DD.jsonl
Composants du flux
Le flux compte aujourd'hui 187 nodes, dont 67 fonctions. Les quatre flux de données ci-dessus en sont le cœur ; le reste assure le pilotage, la persistance et l'observabilité.
| Catégorie | Nodes | Rôle |
|---|---|---|
| Entrée LoRa | lora-srv · lora-app · un node lora device par capteur déclaré |
Packet forwarder + LNS + AppServer + flow-node device-spécifique. Sortie vers on_lora_message. |
| Dispatcher | on_lora_message (function) |
Compteurs + 5 wires sortantes : lora out, build_iobase, build_audit, build_asystom, build_health. |
| Flux 01 IoBase | build_iobase_msg → iobase POST → handle_iobase_resp |
Construit le corps et les en-têtes, envoie avec le jeton. Un échec est journalisé — la mesure n'est pas rejouée. |
| Flux 02 Asystom | build_asystom_msg → asystom POST → handle_asystom_resp |
Build body Loriot-like + Basic auth + X-Network header, POST. |
| Flux 03 DL polling | asystom downlink poll (cron 60s) → build_asystom_dl_get → http GET → parse_asystom_dl → lora-app |
Interroge la plateforme, analyse la réponse et construit un seul message dont la file porte tous les équipements concernés, indexés par DevEUI. |
| Flux 04 Health | health hourly (cron 3600s) + on_lora_message → build_health_msg → health POST → handle_health_resp |
Body 11 champs métriques. POST 1×/h + à chaque event capteur. |
| Audit local | build_audit_line → audit jsonl (file out append) + audit rotate daily 02:00 (cron) → audit_rotate → file out delete |
JSONL append per day dans /mnt/data, cleanup > 15 jours quotidien. |
| DAP auth | 3 inject + 3 build + dap POST + handle_dap_resp + persist_dap + save dap state + chaîne restore au boot |
Register/Token/refresh single-use protégé par mutex. Persistance flash. Voir Chap. 02. |
| Heartbeat life | life tick 1s → life_tick → life counter file out + load life @startup → file in → life_restore |
Compteur 0→32000 cyclique persisté toutes les 60s, restauré au boot. |
| CG métriques | cg admin login + cg metrics poll (cron 300s) → 3 endpoints (internet/diag wwan0/sim) → handle_cg_metrics |
Récupère GSM RSSI, opérateur, technology, RF band depuis l'admin local CG. |
| File de rejeu | aucune | Le node de configuration a été retiré (boucle de plantage). Les mesures ne sont pas rejouées. |
| Cockpit embarqué | 2 serveurs HTTP (ports 8888 et 8889) · 13 routes · cockpit_login,
cockpit_health, cockpit_balises, cockpit_globals,
post_meta, cockpit_downlink… |
Sert l'interface et son API depuis la passerelle. Aucun service externe. Voir Chap. 05. |
| Statistiques horaires | on_lora_message → uplink_hourly ·
persist_uplink_stats (toutes les 10 min) · restore_uplink_stats au boot |
Compteurs par heure et par balise sur 24 h glissantes, dans leur propre fichier. Alimentent les graphiques du tableau de bord. |
| Ajout de capteur | post_balise → sd_login_req → sd_cookie_getflows →
sd_backup → sd_post_prep → réécriture du flux |
La passerelle se déclare elle-même un capteur en réécrivant son propre flux — ce qui redémarre toutes les chaînes. Protégé par plusieurs garde-fous. |
| Supervision externe | build_supervision_ping · build_slack_heartbeat |
Deux chemins de sortie vers l'extérieur, l'un à chaque rafraîchissement de jeton, l'autre périodique. Les deux sont débranchés : les nodes existent, leur déclencheur n'a plus de fil. Ils sont là pour être recâblés vers la supervision d'un exploitant, avec son URL dans la configuration du site. En l'état, la passerelle n'émet rien vers un tiers. |
| Init | startup init → startup_init |
Set globals au boot : start_time, hosts, credentials, defaults. |
Stockage local
Ce que la passerelle écrit sur sa propre mémoire. Tous ces fichiers
appartiennent à root : le compte d'administration ne peut pas les modifier
directement, ce qui explique pourquoi les mises à jour passent par /tmp puis par le
flux lui-même.
| Fichier | Contenu | Écrit | Relu |
|---|---|---|---|
dap_state.json |
Authentification de l'environnement principal : code d'appareil, jetons, horodatage, adresse et identifiants. | à chaque renouvellement | au démarrage |
dap_state_b.json |
Idem pour le second environnement. Sans sa relecture au démarrage, le jeton — à usage unique — serait perdu à chaque redémarrage. | à chaque renouvellement | au démarrage |
balises_meta.json |
Configuration des balises : nom, cadence, site, source de données, modèle, métriques, type, environnement de destination. | depuis le cockpit | au démarrage et à chaque écriture |
balises_state.json |
État de service : dernière trame, compteurs, qualité radio, réglage en cours, historique récent. | toutes les 2 min si modifié | au démarrage |
uplink_hourly.json |
Compteurs par heure et par balise sur 24 h glissantes. Alimente les graphiques. | toutes les 10 min si modifié | au démarrage |
globals_override.json |
Variables saisies au cockpit. Priment sur la copie conservée dans les fichiers d'authentification. | depuis le cockpit | au démarrage |
life_counter.json |
Compteur de vie, qui prouve que le flux tourne. | périodiquement | au démarrage |
cockpit_*.html/js/css |
Les fichiers de l'interface, servis par le flux. | au déploiement de l'interface | à chaque requête |
uplinks-AAAA-MM-JJ.jsonl |
Journal des trames reçues, une ligne par trame. C'est une trace, pas une file : rien n'est renvoyé depuis ce fichier. | à chaque trame | jamais par le flux |
flow_pre_write.json |
Sauvegarde du flux avant qu'il se réécrive lui-même, lors de l'ajout d'un capteur. | avant réécriture | à la main, en cas de besoin |
queue/ |
Vestige de la file de rejeu retirée du flux. Répertoire inutilisé. | — | — |
Relevé le 4 septembre 2026 : 63 journaux pour 13,9 Mo, dont certains remontent à septembre 2025 — alors que la rétention annoncée est de quinze jours. La rotation ne traite qu'un fichier par exécution, celui qui vient d'atteindre l'âge limite ; elle ne rattrape pas ce qui s'est accumulé pendant qu'elle était en panne.
Ce n'est pas urgent — la partition est occupée à 29 %, avec 68 Mo libres — mais l'arriéré ne disparaîtra pas tout seul. Le nettoyage se fait à la main, en supprimant les journaux antérieurs à la fenêtre voulue.
Un second espace, /overlay/etc/luvitred/store/lora_db/, contient la base du réseau
LoRa : sessions, clés et compteurs des capteurs rattachés. Il est géré par la pile radio, pas par
le flux — mais c'est lui qui fait qu'un capteur reste rattaché après un redémarrage.
Résilience
cg metrics poll, qui déclenche un renouvellement préventif du jeton. Les mesures manquées ne sont pas rattrapées.fn_trigger_refresh_on_401 → build_dap_refresh (mutex) → nouveau jeton. La trame qui a essuyé le 401 est perdue ; les suivantes passent.dap_state.json au boot. life_counter restauré. Aucun re-enrôlement administrateur nécessaire.global.dap_refreshing → 1 seul appel /refresh à la fois. Protège le single-use du refresh_token.uplinks-AAAA-MM-JJ.jsonl, conservée
15 jours. C'est une trace, pas une file : rien n'est renvoyé depuis ce fichier.Une mesure qui n'atteint pas sa destination — coupure réseau, plateforme injoignable, jeton expiré au mauvais moment — est perdue. Elle est journalisée sur la passerelle, mais rien ne la renvoie.
Le node de configuration de file a été retiré du flux le 2 août 2026 : il provoquait une boucle
de plantage. Deux requêtes sortantes (health POST et asystom POST)
portent encore un queueenabled à vrai pointant vers une file
qui n'existe pas — configuration orpheline, sans effet, à nettoyer.
Conséquence à connaître : pendant la coupure du service DNS d'IoBase constatée depuis le 29 août 2026, chaque mesure émise a été définitivement perdue. Le journal local permet de savoir ce qui a été perdu, pas de le rattraper.