:cpu0", "AVERAGE:vm::memory"... and rows of values. * series('vm', $uuid, 'cpu0') = [[t, value], ...] oldest first. */ final class Rrd { /** @param array> $columns legend => points */ public function __construct(public array $columns = [], public int $end = 0, public int $step = 5) { } public static function parse(string $xml): self { preg_match_all('#([^<]*)#', $xml, $legend); $names = $legend[1]; $end = preg_match('#(\d+)#', $xml, $m) ? (int)$m[1] : 0; $step = preg_match('#(\d+)#', $xml, $m) ? (int)$m[1] : 5; $columns = array_fill_keys($names, []); preg_match_all('#(.*?)#s', $xml, $rows); foreach ($rows[1] as $row) { $t = preg_match('#(\d+)#', $row, $m) ? (int)$m[1] : 0; preg_match_all('#([^<]*)#', $row, $values); foreach ($values[1] as $i => $value) { if (isset($names[$i]) && is_numeric($value)) { $columns[$names[$i]][] = [$t, (float)$value]; } } } foreach ($columns as &$points) { usort($points, fn ($a, $b) => $a[0] <=> $b[0]); } return new self($columns, $end, $step); } /** @return list */ public function series(string $kind, string $uuid, string $name): array { return $this->columns["AVERAGE:$kind:$uuid:$name"] ?? []; } /** @return list the data source names of one object: cpu0, memory, vif_0_rx... */ public function names(string $kind, string $uuid): array { $out = []; $prefix = "AVERAGE:$kind:$uuid:"; foreach (array_keys($this->columns) as $legend) { if (str_starts_with($legend, $prefix)) { $out[] = substr($legend, strlen($prefix)); } } return $out; } /** The sum of several series, point by point (all CPUs, all network cards). @param list $names */ public function sum(string $kind, string $uuid, array $names, float $scale = 1.0): array { $by = []; foreach ($names as $name) { foreach ($this->series($kind, $uuid, $name) as [$t, $v]) { $by[$t] = ($by[$t] ?? 0) + $v * $scale; } } ksort($by); $out = []; foreach ($by as $t => $v) { $out[] = [(int)$t, $v]; } return $out; } /** @param list $names */ public function average(string $kind, string $uuid, array $names, float $scale = 1.0): array { $sum = $this->sum($kind, $uuid, $names, $scale); $n = max(1, count($names)); return array_map(fn ($p) => [$p[0], $p[1] / $n], $sum); } }