} */ public function series(Server $server, int $windowSeconds, int $buckets = 80): array { $windowSeconds = max(60, $windowSeconds); $buckets = max(1, $buckets); $now = time(); $from = $now - $windowSeconds; $width = $windowSeconds / $buckets; $sum = array_fill(0, $buckets, ['cpu' => 0, 'mem' => 0, 'disk' => 0, 'load' => 0.0, 'n' => 0]); $rows = MetricSample::query() ->where('server_id', $server->id) ->where('sampled_at', '>=', now()->subSeconds($windowSeconds)) ->orderBy('sampled_at') ->get(['cpu', 'mem', 'disk', 'load', 'sampled_at']); foreach ($rows as $r) { $idx = (int) floor(($r->sampled_at->getTimestamp() - $from) / $width); if ($idx < 0) { continue; } if ($idx >= $buckets) { $idx = $buckets - 1; // a sample exactly on the right edge belongs to the last bucket } $sum[$idx]['cpu'] += (int) $r->cpu; $sum[$idx]['mem'] += (int) $r->mem; $sum[$idx]['disk'] += (int) $r->disk; $sum[$idx]['load'] += (float) $r->load; $sum[$idx]['n']++; } $points = []; for ($i = 0; $i < $buckets; $i++) { $n = $sum[$i]['n']; $points[] = [ 't' => (int) round($from + ($i + 0.5) * $width), 'cpu' => $n ? (int) round($sum[$i]['cpu'] / $n) : null, 'mem' => $n ? (int) round($sum[$i]['mem'] / $n) : null, 'disk' => $n ? (int) round($sum[$i]['disk'] / $n) : null, 'load' => $n ? round($sum[$i]['load'] / $n, 2) : null, ]; } return ['window' => $windowSeconds, 'points' => $points]; } }