Accuratezza dell'OEE
Come Rela-ai filtra i bias strutturali (sensori morti, PLC condivisi, downtime fabbricato) perché l'OEE rifletta l'operatività reale — non un numero cosmetico.
Accuratezza dell'OEE
L'OEE mostrato da una dashboard è utile solo se riflette la realtà. Un audit ha fatto emergere diversi punti in cui il calcolo tradizionale si discosta dall'operatività reale: downtime inventato da euristiche, sensori morti che producono un falso 0%, PLC condivisi che gonfiano le metriche per doppio conteggio, calibrazione rotta nascosta sotto un cap del 100%. Questa pagina documenta ciascuno di quei punti e come sono stati chiusi.
A cosa serve
- Sapere se il numero di OEE che guardi è affidabile o contaminato da config errata.
- Auditare il calcolo punto per punto rispetto allo standard Nakajima.
- Individuare sensori morti o PLC condivisi che gonfiano/sgonfiano il numero silenziosamente.
Come funziona
Ogni fonte di bias è chiusa da un filtro esplicito e un flag nella risposta JSON. Se il calcolo è colpito, la risposta lo dice: status=stale_source, downtime_estimation=true, performance_capped=true. L'operatore vede il numero E sa se fidarsi.
Sintesi esecutiva
OEE verificabile: ogni deviazione dallo standard Nakajima è coperta da un filtro esplicito e da un flag nella risposta. L'operatore vede la realtà — incluso quando la realtà è contaminata da config errata.
| Prima | Adesso |
|---|---|
| Downtime fisso 5 min per evento (fermo di 2h riportato come 5 min) | Durata reale da metadata.duration_seconds + flag downtime_estimation |
| Sensore morto poi OEE=0% falso | Filtro di staleness + status=stale_source |
| PLC condiviso tra linee poi conteggio doppio | Scope per asset_id nel $match |
| Performance > 100% capato in silenzio | performance_pct_raw + flag performance_capped |
| Buffer/store-and-forward disallineava i periodi | metadata.timestamp su created_at |
| Config orfana (asset/source cancellato) | Validazione 404 su configure |
| Cambi di soglia riscrivevano lo storico senza traccia | Audit trail fire-and-forget |
| Turni e PM affossavano availability | shift_pattern + subtract_scheduled_maintenance |
| Trend troncato su un errore transitorio | Loop robusto + finestre di giorno calendariale |
| Nessun Pareto né version stamp | downtime_breakdown + config_version |
Findings chiusi
Critici (H)
OEE-H1 — Durata reale del downtime
Il calcolo legacy usava 5 min per evento di downtime indipendentemente dalla durata reale. Un fermo di 2 ore riportato come singolo evento contava 5 min; un fermo di 10 secondi con 12 rimbalzi contava 60 min. La Disponibilità risultante era casuale rispetto alla realtà.
Il resolver segue ora una priorità:
- Somma di
metadata.duration_seconds(prima classe, misurato). - Somma di
metadata.duration_minutes(scorciatoia di comodità). - Euristica legacy 5 min × conteggio eventi (retrocompatibile, segnalata).
La risposta porta downtime_estimation con measured, heuristic o none così la dashboard può avvisare quando il numero è approssimato.
{
"downtime_minutes": 90.0,
"downtime_estimation": "measured"
}OEE-H2 — Guardia di staleness sul count source
Una sorgente di conteggio contrassegnata stale dal sensor_watchdog non alimenta più il calcolo. Al suo posto la risposta esegue short-circuit con status="stale_source" e un messaggio che rimanda al watchdog. Previene un OEE=0% falso quando il sensore è morto ma la linea sta ancora producendo.
{
"status": "stale_source",
"message": "Count source is stale; OEE not computed."
}OEE-H3 — Scope per asset_id
Quando due asset condividono lo stesso count_source_id (tipico: due linee dietro lo stesso PLC), ogni evento veniva conteggiato una volta per asset — Rendimento e Qualità gonfiati simmetricamente, downtime raddoppiato. Il $match include ora una clausola $or che accetta eventi con asset_id alla radice, dentro metadata, o assente (single-asset legacy).
OEE-H4 — Performance reale + performance_capped
performance > 100% non è un glitch — è un segnale forte di calibrazione rotta (cycle_time sotto-configurato o doppio conteggio). Il codice legacy capava silenziosamente al 100%. La risposta ora espone entrambi:
{
"performance_pct": 100.0,
"performance_pct_raw": 173.6,
"performance_capped": true
}Il KPI di OEE continua a usare il valore capato (Nakajima definisce 0-100), ma la dashboard può mostrare un badge "calibrazione da rivedere" quando raw > 100.
Medi (M)
OEE-M1 — Timestamp reale vs created_at
Il $match preferisce metadata.timestamp (ISO) quando l'evento lo porta; created_at è il fallback. Corregge il bias del buffer/store-and-forward: una lettura partita alle 14:00 ma re-ingerita alle 14:45 ora cade nel bucket delle 14:00, non 14:45.
OEE-M2 — shift_pattern per giorno
Nuovo campo di config:
{
"shift_pattern": {
"hours_by_weekday": {"1": 8, "2": 8, "3": 8, "4": 8, "5": 8}
}
}ISO weekday: 1=lunedì, 7=domenica. Uno stabilimento che gira Lun-Ven 8h ed è fermo nei weekend vede planned=0 min il sabato, non 0% OEE contro un piano fantasma da 480 min.
OEE-M3 — PM pianificata sottrae da planned
Nuovo flag subtract_scheduled_maintenance: true (default). I _maintenance_plans con next_due_at nel periodo riducono planned_minutes invece di contare come downtime. Una PM di 4h in un turno di 8h non riporta più Availability=50% (come se la PM fosse un guasto); planned diventa 240 min e Availability resta 100% per le restanti 4h di produzione reale. Opt-out disponibile per i tenant la cui convenzione di audit interno tiene la PM nel bucket di downtime.
OEE-M4 — Validazione di asset_id + count_source_id
POST /configure restituisce 404 quando asset_id non esiste (né come ObjectId di _assets._id né come asset_code) o quando count_source_id non è in _machine_event_sources. Previene config orfane che producono un OEE=0% silenzioso quando l'asset o la sorgente sono stati cancellati.
OEE-M5 — Audit trail sulle mutazioni della config
Qualsiasi modifica ai 6 campi regolati (planned_production_hours, ideal_cycle_time_seconds, count_source_id, count_metric_field, reject_metric_field, downtime_event_type) scrive una entry in _audit_trail con actor, timestamp, snapshot precedente e nuovo. Un auditor può rispondere "chi ha spostato ideal_cycle_time_seconds da 2.5 a 3.0 il 3 marzo".
OEE-M6 — Trend robusto agli errori transitori
Un errore transitorio in DB (es. un timeout al giorno 3 di un trend di 7 giorni) non tronca più il risultato. Ogni giorno computa in un proprio try/except; i fallimenti restituiscono {"date": "...", "status": "error"} come segnaposto e il loop prosegue. Solo un 404 (not-configured) è terminale, perché è un errore di config, non transitorio.
OEE-M7 — Finestre di giorno calendariale
Le finestre del trend sono ancorate alla mezzanotte UTC (00:00 to 24:00) anziché a finestre rotolanti di 24h ancorate al momento della chiamata. L'etichetta "2026-04-17" ora corrisponde esattamente alla finestra che rappresenta.
Bassi (L)
OEE-L2 — Breakdown Pareto del downtime
Nuova lista downtime_breakdown nella risposta, raggruppata per event_type e ordinata decrescente per minuti:
{
"downtime_breakdown": [
{"event_type": "STOP_COMPRESSOR", "event_count": 2, "minutes": 40.0},
{"event_type": "STOP_CHANGEOVER", "event_count": 3, "minutes": 20.0}
]
}Risponde "quale tipo di fermo ci è costato più tempo?" senza una seconda query.
OEE-L3 — config_version nella risposta
Ogni configure_oee fa $inc.config_version su MongoDB. Ogni calculate_oee stampa il config_version attivo nella risposta. Un valore OEE storico è ancorato al set di soglie che lo ha prodotto — la domanda d'audit "con quale config è stato calcolato quell'87%?" è banalmente risolvibile.
Struttura completa della risposta
{
"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"
}Collection MongoDB toccate
| Collection | Uso |
|---|---|
_oee_configs | Config per-asset con config_version monotonico. |
_machine_events | Sorgente unica di eventi (produzione + downtime) filtrata per asset_id + timestamp reale. |
_machine_event_sources | Stato connected/stale letto dal guard di OEE-H2. |
_assets | Validazione di asset_id su configure (dual-lookup: ObjectId o asset_code). |
_maintenance_plans | Finestre pianificate che riducono planned_minutes (OEE-M3). |
_audit_trail | Entry fire-and-forget con action=oee_config_updated. |
Benefici chiave
- Decisioni su dati reali, non euristiche.
- Zero OEE=0% falso da sensori morti.
- Zero doppio conteggio tra linee che condividono un PLC.
- Calibrazione visibile: performance_pct_raw > 100 scatena revisione.
- Trend stabile: etichette e finestre allineate, errori transitori non troncano.
- Audit-ready: ogni mutazione di soglia lascia traccia, ogni KPI storico porta il suo
config_version.