Instrumentation health

Status, freshness, 7-day completeness and a simple failure-risk read for every instrument. The same checks run by cron and email the administrator when something degrades; the laboratory admits its own gaps the moment they open.

'; $insts=db()->query("SELECT * FROM instruments ORDER BY zone, name")->fetchAll(); echo ''; foreach($insts as $i){ $q=db()->prepare("SELECT MAX(r.ts) m FROM readings r JOIN streams s ON s.id=r.stream_id WHERE s.instrument_id=? AND r.is_placeholder=(SELECT IF(MIN(mode)='live',0,1) FROM streams WHERE instrument_id=?)"); $q->execute([$i['id'],$i['id']]); $last=$q->fetch()['m']; $age=$last?(time()-strtotime($last))/60:1e9; $fresh=$last?($ageprepare("SELECT COUNT(*) c FROM readings r JOIN streams s ON s.id=r.stream_id WHERE s.instrument_id=? AND r.ts>DATE_SUB(NOW(),INTERVAL 7 DAY)"); $q2->execute([$i['id']]); $got=$q2->fetch()['c']; $comp=min(100,round(100*$got/ max(1,$exp*max(1,db()->query("SELECT COUNT(*) c FROM streams WHERE instrument_id=".$i['id'])->fetch()['c'])))); $q3=db()->prepare("SELECT COUNT(*) c FROM events WHERE instrument_id=? AND class IN ('fault','gap') AND ts>DATE_SUB(NOW(),INTERVAL 14 DAY)"); $q3->execute([$i['id']]); $recent_faults=$q3->fetch()['c']; $risk = $fresh==='silent'||$fresh==='none' ? 'HIGH' : ($comp<70||$recent_faults>2 ? 'ELEVATED' : ($comp<90||$recent_faults>0 ? 'WATCH' : 'LOW')); $rc=['LOW'=>C_GOOD,'WATCH'=>C_WARN,'ELEVATED'=>C_SERIOUS,'HIGH'=>C_CRIT][$risk]; echo ''; echo ''; echo ''; } echo '
InstrumentZoneStatusLast readingFreshness7-day completenessRisk
'.h($i['name']).'
'.h($i['product']).'
'.h($i['zone']).''.inst_status_badge($i['status']).''.h($last?:'never').''.h($fresh).''.$comp.'%● '.$risk.'
'; echo '

Risk heuristic v0: silence and completeness plus recent fault frequency. As the record grows this page gains trend-based prediction (rising gap frequency, battery and signal telemetry where instruments expose them) so failures are anticipated, not discovered.

'; page_footer();