Diagram: IoT sensors connect to all brokers + OpenRemote UI linked (ORM->UI)

This commit is contained in:
Eric FELIXINE
2026-05-04 21:05:50 -04:00
parent 25e490c758
commit 428dec8509
2 changed files with 427 additions and 435 deletions

View File

@@ -1,464 +1,453 @@
<!DOCTYPE html>
<html lang="fr">
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Smart City Digital Twin - Flux de Données</title>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>data-flow-diagram</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
/* Default styles provided by pandoc.
** See https://pandoc.org/MANUAL.html#variables-for-html for config info.
*/
html {
color: #1a1a1a;
background-color: #fdfdfd;
}
body {
font-family: 'JetBrains Mono', monospace;
background: #020617;
min-height: 100vh;
padding: 2rem;
color: white;
}
.container {
max-width: 1400px;
margin: 0 auto;
max-width: 36em;
padding-left: 50px;
padding-right: 50px;
padding-top: 50px;
padding-bottom: 50px;
hyphens: auto;
overflow-wrap: break-word;
text-rendering: optimizeLegibility;
font-kerning: normal;
}
.header {
margin-bottom: 2rem;
@media (max-width: 600px) {
body {
font-size: 0.9em;
padding: 12px;
}
h1 {
font-size: 1.8em;
}
}
.header-row {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 0.5rem;
@media print {
html {
background-color: white;
}
body {
background-color: transparent;
color: black;
font-size: 12pt;
}
p, h2, h3 {
orphans: 3;
widows: 3;
}
h2, h3, h4 {
page-break-after: avoid;
}
}
.pulse-dot {
width: 12px;
height: 12px;
background: #22d3ee;
border-radius: 50%;
animation: pulse 2s infinite;
p {
margin: 1em 0;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
a {
color: #1a1a1a;
}
h1 {
font-size: 1.5rem;
font-weight: 700;
letter-spacing: -0.025em;
a:visited {
color: #1a1a1a;
}
.subtitle {
color: #94a3b8;
font-size: 0.875rem;
margin-left: 1.75rem;
img {
max-width: 100%;
}
.diagram-container {
background: rgba(15, 23, 42, 0.5);
border-radius: 1rem;
border: 1px solid #1e293b;
padding: 1.5rem;
overflow-x: auto;
}
svg {
height: auto;
max-width: 100%;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 1.4em;
}
h5, h6 {
font-size: 1em;
font-style: italic;
}
h6 {
font-weight: normal;
}
ol, ul {
padding-left: 1.7em;
margin-top: 1em;
}
li > ol, li > ul {
margin-top: 0;
}
blockquote {
margin: 1em 0 1em 1.7em;
padding-left: 1em;
border-left: 2px solid #e6e6e6;
color: #606060;
}
code {
font-family: Menlo, Monaco, Consolas, 'Lucida Console', monospace;
font-size: 85%;
margin: 0;
hyphens: manual;
}
pre {
margin: 1em 0;
overflow: auto;
}
pre code {
padding: 0;
overflow: visible;
overflow-wrap: normal;
}
.sourceCode {
background-color: transparent;
overflow: visible;
}
hr {
border: none;
border-top: 1px solid #1a1a1a;
height: 1px;
margin: 1em 0;
}
table {
margin: 1em 0;
border-collapse: collapse;
width: 100%;
min-width: 1200px;
overflow-x: auto;
display: block;
font-variant-numeric: lining-nums tabular-nums;
}
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1rem;
margin-top: 2rem;
table caption {
margin-bottom: 0.75em;
}
.card {
background: rgba(15, 23, 42, 0.5);
border-radius: 0.75rem;
border: 1px solid #1e293b;
padding: 1.25rem;
tbody {
margin-top: 0.5em;
border-top: 1px solid #1a1a1a;
border-bottom: 1px solid #1a1a1a;
}
.card-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.75rem;
th {
border-top: 1px solid #1a1a1a;
padding: 0.25em 0.5em 0.25em 0.5em;
}
.card-dot {
width: 8px;
height: 8px;
border-radius: 50%;
td {
padding: 0.125em 0.5em 0.25em 0.5em;
}
.card-dot.cyan { background: #22d3ee; }
.card-dot.emerald { background: #34d399; }
.card-dot.violet { background: #a78bfa; }
.card-dot.amber { background: #fbbf24; }
.card-dot.rose { background: #fb7185; }
.card-dot.orange { background: #fb923c; }
.card h3 {
font-size: 0.875rem;
font-weight: 600;
}
.card ul {
list-style: none;
color: #94a3b8;
font-size: 0.75rem;
}
.card li {
margin-bottom: 0.375rem;
}
.footer {
header {
margin-bottom: 4em;
text-align: center;
margin-top: 1.5rem;
color: #475569;
font-size: 0.75rem;
}
#TOC li {
list-style: none;
}
#TOC ul {
padding-left: 1.3em;
}
#TOC > ul {
padding-left: 0;
}
#TOC a:not(:hover) {
text-decoration: none;
}
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
/* The extra [class] is a hack that increases specificity enough to
override a similar rule in reveal.js */
ul.task-list[class]{list-style: none;}
ul.task-list li input[type="checkbox"] {
font-size: inherit;
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
}
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
</style>
</head>
<body>
<div class="container">
<!-- Header -->
<div class="header">
<div class="header-row">
<div class="pulse-dot"></div>
<h1>Smart City Digital Twin — Flux de Données</h1>
</div>
<p class="subtitle">Martinique • Simulator → Brokers → Context Brokers → Visualization</p>
</div>
<nav id="TOC" role="doc-toc">
<ul>
<li><a href="#smart-city-digital-twin-diagramme-des-flux-de-données"
id="toc-smart-city-digital-twin-diagramme-des-flux-de-données">Smart
City Digital Twin — Diagramme des Flux de Données</a>
<ul>
<li><a href="#vue-densemble" id="toc-vue-densemble">Vue
densemble</a></li>
<li><a href="#diagramme-mermaid" id="toc-diagramme-mermaid">Diagramme
Mermaid</a></li>
<li><a href="#description-des-flux"
id="toc-description-des-flux">Description des flux</a>
<ul>
<li><a href="#génération-des-données-simulator"
id="toc-génération-des-données-simulator">1. <strong>Génération des
données (Simulator)</strong></a></li>
<li><a href="#ingestion-mqtt-brokers" id="toc-ingestion-mqtt-brokers">2.
<strong>Ingestion MQTT (Brokers)</strong></a></li>
<li><a href="#context-brokers-ngsi-ld-sensorthings"
id="toc-context-brokers-ngsi-ld-sensorthings">3. <strong>Context Brokers
(NGSI-LD &amp; SensorThings)</strong></a></li>
<li><a href="#plateforme-iot-openremote"
id="toc-plateforme-iot-openremote">4. <strong>Plateforme IoT
(OpenRemote)</strong></a></li>
<li><a href="#stockage-métriques" id="toc-stockage-métriques">5.
<strong>Stockage &amp; Métriques</strong></a></li>
<li><a href="#visualisation-analyse" id="toc-visualisation-analyse">6.
<strong>Visualisation &amp; Analyse</strong></a></li>
</ul></li>
<li><a href="#technologies-clés" id="toc-technologies-clés">Technologies
clés</a></li>
<li><a href="#fichiers-associés" id="toc-fichiers-associés">Fichiers
associés</a></li>
</ul></li>
</ul>
</nav>
<h1 id="smart-city-digital-twin-diagramme-des-flux-de-données">Smart
City Digital Twin — Diagramme des Flux de Données</h1>
<h2 id="vue-densemble">Vue densemble</h2>
<p>Ce diagramme illustre le flux complet des données IoT du simulateur
vers les différentes couches de traitement, de stockage et de
visualisation.</p>
<hr />
<h2 id="diagramme-mermaid">Diagramme Mermaid</h2>
<pre class="mermaid"><code>graph TB
SIM[Smart City Simulator]
SENS[Capteurs IoT Reels]
EMQ[EMQX]
MOS[Mosquitto]
BUN[BunkerM]
ORI[Orion-LD]
STE[Stellio]
FRO[FROST-Server]
ORM[OpenRemote Manager]
KC[Keycloak]
INF[InfluxDB]
PRO[Prometheus]
GEO[GeoServer]
GRA[Grafana]
MAP[MapStore]
UI[OpenRemote UI]
<!-- Main Diagram -->
<div class="diagram-container">
<svg viewBox="0 0 1300 750">
<!-- Definitions -->
<defs>
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#64748b" />
</marker>
<marker id="arrowhead-cyan" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#22d3ee" />
</marker>
<marker id="arrowhead-emerald" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#34d399" />
</marker>
<marker id="arrowhead-orange" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#fb923c" />
</marker>
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="#1e293b" stroke-width="0.5"/>
</pattern>
</defs>
<!-- Background Grid -->
<rect width="100%" height="100%" fill="url(#grid)" />
<!-- ===== LAYER 1: Data Sources ===== -->
<text x="30" y="30" fill="#94a3b8" font-size="12" font-weight="600">📡 COUCHE 1 : SOURCES DE DONNÉES</text>
<!-- Smart City Simulator -->
<rect x="30" y="50" width="180" height="70" rx="6" fill="#0f172a"/>
<rect x="30" y="50" width="180" height="70" rx="6" fill="rgba(251, 146, 60, 0.3)" stroke="#fb923c" stroke-width="1.5"/>
<text x="120" y="80" fill="white" font-size="12" font-weight="600" text-anchor="middle">Smart City Simulator</text>
<text x="120" y="96" fill="#94a3b8" font-size="9" text-anchor="middle">Python • 10 capteurs</text>
<text x="120" y="112" fill="#fb923c" font-size="8" text-anchor="middle">MQTT + REST API</text>
<!-- ===== LAYER 2: MQTT BROKERS ===== -->
<text x="30" y="170" fill="#94a3b8" font-size="12" font-weight="600">📡 COUCHE 2 : MQTT BROKERS</text>
<!-- EMQX -->
<rect x="30" y="190" width="140" height="60" rx="6" fill="#0f172a"/>
<rect x="30" y="190" width="140" height="60" rx="6" fill="rgba(34, 211, 238, 0.3)" stroke="#22d3ee" stroke-width="1.5"/>
<text x="100" y="215" fill="white" font-size="11" font-weight="600" text-anchor="middle">EMQX</text>
<text x="100" y="231" fill="#94a3b8" font-size="9" text-anchor="middle">Port 11883 (MQTT)</text>
<!-- Mosquitto -->
<rect x="190" y="190" width="140" height="60" rx="6" fill="#0f172a"/>
<rect x="190" y="190" width="140" height="60" rx="6" fill="rgba(34, 211, 238, 0.3)" stroke="#22d3ee" stroke-width="1.5"/>
<text x="260" y="215" fill="white" font-size="11" font-weight="600" text-anchor="middle">Mosquitto</text>
<text x="260" y="231" fill="#94a3b8" font-size="9" text-anchor="middle">Port 1883 (MQTT)</text>
<!-- BunkerM -->
<rect x="350" y="190" width="140" height="60" rx="6" fill="#0f172a"/>
<rect x="350" y="190" width="140" height="60" rx="6" fill="rgba(34, 211, 238, 0.3)" stroke="#22d3ee" stroke-width="1.5"/>
<text x="420" y="215" fill="white" font-size="11" font-weight="600" text-anchor="middle">BunkerM</text>
<text x="420" y="231" fill="#94a3b8" font-size="9" text-anchor="middle">Port 1900 (MQTTS)</text>
<!-- ===== LAYER 3: CONTEXT BROKERS ===== -->
<text x="30" y="290" fill="#94a3b8" font-size="12" font-weight="600">🔄 COUCHE 3 : CONTEXT BROKERS (NGSI-LD)</text>
<!-- Orion-LD -->
<rect x="30" y="310" width="160" height="80" rx="6" fill="#0f172a"/>
<rect x="30" y="310" width="160" height="80" rx="6" fill="rgba(52, 211, 153, 0.3)" stroke="#34d399" stroke-width="1.5"/>
<text x="110" y="335" fill="white" font-size="11" font-weight="600" text-anchor="middle">Orion-LD</text>
<text x="110" y="351" fill="#94a3b8" font-size="9" text-anchor="middle">NGSI-LD</text>
<text x="110" y="367" fill="#94a3b8" font-size="8" text-anchor="middle">Port 1026</text>
<text x="110" y="383" fill="#34d399" font-size="8" text-anchor="middle">Entities: Traffic, Air</text>
<!-- Stellio -->
<rect x="210" y="310" width="160" height="80" rx="6" fill="#0f172a"/>
<rect x="210" y="310" width="160" height="80" rx="6" fill="rgba(52, 211, 153, 0.3)" stroke="#34d399" stroke-width="1.5"/>
<text x="290" y="335" fill="white" font-size="11" font-weight="600" text-anchor="middle">Stellio</text>
<text x="290" y="351" fill="#94a3b8" font-size="9" text-anchor="middle">NGSI-LD</text>
<text x="290" y="367" fill="#94a3b8" font-size="8" text-anchor="middle">Port 8080</text>
<text x="290" y="383" fill="#34d399" font-size="8" text-anchor="middle">14 payloads entities</text>
<!-- FROST-Server -->
<rect x="390" y="310" width="160" height="80" rx="6" fill="#0f172a"/>
<rect x="390" y="310" width="160" height="80" rx="6" fill="rgba(52, 211, 153, 0.3)" stroke="#34d399" stroke-width="1.5"/>
<text x="470" y="335" fill="white" font-size="11" font-weight="600" text-anchor="middle">FROST-Server</text>
<text x="470" y="351" fill="#94a3b8" font-size="9" text-anchor="middle">SensorThings API</text>
<text x="470" y="367" fill="#94a3b8" font-size="8" text-anchor="middle">21k+ observations</text>
<text x="470" y="383" fill="#34d399" font-size="8" text-anchor="middle">PostgreSQL+Timescale</text>
<!-- ===== LAYER 4: IOT PLATFORM ===== -->
<text x="30" y="430" fill="#94a3b8" font-size="12" font-weight="600">🏠 COUCHE 4 : PLATEFORME IOT (OpenRemote)</text>
<!-- OpenRemote Manager -->
<rect x="30" y="450" width="200" height="90" rx="6" fill="#0f172a"/>
<rect x="30" y="450" width="200" height="90" rx="6" fill="rgba(167, 139, 250, 0.3)" stroke="#a78bfa" stroke-width="1.5"/>
<text x="130" y="478" fill="white" font-size="12" font-weight="600" text-anchor="middle">OpenRemote Manager</text>
<text x="130" y="494" fill="#94a3b8" font-size="9" text-anchor="middle">Realm: Smart City</text>
<text x="130" y="510" fill="#94a3b8" font-size="8" text-anchor="middle">33 assets IoT</text>
<text x="130" y="526" fill="#a78bfa" font-size="8" text-anchor="middle">Port 8080 + Keycloak</text>
<!-- Keycloak -->
<rect x="250" y="465" width="120" height="60" rx="6" fill="#0f172a"/>
<rect x="250" y="465" width="120" height="60" rx="6" fill="rgba(251, 113, 133, 0.3)" stroke="#fb7185" stroke-width="1.5"/>
<text x="310" y="490" fill="white" font-size="10" font-weight="600" text-anchor="middle">Keycloak</text>
<text x="310" y="506" fill="#94a3b8" font-size="8" text-anchor="middle">Auth OpenID</text>
<!-- ===== LAYER 5: STORAGE & METRICS ===== -->
<text x="30" y="570" fill="#94a3b8" font-size="12" font-weight="600">💾 COUCHE 5 : STOCKAGE & MÉTRIQUES</text>
<!-- InfluxDB -->
<rect x="30" y="590" width="140" height="60" rx="6" fill="#0f172a"/>
<rect x="30" y="590" width="140" height="60" rx="6" fill="rgba(120, 53, 15, 0.3)" stroke="#fbbf24" stroke-width="1.5"/>
<text x="100" y="615" fill="white" font-size="11" font-weight="600" text-anchor="middle">InfluxDB</text>
<text x="100" y="631" fill="#94a3b8" font-size="9" text-anchor="middle">IoT Data Bucket</text>
<!-- Prometheus -->
<rect x="190" y="590" width="140" height="60" rx="6" fill="#0f172a"/>
<rect x="190" y="590" width="140" height="60" rx="6" fill="rgba(120, 53, 15, 0.3)" stroke="#fbbf24" stroke-width="1.5"/>
<text x="260" y="615" fill="white" font-size="11" font-weight="600" text-anchor="middle">Prometheus</text>
<text x="260" y="631" fill="#94a3b8" font-size="9" text-anchor="middle">Metrics + Alerting</text>
<!-- GeoServer -->
<rect x="350" y="590" width="140" height="60" rx="6" fill="#0f172a"/>
<rect x="350" y="590" width="140" height="60" rx="6" fill="rgba(120, 53, 15, 0.3)" stroke="#fbbf24" stroke-width="1.5"/>
<text x="420" y="615" fill="white" font-size="11" font-weight="600" text-anchor="middle">GeoServer</text>
<text x="420" y="631" fill="#94a3b8" font-size="9" text-anchor="middle">WMS/WFS + PostGIS</text>
<!-- ===== LAYER 6: VISUALIZATION ===== -->
<text x="650" y="170" fill="#94a3b8" font-size="12" font-weight="600">📊 COUCHE 6 : VISUALISATION & ANALYSE</text>
<!-- Grafana -->
<rect x="650" y="190" width="160" height="80" rx="6" fill="#0f172a"/>
<rect x="650" y="190" width="160" height="80" rx="6" fill="rgba(8, 51, 68, 0.4)" stroke="#22d3ee" stroke-width="1.5"/>
<text x="730" y="218" fill="white" font-size="12" font-weight="600" text-anchor="middle">Grafana</text>
<text x="730" y="234" fill="#94a3b8" font-size="9" text-anchor="middle">Dashboards</text>
<text x="730" y="250" fill="#94a3b8" font-size="8" text-anchor="middle">Datasources:</text>
<text x="730" y="266" fill="#22d3ee" font-size="8" text-anchor="middle">InfluxDB, FROST, Orion</text>
<!-- MapStore -->
<rect x="830" y="190" width="160" height="80" rx="6" fill="#0f172a"/>
<rect x="830" y="190" width="160" height="80" rx="6" fill="rgba(8, 51, 68, 0.4)" stroke="#22d3ee" stroke-width="1.5"/>
<text x="910" y="218" fill="white" font-size="12" font-weight="600" text-anchor="middle">MapStore</text>
<text x="910" y="234" fill="#94a3b8" font-size="9" text-anchor="middle">Cartographie</text>
<text x="910" y="250" fill="#94a3b8" font-size="8" text-anchor="middle">Sources:</text>
<text x="910" y="266" fill="#22d3ee" font-size="8" text-anchor="middle">GeoServer WMS</text>
<!-- OpenRemote UI -->
<rect x="650" y="310" width="160" height="80" rx="6" fill="#0f172a"/>
<rect x="650" y="310" width="160" height="80" rx="6" fill="rgba(8, 51, 68, 0.4)" stroke="#22d3ee" stroke-width="1.5"/>
<text x="730" y="338" fill="white" font-size="12" font-weight="600" text-anchor="middle">OpenRemote UI</text>
<text x="730" y="354" fill="#94a3b8" font-size="9" text-anchor="middle">Manager Interface</text>
<text x="730" y="370" fill="#22d3ee" font-size="8" text-anchor="middle">Realm: Smart City</text>
<!-- ===== ARROWS: Data Flows ===== -->
<!-- Simulator → MQTT Brokers -->
<line x1="120" y1="120" x2="100" y2="188" stroke="#fb923c" stroke-width="2" marker-end="url(#arrowhead-orange)"/>
<text x="95" y="150" fill="#fb923c" font-size="8">MQTT</text>
<line x1="120" y1="120" x2="260" y2="188" stroke="#fb923c" stroke-width="2" marker-end="url(#arrowhead-orange)"/>
<line x1="120" y1="120" x2="420" y2="188" stroke="#fb923c" stroke-width="2" marker-end="url(#arrowhead-orange)"/>
<!-- MQTT Brokers → Context Brokers -->
<line x1="100" y1="252" x2="110" y2="308" stroke="#22d3ee" stroke-width="2" marker-end="url(#arrowhead-cyan)"/>
<text x="85" y="280" fill="#22d3ee" font-size="8">NGSI-LD</text>
<line x1="260" y1="252" x2="290" y2="308" stroke="#22d3ee" stroke-width="2" marker-end="url(#arrowhead-cyan)"/>
<line x1="420" y1="252" x2="470" y2="308" stroke="#22d3ee" stroke-width="2" marker-end="url(#arrowhead-cyan)"/>
<!-- Simulator → OpenRemote (REST) -->
<line x1="180" y1="85" x2="130" y2="448" stroke="#a78bfa" stroke-width="2" stroke-dasharray="5,5" marker-end="url(#arrowhead-emerald)"/>
<text x="160" y="250" fill="#a78bfa" font-size="8">REST API</text>
<!-- Simulator → InfluxDB -->
<line x1="180" y1="100" x2="100" y2="588" stroke="#fbbf24" stroke-width="2" marker-end="url(#arrowhead)"/>
<text x="145" y="340" fill="#fbbf24" font-size="8">HTTP</text>
<!-- Context Brokers → Visualization -->
<line x1="110" y1="392" x2="730" y2="188" stroke="#34d399" stroke-width="2" marker-end="url(#arrowhead-emerald)"/>
<text x="350" y="280" fill="#34d399" font-size="8">Query</text>
<line x1="290" y1="392" x2="730" y2="188" stroke="#34d399" stroke-width="2" marker-end="url(#arrowhead-emerald)"/>
<line x1="470" y1="392" x2="730" y2="188" stroke="#34d399" stroke-width="2" marker-end="url(#arrowhead-emerald)"/>
<!-- GeoServer → MapStore -->
<line x1="420" y1="652" x2="910" y2="268" stroke="#fbbf24" stroke-width="2" marker-end="url(#arrowhead)"/>
<text x="600" y="440" fill="#fbbf24" font-size="8">WMS/WFS</text>
<!-- Context Brokers → GeoServer (PostGIS) -->
<path d="M 110 392 Q 110 500 420 590" fill="none" stroke="#34d399" stroke-width="1.5" stroke-dasharray="3,3"/>
<text x="200" y="480" fill="#34d399" font-size="7">DB Sync</text>
<!-- OpenRemote → Grafana -->
<line x1="130" y1="542" x2="730" y2="268" stroke="#a78bfa" stroke-width="2" marker-end="url(#arrowhead-emerald)"/>
<text x="350" y="380" fill="#a78bfa" font-size="8">API Query</text>
<!-- All → Prometheus (Metrics) -->
<line x1="320" y1="500" x2="260" y2="588" stroke="#fbbf24" stroke-width="1.5" stroke-dasharray="4,4"/>
<text x="260" y="540" fill="#fbbf24" font-size="7">Metrics</text>
<!-- ===== LEGEND ===== -->
<text x="1050" y="30" fill="white" font-size="11" font-weight="600">Légende</text>
<rect x="1050" y="42" width="16" height="12" rx="2" fill="rgba(251, 146, 60, 0.3)" stroke="#fb923c" stroke-width="1"/>
<text x="1072" y="52" fill="#94a3b8" font-size="9">Source de données</text>
<rect x="1050" y="60" width="16" height="12" rx="2" fill="rgba(34, 211, 238, 0.3)" stroke="#22d3ee" stroke-width="1"/>
<text x="1072" y="70" fill="#94a3b8" font-size="9">MQTT Broker</text>
<rect x="1050" y="78" width="16" height="12" rx="2" fill="rgba(52, 211, 153, 0.3)" stroke="#34d399" stroke-width="1"/>
<text x="1072" y="88" fill="#94a3b8" font-size="9">Context Broker</text>
<rect x="1050" y="96" width="16" height="12" rx="2" fill="rgba(167, 139, 250, 0.3)" stroke="#a78bfa" stroke-width="1"/>
<text x="1072" y="106" fill="#94a3b8" font-size="9">IoT Platform</text>
<rect x="1050" y="114" width="16" height="12" rx="2" fill="rgba(120, 53, 15, 0.3)" stroke="#fbbf24" stroke-width="1"/>
<text x="1072" y="124" fill="#94a3b8" font-size="9">Stockage / Métriques</text>
<rect x="1050" y="132" width="16" height="12" rx="2" fill="rgba(8, 51, 68, 0.4)" stroke="#22d3ee" stroke-width="1"/>
<text x="1072" y="142" fill="#94a3b8" font-size="9">Visualisation</text>
<line x1="1050" y1="156" x2="1066" y2="156" stroke="#34d399" stroke-width="2" marker-end="url(#arrowhead-emerald)"/>
<text x="1072" y="159" fill="#94a3b8" font-size="9">Flux de données</text>
<line x1="1050" y1="174" x2="1066" y2="174" stroke="#fb923c" stroke-width="2" marker-end="url(#arrowhead-orange)"/>
<text x="1072" y="177" fill="#94a3b8" font-size="9">MQTT</text>
<line x1="1050" y1="190" x2="1066" y2="190" stroke="#a78bfa" stroke-width="2" stroke-dasharray="5,5"/>
<text x="1072" y="193" fill="#94a3b8" font-size="9">REST API</text>
</svg>
</div>
<!-- Info Cards -->
<div class="cards">
<div class="card">
<div class="card-header">
<div class="card-dot orange"></div>
<h3>Sources & Simulator</h3>
</div>
<ul>
<li>• Smart City Simulator (Python)</li>
<li>• 10 capteurs : Traffic, Air, Parking, Noise, Weather, Light</li>
<li>• Intervalle : 10 secondes</li>
<li>• Protocoles : MQTT + REST API</li>
</ul>
</div>
<div class="card">
<div class="card-header">
<div class="card-dot cyan"></div>
<h3>MQTT Brokers</h3>
</div>
<ul>
<li>• EMQX : Port 11883 (public)</li>
<li>• Mosquitto : Port 1883 (Traefik)</li>
<li>• BunkerM : Port 1900 (TLS)</li>
<li>• OpenRemote : Port 1883 (interne)</li>
</ul>
</div>
<div class="card">
<div class="card-header">
<div class="card-dot emerald"></div>
<h3>Context Brokers (NGSI-LD)</h3>
</div>
<ul>
<li>• Orion-LD : 10 entités NGSI-LD</li>
<li>• Stellio : 14 payloads entités</li>
<li>• FROST-Server : 21k+ observations</li>
<li>• Smart Data Models utilisés</li>
</ul>
</div>
<div class="card">
<div class="card-header">
<div class="card-dot violet"></div>
<h3>OpenRemote Platform</h3>
</div>
<ul>
<li>• Realm : Smart City Martinique</li>
<li>• 33 assets IoT configurés</li>
<li>• Keycloak pour l'authentification</li>
<li>• REST API pour les capteurs</li>
</ul>
</div>
<div class="card">
<div class="card-header">
<div class="card-dot amber"></div>
<h3>Stockage & Métriques</h3>
</div>
<ul>
<li>• InfluxDB : Bucket iot_data</li>
<li>• Prometheus : Metrics brokers</li>
<li>• GeoServer : PostGIS + WMS</li>
<li>• PostgreSQL : OpenRemote + FROST</li>
</ul>
</div>
<div class="card">
<div class="card-header">
<div class="card-dot cyan"></div>
<h3>Visualisation & Analyse</h3>
</div>
<ul>
<li>• Grafana : Dashboards (port 3001)</li>
<li>• MapStore : Cartographie WMS</li>
<li>• OpenRemote UI : Manager Smart City</li>
<li>• Datasources : InfluxDB, FROST, Orion</li>
</ul>
</div>
</div>
<!-- Footer -->
<p class="footer">
Smart City Digital Twin Martinique • Stack complet déployé sur digitribe.fr •
Dernière mise à jour : 04 Mai 2026
</p>
</div>
SIM --&gt; EMQ
SIM --&gt; MOS
SIM --&gt; BUN
SENS --&gt; EMQ
SENS --&gt; MOS
SENS --&gt; BUN
SENS -.-&gt; ORM
EMQ --&gt; ORI
EMQ --&gt; STE
EMQ --&gt; FRO
EMQ --&gt; ORM
MOS --&gt; ORM
BUN --&gt; ORM
ORM --&gt; UI
ORM -.-&gt; KC
SIM --&gt; INF
ORI --&gt; GRA
STE --&gt; GRA
FRO --&gt; GRA
ORI -.-&gt; GEO
STE -.-&gt; GEO
FRO -.-&gt; GEO
GEO --&gt; MAP
ORM --&gt; GRA
EMQ -.-&gt; PRO
ORI -.-&gt; PRO
STE -.-&gt; PRO
ORM -.-&gt; PRO</code></pre>
<hr />
<h2 id="description-des-flux">Description des flux</h2>
<h3 id="génération-des-données-simulator">1. <strong>Génération des
données (Simulator)</strong></h3>
<ul>
<li><strong>Smart City Simulator</strong> (Python) génère des données
pour 10 capteurs (Traffic, Air Quality, Parking, Noise, Weather,
Light)</li>
<li>Intervalle de publication : 10 secondes</li>
<li>Protocoles : MQTT (vers brokers uniquement)</li>
<li><strong>⚠️ Projet</strong> : Le simulateur nenvoie PAS directement
à OpenRemote (pas de REST API)</li>
</ul>
<h3 id="ingestion-mqtt-brokers">2. <strong>Ingestion MQTT
(Brokers)</strong></h3>
<ul>
<li><strong>EMQX</strong> (port 11883) : Broker public, reçoit tous les
capteurs</li>
<li><strong>Mosquitto</strong> (port 1883) : Via Traefik, accès
externe</li>
<li><strong>BunkerM</strong> (port 1900) : MQTTS (TLS), accès
sécurisé</li>
</ul>
<h3 id="context-brokers-ngsi-ld-sensorthings">3. <strong>Context Brokers
(NGSI-LD &amp; SensorThings)</strong></h3>
<ul>
<li><strong>Orion-LD</strong> : Reçoit les données au format NGSI-LD
<ul>
<li>10 entités (TrafficFlowObserved, AirQualityObserved, etc.)</li>
<li>Smart Data Models utilisés</li>
</ul></li>
<li><strong>Stellio</strong> : Alternative NGSI-LD
<ul>
<li>14 payloads entités</li>
<li>Contexte :
<code>https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld</code></li>
</ul></li>
<li><strong>FROST-Server</strong> : SensorThings API
<ul>
<li>21 256+ observations</li>
<li>PostgreSQL + TimescaleDB</li>
</ul></li>
</ul>
<h3 id="plateforme-iot-openremote">4. <strong>Plateforme IoT
(OpenRemote)</strong></h3>
<ul>
<li><strong>OpenRemote Manager</strong> (realm <code>smartcity</code>)
<ul>
<li>33 assets IoT configurés</li>
<li>Carte Martinique (mapsettings.json)</li>
<li>Réception via <strong>MQTT Agent</strong> depuis les brokers (EMQX,
Mosquitto, BunkerM)</li>
<li>Peut aussi recevoir directement des capteurs IoT (via MQTT)</li>
</ul></li>
<li><strong>Keycloak</strong> : Authentification OpenID Connect
<ul>
<li>Client <code>openremote</code> avec Service Account</li>
<li>Token endpoint :
<code>/auth/realms/smartcity/protocol/openid-connect/token</code></li>
</ul></li>
</ul>
<h3 id="stockage-métriques">5. <strong>Stockage &amp;
Métriques</strong></h3>
<ul>
<li><strong>InfluxDB</strong> : Stockage temporel pour Grafana
<ul>
<li>Bucket : <code>iot_data</code></li>
<li>Datasource dans Grafana</li>
</ul></li>
<li><strong>Prometheus</strong> : Collecte des métriques
<ul>
<li>MQTT brokers, Context brokers, OpenRemote</li>
</ul></li>
<li><strong>GeoServer</strong> : Données géospatiales
<ul>
<li>PostGIS pour centralisation</li>
<li>WMS/WFS pour MapStore</li>
</ul></li>
</ul>
<h3 id="visualisation-analyse">6. <strong>Visualisation &amp;
Analyse</strong></h3>
<ul>
<li><strong>Grafana</strong> (port 3001)
<ul>
<li>Dashboard : <code>smartcity-martinique-2026</code></li>
<li>Datasources : InfluxDB, FROST, Orion-LD</li>
</ul></li>
<li><strong>MapStore</strong> : Cartographie
<ul>
<li>Sources WMS/WFS depuis GeoServer</li>
</ul></li>
<li><strong>OpenRemote UI</strong> : Manager Interface
<ul>
<li>Visualisation des assets realm Smart City</li>
</ul></li>
</ul>
<hr />
<h2 id="technologies-clés">Technologies clés</h2>
<table>
<thead>
<tr>
<th>Composant</th>
<th>Technologie</th>
<th>Port</th>
<th>Statut</th>
</tr>
</thead>
<tbody>
<tr>
<td>Simulator</td>
<td>Python + paho-mqtt</td>
<td>Interne</td>
<td>✅ Actif</td>
</tr>
<tr>
<td>EMQX</td>
<td>MQTT Broker</td>
<td>11883</td>
<td>✅ Connecté</td>
</tr>
<tr>
<td>Orion-LD</td>
<td>NGSI-LD Broker</td>
<td>1026</td>
<td>⚠️ À vérifier</td>
</tr>
<tr>
<td>Stellio</td>
<td>NGSI-LD Broker</td>
<td>8080</td>
<td>⚠️ À vérifier</td>
</tr>
<tr>
<td>FROST-Server</td>
<td>SensorThings API</td>
<td>8080</td>
<td>⚠️ À vérifier</td>
</tr>
<tr>
<td>OpenRemote</td>
<td>IoT Platform</td>
<td>8080</td>
<td>⚠️ 403 (Service Account)</td>
</tr>
<tr>
<td>InfluxDB</td>
<td>Time Series DB</td>
<td>8086</td>
<td>✅ Configuré</td>
</tr>
<tr>
<td>Grafana</td>
<td>Visualization</td>
<td>3001</td>
<td>✅ Dashboard créé</td>
</tr>
<tr>
<td>GeoServer</td>
<td>GeoServer</td>
<td>8080</td>
<td>⚠️ À intégrer</td>
</tr>
<tr>
<td>Prometheus</td>
<td>Metrics</td>
<td>9090</td>
<td>✅ En cours</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="fichiers-associés">Fichiers associés</h2>
<ul>
<li><strong>Simulator</strong> :
<code>~/smart-city-digital-twin-martinique/simulator.py</code></li>
<li><strong>Dashboard Grafana</strong> :
<code>~/smart-city-digital-twin-martinique/grafana_dashboard_smartcity.json</code></li>
<li><strong>Ce diagramme</strong> :
<code>~/smart-city-digital-twin-martinique/data-flow-diagram.md</code></li>
<li><strong>Session Resume</strong> :
<code>~/smart-city-digital-twin-martinique/session_resume_2026-05-04.md</code></li>
</ul>
<hr />
<p><strong>Dernière mise à jour :</strong> 04 Mai 2026<br />
<strong>Projet :</strong> Smart City Digital Twin Martinique<br />
<strong>URL Grafana :</strong>
http://localhost:3001/d/smartcity-martinique-2026</p>
</body>
</html>