/** * Line KPI tiles. * * OEE gets a hero figure; its three factors sit beside it as their own tiles with * magnitude meters. Deliberately tiles rather than a chart: these are five * single headline numbers, and a bar chart of five unrelated measures would be * harder to read, not easier. */ import { useStore } from '../store.js'; function Meter({ value, color }) { return (
); } /** Colour the OEE factors by how bad the loss is, always beside a number. */ function factorColor(v) { if (v >= 0.95) return 'var(--good)'; if (v >= 0.85) return 'var(--series-1)'; if (v >= 0.70) return 'var(--warning)'; return 'var(--critical)'; } export function KpiRow() { const kpi = useStore((s) => (s.frame ? s.frame.kpi : null)); const windowSec = useStore((s) => (s.meta ? s.meta.kpiWindowSec : 1200)); if (!kpi) { return
Waiting for telemetry…
; } const factors = [ { label: 'Availability', v: kpi.availability, foot: 'Stoppage losses' }, { label: 'Performance', v: kpi.performance, foot: 'Micro-stops, slow cycles' }, { label: 'Quality', v: kpi.quality, foot: 'Rejects at INS-04' }, ]; return (
OEE · rolling {Math.round(windowSec / 60)} min {(kpi.oee * 100).toFixed(1)}% Availability × Performance × Quality
{factors.map((f) => (
{f.label} {(f.v * 100).toFixed(1)}% {f.foot}
))}
Output {kpi.throughputPerHour.toFixed(0)}/h {kpi.good.toLocaleString()} good · {kpi.rejected.toLocaleString()} rejected
Energy {kpi.energyKw.toFixed(0)}kW {kpi.energyPerUnit > 0 ? `${kpi.energyPerUnit.toFixed(3)} kWh/unit` : '—'}
); }