diff --git a/Plugin/unraid/api/monitor.php b/Plugin/unraid/api/monitor.php index bfe8825..30fc135 100644 --- a/Plugin/unraid/api/monitor.php +++ b/Plugin/unraid/api/monitor.php @@ -25,6 +25,7 @@ echo json_encode([ 'parity' => vv_parity_status(), 'storage' => vv_storage_pools(), 'array_disks' => vv_array_disks(), + 'disk_io' => vv_disk_io_rates(), 'watchdog' => vv_watchdog_summary(), 'scripts' => vv_scripts_status(), 'thresholds' => vv_disk_thresholds(), diff --git a/Plugin/unraid/include/common.php b/Plugin/unraid/include/common.php index a70579b..3e29c02 100644 --- a/Plugin/unraid/include/common.php +++ b/Plugin/unraid/include/common.php @@ -536,6 +536,41 @@ function vv_array_disks(): array { return array_merge($parity, $data); } +function vv_disk_io_rates(): array { + $snapFile = '/tmp/vv_diskio_snap.json'; + $now = microtime(true); + + // Read current whole-disk stats from /proc/diskstats + $current = []; + foreach (@file('/proc/diskstats', FILE_IGNORE_NEW_LINES) ?: [] as $line) { + $p = preg_split('/\s+/', trim($line)); + if (count($p) < 14) continue; + $dev = $p[2]; + // Keep only whole disks: sda/sdb, nvme0n1, md*, not sda1/nvme0n1p1 + if (!preg_match('/^(sd[a-z]+|nvme\d+n\d+|md\d+)$/', $dev)) continue; + $current[$dev] = [(int)$p[5], (int)$p[9]]; // [sectors_read, sectors_written] + } + + // Load previous snapshot + $snap = @json_decode(@file_get_contents($snapFile) ?: '', true) ?: []; + $prevTime = (float)($snap['t'] ?? $now); + $prev = $snap['d'] ?? []; + + // Save current snapshot + @file_put_contents($snapFile, json_encode(['t' => $now, 'd' => $current], JSON_UNESCAPED_UNICODE)); + + $dt = max(0.5, $now - $prevTime); + $rates = []; + foreach ($current as $dev => [$rs, $ws]) { + if (!isset($prev[$dev])) continue; + [$prs, $pws] = $prev[$dev]; + $r = max(0.0, ($rs - $prs) * 512 / $dt / 1048576); + $w = max(0.0, ($ws - $pws) * 512 / $dt / 1048576); + if ($r > 0.01 || $w > 0.01) $rates[$dev] = [round($r, 1), round($w, 1)]; + } + return $rates; +} + function vv_disk_thresholds(): array { $cfg = @file_get_contents('/boot/config/plugins/dynamix/dynamix.cfg') ?: ''; $get = function(string $key) use ($cfg): ?int { diff --git a/Plugin/unraid/pages/monitor.php b/Plugin/unraid/pages/monitor.php index 0dd8277..9aae79c 100644 --- a/Plugin/unraid/pages/monitor.php +++ b/Plugin/unraid/pages/monitor.php @@ -208,6 +208,7 @@ let vvNetRxHistory = []; let vvNetTxHistory = []; let vvPoolsOpen = {}; let vvPoolGroupOpen = {}; +let vvDiskIo = {}; // {device: [readMBs, writeMBs]} let vvLastStorageDisks = []; // ── Canvas chart ────────────────────────────────────────────────────────────── @@ -506,6 +507,27 @@ function vvTempColor(tempC, transport) { function vvFmt(v) { return v >= 1024 ? (v / 1024).toFixed(1) + ' TB' : v + ' GB'; } +function vvFmtRate(mbs) { + if (mbs >= 1000) return (mbs / 1024).toFixed(1) + ' GB/s'; + if (mbs >= 100) return Math.round(mbs) + ' MB/s'; + return mbs.toFixed(1) + ' MB/s'; +} +function vvIoChip(dev) { + const io = vvDiskIo[dev]; + if (!io) return ''; + const [r, w] = io; + if (r < 0.05 && w < 0.05) return ''; + const parts = []; + if (r >= 0.05) parts.push(`↓${vvFmtRate(r)}`); + if (w >= 0.05) parts.push(`↑${vvFmtRate(w)}`); + return `${parts.join(' ')}`; +} +function vvIoSum(devices) { + let r = 0, w = 0; + devices.forEach(dev => { const io = vvDiskIo[dev]; if (io) { r += io[0]; w += io[1]; } }); + return [r, w]; +} + function vvDiskRow(disk) { const tempC = disk.temp; const tempColor = vvTempColor(tempC, disk.transport); @@ -524,7 +546,7 @@ function vvDiskRow(disk) { : `${vvFmt(disk.used_gb)} / ${vvFmt(disk.size_gb)}`; return `