namespace MoonlightServers.Daemon.Helpers; public partial class SystemMetrics { /// Network throughput for a single interface, computed between two samples. /// Interface name, e.g. eth0, ens3. /// Received bytes per second. /// Transmitted bytes per second. /// Received packets per second. /// Transmitted packets per second. /// Cumulative receive error count (not a rate). /// Cumulative transmit error count (not a rate). public record NetworkInterfaceInfo( string Name, long RxBytesPerSec, long TxBytesPerSec, long RxPacketsPerSec, long TxPacketsPerSec, long RxErrors, long TxErrors ); // Network private record RawNetLine( string Iface, long RxBytes, long RxPackets, long RxErrors, long TxBytes, long TxPackets, long TxErrors ); private static async Task> ReadRawNetStatsAsync() { var lines = await File.ReadAllLinesAsync("/proc/net/dev"); var result = new List(); // The first two lines are the column-header banner. foreach (var line in lines.Skip(2)) { var colon = line.IndexOf(':'); if (colon < 0) continue; var iface = line[..colon].Trim(); // Skip loopback and ephemeral veth pairs created by container runtimes. if (iface == "lo" || iface.StartsWith("veth", StringComparison.Ordinal)) continue; var f = line[(colon + 1)..].Split(' ', StringSplitOptions.RemoveEmptyEntries); if (f.Length < 16) continue; // Column layout (after the colon, 0-based): // RX: [0]bytes [1]packets [2]errs [3]drop [4]fifo [5]frame [6]compressed [7]multicast // TX: [8]bytes [9]packets [10]errs [11]drop [12]fifo [13]colls [14]carrier [15]compressed result.Add(new RawNetLine( Iface: iface, RxBytes: long.Parse(f[0]), RxPackets: long.Parse(f[1]), RxErrors: long.Parse(f[2]), TxBytes: long.Parse(f[8]), TxPackets: long.Parse(f[9]), TxErrors: long.Parse(f[10]) )); } return result; } private static IReadOnlyList ComputeNetworkRates( List s1, List s2, double intervalSecs ) { var prev = s1.ToDictionary(x => x.Iface); var result = new List(); var div = intervalSecs > 0 ? intervalSecs : 1.0; foreach (var cur in s2) { if (!prev.TryGetValue(cur.Iface, out var p)) continue; result.Add(new NetworkInterfaceInfo( Name: cur.Iface, RxBytesPerSec: (long)((cur.RxBytes - p.RxBytes) / div), TxBytesPerSec: (long)((cur.TxBytes - p.TxBytes) / div), RxPacketsPerSec: (long)((cur.RxPackets - p.RxPackets) / div), TxPacketsPerSec: (long)((cur.TxPackets - p.TxPackets) / div), RxErrors: cur.RxErrors, TxErrors: cur.TxErrors )); } return result; } }