diff --git a/Plugin/unraid/include/common.php b/Plugin/unraid/include/common.php index aaf3fa9..f6e94f9 100644 --- a/Plugin/unraid/include/common.php +++ b/Plugin/unraid/include/common.php @@ -448,8 +448,11 @@ function vv_disk_entry(array $d, string $key, string $role = 'data'): ?array { 'name' => $name, 'device' => $d['device'] ?? $key, 'role' => $role, - 'size_gb' => round($size_kb / 1048576, 1), - 'used_gb' => round($used_kb / 1048576, 1), + // disks.ini is KiB. This path was arithmetically right and still disagreed with the API + // path by 2.4% and with Unraid's own Main page by 7.4% — it produced GiB under a "GB" + // name. Decimal GB, so both sources of the same disk now land on the same number. + 'size_gb' => round($size_kb * 1024 / 1e9, 1), + 'used_gb' => round($used_kb * 1024 / 1e9, 1), 'pct' => (!$isParity && $size_kb > 0) ? round($used_kb / $size_kb * 100, 1) : null, 'temp' => is_numeric($tempRaw) ? (int)$tempRaw : null, 'transport' => $d['transport'] ?? 'ata', @@ -785,7 +788,7 @@ function vv_remote_hosts_stats(): array { $availBytes = (float)($mMem['available'] ?? 0); $memPct = $totalBytes > 0 ? (int)round(($totalBytes - $availBytes) / $totalBytes * 100) : 0; } - $memTotalGb = isset($mMem['total']) ? _vv_api_bytes_to_gb((float)$mMem['total']) : 0; + $memTotalGb = isset($mMem['total']) ? _vv_api_bytes_to_gib((float)$mMem['total']) : 0; $uptimeRaw = $os['uptime'] ?? ''; if (is_numeric($uptimeRaw)) { diff --git a/Plugin/unraid/include/unraid_api.php b/Plugin/unraid/include/unraid_api.php index 59f986f..58e3ee2 100644 --- a/Plugin/unraid/include/unraid_api.php +++ b/Plugin/unraid/include/unraid_api.php @@ -134,13 +134,26 @@ GQL; // ── Disk data helpers ───────────────────────────────────────────────────────── -// API size fields (BigInt) are in bytes on this schema. -// Heuristic: if raw > 100 billion → bytes; else → KB (covers both possible encodings). -function _vv_api_bytes_to_gb(float $raw): float { - return $raw > 100_000_000_000 - ? round($raw / (1024 ** 3), 1) - : round($raw / (1024 ** 2), 1); -} +// The API mixes three units and never says so. Measured against df and /var/local/emhttp/disks.ini +// on 7.3.2, per field: +// metrics.memory.* real bytes +// disk size KiB — 1024-byte units, the raw device +// disk fsSize/fsUsed kB — 1000-byte units, the filesystem +// disks.ini fsSize * 1024 and API fsSize * 1000 agree to the digit, which is what pins it down. +// +// This replaced one helper that guessed the unit from magnitude — "> 100 billion means bytes, +// else KB". It was wrong three ways at once. Every filesystem size read 2.4% high because kB got +// a KiB divisor; any memory total under 100 GB fell down the KB branch, so HOST2's 64 GB would +// have reported as 65536 GB; and a disk is only ever one of these units regardless of its size, +// so magnitude was never evidence of anything. Convert by which field it came from, not how big +// the number is. +function _vv_api_bytes_to_gib(float $bytes): float { return round($bytes / (1024 ** 3), 1); } + +// Capacity is reported the way drives are sold and the way Unraid's own Main page reports it — +// decimal GB. A 12 TB disk reads 12000 GB here, not 10914 GiB wearing a "GB" label. Memory stays +// binary above, because 128 GB of RAM genuinely is 125.8 GiB and every tool on the box says so. +function _vv_api_fs_gb(float $kb): float { return round($kb * 1000 / 1e9, 1); } // fsSize/fsUsed +function _vv_api_dev_gb(float $kib): float { return round($kib * 1024 / 1e9, 1); } // size, disks.ini // Map ArrayDiskType enum → role string used by the rest of the plugin. // Unraid 6.9 used CACHE; 6.10+ renamed pools to POOL. FLASH is the USB boot drive (skip). @@ -164,19 +177,26 @@ function vv_api_disk_entry(array $d, string $role = '', int $ini_used_kb = 0): ? // Parity disks have no filesystem — use raw size only. $sizeRaw = (float)($d['size'] ?? 0); if ($sizeRaw <= 0) return null; - $sizeGb = _vv_api_bytes_to_gb($sizeRaw); + $sizeGb = _vv_api_dev_gb($sizeRaw); $usedGb = 0.0; $pct = null; } else { - // Data/cache disks: prefer fsSize/fsUsed; fall back to size if unmounted. - $sizeRaw = (float)($d['fsSize'] ?? $d['size'] ?? 0); - if ($sizeRaw <= 0) return null; + // Data/cache disks: prefer fsSize/fsUsed; fall back to size if unmounted. The fallback + // changes the unit as well as the source — fsSize is kB, size is KiB — so the two cannot + // collapse into one variable and share a conversion the way they used to. + $fsRaw = (float)($d['fsSize'] ?? 0); + if ($fsRaw > 0.0) { + $sizeGb = _vv_api_fs_gb($fsRaw); + } else { + $devRaw = (float)($d['size'] ?? 0); + if ($devRaw <= 0) return null; + $sizeGb = _vv_api_dev_gb($devRaw); + } $usedRaw = (float)($d['fsUsed'] ?? 0); - $sizeGb = _vv_api_bytes_to_gb($sizeRaw); if ($usedRaw > 0.0) { - $usedGb = _vv_api_bytes_to_gb($usedRaw); + $usedGb = _vv_api_fs_gb($usedRaw); } elseif (!($d['isSpinning'] ?? true) && $ini_used_kb > 0) { - $usedGb = round($ini_used_kb / 1048576, 1); + $usedGb = _vv_api_dev_gb((float)$ini_used_kb); // disks.ini is KiB, not kB } else { $usedGb = 0.0; } @@ -211,8 +231,8 @@ function vv_api_node_metrics(?array $d): array { if (!$d) return []; $cpu = (int)round((float)($d['metrics']['cpu']['percentTotal'] ?? 0)); $mem = $d['metrics']['memory'] ?? []; - $ramUsed = isset($mem['used']) ? _vv_api_bytes_to_gb((float)$mem['used']) : null; - $ramTot = isset($mem['total']) ? _vv_api_bytes_to_gb((float)$mem['total']) : null; + $ramUsed = isset($mem['used']) ? _vv_api_bytes_to_gib((float)$mem['used']) : null; + $ramTot = isset($mem['total']) ? _vv_api_bytes_to_gib((float)$mem['total']) : null; $disks = $d['array']['disks'] ?? []; $caches = $d['array']['caches'] ?? []; @@ -221,8 +241,8 @@ function vv_api_node_metrics(?array $d): array { foreach (array_merge($disks, $caches) as $dk) { $sz = (float)($dk['fsSize'] ?? 0); if ($sz <= 0) continue; - $totGb += _vv_api_bytes_to_gb($sz); - $usedGb += _vv_api_bytes_to_gb((float)($dk['fsUsed'] ?? 0)); + $totGb += _vv_api_fs_gb($sz); + $usedGb += _vv_api_fs_gb((float)($dk['fsUsed'] ?? 0)); } $temps = array_filter( array_merge(array_column($disks,'temp'), array_column($caches,'temp'), array_column($pars,'temp')), @@ -264,7 +284,7 @@ function vv_local_host_stats(): array { $tot = (float)($mem['total'] ?? 0); $avail = (float)($mem['available'] ?? 0); $memPct = $tot > 0 ? (int)round(($tot - $avail) / $tot * 100) : 0; } - $memTotalGb = isset($mem['total']) ? _vv_api_bytes_to_gb((float)$mem['total']) : 0; + $memTotalGb = isset($mem['total']) ? _vv_api_bytes_to_gib((float)$mem['total']) : 0; $uptimeRaw = $os['uptime'] ?? ''; if (is_numeric($uptimeRaw)) { diff --git a/Plugin/unraid/pages/monitor.php b/Plugin/unraid/pages/monitor.php index 871661c..50699a5 100644 --- a/Plugin/unraid/pages/monitor.php +++ b/Plugin/unraid/pages/monitor.php @@ -738,7 +738,9 @@ function vvTempColor(tempC, transport) { return tempC >= crit ? '#f44336' : tempC >= warn ? '#ff9800' : '#4caf50'; } -function vvFmt(v) { return v >= 1024 ? (v / 1024).toFixed(1) + ' TB' : v + ' GB'; } +// Capacity only — size_gb/used_gb are decimal GB, so TB is 1000 of them. vvFmtGb below is the +// binary one and stays binary: it formats cumulative bytes read/written, not drive capacity. +function vvFmt(v) { return v >= 1000 ? (v / 1000).toFixed(1) + ' TB' : v + ' GB'; } function vvFmtRate(mbs) { if (mbs >= 1000) return (mbs / 1024).toFixed(1) + ' GB/s';