UD03T03_DashboardIoT
03_Modelo
📦 DESARROLLO DEL MODELO (IotModel.java)
📋 PASO 1: MODELO DE DATOS
🎯 Responsabilidad Única del Modelo
- Almacenar:
- Información fija del sensor (nombre, modelo, ubicación).
- Valores actuales de temperatura, humedad y presión.
- Valores máximos permitidos para cada magnitud.
- Mantener históricos de valores para cada magnitud.
- Simular nuevas lecturas del sensor:
- Generar valores aleatorios realistas.
- Actualizar valores actuales.
- Guardar el histórico.
- Proporcionar los datos necesarios para el gráfico.
- Detectar cuándo un valor supera su umbral máximo.
🧱 ESTRUCTURA COMPLETA DE LA CLASE
1.1. Constantes del Sensor
// =========================
// CONSTANTES DEL SENSOR
// =========================
private final String nombre = "Sonda Ambiental";
private final String modelo = "DHT-Pro + BMP";
private final String ubicacion = "Aula 2.1 (ventana)";
Propósito: Información descriptiva del sensor físico simulado.
Diseño: Variables final porque son características fijas del dispositivo.
1.2. Umbrales de Alerta
// =========================
// UMBRALES (máximos)
// =========================
private static final double TEMP_MAX = 35.0; // °C
private static final double HUM_MAX = 80.0; // %
private static final double PRES_MAX = 1035.0; // hPa
Propósito: Valores máximos permitidos. Si se superan → generar alerta.
Criterios de diseño:
static final: Constantes compartidas por todas las instancias- Valores realistas basados en condiciones ambientales típicas
- Fácilmente modificables para diferentes contextos
1.3. Estado Actual del Sensor
// =========================
// VALORES ACTUALES DEL SENSOR
// =========================
private double tempActual = 22.0;
private double humActual = 45.0;
private double presActual = 1015.0;
Propósito: Almacenar la última lectura generada.
Valores iniciales: Condiciones ambientales normales típicas de interior:
- 22°C: Temperatura confortable
- 45%: Humedad moderada
- 1015 hPa: Presión atmosférica media a nivel del mar
1.4. Control de Tiempo
Variables explicadas:
| Variable | Tipo | Propósito |
|---|---|---|
tiempoSimulacion |
double | Tiempo transcurrido desde inicio (eje X gráfico) |
ultimaMedida |
LocalDateTime | Timestamp real de última lectura |
formatoFecha |
DateTimeFormatter | Formato "02/02/2026 15:30:45" |
1.5. Configuración de Visualización
Propósito:
| Constante | Valor | Función |
|---|---|---|
MAX_PUNTOS_POR_SERIE |
200 | Solo se almacenarán 200 valores en cada serie |
VENTANA_TIEMPO |
30.0s | Segundos visibles en gráfico simultáneamente (eje X) |
1.6. Constructor
/**
* Constructor: inicializa las series con sus nombres.
* Los nombres aparecerán en la leyenda del gráfico.
*/
🔧 MÉTODOS PRINCIPALES - DIAGRAMAS DE FLUJO
2.1. Método generarLectura()
Propósito: Simular una nueva lectura del sensor con variaciones realistas.
Diagrama de Flujo Detallado:
┌──────────────────────────────┐
│ generarLectura() │
└──────────┬───────────────────┘
│
↓
╔══════════════════════════════════════════╗
║ FASE 1: VARIACIONES NORMALES ║
╚══════════════╦═══════════════════════════╝
│
↓
┌──────────────────────────────────────┐
│ tempActual = limitarRango( │
│ tempActual + rnd(-0.8, 0.8), │
│ 10, 40) │
│ │
│ Explicación: │
│ - Suma variación: -0.8°C a +0.8°C │
│ - Limita resultado entre 10°C y 40°C │
└──────────┬───────────────────────────┘
│
↓
┌──────────────────────────────────────┐
│ humActual = limitarRango( │
│ humActual + rnd(-1.6, 1.6), │
│ 20, 90) │
│ │
│ Explicación: │
│ - Suma variación: -1.6% a +1.6% │
│ - Limita resultado entre 20% y 90% │
└──────────┬───────────────────────────┘
│
↓
┌──────────────────────────────────────┐
│ presActual = limitarRango( │
│ presActual + rnd(-1.1, 1.1), │
│ 980, 1040) │
│ │
│ Explicación: │
│ - Suma variación: -1.1 a +1.1 hPa │
│ - Limita entre 980 y 1040 hPa │
└──────────┬───────────────────────────┘
│
↓
╔══════════════════════════════════════════╗
║ FASE 2: PICOS ALEATORIOS (eventos) ║
╚══════════════╦═══════════════════════════╝
│
↓
┌──────────────────────────────────────┐
│ ¿Random < 0.05? (5% probabilidad) │
└──────────┬───────────────────────────┘
SÍ ←─┤ │ NO
│ ↓
↓ ┌──────────────┐
┌────────────────────┐ │
│ tempActual += │ │
│ rnd(4, 8) │ │
│ │ │
│ (Pico: +4°C a +8°C)│ │
│ Simula: ventana │ │
│ abierta, sol │ │
└────────┬───────────┘ │
│ │
└────→ ┌ ← ← ← ← ← ←┘
│
↓
┌──────────────────────────────────┐
│ ¿Random < 0.05? (5% prob.) │
└──────────┬─────────────────────────┘
SÍ ←─┤ │ NO
│ ↓
↓ ┌──────────────┐
┌────────────────────┐ │
│ humActual += │ │
│ rnd(8, 15) │ │
│ │ │
│ (Pico: +8% a +15%) │ │
│ Simula: lluvia, │ │
│ vapor de agua │ │
└────────┬───────────┘ │
│ │
└────→ ┌ ← ← ← ← ← ←┘
│
↓
┌──────────────────────────────────┐
│ ¿Random < 0.05? (4% prob. aprox) │
└──────────┬─────────────────────────┘
SÍ ←─┤ │ NO
│ ↓
↓ ┌──────────────┐
┌────────────────────────┐ │
│ presActual += │ │
│ rnd(8, 18) │ │
│ │ │
│ (Pico: +8 a +18 hPa) │ │
│ Simula: cambio │ │
│ meteorológico │ │
└────────┬───────────────┘ │
│ │
└────→ ┌ ← ← ← ← ← ←┘
│
↓
╔══════════════════════════════════════════╗
║ FASE 3: ACTUALIZAR TIMESTAMP ║
╚══════════════╦═══════════════════════════╝
│
↓
┌──────────────────────────────────────┐
│ ultimaMedida = LocalDateTime.now() │
│ │
│ Captura fecha/hora actual del sistema│
└──────────────────────────────────────┘
│
↓
(FIN)
Código Completo:
/**
* Genera una nueva lectura del sensor con variaciones aleatorias.
*
* Algoritmo:
* 1. Aplica pequeñas variaciones a cada magnitud
* 2. Limita los valores entre mínimos y máximos realistas
* 3. Aleatoriamente genera "picos" para simular eventos
* 4. Actualiza la marca de tiempo
*/
public void generarLectura() {
// FASE 1: Variaciones normales (pequeños cambios graduales)
tempActual = limitarRango(tempActual + rnd(-0.8, 0.8), 10, 40);
humActual = limitarRango(humActual + rnd(-1.6, 1.6), 20, 90);
presActual = limitarRango(presActual + rnd(-1.1, 1.1), 980, 1040);
// FASE 2: Picos aleatorios (simulan eventos repentinos)
if (ThreadLocalRandom.current().nextDouble() < 0.05) {
tempActual += rnd(4, 8); // Pico de temperatura
}
if (ThreadLocalRandom.current().nextDouble() < 0.05) {
humActual += rnd(8, 15); // Pico de humedad
}
if (ThreadLocalRandom.current().nextDouble() < 0.05) {
presActual += rnd(8, 18); // Pico de presión
}
// FASE 3: Actualizar timestamp
ultimaMedida = LocalDateTime.now();
}
Métodos Auxiliares:
/**
* Genera un número aleatorio en el rango [min, max).
* Usa ThreadLocalRandom para mejor rendimiento en entornos concurrentes.
*
* @param min Valor mínimo (inclusivo)
* @param max Valor máximo (exclusivo)
* @return Número aleatorio en el rango
*/
private double rnd(double min, double max) {
return ThreadLocalRandom.current().nextDouble(min, max);
}
/**
* Limita un valor entre un mínimo y un máximo (clamp).
*
* @param v Valor a limitar
* @param min Límite inferior
* @param max Límite superior
* @return Valor limitado
*/
private double limitarRango(double v, double min, double max) {
return Math.max(min, Math.min(max, v));
}
Explicación del algoritmo clamp:
Si v < min → retorna min
Si v > max → retorna max
Si min ≤ v ≤ max → retorna v
2.2. Método agregarPuntosASeries()
Propósito: Añadir los valores actuales a las series del gráfico, si los checkbox de la serie están seleccionados.
Diagrama de Flujo:
┌────────────────────────────────────┐
│ agregarPuntosASeries( │
│ tempHabilitada, │
│ humHabilitada, │
│ presHabilitada, │
│ incrementoTiempo) │
└─────────┬──────────────────────────┘
│
↓
┌─────────────────────────────────────┐
│ actualizamos el tiempo de la |
| simulación con el nuevo incremento |
| de timepo |
│ (Avanzar tiempo del eje X) │
└─────────┬───────────────────────────┘
│
↓
┌─────────────────────────────────────┐
│ si el checkbox de la serie |
| correspondiente está marcado │
└───────┬─────────────────────────────┘
SÍ ┤ │ NO
│ |
↓ └───────┐
┌──────────────────────────┐ │
│ Añade punto a la serie: │ │
│ (tiempo, temperatura) │ │
└──────────┬───────────────┘ │
│ │
↓ │
┌────────────────────────┐ │
│ Elimina puntos │ │
│ antiguos si tamaño>200 | │
└──────────┬─────────────┘ │
│ │
└────→ ┌ ← ← ← ← ┘
│
↓
┌─────────────────────────────────┐
│ repetir para todos lo sensores │
└────┬────────────────────────────┘
SÍ┤ │ NO
│ ↓
[Mismo proceso] [Continuar]
| │
│ │
└────→ ┌ ← ←┘
│
↓
(FIN)
2.3. Método recortarSerie()
Propósito: Evitar crecimiento infinito de memoria eliminando puntos antiguos.
Diagrama de Flujo:
┌───────────────────────────────┐
│ recortarSerie(serie) │
└──────────┬────────────────────┘
│
↓
┌────────────────────────────────────────┐
│ tamaño de serie > MAX_PUNTOS_POR_SERIE |
└──────────┬─────────────────────────────┘
SÍ ←─┤ │ NO
│ ↓
↓ ┌──────────┐
┌────────────────────────────┐ │ (FIN)
│ Si size=250 puntos: │ │
│ eliminar = 250 - 200 = 50│ │
│ Elimina primeros 50 │ │
└────────┬───────────────────┘ │
│ │
└─────→ ┌ ← ← ← ← ← ← ┘
│
↓
(FIN)
calcularLimiteInferiorX() y calcularLimiteSuperiorX()
Propósito: Crear efecto de "ventana deslizante" en el gráfico.
Diagrama de Flujo Combinado:
╔══════════════════════════════════════════════════════════╗
║ VENTANA DESLIZANTE - LÓGICA ║
╚══════════════════════════════════════════════════════════╝
FASE INICIAL (t ≤ 30s):
┌────────────────────────────────────────┐
│ Gráfico muestra: [0s ──────── 30s] │
│ Ventana FIJA al principio │
└────────────────────────────────────────┘
TRANSICIÓN (t = 35s):
┌────────────────────────────────────────┐
│ calcularLimiteInferiorX() │
│ ┌────────────────────────────────────┐ │
│ │ ¿tiempoSimulacion > VENTANA_TIEMPO?│ │
│ │ ¿35 > 30? │ │
│ └────────┬───────────────────────────┘ │
│ │ SÍ │
│ ↓ │
│ ┌────────────────────────────────────┐ │
│ │ return tiempoSimulacion - │ │
│ │ VENTANA_TIEMPO │ │
│ │ return 35 - 30 = 5s │ │
│ └────────────────────────────────────┘ │
└────────────────────────────────────────┘
RESULTADO:
┌────────────────────────────────────────┐
│ Gráfico muestra: [5s ──────── 35s] │
│ Ventana DESLIZANTE (se mueve) │
└────────────────────────────────────────┘
FASE AVANZADA (t = 60s):
┌────────────────────────────────────────┐
│ LimiteInferior: 60 - 30 = 30s │
│ LimiteSuperior: 60s │
│ Gráfico muestra: [30s ─────── 60s] │
└────────────────────────────────────────┘
2.4. Validación de Umbrales
Métodos hayAlertaXXX()
Propósito: Detectar si alguna magnitud supera su umbral máximo.
Métodos getMensajeAlertaXXX()
Propósito: Generar mensajes descriptivos para mostrar en la UI con los sigueitne formatos:
- "Temp = XX.X °C (máx YY.Y)".
- "Hum = XX.X % (máx YY.Y)"
- "Pres = XXXX.X hPa (máx YYYY.Y)"
Ejemplos de salida:
"Temp = 36.2 °C (máx 35.0)"
"Hum = 85.7 % (máx 80.0)"
"Pres = 1038.4 hPa (máx 1035.0)"
2.5. Formateo de Valores
Métodos getXXXFormateada()
Propósito: Convertir valores numéricos a String con formato específico para UI.
- Números con 1 decimal
- Fecha "dd/MM/yyyy HH:mm:ss"
2.6. Método resetearDatos()
Propósito: Reiniciar toda la simulación a su estado inicial.
Diagrama de Flujo:
┌────────────────────────┐
│ resetearDatos() │
└──────────┬─────────────┘
│
↓
┌──────────────────────────────┐
│ (Eliminar TODOS los puntos │
│ de la serie temperatura) │
└──────────┬───────────────────┘
│
↓
┌──────────────────────────────┐
│ (Eliminar TODOS los puntos │
│ de la serie humedad) │
└──────────┬───────────────────┘
│
↓
┌──────────────────────────────┐
│ (Eliminar TODOS los puntos │
│ de la serie presión) │
└──────────┬───────────────────┘
│
↓
┌──────────────────────────────┐
│ (Resetear contador tiempo) │
└──────────┬───────────────────┘
│
↓
┌──────────────────────────────┐
│ (Borrar timestamp) │
└──────────────────────────────┘
│
↓
(FIN)
2.7. Getters Básicos
Propósito: Proporcionar acceso de solo lectura a los atributos privados.