prepare('SELECT s.*, i.name inst_name, i.product, i.vendor_link, i.data_path, i.zone, i.cadence_min, i.status inst_status FROM streams s JOIN instruments i ON i.id=s.instrument_id WHERE s.code=?'); $q->execute([$code]); $r=$q->fetch(); return $r?:null; } function visible(array $s): bool { return $s['visibility']==='public'; } /* Fetch series honouring placeholder/live mode: live mode excludes placeholder rows. */ function series(array $stream, string $from, string $to, int $maxpts=800): array { $ph = $stream['mode']==='placeholder' ? 1 : 0; $q=db()->prepare('SELECT ts,value FROM readings WHERE stream_id=? AND is_placeholder=? AND ts BETWEEN ? AND ? ORDER BY ts'); $q->execute([$stream['id'],$ph,$from,$to]); $rows=$q->fetchAll(); $n=count($rows); if($n>$maxpts){ $step=(int)ceil($n/$maxpts); $rows=array_values(array_filter($rows,fn($k)=>$k%$step===0,ARRAY_FILTER_USE_KEY)); } return $rows; } function latest(array $stream): ?array { $ph = $stream['mode']==='placeholder' ? 1 : 0; $q=db()->prepare('SELECT ts,value FROM readings WHERE stream_id=? AND is_placeholder=? ORDER BY ts DESC LIMIT 1'); $q->execute([$stream['id'],$ph]); $r=$q->fetch(); return $r?:null; } function mode_badge(array $stream): string { return $stream['mode']==='live' ? 'LIVE DATA' : 'PLACEHOLDER DATA'; } function inst_status_badge(string $st): string { $map=['ok'=>['OK',C_GOOD],'late'=>['LATE',C_WARN],'silent'=>['SILENT',C_SERIOUS],'fault'=>['FAULT',C_CRIT],'pending'=>['PENDING','#6b7480']]; [$t,$c]=$map[$st]??['?','#6b7480']; return ''.$t.''; } function json_series(array $rows): string { return json_encode(array_map(fn($r)=>[substr($r['ts'],0,16),(float)$r['value']],$rows)); } function instrument_card(array $s): string { $link=$s['vendor_link']?''.h($s['vendor_link']).'':''; return '
Instrument
' .'
'.h($s['product']).'
' .'
'.h($s['inst_name']).' · '.h($s['zone']).'
' .'
Data path: '.h($s['data_path']).' · Cadence: every '.h((string)$s['cadence_min']).' min
' .'
'.$link.'
'; }