Precisión del OEE
Cómo Rela-ai filtra los sesgos estructurales (sensores muertos, PLCs compartidos, downtime fabricado) para que el OEE refleje la realidad operativa — no un número cosmético.
Precisión del OEE
El OEE que muestra un dashboard sólo sirve si refleja la realidad. Una auditoría reveló varios puntos donde el cálculo tradicional se desvía de la operación real: downtime inventado por heurísticas, sensores muertos que producen 0% falso, PLCs compartidos que inflan métricas por doble conteo, calibración rota escondida bajo un tope de 100%. Esta página documenta cada uno de esos puntos y cómo se cerraron.
¿Para qué sirve?
- Saber si el número de OEE que mirás es confiable o está contaminado por config errónea.
- Auditar el cálculo punto por punto contra el estándar Nakajima.
- Detectar sensores muertos o PLCs compartidos que inflan/deflan el número silenciosamente.
¿Cómo funciona?
Cada fuente de sesgo se cerró con un filtro explícito y un flag en la respuesta JSON. Si el cálculo se ve afectado, la respuesta lo dice: status=stale_source, downtime_estimation=true, performance_capped=true. El operador ve el número Y sabe si es confiable.
Resumen ejecutivo
OEE verificable: cada desvío del estándar Nakajima está cubierto por un filtro explícito y un flag en la respuesta. El operador ve la realidad — incluyendo cuando la realidad está contaminada por config incorrecta.
| Antes | Ahora |
|---|---|
| Downtime fijo 5 min por evento (parada de 2h reportada como 5 min) | Duración real desde metadata.duration_seconds + flag downtime_estimation |
| Sensor muerto → OEE=0% falso | Filtro de staleness + status=stale_source |
| PLC compartido entre líneas → conteo duplicado | Scope por asset_id en el $match |
| Performance > 100% silenciosamente capeado | performance_pct_raw + performance_capped flag |
| Buffer/store-and-forward desalineaba períodos | metadata.timestamp sobre created_at |
| Config huérfana (asset/source borrado) | Validación 404 en configure |
| Cambios de umbrales reescribían el histórico sin huella | Audit trail fire-and-forget |
| Turnos y PM tanquean availability | shift_pattern + subtract_scheduled_maintenance |
| Trend se truncaba en un error transitorio | Loop robusto + ventanas de día calendario |
| Sin Pareto de paradas ni version stamp | downtime_breakdown + config_version |
Hallazgos cerrados
🔴 Críticos (H)
OEE-H1 — Duración real de downtime
El cálculo histórico usaba 5 min por evento de downtime, independientemente de la duración real. Un paro de 2 horas reportado como un solo evento contaba 5 min; un paro de 10 segundos con 12 rebotes contaba 60 min. La Disponibilidad resultante era aleatoria respecto a la realidad.
Ahora el resolvedor sigue una prioridad:
- Suma de
metadata.duration_seconds(primera clase, medido). - Suma de
metadata.duration_minutes(atajo de conveniencia). - Heurística legacy 5 min × conteo (compatibilidad, pero marcado).
La respuesta incluye downtime_estimation con valores measured, heuristic o none para que el dashboard muestre un aviso cuando el número es aproximado.
{
"downtime_minutes": 90.0,
"downtime_estimation": "measured"
}OEE-H2 — Guardia de staleness en el count source
Un sensor de conteo marcado como stale por el sensor_watchdog ya no alimenta el cálculo. En su lugar, la respuesta corta con status="stale_source" y un mensaje guiando al usuario al watchdog. Previene OEE=0% falso cuando el sensor murió y la línea sigue produciendo normal.
{
"status": "stale_source",
"message": "Count source is stale; OEE not computed."
}OEE-H3 — Scope por asset_id
Cuándo dos activos comparten el mismo count_source_id (típico: dos líneas detrás del mismo PLC), cada evento se contaba una vez por cada activo — Rendimiento y Calidad inflados simétricamente en ambos, downtime duplicado. El $match ahora incluye una cláusula $or que acepta eventos con asset_id a nivel raíz, dentro de metadata, o ausente (single-asset legacy).
OEE-H4 — Performance real + performance_capped
performance > 100% no es un glitch — es un indicador fuerte de calibración rota (cycle_time subconfigurado o doble conteo). El código legado capaba silenciosamente a 100%. La respuesta ahora expone ambos:
{
"performance_pct": 100.0,
"performance_pct_raw": 173.6,
"performance_capped": true
}El KPI de OEE sigue usando el valor capeado (Nakajima define el rango 0-100), pero el dashboard puede mostrar un badge "calibración a revisar" cuando raw > 100.
🟡 Medios (M)
OEE-M1 — Timestamp real vs created_at
El $match prefiere metadata.timestamp (ISO) cuando el evento lo lleva; created_at es el fallback. Corrige el sesgo del buffer/store-and-forward: una lectura que ocurrió a las 14:00 pero se re-ingiere a las 14:45 ahora cae en el bucket de 14:00, no 14:45.
OEE-M2 — shift_pattern por día
Nuevo campo en la config:
{
"shift_pattern": {
"hours_by_weekday": {"1": 8, "2": 8, "3": 8, "4": 8, "5": 8}
}
}ISO weekday: 1=lunes, 7=domingo. Una planta que corre Mon-Fri 8h y está apagada los fines de semana ve planned=0 min en sábado, no 0% OEE contra un plan fantasma de 480 min.
OEE-M3 — PM planificado resta del planned
Nuevo flag subtract_scheduled_maintenance: true (default). Los _maintenance_plans con next_due_at dentro del período reducen planned_minutes en lugar de contar como downtime. Un PM de 4h dentro de un turno de 8h ya no reporta Availability=50% (como si el PM fuera una falla); ahora planned se convierte en 240 min y Availability se mantiene 100% para las 4h restantes de producción real. Opt-out disponible para tenants cuya convención de auditoría interna sí cuenta el PM como downtime.
OEE-M4 — Validación de asset_id + count_source_id
POST /configure devuelve 404 si el asset_id no existe (ni como ObjectId de _assets._id ni como asset_code) o si el count_source_id no existe en _machine_event_sources. Evita configuraciones huérfanas que producen un OEE=0% silencioso cuando el activo o la fuente han sido eliminados.
OEE-M5 — Audit trail en mutación de config
Cualquier cambio a los 6 campos regulados (planned_production_hours, ideal_cycle_time_seconds, count_source_id, count_metric_field, reject_metric_field, downtime_event_type) escribe una entrada en _audit_trail con actor, timestamp, snapshot previo y nuevo. Un auditor puede responder "quién movió ideal_cycle_time_seconds de 2.5 a 3.0 el 3 de marzo".
OEE-M6 — Trend robusto ante errores transitorios
Un error transitorio en la DB (p.ej. un timeout en día 3 de un trend de 7 días) ya no trunca el resultado. Cada día se calcula en su propio try/except; los fallos devuelven {"date": "...", "status": "error"} como placeholder y el loop sigue. Sólo un 404 (not-configured) detiene el loop por ser un error de configuración, no transitorio.
OEE-M7 — Ventanas de día calendario
Las ventanas del trend están ancladas a medianoche UTC (00:00 → 24:00) en lugar de ventanas rodantes de 24 h ancladas al momento de llamada. La etiqueta "2026-04-17" ahora coincide exactamente con la ventana que representa.
🟢 Bajos (L)
OEE-L2 — Breakdown Pareto del downtime
Nueva lista downtime_breakdown en la respuesta, agrupada por event_type y ordenada descendente por minutos:
{
"downtime_breakdown": [
{"event_type": "STOP_COMPRESSOR", "event_count": 2, "minutes": 40.0},
{"event_type": "STOP_CHANGEOVER", "event_count": 3, "minutes": 20.0}
]
}Responde "¿cuál fue la mayor causa de pérdida de tiempo?" sin un segundo query.
OEE-L3 — config_version en la respuesta
Cada configure_oee hace $inc.config_version en MongoDB. Cada calculate_oee estampa el config_version activo en la respuesta. Así un OEE histórico queda anclado al conjunto de umbrales que lo produjo — la pregunta de auditoría "¿con qué config se calculó ese 87%?" es trivialmente resoluble.
Estructura de la respuesta completa
{
"asset_id": "line-01",
"oee_pct": 72.3,
"availability_pct": 93.8,
"performance_pct": 85.0,
"performance_pct_raw": 85.0,
"performance_capped": false,
"quality_pct": 98.2,
"total_count": 4800,
"good_count": 4714,
"reject_count": 86,
"planned_minutes": 480.0,
"operating_minutes": 450.0,
"downtime_minutes": 30.0,
"downtime_estimation": "measured",
"downtime_breakdown": [
{"event_type": "STOP_COMPRESSOR", "event_count": 2, "minutes": 22.0},
{"event_type": "STOP_CHANGEOVER", "event_count": 1, "minutes": 8.0}
],
"scheduled_maintenance_minutes": 0.0,
"config_version": 7,
"period_start": "2026-04-17T00:00:00+00:00",
"period_end": "2026-04-18T00:00:00+00:00"
}Colecciones MongoDB tocadas
| Colección | Uso |
|---|---|
_oee_configs | Config per-asset con config_version monotónico. |
_machine_events | Fuente única de eventos (producción + downtime) — filtrada por asset_id + timestamp real. |
_machine_event_sources | Estado connected/stale leído por el guard de OEE-H2. |
_assets | Validación de asset_id en configure (dual-lookup ObjectId o asset_code). |
_maintenance_plans | Ventanas planificadas que reducen planned_minutes (OEE-M3). |
_audit_trail | Entradas fire-and-forget con action=oee_config_updated. |
Beneficios clave
- Toma de decisiones sobre datos reales, no heurísticas.
- Cero OEE=0% falso por sensor muerto.
- Cero doble conteo entre líneas que comparten PLC.
- Calibración visible: performance_pct_raw > 100 dispara revisión.
- Trend estable: etiquetas y ventanas alineadas, errores transitorios no truncan.
- Audit-ready: cada mutación de umbral deja huella, cada KPI histórico lleva su
config_version.