import 'package:fl_lib/fl_lib.dart'; import 'package:server_box/data/model/server/time_seq.dart'; import '../../res/misc.dart'; class Disk { final String fs; final String mount; final int usedPercent; final BigInt used; final BigInt size; final BigInt avail; const Disk({ required this.fs, required this.mount, required this.usedPercent, required this.used, required this.size, required this.avail, }); static List parse(String raw) { final list = []; final items = raw.split('\n'); items.removeAt(0); var pathCache = ''; for (var item in items) { if (item.isEmpty) { continue; } final vals = item.split(Miscs.blankReg); if (vals.length == 1) { pathCache = vals[0]; continue; } if (pathCache != '') { vals[0] = pathCache; pathCache = ''; } try { final fs = vals[0]; final mount = vals[5]; if (!_shouldCalc(fs, mount)) continue; list.add(Disk( fs: fs, mount: mount, usedPercent: int.parse(vals[4].replaceFirst('%', '')), used: BigInt.parse(vals[2]), size: BigInt.parse(vals[1]), avail: BigInt.parse(vals[3]), )); } catch (e) { continue; } } return list; } @override String toString() { return 'Disk{dev: $fs, mount: $mount, usedPercent: $usedPercent, used: $used, size: $size, avail: $avail}'; } } class DiskIO extends TimeSeq> { DiskIO(super.init1, super.init2); @override void onUpdate() { cachedAllSpeed = _getAllSpeed(); } (double?, double?) _getSpeed(String dev) { if (dev.startsWith('/dev/')) dev = dev.substring(5); final old = pre.firstWhereOrNull((e) => e.dev == dev); final new_ = now.firstWhereOrNull((e) => e.dev == dev); if (old == null || new_ == null) return (null, null); final sectorsRead = new_.sectorsRead - old.sectorsRead; final sectorsWrite = new_.sectorsWrite - old.sectorsWrite; final time = new_.time - old.time; final read = sectorsRead / time * 512; final write = sectorsWrite / time * 512; return (read, write); } (String?, String?) getSpeed(String dev) { final (read_, write_) = _getSpeed(dev); if (read_ == null || write_ == null) return (null, null); final read = '${read_.bytes2Str}/s'; final write = '${write_.bytes2Str}/s'; return (read, write); } (String?, String?) cachedAllSpeed = (null, null); (String?, String?) _getAllSpeed() { if (pre.isEmpty || now.isEmpty) return (null, null); var (read, write) = (0.0, 0.0); for (var item in pre) { /// Issue #314 /// Only calc nvme, sd, vd, hd, mmcblk, sr if (!item.dev.startsWith('nvme') && !item.dev.startsWith('sd') && !item.dev.startsWith('vd') && !item.dev.startsWith('hd') && !item.dev.startsWith('mmcblk') && !item.dev.startsWith('sr')) continue; final (read_, write_) = _getSpeed(item.dev); read += read_ ?? 0; write += write_ ?? 0; } final readStr = '${read.bytes2Str}/s'; final writeStr = '${write.bytes2Str}/s'; return (readStr, writeStr); } static List parse(String raw, int time) { final lines = raw.split('\n'); if (lines.isEmpty) return []; final items = []; for (var item in lines) { item = item.trim(); if (item.isEmpty) continue; final vals = item.split(Miscs.blankReg); if (vals.length < 10) continue; try { final dev = vals[2]; if (dev.startsWith('loop')) continue; items.add(DiskIOPiece( dev: dev, sectorsRead: int.parse(vals[5]), sectorsWrite: int.parse(vals[9]), time: time, )); } catch (e) { continue; } } return items; } } class DiskIOPiece extends TimeSeqIface { final String dev; final int sectorsRead; final int sectorsWrite; final int time; DiskIOPiece({ required this.dev, required this.sectorsRead, required this.sectorsWrite, required this.time, }); @override bool same(DiskIOPiece other) => dev == other.dev; } class DiskUsage { final BigInt used; final BigInt size; DiskUsage({ required this.used, required this.size, }); double get usedPercent => used / size * 100; /// Find all devs, add their used and size static DiskUsage parse(List disks) { final devs = {}; var used = BigInt.zero; var size = BigInt.zero; for (var disk in disks) { if (!_shouldCalc(disk.fs, disk.mount)) continue; if (devs.contains(disk.fs)) continue; devs.add(disk.fs); used += disk.used; size += disk.size; } return DiskUsage(used: used, size: size); } } bool _shouldCalc(String fs, String mount) { if (fs.startsWith('/dev')) return true; // Some NAS may have mounted path like this `//192.168.1.2/` if (fs.startsWith('//')) return true; if (mount.startsWith('/mnt')) return true; // if (fs.startsWith('shm') || // fs.startsWith('overlay') || // fs.startsWith('tmpfs')) return false; return false; }