1+ using System ;
2+ using System . Diagnostics ;
3+ using System . IO ;
4+ using System . Runtime . InteropServices ;
5+ using System . Threading ;
6+
7+ #if WINDOWS
8+ using System . Diagnostics . PerformanceCounter ;
9+ #endif
10+
11+ namespace Trion . Core . Monitoring ;
12+
13+ public sealed class MachineMetricsProvider : IMachineMetricsProvider
14+ {
15+ private readonly object _lock = new ( ) ;
16+ #if WINDOWS
17+ private PerformanceCounter ? _cpu ;
18+ #endif
19+ private ( long read , long write ) _prevDisk ;
20+ private ( long recv , long sent ) _prevNet ;
21+
22+ public MachineMetrics GetSnapshot ( )
23+ {
24+ lock ( _lock )
25+ {
26+ var now = DateTimeOffset . UtcNow ;
27+ var cpu = GetCpuPercent ( ) ;
28+ var ( total , used ) = GetRamBytes ( ) ;
29+ var ( diskRead , diskWrite ) = GetDiskBytesDelta ( now ) ;
30+ var ( netRecv , netSent ) = GetNetworkBytesDelta ( now ) ;
31+ return new MachineMetrics ( cpu , used / 1024 / 1024 , total / 1024 / 1024 ,
32+ diskRead , diskWrite , netRecv , netSent , now ) ;
33+ }
34+ }
35+
36+ private double GetCpuPercent ( )
37+ {
38+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
39+ {
40+ #if WINDOWS
41+ _cpu ??= new PerformanceCounter ( "Processor" , "% Processor Time" , "_Total" ) ;
42+ return Math . Clamp ( _cpu . NextValue ( ) , 0 , 100 ) ;
43+ #else
44+ return 0 ;
45+ #endif
46+ }
47+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) )
48+ {
49+ var line = File . ReadLines ( "/proc/stat" ) . First ( l => l . StartsWith ( "cpu " ) ) ;
50+ var parts = line . Split ( ' ' , StringSplitOptions . RemoveEmptyEntries ) ;
51+ var user = double . Parse ( parts [ 1 ] ) ;
52+ var nice = double . Parse ( parts [ 2 ] ) ;
53+ var sys = double . Parse ( parts [ 3 ] ) ;
54+ var idle = double . Parse ( parts [ 4 ] ) ;
55+ var total = user + nice + sys + idle ;
56+ return total == 0 ? 0 : Math . Clamp ( ( 1.0 - idle / total ) * 100.0 , 0 , 100 ) ;
57+ }
58+ return 0 ;
59+ }
60+
61+ private ( long total , long used ) GetRamBytes ( )
62+ {
63+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
64+ {
65+ #if WINDOWS
66+ using var mem = new PerformanceCounter ( "Memory" , "Available Bytes" ) ;
67+ var avail = ( long ) mem . RawValue ;
68+ var total = GetTotalRamWindows ( ) ;
69+ return ( total , total - avail ) ;
70+ #else
71+ return ( 0 , 0 ) ;
72+ #endif
73+ }
74+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) )
75+ {
76+ var total = long . Parse ( File . ReadLines ( "/proc/meminfo" )
77+ . First ( l => l . StartsWith ( "MemTotal" ) )
78+ . Split ( ' ' , StringSplitOptions . RemoveEmptyEntries ) [ 1 ] ) * 1024 ;
79+ var avail = long . Parse ( File . ReadLines ( "/proc/meminfo" )
80+ . First ( l => l . StartsWith ( "MemAvailable" ) )
81+ . Split ( ' ' , StringSplitOptions . RemoveEmptyEntries ) [ 1 ] ) * 1024 ;
82+ return ( total , total - avail ) ;
83+ }
84+ return ( 0 , 0 ) ;
85+ }
86+
87+ private ( long read , long write ) GetDiskBytesDelta ( DateTimeOffset now )
88+ {
89+ ( long read , long write ) raw = RuntimeInformation . IsOSPlatform ( OSPlatform . Windows )
90+ ? GetWindowsDiskRaw ( )
91+ : GetLinuxDiskRaw ( ) ;
92+ long deltaRead = raw . read - _prevDisk . read ;
93+ long deltaWrite = raw . write - _prevDisk . write ;
94+ _prevDisk = raw ;
95+ return ( deltaRead , deltaWrite ) ;
96+ }
97+
98+ private ( long recv , long sent ) GetNetworkBytesDelta ( DateTimeOffset now )
99+ {
100+ ( long recv , long sent ) raw = RuntimeInformation . IsOSPlatform ( OSPlatform . Windows )
101+ ? ( 0 , 0 ) // replace with perf-counter if wanted
102+ : ( 0 , 0 ) ;
103+ long deltaRecv = raw . recv - _prevNet . recv ;
104+ long deltaSent = raw . sent - _prevNet . sent ;
105+ _prevNet = raw ;
106+ return ( deltaRecv , deltaSent ) ;
107+ }
108+
109+ /* ---------- helpers ---------- */
110+ private static ( long read , long write ) GetWindowsDiskRaw ( )
111+ {
112+ #if WINDOWS
113+ using var r = new PerformanceCounter ( "PhysicalDisk" , "Disk Read Bytes/sec" , "_Total" ) ;
114+ using var w = new PerformanceCounter ( "PhysicalDisk" , "Disk Write Bytes/sec" , "_Total" ) ;
115+ return ( ( long ) r . RawValue , ( long ) w . RawValue ) ;
116+ #else
117+ return ( 0L , 0L ) ;
118+ #endif
119+ }
120+
121+ private static ( long read , long write ) GetLinuxDiskRaw ( ) => ( 0L , 0L ) ;
122+
123+ #if WINDOWS
124+ private static long GetTotalRamWindows ( )
125+ {
126+ using var mc = new System . Management . ManagementObjectSearcher (
127+ "SELECT TotalPhysicalMemory FROM Win32_ComputerSystem" ) ;
128+ return ( long ) mc . Get ( ) . Cast < System . Management . ManagementObject > ( ) . First ( ) [ "TotalPhysicalMemory" ] ;
129+ }
130+ #endif
131+ }
0 commit comments