feat(simulator): real-time (1s), fix ENABLE_PULSAR, add Pulsar/Redpanda publish, fix InfluxDB URL
- Change INTERVAL to 1s for real-time sensor data - Fix ENABLE_PULSAR comparison (accept 'true'/'false' strings) - Add publish_pulsar() and publish_redpanda() functions - Fix InfluxDB URL (smart-city-influxdb instead of digital-twin-influxdb) - Add docker-compose.yml with simulator service - Add redpanda config and start script - Add session_resume_2026-05-05.md
This commit is contained in:
57
docker-compose.yml
Normal file
57
docker-compose.yml
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# Smart City Digital Twin Martinique — Main Docker Compose
|
||||||
|
# Usage: docker compose -p smart-city up -d
|
||||||
|
# This file defines the simulator and includes other services
|
||||||
|
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
networks:
|
||||||
|
smartcity-shared:
|
||||||
|
external: true
|
||||||
|
traefik-public:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
services:
|
||||||
|
# Smart City Simulator
|
||||||
|
simulator:
|
||||||
|
build: .
|
||||||
|
container_name: smart-city-simulator
|
||||||
|
networks:
|
||||||
|
- smartcity-shared
|
||||||
|
- traefik-public
|
||||||
|
environment:
|
||||||
|
# MQTT Brokers
|
||||||
|
- ENABLE_EMQX=true
|
||||||
|
- ENABLE_MOSQUITTO=true
|
||||||
|
- ENABLE_BUNKER=true
|
||||||
|
# Context Brokers
|
||||||
|
- ENABLE_ORION=true
|
||||||
|
- ENABLE_STELLIO=true
|
||||||
|
- ENABLE_FROST=true
|
||||||
|
# Databases
|
||||||
|
- ENABLE_INFLUX=true
|
||||||
|
- INFLUX_URL=http://smart-city-influxdb:8086
|
||||||
|
# Pulsar
|
||||||
|
- ENABLE_PULSAR=true
|
||||||
|
- PULSAR_HOST=smart-city-pulsar
|
||||||
|
- PULSAR_PORT=8080
|
||||||
|
# Redpanda (Kafka)
|
||||||
|
- ENABLE_REDPANDA=false # Disabled - troubleshooting
|
||||||
|
- REDPANDA_BROKERS=smart-city-redpanda:9092
|
||||||
|
# Simulation settings
|
||||||
|
- INTERVAL=30
|
||||||
|
- LOG_LEVEL=INFO
|
||||||
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=false"
|
||||||
|
|
||||||
|
# InfluxDB (defined in docker-compose.influxdb.yml)
|
||||||
|
# Run with: docker compose -f docker-compose.yml -f docker-compose.influxdb.yml up -d
|
||||||
|
|
||||||
|
# Grafana (defined in docker-compose.grafana.yml)
|
||||||
|
# Run with: docker compose -f docker-compose.yml -f docker-compose.grafana.yml up -d
|
||||||
|
|
||||||
|
# Pulsar (defined in pulsar/docker-compose.yml)
|
||||||
|
# Run with: docker compose -f docker-compose.yml -f pulsar/docker-compose.yml up -d
|
||||||
|
|
||||||
|
# Redpanda (defined in redpanda/docker-compose.yml)
|
||||||
|
# Run with: docker compose -f docker-compose.yml -f redpanda/docker-compose.yml up -d
|
||||||
42
redpanda/docker-compose.yml
Normal file
42
redpanda/docker-compose.yml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Redpanda (Kafka-compatible) — Single Node for Smart City Digital Twin Martinique
|
||||||
|
# Usage: docker compose -p smart-city -f redpanda/docker-compose.yml up -d
|
||||||
|
# Ports: 19092=Kafka (host), 9644=Admin API
|
||||||
|
services:
|
||||||
|
redpanda:
|
||||||
|
image: redpandadata/redpanda:v24.3.14
|
||||||
|
container_name: smart-city-redpanda
|
||||||
|
entrypoint: ["/bin/bash", "/start.sh"]
|
||||||
|
volumes:
|
||||||
|
- redpanda-data:/var/lib/redpanda/data
|
||||||
|
- ./start.sh:/start.sh:ro
|
||||||
|
ports:
|
||||||
|
- "19092:9092"
|
||||||
|
- "19644:9644"
|
||||||
|
networks:
|
||||||
|
- traefik-public
|
||||||
|
- smartcity-shared
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 2G
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "curl -sf http://localhost:9644/v1/status/ready 2>/dev/null || exit 1"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 10
|
||||||
|
start_period: 60s
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.redpanda.rule=Host(`redpanda.digitribe.fr`)"
|
||||||
|
- "traefik.http.routers.redpanda.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.redpanda.tls=true"
|
||||||
|
- "traefik.http.services.redpanda.loadbalancer.server.port=9644"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik-public:
|
||||||
|
external: true
|
||||||
|
smartcity-shared:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
redpanda-data:
|
||||||
29
redpanda/redpanda.yaml
Normal file
29
redpanda/redpanda.yaml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Redpanda configuration for Smart City Digital Twin Martinique
|
||||||
|
# Minimal working config - Kafka + Admin API only
|
||||||
|
|
||||||
|
redpanda:
|
||||||
|
node_id: 0
|
||||||
|
data_directory: /var/lib/redpanda/data
|
||||||
|
|
||||||
|
kafka_api:
|
||||||
|
- name: internal
|
||||||
|
address: 0.0.0.0
|
||||||
|
port: 9092
|
||||||
|
|
||||||
|
advertised_kafka_api:
|
||||||
|
- name: internal
|
||||||
|
address: smart-city-redpanda
|
||||||
|
port: 9092
|
||||||
|
|
||||||
|
admin:
|
||||||
|
- address: 0.0.0.0
|
||||||
|
port: 9644
|
||||||
|
|
||||||
|
# Seastar settings
|
||||||
|
seastar:
|
||||||
|
smp: 1
|
||||||
|
memory: 1G
|
||||||
|
reserve_memory: 256M
|
||||||
|
overprovisioned: true
|
||||||
|
|
||||||
|
developer_mode: true
|
||||||
12
redpanda/start.sh
Executable file
12
redpanda/start.sh
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Start Redpanda with minimal dev config
|
||||||
|
exec /usr/bin/rpk redpanda start \
|
||||||
|
--mode dev \
|
||||||
|
--smp 1 \
|
||||||
|
--memory 1G \
|
||||||
|
--overprovisioned \
|
||||||
|
--kafka-addr 0.0.0.0:9092 \
|
||||||
|
--advertise-kafka-addr smart-city-redpanda:9092 \
|
||||||
|
--rpc-addr 0.0.0.0:33145 \
|
||||||
|
--advertise-rpc-addr smart-city-redpanda:33145 \
|
||||||
|
--check=false
|
||||||
53
session_resume_2026-05-05.md
Normal file
53
session_resume_2026-05-05.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# Session Resume — 05 Mai 2026
|
||||||
|
|
||||||
|
## ✅ Réalisé dans cette session (reprise après crash)
|
||||||
|
|
||||||
|
### 1. Diagnostic des dashboards Grafana cassés
|
||||||
|
- **Problème** : Erreurs `"Dashboard title cannot be empty"` pour 2 fichiers dans les logs de `digital-twin-grafana`
|
||||||
|
- **Cause racine** : Les fichiers JSON de provisioning avaient un objet `dashboard` imbriqué au lieu de `title` à la racine — Grafana file provider exige `title` au niveau root
|
||||||
|
- **Fichiers affectés** :
|
||||||
|
- `smart-city-overview.json` : title=MISSING, panels=0 (❌)
|
||||||
|
- `twin-overview.json` : title=MISSING, panels=0 (❌)
|
||||||
|
|
||||||
|
### 2. Correction des JSON (flattening)
|
||||||
|
- Script Python `/tmp/fix_grafana_dashboards.py` → extraction de `dashboard` vers le niveau root
|
||||||
|
- Résultat après fix :
|
||||||
|
- `twin-overview.json` : title="TWIN Supply Chain - Overview", 3 panels ✅
|
||||||
|
- `smart-city-overview.json` : title="Smart City Digital Twin - Overview", 8 panels ✅
|
||||||
|
- Copie dans le container : `docker cp /tmp/... digital-twin-grafana:/etc/grafana/provisioning/dashboards/`
|
||||||
|
- Redémarrage : `docker restart digital-twin-grafana`
|
||||||
|
|
||||||
|
### 3. Vérification
|
||||||
|
- ✅ Erreurs "Dashboard title cannot be empty" disparues des logs
|
||||||
|
- ✅ InfluxDB `iot_data` contient des données en temps réel (air quality, traffic, weather, parking, noise, light)
|
||||||
|
- ✅ Simulateur actif (6h+ uptime), push vers EMQX + InfluxDB
|
||||||
|
|
||||||
|
### 4. Commit Gitea
|
||||||
|
- `83d567b` — "Grafana: Fix dashboard provisioning (flatten nested dashboard objects)"
|
||||||
|
- 2 fichiers ajoutés au repo : `grafana_twin-overview.json`, `grafana_smart-city-overview.json`
|
||||||
|
|
||||||
|
## 📊 État actuel des services
|
||||||
|
|
||||||
|
| Service | Status | Notes |
|
||||||
|
|---------|--------|-------|
|
||||||
|
| Simulateur Python | ✅ Actif (6h+) | MQTT (EMQX) + InfluxDB |
|
||||||
|
| EMQX | ✅ | Port 11883 |
|
||||||
|
| InfluxDB (iot_data) | ✅ | Données en temps réel Martinique |
|
||||||
|
| FROST-Server | ✅ | Container frost-api-8090 |
|
||||||
|
| Orion-LD | ✅ | source/mqttTopic traceability |
|
||||||
|
| Stellio | ✅ | NGSI-LD tenant default |
|
||||||
|
| OpenRemote | ⚠️ OR:False | Simulateur échoue auth (localhost:8080) |
|
||||||
|
| Grafana | ✅ Corrigé | Dashboards chargés, 5 dashboards |
|
||||||
|
|
||||||
|
## ⏳ Reste à faire
|
||||||
|
|
||||||
|
1. **OpenRemote** — Corriger l'authentification du simulateur (OR: False)
|
||||||
|
2. **Grafana** — Affiner les panels (granularité, datasource queries)
|
||||||
|
3. **Carte OpenRemote / Cesium / Piero** — Configuration finale
|
||||||
|
|
||||||
|
## 🔗 URLs
|
||||||
|
- **Grafana** : https://grafana.digitribe.fr (admin / Digitribe972)
|
||||||
|
- **Gitea** : https://gitea.digitribe.fr/eric/smart-city-digital-twin-martinique
|
||||||
|
|
||||||
|
---
|
||||||
|
*Session reprise après crash du 05 mai 2026 à 00:25*
|
||||||
158
simulator.py
158
simulator.py
@@ -15,12 +15,22 @@ Context Brokers REST:
|
|||||||
- Stellio: stellio-api-gateway:8080 (NGSI-LD)
|
- Stellio: stellio-api-gateway:8080 (NGSI-LD)
|
||||||
- FROST: frost_allinone-web-1:8080/FROST-Server/v1.1 (SensorThings)
|
- FROST: frost_allinone-web-1:8080/FROST-Server/v1.1 (SensorThings)
|
||||||
|
|
||||||
|
Streaming Platforms:
|
||||||
|
- Pulsar: smart-city-pulsar:8080 (HTTP REST Producer API)
|
||||||
|
- Redpanda: smart-city-redpanda:8082 (Kafka REST Proxy)
|
||||||
|
|
||||||
|
Time-Series DB:
|
||||||
|
- InfluxDB v2: smart-city-influxdb:8086 (via influxdb-client)
|
||||||
|
|
||||||
Variables d'environnement:
|
Variables d'environnement:
|
||||||
PUBLISH_INTERVAL_SEC : intervalle de publication (défaut: 10s)
|
PUBLISH_INTERVAL_SEC : intervalle de publication (défaut: 10s)
|
||||||
BASE_LAT / BASE_LON : coordonnées de base (défaut: Fort-de-France)
|
BASE_LAT / BASE_LON : coordonnées de base (défaut: Fort-de-France)
|
||||||
ENABLE_ORION=1 : activer Orion-LD (défaut: 1)
|
ENABLE_ORION=1 : activer Orion-LD (défaut: 1)
|
||||||
ENABLE_STELLIO=1 : activer Stellio (défaut: 1)
|
ENABLE_STELLIO=1 : activer Stellio (défaut: 1)
|
||||||
ENABLE_FROST=1 : activer FROST-Server (défaut: 1)
|
ENABLE_FROST=1 : activer FROST-Server (défaut: 1)
|
||||||
|
ENABLE_INFLUX=1 : activer InfluxDB v2 (défaut: 1)
|
||||||
|
ENABLE_PULSAR=1 : activer Apache Pulsar (défaut: 1)
|
||||||
|
ENABLE_REDPANDA=1 : activer Redpanda Kafka (défaut: 1)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os, sys, json, time, random, signal, queue, threading, ssl, urllib.parse
|
import os, sys, json, time, random, signal, queue, threading, ssl, urllib.parse
|
||||||
@@ -49,7 +59,7 @@ BUNKERM_PORT = int(os.environ.get("BUNKERM_PORT", "1900"))
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
BASE_LAT = float(os.environ.get("BASE_LAT", "14.6091"))
|
BASE_LAT = float(os.environ.get("BASE_LAT", "14.6091"))
|
||||||
BASE_LON = float(os.environ.get("BASE_LON", "-61.2155"))
|
BASE_LON = float(os.environ.get("BASE_LON", "-61.2155"))
|
||||||
INTERVAL = int(os.environ.get("PUBLISH_INTERVAL_SEC", "10"))
|
INTERVAL = int(os.environ.get("PUBLISH_INTERVAL_SEC", "1")) # 1s pour temps réel
|
||||||
ENABLE_ORION = os.environ.get("ENABLE_ORION", "1") == "1"
|
ENABLE_ORION = os.environ.get("ENABLE_ORION", "1") == "1"
|
||||||
ENABLE_STELLIO = os.environ.get("ENABLE_STELLIO", "1") == "1"
|
ENABLE_STELLIO = os.environ.get("ENABLE_STELLIO", "1") == "1"
|
||||||
ENABLE_FROST = os.environ.get("ENABLE_FROST", "1") == "1"
|
ENABLE_FROST = os.environ.get("ENABLE_FROST", "1") == "1"
|
||||||
@@ -60,9 +70,21 @@ OR_REALM = os.environ.get("OR_REALM", "smartcity")
|
|||||||
OR_TOKEN_REALM = os.environ.get("OR_TOKEN_REALM", "master") # Realm pour obtention token
|
OR_TOKEN_REALM = os.environ.get("OR_TOKEN_REALM", "master") # Realm pour obtention token
|
||||||
FROST_URL = os.environ.get("FROST_URL", "http://localhost:8090/FROST-Server/v1.1") # Exposer frost_http-web-1:8080 -> host:8086
|
FROST_URL = os.environ.get("FROST_URL", "http://localhost:8090/FROST-Server/v1.1") # Exposer frost_http-web-1:8080 -> host:8086
|
||||||
|
|
||||||
|
# Pulsar config (HTTP REST — pulsar-admin + producer REST API)
|
||||||
|
ENABLE_PULSAR = os.environ.get("ENABLE_PULSAR", "1").lower() in ("1", "true", "yes", "on")
|
||||||
|
PULSAR_HOST = os.environ.get("PULSAR_HOST", "smart-city-pulsar")
|
||||||
|
PULSAR_PORT = int(os.environ.get("PULSAR_PORT", "8080"))
|
||||||
|
PULSAR_BASE = f"http://{PULSAR_HOST}:{PULSAR_PORT}"
|
||||||
|
|
||||||
|
# Redpanda / Kafka config (REST Proxy HTTP)
|
||||||
|
ENABLE_REDPANDA = os.environ.get("ENABLE_REDPANDA", "1") == "1"
|
||||||
|
REDPANDA_HOST = os.environ.get("REDPANDA_HOST", "smart-city-redpanda")
|
||||||
|
REDPANDA_PORT = int(os.environ.get("REDPANDA_PORT", "8082"))
|
||||||
|
REDPANDA_BASE = f"http://{REDPANDA_HOST}:{REDPANDA_PORT}"
|
||||||
|
|
||||||
# InfluxDB config
|
# InfluxDB config
|
||||||
ENABLE_INFLUX = os.environ.get("ENABLE_INFLUX", "1") == "1"
|
ENABLE_INFLUX = os.environ.get("ENABLE_INFLUX", "1") == "1"
|
||||||
INFLUX_URL = os.environ.get("INFLUX_URL", "http://localhost:8086") # InfluxDB exposé sur host:8086
|
INFLUX_URL = os.environ.get("INFLUX_URL", "http://smart-city-influxdb:8086") # InfluxDB v2 sur smartcity-shared
|
||||||
INFLUX_ORG = os.environ.get("INFLUX_ORG", "digitribe")
|
INFLUX_ORG = os.environ.get("INFLUX_ORG", "digitribe")
|
||||||
INFLUX_BUCKET = os.environ.get("INFLUX_BUCKET", "iot_data")
|
INFLUX_BUCKET = os.environ.get("INFLUX_BUCKET", "iot_data")
|
||||||
INFLUX_TOKEN = os.environ.get("INFLUX_TOKEN", "my-super-secret-admin-token")
|
INFLUX_TOKEN = os.environ.get("INFLUX_TOKEN", "my-super-secret-admin-token")
|
||||||
@@ -766,6 +788,115 @@ def publish_openremote(sid: str, sensor: dict, values: dict) -> bool:
|
|||||||
}
|
}
|
||||||
return _or_put(asset_id, payload)
|
return _or_put(asset_id, payload)
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Pulsar — HTTP REST Producer
|
||||||
|
# API: POST http://host:8080/admin/v2/persistent/public/default/{topic}/produce
|
||||||
|
# Payload: {"messages": [{"payload": "<base64>", "properties": {...}}]}
|
||||||
|
# Topics auto-créés par le premier message (Pulsar standalone)
|
||||||
|
# =============================================================================
|
||||||
|
_pulsar_session = None
|
||||||
|
|
||||||
|
def _get_pulsar_session():
|
||||||
|
global _pulsar_session
|
||||||
|
if _pulsar_session is None:
|
||||||
|
import urllib.request
|
||||||
|
_pulsar_session = urllib.request
|
||||||
|
return _pulsar_session
|
||||||
|
|
||||||
|
def _init_pulsar() -> bool:
|
||||||
|
"""Teste la connectivité Pulsar au démarrage."""
|
||||||
|
try:
|
||||||
|
import urllib.request
|
||||||
|
req = urllib.request.Request(f"{PULSAR_BASE}/admin/v2/clusters")
|
||||||
|
with urllib.request.urlopen(req, timeout=5) as resp:
|
||||||
|
if resp.status == 200:
|
||||||
|
print(f"[PULSAR] ✅ Connected to {PULSAR_BASE}")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[PULSAR] ⚠️ Cannot reach {PULSAR_BASE}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def publish_pulsar(sid: str, sensor: dict, payload: dict) -> bool:
|
||||||
|
"""Publie un message sur Pulsar via l'API REST producer."""
|
||||||
|
stype = sensor["type"]
|
||||||
|
topic = stype # air-quality, traffic, weather, parking, noise, light
|
||||||
|
try:
|
||||||
|
import urllib.request, base64
|
||||||
|
# Pulsar REST producer attend du base64
|
||||||
|
body = json.dumps(payload, ensure_ascii=False)
|
||||||
|
b64 = base64.b64encode(body.encode()).decode()
|
||||||
|
msg = {"messages": [{"payload": b64, "properties": {"sensor_id": sid, "source": "simulator"}}]}
|
||||||
|
url = f"{PULSAR_BASE}/admin/v2/persistent/public/default/{topic}/produce"
|
||||||
|
req = urllib.request.Request(
|
||||||
|
url,
|
||||||
|
data=json.dumps(msg).encode(),
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
method="POST"
|
||||||
|
)
|
||||||
|
with urllib.request.urlopen(req, timeout=8) as resp:
|
||||||
|
return resp.status in (200, 204)
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
print(f" ⚠️ Pulsar → {e.code}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ⚠️ Pulsar → {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Redpanda / Kafka — HTTP REST Proxy
|
||||||
|
# API: POST http://host:8082/topics/{topic}
|
||||||
|
# Payload: {"records": [{"value": "<base64>"}]}
|
||||||
|
# Topics auto-créés par le premier message (Redpanda)
|
||||||
|
# =============================================================================
|
||||||
|
_redpanda_session = None
|
||||||
|
|
||||||
|
def _get_redpanda_session():
|
||||||
|
global _redpanda_session
|
||||||
|
if _redpanda_session is None:
|
||||||
|
import urllib.request
|
||||||
|
_redpanda_session = urllib.request
|
||||||
|
return _redpanda_session
|
||||||
|
|
||||||
|
def _init_redpanda() -> bool:
|
||||||
|
"""Teste la connectivité Redpanda au démarrage."""
|
||||||
|
try:
|
||||||
|
import urllib.request
|
||||||
|
req = urllib.request.Request(f"{REDPANDA_BASE}/v1/status/alive")
|
||||||
|
with urllib.request.urlopen(req, timeout=5) as resp:
|
||||||
|
if resp.status == 200:
|
||||||
|
print(f"[REDPANDA] ✅ Connected to {REDPANDA_BASE}")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[REDPANDA] ⚠️ Cannot reach {REDPANDA_BASE}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def publish_redpanda(sid: str, sensor: dict, payload: dict) -> bool:
|
||||||
|
"""Publie un message sur Redpanda/Kafka via le REST Proxy."""
|
||||||
|
stype = sensor["type"]
|
||||||
|
topic = stype # air-quality, traffic, weather, parking, noise, light
|
||||||
|
try:
|
||||||
|
import urllib.request, base64
|
||||||
|
body = json.dumps(payload, ensure_ascii=False)
|
||||||
|
b64 = base64.b64encode(body.encode()).decode()
|
||||||
|
record = {
|
||||||
|
"records": [{"value": b64, "headers": {"sensor_id": sid, "source": "simulator"}}]
|
||||||
|
}
|
||||||
|
url = f"{REDPANDA_BASE}/topics/{topic}"
|
||||||
|
req = urllib.request.Request(
|
||||||
|
url,
|
||||||
|
data=json.dumps(record).encode(),
|
||||||
|
headers={"Content-Type": "application/vnd.api+json"},
|
||||||
|
method="POST"
|
||||||
|
)
|
||||||
|
with urllib.request.urlopen(req, timeout=8) as resp:
|
||||||
|
return resp.status in (200, 201, 204)
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
print(f" ⚠️ Redpanda → {e.code}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ⚠️ Redpanda → {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
def publish_influx(sid: str, sensor: dict, values: dict) -> bool:
|
def publish_influx(sid: str, sensor: dict, values: dict) -> bool:
|
||||||
"""Write sensor data to InfluxDB (async, non-blocking)."""
|
"""Write sensor data to InfluxDB (async, non-blocking)."""
|
||||||
if not _influx_write_api:
|
if not _influx_write_api:
|
||||||
@@ -805,6 +936,18 @@ def main():
|
|||||||
print("╚══════════════════════════════════════════════════╝")
|
print("╚══════════════════════════════════════════════════╝")
|
||||||
print(f"[CFG] Capteurs: {len(SENSORS)} | Intervalle: {INTERVAL}s")
|
print(f"[CFG] Capteurs: {len(SENSORS)} | Intervalle: {INTERVAL}s")
|
||||||
print(f"[CFG] Orion-LD: {ENABLE_ORION} | Stellio: {ENABLE_STELLIO} | FROST: {ENABLE_FROST}")
|
print(f"[CFG] Orion-LD: {ENABLE_ORION} | Stellio: {ENABLE_STELLIO} | FROST: {ENABLE_FROST}")
|
||||||
|
print(f"[CFG] InfluxDB: {ENABLE_INFLUX} | Pulsar: {ENABLE_PULSAR} | Redpanda: {ENABLE_REDPANDA}")
|
||||||
|
|
||||||
|
# Init connectivity checks
|
||||||
|
if ENABLE_PULSAR:
|
||||||
|
_init_pulsar()
|
||||||
|
# Test immédiat
|
||||||
|
print(f" 🌪️ DEBUG: Test Pulsar direct...", flush=True)
|
||||||
|
test_payload = {"type": "test", "value": 123}
|
||||||
|
test_result = publish_pulsar("test_001", {"type": "air-quality"}, test_payload)
|
||||||
|
print(f" 🌪️ DEBUG: Test Pulsar result: {test_result}", flush=True)
|
||||||
|
if ENABLE_REDPANDA:
|
||||||
|
_init_redpanda()
|
||||||
|
|
||||||
mqtt_client = MultiMQTT()
|
mqtt_client = MultiMQTT()
|
||||||
|
|
||||||
@@ -898,6 +1041,17 @@ def main():
|
|||||||
ok_influx = publish_influx(sid, sensor, influx_vals)
|
ok_influx = publish_influx(sid, sensor, influx_vals)
|
||||||
print(f" 📈 InfluxDB: {'✅' if ok_influx else '❌'}")
|
print(f" 📈 InfluxDB: {'✅' if ok_influx else '❌'}")
|
||||||
|
|
||||||
|
# --- Pulsar (HTTP REST) ---
|
||||||
|
if ENABLE_PULSAR:
|
||||||
|
print(f" 🌪️ DEBUG: calling publish_pulsar for {sid}, payload_mqtt exists: {bool(locals().get('payload_mqtt'))}", flush=True)
|
||||||
|
ok_pulsar = publish_pulsar(sid, sensor, payload_mqtt)
|
||||||
|
print(f" 🌪️ Pulsar: {'✅' if ok_pulsar else '❌'}")
|
||||||
|
|
||||||
|
# --- Redpanda (Kafka REST Proxy) ---
|
||||||
|
if ENABLE_REDPANDA:
|
||||||
|
ok_redpanda = publish_redpanda(sid, sensor, payload_mqtt)
|
||||||
|
print(f" 🐟 Redpanda: {'✅' if ok_redpanda else '❌'}")
|
||||||
|
|
||||||
# --- BunkerM HTTP ---
|
# --- BunkerM HTTP ---
|
||||||
if os.getenv("BUNKERM_HTTP", "0") == "1":
|
if os.getenv("BUNKERM_HTTP", "0") == "1":
|
||||||
ok_bunkerm = publish_bunkerm(sid, sensor, payload_mqtt)
|
ok_bunkerm = publish_bunkerm(sid, sensor, payload_mqtt)
|
||||||
|
|||||||
Reference in New Issue
Block a user