, * total_down:int, total_up:int, peak_down:int, peak_up:int} */ public function series(int $windowSeconds, int $buckets = 60): array { $windowSeconds = max(60, $windowSeconds); $buckets = max(1, $buckets); $now = time(); $from = $now - $windowSeconds; $width = $windowSeconds / $buckets; $down = array_fill(0, $buckets, 0); $up = array_fill(0, $buckets, 0); $rows = WgTrafficSample::query() ->where('sampled_at', '>=', now()->subSeconds($windowSeconds + (int) ceil($width) + 5)) ->orderBy('peer_pubkey') ->orderBy('sampled_at') ->get(['peer_pubkey', 'rx', 'tx', 'sampled_at']); $prev = []; foreach ($rows as $r) { $pk = $r->peer_pubkey; $t = $r->sampled_at->getTimestamp(); if (isset($prev[$pk])) { $dRx = max(0, (int) $r->rx - $prev[$pk]['rx']); $dTx = max(0, (int) $r->tx - $prev[$pk]['tx']); $idx = (int) floor(($t - $from) / $width); if ($idx >= 0 && $idx < $buckets) { $down[$idx] += $dRx; $up[$idx] += $dTx; } } $prev[$pk] = ['rx' => (int) $r->rx, 'tx' => (int) $r->tx]; } $points = []; for ($i = 0; $i < $buckets; $i++) { $points[] = [ 't' => (int) round($from + ($i + 0.5) * $width), 'down' => $down[$i], 'up' => $up[$i], ]; } return [ 'window' => $windowSeconds, 'points' => $points, 'total_down' => array_sum($down), 'total_up' => array_sum($up), 'peak_down' => $down === [] ? 0 : max($down), 'peak_up' => $up === [] ? 0 : max($up), ]; } }