1- import 'package:fl_lib/fl_lib. dart' ;
1+ import 'dart:convert ' ;
22
33import 'package:server_box/data/res/misc.dart' ;
44
@@ -13,6 +13,8 @@ class _ProcValIdxMap {
1313 final int ? stat;
1414 final int ? start;
1515 final int ? time;
16+ final int ? readBytes;
17+ final int ? writeBytes;
1618 final int command;
1719
1820 const _ProcValIdxMap ({
@@ -26,6 +28,8 @@ class _ProcValIdxMap {
2628 this .stat,
2729 this .start,
2830 this .time,
31+ this .readBytes,
32+ this .writeBytes,
2933 required this .command,
3034 });
3135}
@@ -42,6 +46,10 @@ class Proc {
4246 final String ? stat;
4347 final String ? start;
4448 final String ? time;
49+ final int ? readBytes;
50+ final int ? writeBytes;
51+ final double ? readSpeed;
52+ final double ? writeSpeed;
4553 final String command;
4654
4755 const Proc ({
@@ -55,11 +63,28 @@ class Proc {
5563 this .stat,
5664 this .start,
5765 this .time,
66+ this .readBytes,
67+ this .writeBytes,
68+ this .readSpeed,
69+ this .writeSpeed,
5870 required this .command,
5971 });
6072
61- factory Proc ._parse (String raw, _ProcValIdxMap map) {
73+ factory Proc ._parse (
74+ String raw,
75+ _ProcValIdxMap map, {
76+ Proc ? previous,
77+ double ? elapsedSeconds,
78+ }) {
6279 final parts = raw.split (RegExp (r'\s+' ));
80+ final readBytes = _parseNullableInt (parts, map.readBytes);
81+ final writeBytes = _parseNullableInt (parts, map.writeBytes);
82+ final (readSpeed, writeSpeed) = _calculateSpeeds (
83+ readBytes: readBytes,
84+ writeBytes: writeBytes,
85+ previous: previous,
86+ elapsedSeconds: elapsedSeconds,
87+ );
6388 return Proc (
6489 user: map.user == null ? null : parts[map.user! ],
6590 pid: int .parse (parts[map.pid]),
@@ -71,10 +96,47 @@ class Proc {
7196 stat: map.stat == null ? null : parts[map.stat! ],
7297 start: map.start == null ? null : parts[map.start! ],
7398 time: map.time == null ? null : parts[map.time! ],
99+ readBytes: readBytes,
100+ writeBytes: writeBytes,
101+ readSpeed: readSpeed,
102+ writeSpeed: writeSpeed,
74103 command: parts.sublist (map.command).join (' ' ),
75104 );
76105 }
77106
107+ factory Proc ._parseWindowsJson (
108+ Map <String , dynamic > raw, {
109+ Proc ? previous,
110+ double ? elapsedSeconds,
111+ }) {
112+ final readBytes = _parseDynamicInt (
113+ raw['IOReadBytes' ] ?? raw['ReadTransferCount' ],
114+ );
115+ final writeBytes = _parseDynamicInt (
116+ raw['IOWriteBytes' ] ?? raw['WriteTransferCount' ],
117+ );
118+ final (readSpeed, writeSpeed) = _calculateSpeeds (
119+ readBytes: readBytes,
120+ writeBytes: writeBytes,
121+ previous: previous,
122+ elapsedSeconds: elapsedSeconds,
123+ );
124+ final name = raw['ProcessName' ] ?? raw['Name' ];
125+ final command = raw['CommandLine' ] ?? raw['Path' ] ?? name ?? '' ;
126+ return Proc (
127+ pid: _parseDynamicInt (raw['Id' ] ?? raw['ProcessId' ])! ,
128+ cpu: _parseDynamicDouble (raw['CPU' ]),
129+ rss: _parseDynamicInt (
130+ raw['WorkingSet' ] ?? raw['WorkingSetSize' ],
131+ )? .toString (),
132+ readBytes: readBytes,
133+ writeBytes: writeBytes,
134+ readSpeed: readSpeed,
135+ writeSpeed: writeSpeed,
136+ command: command.toString (),
137+ );
138+ }
139+
78140 Map toJson () {
79141 return {
80142 'user' : user,
@@ -87,6 +149,10 @@ class Proc {
87149 'stat' : stat,
88150 'start' : start,
89151 'time' : time,
152+ 'readBytes' : readBytes,
153+ 'writeBytes' : writeBytes,
154+ 'readSpeed' : readSpeed,
155+ 'writeSpeed' : writeSpeed,
90156 'command' : command,
91157 };
92158 }
@@ -106,12 +172,41 @@ class Proc {
106172class PsResult {
107173 final List <Proc > procs;
108174 final String ? error;
175+ final int sampledAtMillis;
176+
177+ const PsResult ({required this .procs, this .error, this .sampledAtMillis = 0 });
109178
110- const PsResult ({required this .procs, this .error});
179+ factory PsResult .parse (
180+ String raw, {
181+ ProcSortMode sort = ProcSortMode .cpu,
182+ PsResult ? previous,
183+ int ? sampledAtMillis,
184+ }) {
185+ final currentSampledAtMillis =
186+ sampledAtMillis ?? DateTime .now ().millisecondsSinceEpoch;
187+ final previousByPid = {
188+ for (final proc in previous? .procs ?? const < Proc > []) proc.pid: proc,
189+ };
190+ final elapsedSeconds = previous == null || previous.sampledAtMillis <= 0
191+ ? null
192+ : (currentSampledAtMillis - previous.sampledAtMillis) / 1000.0 ;
193+ final jsonResult = _parseWindowsJsonResult (
194+ raw,
195+ previousByPid: previousByPid,
196+ elapsedSeconds: elapsedSeconds,
197+ sampledAtMillis: currentSampledAtMillis,
198+ sort: sort,
199+ );
200+ if (jsonResult != null ) return jsonResult;
111201
112- factory PsResult .parse (String raw, {ProcSortMode sort = ProcSortMode .cpu}) {
113- final lines = raw.split ('\n ' ).map ((e) => e.trim ()).toList ();
114- if (lines.isEmpty) return const PsResult (procs: [], error: null );
202+ final lines = raw
203+ .split ('\n ' )
204+ .map ((e) => e.trim ())
205+ .where ((e) => e.isNotEmpty)
206+ .toList ();
207+ if (lines.isEmpty) {
208+ return PsResult (procs: const [], sampledAtMillis: currentSampledAtMillis);
209+ }
115210
116211 final header = lines[0 ];
117212 final parts = header.split (RegExp (r'\s+' ));
@@ -127,6 +222,8 @@ class PsResult {
127222 stat: parts.indexOfOrNull ('STAT' ),
128223 start: parts.indexOfOrNull ('START' ),
129224 time: parts.indexOfOrNull ('TIME' ),
225+ readBytes: parts.indexOfOrNull ('READ_BYTES' ),
226+ writeBytes: parts.indexOfOrNull ('WRITE_BYTES' ),
130227 command: parts.indexOfOrNull ('COMMAND' ) ?? parts.indexOfOrNull ('CMD' )! ,
131228 );
132229
@@ -136,19 +233,83 @@ class PsResult {
136233 final line = lines[i];
137234 if (line.isEmpty) continue ;
138235 try {
139- procs.add (Proc ._parse (line, map));
140- } catch (e, trace) {
236+ final pid = _parsePid (line, map.pid);
237+ procs.add (
238+ Proc ._parse (
239+ line,
240+ map,
241+ previous: previousByPid[pid],
242+ elapsedSeconds: elapsedSeconds,
243+ ),
244+ );
245+ } catch (e) {
141246 errs.add ('$line : $e ' );
142- Loggers .app.warning ('Process failed' , e, trace);
143247 }
144248 }
145249
250+ _sort (procs, sort);
251+ return PsResult (
252+ procs: procs,
253+ error: errs.isEmpty ? null : errs.join ('\n ' ),
254+ sampledAtMillis: currentSampledAtMillis,
255+ );
256+ }
257+
258+ static PsResult ? _parseWindowsJsonResult (
259+ String raw, {
260+ required Map <int , Proc > previousByPid,
261+ required double ? elapsedSeconds,
262+ required int sampledAtMillis,
263+ required ProcSortMode sort,
264+ }) {
265+ final trimmed = raw.trim ();
266+ if (! trimmed.startsWith ('{' ) && ! trimmed.startsWith ('[' )) return null ;
267+ try {
268+ final decoded = json.decode (trimmed);
269+ final items = decoded is List ? decoded : [decoded];
270+ final procs = < Proc > [];
271+ final errs = < String > [];
272+ for (final item in items) {
273+ if (item is ! Map ) continue ;
274+ try {
275+ final map = Map <String , dynamic >.from (item);
276+ final pid = _parseDynamicInt (map['Id' ] ?? map['ProcessId' ]);
277+ if (pid == null ) continue ;
278+ procs.add (
279+ Proc ._parseWindowsJson (
280+ map,
281+ previous: previousByPid[pid],
282+ elapsedSeconds: elapsedSeconds,
283+ ),
284+ );
285+ } catch (e) {
286+ errs.add ('$item : $e ' );
287+ }
288+ }
289+ _sort (procs, sort);
290+ return PsResult (
291+ procs: procs,
292+ error: errs.isEmpty ? null : errs.join ('\n ' ),
293+ sampledAtMillis: sampledAtMillis,
294+ );
295+ } catch (_) {
296+ return null ;
297+ }
298+ }
299+
300+ static void _sort (List <Proc > procs, ProcSortMode sort) {
146301 switch (sort) {
147302 case ProcSortMode .cpu:
148- procs.sort ((a, b) => b.cpu ? . compareTo (a.cpu ?? 0 ) ?? 0 );
303+ procs.sort ((a, b) => _compareNullableDesc (a.cpu, b.cpu) );
149304 break ;
150305 case ProcSortMode .mem:
151- procs.sort ((a, b) => b.mem? .compareTo (a.mem ?? 0 ) ?? 0 );
306+ procs.sort ((a, b) => _compareNullableDesc (a.mem, b.mem));
307+ break ;
308+ case ProcSortMode .read:
309+ procs.sort ((a, b) => _compareNullableDesc (a.readSpeed, b.readSpeed));
310+ break ;
311+ case ProcSortMode .write:
312+ procs.sort ((a, b) => _compareNullableDesc (a.writeSpeed, b.writeSpeed));
152313 break ;
153314 case ProcSortMode .pid:
154315 procs.sort ((a, b) => a.pid.compareTo (b.pid));
@@ -160,15 +321,71 @@ class PsResult {
160321 procs.sort ((a, b) => a.binary.compareTo (b.binary));
161322 break ;
162323 }
163- return PsResult (procs: procs, error: errs.isEmpty ? null : errs.join ('\n ' ));
164324 }
165325}
166326
167- enum ProcSortMode { cpu, mem, pid, user, name }
327+ enum ProcSortMode { cpu, mem, read, write, pid, user, name }
168328
169329extension _StrIndex on List <String > {
170330 int ? indexOfOrNull (String val) {
171331 final idx = indexOf (val);
172332 return idx == - 1 ? null : idx;
173333 }
174334}
335+
336+ int _parsePid (String raw, int pidIndex) {
337+ final parts = raw.split (RegExp (r'\s+' ));
338+ return int .parse (parts[pidIndex]);
339+ }
340+
341+ int ? _parseNullableInt (List <String > parts, int ? idx) {
342+ if (idx == null || idx >= parts.length) return null ;
343+ return _parseDynamicInt (parts[idx]);
344+ }
345+
346+ int ? _parseDynamicInt (Object ? val) {
347+ if (val == null ) return null ;
348+ if (val is int ) return val;
349+ if (val is num ) return val.toInt ();
350+ final str = val.toString ();
351+ if (str.isEmpty || str == '-' ) return null ;
352+ return int .tryParse (str);
353+ }
354+
355+ double ? _parseDynamicDouble (Object ? val) {
356+ if (val == null ) return null ;
357+ if (val is double ) return val;
358+ if (val is num ) return val.toDouble ();
359+ final str = val.toString ();
360+ if (str.isEmpty || str == '-' ) return null ;
361+ return double .tryParse (str);
362+ }
363+
364+ (double ? , double ? ) _calculateSpeeds ({
365+ required int ? readBytes,
366+ required int ? writeBytes,
367+ required Proc ? previous,
368+ required double ? elapsedSeconds,
369+ }) {
370+ if (previous == null || elapsedSeconds == null || elapsedSeconds <= 0 ) {
371+ return (null , null );
372+ }
373+ return (
374+ _calculateSpeed (readBytes, previous.readBytes, elapsedSeconds),
375+ _calculateSpeed (writeBytes, previous.writeBytes, elapsedSeconds),
376+ );
377+ }
378+
379+ double ? _calculateSpeed (int ? current, int ? previous, double elapsedSeconds) {
380+ if (current == null || previous == null ) return null ;
381+ final diff = current - previous;
382+ if (diff < 0 ) return null ;
383+ return diff / elapsedSeconds;
384+ }
385+
386+ int _compareNullableDesc (num ? a, num ? b) {
387+ if (a == null && b == null ) return 0 ;
388+ if (a == null ) return 1 ;
389+ if (b == null ) return - 1 ;
390+ return b.compareTo (a);
391+ }
0 commit comments