Este sistema permite generar datos simulados de sensores de invernadero cuando los sensores físicos no están disponibles (por ejemplo, debido a condiciones climáticas adversas como lluvia).
Los datos simulados:
- ✅ Tienen valores realistas (temperaturas, humedades, etc.)
- ✅ Pasan por el mismo flujo que datos reales
- ✅ Se guardan en Redis (cache) y TimescaleDB (persistencia)
- ✅ Se publican vía WebSocket a clientes móviles (iOS, Android, Web, Desktop)
- ✅ Se generan automáticamente cada 5 segundos
# Linux/Mac
export GREENHOUSE_SIMULATION_ENABLED=true
# Windows PowerShell
$env:GREENHOUSE_SIMULATION_ENABLED="true"
# Windows CMD
set GREENHOUSE_SIMULATION_ENABLED=trueLuego iniciar la aplicación:
./gradlew bootRunEditar src/main/resources/application.yaml:
greenhouse:
simulation:
enabled: true # Cambiar de false a true
interval-ms: 5000
greenhouse-id: "001"Agregar al docker-compose.yaml:
services:
api:
environment:
- GREENHOUSE_SIMULATION_ENABLED=true- Invernadero 01: 20-28°C (base: 24°C ± 4°C)
- Invernadero 02: 18-26°C (base: 22°C ± 4°C)
- Invernadero 03: 21-29°C (base: 25°C ± 4°C)
Distribución: Gaussiana (más valores cerca de la media, más realista)
- Invernadero 01: 50-80% (base: 65% ± 15%)
- Invernadero 02: 45-75% (base: 60% ± 15%)
- Invernadero 03: 55-85% (base: 70% ± 15%)
Distribución: Gaussiana con clamping en 30-90%
- Rango: 0-100%
- Comportamiento:
- 70% de probabilidad: valores moderados (30-70%)
- 30% de probabilidad: valores extremos (0% o 100%)
- Valores: 0.0 (apagado) o 1.0 (encendido)
- Probabilidad: 30% encendido, 70% apagado
- Rango: 0-100 (valor aleatorio uniforme)
Cuando inicies con simulación activada, verás:
╔════════════════════════════════════════════════════════════╗
║ SIMULACIÓN DE DATOS DE INVERNADERO ACTIVADA ║
║ ║
║ Los datos mostrados son SIMULADOS, no provienen de ║
║ sensores reales. Esto es debido a que los sensores ║
║ físicos están fuera de servicio. ║
║ ║
║ Para desactivar: greenhouse.simulation.enabled=false ║
╚════════════════════════════════════════════════════════════╝
Y cada 5 segundos:
========================================
INICIANDO SIMULACIÓN DE DATOS DE INVERNADERO
Generando datos cada 5 segundos
Greenhouse ID: 001
========================================
Datos simulados procesados exitosamente - Temp01: 23.45°C, Hum01: 67.23%
Tu aplicación Kotlin Multiplatform recibirá datos en el topic STOMP:
/topic/greenhouse/messages
Estructura recibida:
{
"timestamp": "2025-01-14T12:34:56.789Z",
"TEMPERATURA INVERNADERO 01": 23.45,
"HUMEDAD INVERNADERO 01": 67.23,
"TEMPERATURA INVERNADERO 02": 21.87,
"HUMEDAD INVERNADERO 02": 59.12,
"TEMPERATURA INVERNADERO 03": 24.56,
"HUMEDAD INVERNADERO 03": 71.34,
"INVERNADERO_01_SECTOR_01": 45.67,
"INVERNADERO_01_SECTOR_02": 52.34,
"INVERNADERO_01_SECTOR_03": 38.91,
"INVERNADERO_01_SECTOR_04": 61.23,
"INVERNADERO_02_SECTOR_01": 0.0,
"INVERNADERO_02_SECTOR_02": 100.0,
"INVERNADERO_02_SECTOR_03": 47.89,
"INVERNADERO_02_SECTOR_04": 53.45,
"INVERNADERO_03_SECTOR_01": 42.11,
"INVERNADERO_03_SECTOR_02": 58.76,
"INVERNADERO_03_SECTOR_03": 0.0,
"INVERNADERO_03_SECTOR_04": 55.32,
"INVERNADERO_01_EXTRACTOR": 1.0,
"INVERNADERO_02_EXTRACTOR": 0.0,
"INVERNADERO_03_EXTRACTOR": 0.0,
"RESERVA": 73.21,
"greenhouseId": "001"
}curl http://localhost:8080/api/simulation/statusRespuesta:
{
"enabled": true,
"schedulerActive": true,
"intervalMs": 5000,
"greenhouseId": "001",
"message": "Simulación activa - Generando datos cada 5 segundos"
}curl -X POST http://localhost:8080/api/simulation/generate?greenhouseId=001curl http://localhost:8080/api/simulation/preview?greenhouseId=001curl http://localhost:8080/api/simulation/infoAccede a: http://localhost:8080/swagger-ui.html
Busca la sección "Simulation" para probar los endpoints interactivamente.
Redis Cache:
redis-cli -h 138.199.157.58 -p 30379 -a AppToLast2023%
ZRANGE greenhouse:messages 0 -1
ZREVRANGE greenhouse:messages 0 9 WITHSCORESTimescaleDB:
-- Conectar a TimescaleDB
psql -h 138.199.157.58 -p 30432 -U admin -d greenhouse_timeseries
-- Ver últimas lecturas
SELECT time, sensor_id, greenhouse_id, value, unit
FROM sensor_reading
WHERE greenhouse_id = '001'
ORDER BY time DESC
LIMIT 50;
-- Contar lecturas por hora
SELECT time_bucket('1 hour', time) as hour,
COUNT(*) as readings
FROM sensor_reading
WHERE greenhouse_id = '001'
GROUP BY hour
ORDER BY hour DESC;- Activar simulación:
GREENHOUSE_SIMULATION_ENABLED=true - Iniciar aplicación
- La app móvil recibirá datos simulados cada 5 segundos
- Los datos se verán como datos reales en toda la infraestructura
# Terminal 1: Iniciar API con simulación
GREENHOUSE_SIMULATION_ENABLED=true ./gradlew bootRun
# Terminal 2: Generar datos adicionales bajo demanda
curl -X POST http://localhost:8080/api/simulation/generate- Activar simulación antes de la demo
- Los datos fluyen automáticamente cada 5 segundos
- El cliente ve el sistema funcionando aunque no haya sensores reales
# Solo preview (no guarda en BD)
curl http://localhost:8080/api/simulation/preview
# Ver qué datos se generarían sin contaminar la base de datosEditar application.yaml:
greenhouse:
simulation:
interval-ms: 3000 # Generar cada 3 segundosgreenhouse:
simulation:
greenhouse-id: "002" # Simular otro invernaderoEditar GreenhouseDataSimulator.kt:
// Cambiar temperatura base del invernadero 01
temperaturaInvernadero01 = generateTemperature(baseTemp = 26.0, variation = 3.0)
// Cambiar rango de humedad
humedadInvernadero01 = generateHumidity(baseHumidity = 70.0, variation = 10.0)export GREENHOUSE_SIMULATION_ENABLED=falsegreenhouse:
simulation:
enabled: falseunset GREENHOUSE_SIMULATION_ENABLED
# El valor por defecto es 'false'src/main/kotlin/com/apptolast/invernaderos/
├── service/
│ └── GreenhouseDataSimulator.kt # Generador de datos realistas
├── scheduler/
│ └── GreenhouseSimulationScheduler.kt # Scheduler automático (cada 5s)
├── controllers/
│ └── SimulationController.kt # API REST para control manual
└── InvernaderosApplication.kt # Actualizado con @EnableScheduling
src/main/resources/
└── application.yaml # Configuración de simulación añadida
Verificar:
- ✅
greenhouse.simulation.enabled=trueen application.yaml - ✅ O
GREENHOUSE_SIMULATION_ENABLED=truecomo variable de entorno - ✅
@EnableSchedulingenInvernaderosApplication.kt - ✅ Reiniciar la aplicación después de cambios
Ver logs:
# Debe aparecer el banner de "SIMULACIÓN ACTIVADA"
./gradlew bootRun | grep -A 10 "SIMULACIÓN"Verificar:
- ✅ Cliente conectado al WebSocket:
ws://host:8080/ws/greenhouse - ✅ Suscrito al topic correcto:
/topic/greenhouse/messages - ✅ Ver logs del
GreenhouseWebSocketHandler:Evento recibido, transmitiendo via WebSocket Mensaje transmitido exitosamente via WebSocket
Causa: El SimulationController solo se crea si greenhouse.simulation.enabled=true
Solución: Activar simulación primero, luego los endpoints estarán disponibles.
Solución 1: Reducir intervalo
interval-ms: 10000 # Cada 10 segundos en lugar de 5Solución 2: Usar solo preview (no guarda en BD)
curl http://localhost:8080/api/simulation/previewSolución 3: Limpiar datos antiguos
DELETE FROM sensor_reading
WHERE greenhouse_id = '001'
AND time < NOW() - INTERVAL '1 day';- Producción: Mantener
enabled: falsecuando los sensores reales funcionen - Datos realistas: La distribución gaussiana crea patrones más naturales
- Mismo flujo: Los datos simulados pasan por Redis → TimescaleDB → WebSocket
- Performance: Generar datos cada 5 segundos es eficiente y realista
- Extensibilidad: Fácil ajustar rangos editando
GreenhouseDataSimulator.kt
┌─────────────────────────────────────────────────────────────┐
│ GreenhouseSimulationScheduler (cada 5 segundos) │
└─────────────────────────┬───────────────────────────────────┘
│
↓
┌────────────────────────────────┐
│ GreenhouseDataSimulator │
│ - generateRealisticData() │
│ - Distribución gaussiana │
│ - Rangos realistas │
└────────────────┬───────────────┘
│
↓ RealDataDto
┌────────────────────────────────┐
│ RealDataDto.toJson() │
│ (con @JsonProperty mapping) │
└────────────────┬───────────────┘
│
↓ JSON String
┌────────────────────────────────┐
│ MqttMessageProcessor │
│ .processGreenhouseData() │
└─────────────┬──────────────────┘
│
┌──────────────┼──────────────┐
↓ ↓ ↓
┌──────┐ ┌──────────┐ ┌─────────┐
│ Redis│ │TimescaleDB │ Event │
│Cache │ │ Batch Save │Publisher│
└──────┘ └──────────┘ └────┬────┘
│
↓
┌───────────────────────┐
│GreenhouseWebSocket │
│Handler │
│@EventListener │
└──────────┬────────────┘
│
↓ SimpMessagingTemplate
┌───────────────────────┐
│ /topic/greenhouse/ │
│ messages │
└──────────┬────────────┘
│
↓
┌────────────────────────────────┐
│ Clientes WebSocket/STOMP │
│ - iOS App │
│ - Android App │
│ - Web App │
│ - Desktop App │
└────────────────────────────────┘
-
GreenhouseDataSimulator.kt- Generador con distribución gaussiana -
GreenhouseSimulationScheduler.kt- Scheduler cada 5 segundos -
SimulationController.kt- API REST para control -
application.yaml- Configuración añadida -
@EnableScheduling- Habilitado en aplicación principal - Documentación - Esta guía completa
- Integración - Usa flujo existente (Redis + DB + WebSocket)
- Testing - Endpoints REST para pruebas manuales
¡Todo listo! 🎉
Para activar la simulación ahora mismo:
export GREENHOUSE_SIMULATION_ENABLED=true
./gradlew bootRunY tu app móvil empezará a recibir datos simulados cada 5 segundos a través del WebSocket.