/// Parses the text values produced by [LinuxMonitor] into numbers for /// display. Anything that does not match stays null, and the UI then shows /// the original text instead of a guess. class MetricValues { final double? cpu, ramPercent, diskPercent, uptimeSeconds; final int? ramUsedBytes, ramTotalBytes, diskUsedBytes, diskTotalBytes; final List? load; const MetricValues({ this.cpu, this.ramPercent, this.ramUsedBytes, this.ramTotalBytes, this.diskPercent, this.diskUsedBytes, this.diskTotalBytes, this.uptimeSeconds, this.load, }); static final _percent = RegExp(r'^\s*([0-9]+(?:\.[0-9]+)?)\s*%'); static final _ram = RegExp(r'\((\d+) / (\d+) MiB\)'); static final _disk = RegExp(r'\((\d+) / (\d+) KiB\)'); factory MetricValues.parse(Map m) { double? percent(String? v) => v == null ? null : double.tryParse(_percent.firstMatch(v)?[1] ?? ''); final ram = _ram.firstMatch(m['ram'] ?? ''); final disk = _disk.firstMatch(m['disk'] ?? ''); final load = (m['load'] ?? '') .trim() .split(RegExp(r'\s+')) .map(double.tryParse) .toList(); final uptime = double.tryParse((m['uptime'] ?? '').trim()); return MetricValues( cpu: percent(m['cpu']), ramPercent: percent(m['ram']), ramUsedBytes: ram == null ? null : int.parse(ram[1]!) * 1024 * 1024, ramTotalBytes: ram == null ? null : int.parse(ram[2]!) * 1024 * 1024, diskPercent: percent(m['disk']), diskUsedBytes: disk == null ? null : int.parse(disk[1]!) * 1024, diskTotalBytes: disk == null ? null : int.parse(disk[2]!) * 1024, uptimeSeconds: uptime != null && uptime >= 0 ? uptime : null, load: load.length == 3 && load.every((v) => v != null && v >= 0) ? load.cast() : null, ); } } /// Whole days, hours and minutes of an uptime in seconds. ({int days, int hours, int minutes}) splitUptime(double seconds) { final total = seconds.floor() ~/ 60; return (days: total ~/ 1440, hours: total % 1440 ~/ 60, minutes: total % 60); } /// Binary (IEC) units, as reported by the kernel: 1 KiB = 1024 bytes. /// [decimal] formats the number for the locale, e.g. "1,9" in Czech. String formatBytes(int bytes, String Function(double, int digits) decimal) { const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB']; var value = bytes.toDouble(); var unit = 0; while (value >= 1024 && unit < units.length - 1) { value /= 1024; unit++; } final digits = unit == 0 || value >= 100 ? 0 : 1; return '${decimal(value, digits)} ${units[unit]}'; }