Skip to content

Commit 6f0874e

Browse files
committed
fix: PowerShell script error repair, Windows data parsing repair
1 parent 22aa718 commit 6f0874e

File tree

9 files changed

+223
-122
lines changed

9 files changed

+223
-122
lines changed

lib/core/extension/ssh_client.dart

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -175,21 +175,26 @@ extension SSHClientX on SSHClient {
175175
/// Runs a command and decodes output safely with encoding fallback
176176
///
177177
/// [systemType] - The system type (affects encoding choice)
178-
/// [context] - Optional context for debugging
178+
/// Runs a command and safely decodes the result
179179
Future<String> runSafe(
180180
String command, {
181181
SystemType? systemType,
182182
String? context,
183183
}) async {
184+
// Let SSH errors propagate with their original type (e.g., SSHError subclasses)
185+
final result = await run(command);
186+
187+
// Only catch decoding failures and add context
184188
try {
185-
final result = await run(command);
186189
return SSHDecoder.decode(
187190
result,
188191
isWindows: systemType == SystemType.windows,
189192
context: context,
190193
);
191-
} catch (e) {
192-
throw Exception('Failed to run command${context != null ? " [$context]" : ""}: $e');
194+
} on FormatException catch (e) {
195+
throw Exception(
196+
'Failed to decode command output${context != null ? " [$context]" : ""}: $e',
197+
);
193198
}
194199
}
195200

@@ -231,17 +236,32 @@ extension SSHClientX on SSHClient {
231236
final stdoutBytes = stdoutBuilder.takeBytes();
232237
final stderrBytes = stderrBuilder.takeBytes();
233238

234-
final stdout = SSHDecoder.decode(
235-
stdoutBytes,
236-
isWindows: systemType == SystemType.windows,
237-
context: context != null ? '$context (stdout)' : 'stdout',
238-
);
239+
// Only catch decoding failures, let other errors propagate
240+
String stdout;
241+
try {
242+
stdout = SSHDecoder.decode(
243+
stdoutBytes,
244+
isWindows: systemType == SystemType.windows,
245+
context: context != null ? '$context (stdout)' : 'stdout',
246+
);
247+
} on FormatException catch (e) {
248+
throw Exception(
249+
'Failed to decode stdout${context != null ? " [$context]" : ""}: $e',
250+
);
251+
}
239252

240-
final stderr = SSHDecoder.decode(
241-
stderrBytes,
242-
isWindows: systemType == SystemType.windows,
243-
context: context != null ? '$context (stderr)' : 'stderr',
244-
);
253+
String stderr;
254+
try {
255+
stderr = SSHDecoder.decode(
256+
stderrBytes,
257+
isWindows: systemType == SystemType.windows,
258+
context: context != null ? '$context (stderr)' : 'stderr',
259+
);
260+
} on FormatException catch (e) {
261+
throw Exception(
262+
'Failed to decode stderr${context != null ? " [$context]" : ""}: $e',
263+
);
264+
}
245265

246266
return (stdout, stderr);
247267
}

lib/data/helper/system_detector.dart

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:dartssh2/dartssh2.dart';
22
import 'package:fl_lib/fl_lib.dart';
3+
import 'package:server_box/core/extension/ssh_client.dart';
34
import 'package:server_box/data/model/server/server_private_info.dart';
45
import 'package:server_box/data/model/server/system.dart';
56

@@ -23,7 +24,10 @@ class SystemDetector {
2324

2425
try {
2526
// Try to detect Unix/Linux/BSD systems first (more reliable and doesn't create files)
26-
final unixResult = await client.run('uname -a 2>/dev/null').string;
27+
final unixResult = await client.runSafe(
28+
'uname -a 2>/dev/null',
29+
context: 'uname detection for ${spi.oldId}',
30+
);
2731
if (unixResult.contains('Linux')) {
2832
detectedSystemType = SystemType.linux;
2933
dprint('Detected Linux system type for ${spi.oldId}');
@@ -35,15 +39,19 @@ class SystemDetector {
3539
}
3640

3741
// If uname fails, try to detect Windows systems
38-
final powershellResult = await client.run('ver 2>nul').string;
42+
final powershellResult = await client.runSafe(
43+
'ver 2>nul',
44+
systemType: SystemType.windows,
45+
context: 'ver detection for ${spi.oldId}',
46+
);
3947
if (powershellResult.isNotEmpty &&
4048
(powershellResult.contains('Windows') || powershellResult.contains('NT'))) {
4149
detectedSystemType = SystemType.windows;
4250
dprint('Detected Windows system type for ${spi.oldId}');
4351
return detectedSystemType;
4452
}
45-
} catch (e) {
46-
Loggers.app.warning('System detection failed for ${spi.oldId}: $e');
53+
} catch (e, stackTrace) {
54+
Loggers.app.warning('System detection failed for ${spi.oldId}: $e\n$stackTrace');
4755
}
4856

4957
// Default fallback

lib/data/model/app/scripts/cmd_types.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ enum WindowsStatusCmdType implements ShellCmdType {
182182
sys('(Get-ComputerInfo).OsName'),
183183
cpu(
184184
'Get-WmiObject -Class Win32_Processor | '
185-
'Select-Object Name, LoadPercentage | ConvertTo-Json',
185+
'Select-Object Name, LoadPercentage, NumberOfCores, NumberOfLogicalProcessors | ConvertTo-Json',
186186
),
187187
uptime('(Get-CimInstance -ClassName Win32_OperatingSystem).LastBootUpTime'),
188188
conn('(netstat -an | findstr ESTABLISHED | Measure-Object -Line).Count'),
@@ -287,7 +287,7 @@ enum WindowsStatusCmdType implements ShellCmdType {
287287
String get separator => ScriptConstants.getCmdSeparator(name);
288288

289289
@override
290-
String get divider => ScriptConstants.getCmdDivider(name);
290+
String get divider => ScriptConstants.getWindowsCmdDivider(name);
291291

292292
@override
293293
CmdTypeSys get sysType => CmdTypeSys.windows;

lib/data/model/app/scripts/script_consts.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ class ScriptConstants {
2929
/// Generate command-specific divider
3030
static String getCmdDivider(String cmdName) => '\necho ${getCmdSeparator(cmdName)}\n\t';
3131

32+
/// Generate command-specific divider for Windows PowerShell
33+
static String getWindowsCmdDivider(String cmdName) => '\n Write-Host "${getCmdSeparator(cmdName)}"\n ';
34+
3235
/// Parse script output into command-specific map
3336
static Map<String, String> parseScriptOutput(String raw) {
3437
final result = <String, String>{};

lib/data/model/server/cpu.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,20 @@ class Cpus extends TimeSeq<List<SingleCpuCore>> {
1414
@override
1515
void onUpdate() {
1616
_coresCount = now.length;
17+
if (pre.isEmpty || now.isEmpty || pre.length != now.length) {
18+
_totalDelta = 0;
19+
_user = 0;
20+
_sys = 0;
21+
_iowait = 0;
22+
_idle = 0;
23+
return;
24+
}
1725
_totalDelta = now[0].total - pre[0].total;
1826
_user = _getUser();
1927
_sys = _getSys();
2028
_iowait = _getIowait();
2129
_idle = _getIdle();
2230
_updateSpots();
23-
//_updateRange();
2431
}
2532

2633
double usedPercent({int coreIdx = 0}) {

lib/data/model/server/server_status_update_req.dart

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -378,18 +378,27 @@ void _parseWindowsCpuData(ServerStatusUpdateReq req, Map<String, String> parsedO
378378
// Windows CPU parsing - JSON format from PowerShell
379379
final cpuRaw = WindowsStatusCmdType.cpu.findInMap(parsedOutput);
380380
if (cpuRaw.isNotEmpty && cpuRaw != 'null' && !cpuRaw.contains('error') && !cpuRaw.contains('Exception')) {
381-
final cpus = WindowsParser.parseCpu(cpuRaw, req.ss);
382-
if (cpus.isNotEmpty) {
383-
req.ss.cpu.update(cpus);
381+
final cpuResult = WindowsParser.parseCpu(cpuRaw, req.ss);
382+
if (cpuResult.cores.isNotEmpty) {
383+
req.ss.cpu.update(cpuResult.cores);
384+
final brandRaw = WindowsStatusCmdType.cpuBrand.findInMap(parsedOutput);
385+
if (brandRaw.isNotEmpty && brandRaw != 'null') {
386+
req.ss.cpu.brand.clear();
387+
final brandLines = brandRaw.trim().split('\n');
388+
final uniqueBrands = <String>{};
389+
for (final line in brandLines) {
390+
final trimmedLine = line.trim();
391+
if (trimmedLine.isNotEmpty) {
392+
uniqueBrands.add(trimmedLine);
393+
}
394+
}
395+
if (uniqueBrands.isNotEmpty) {
396+
final brandName = uniqueBrands.first;
397+
req.ss.cpu.brand[brandName] = cpuResult.coreCount;
398+
}
399+
}
384400
}
385401
}
386-
387-
// Windows CPU brand parsing
388-
final brandRaw = WindowsStatusCmdType.cpuBrand.findInMap(parsedOutput);
389-
if (brandRaw.isNotEmpty && brandRaw != 'null') {
390-
req.ss.cpu.brand.clear();
391-
req.ss.cpu.brand[brandRaw.trim()] = 1;
392-
}
393402
} catch (e, s) {
394403
Loggers.app.warning('Windows CPU parsing failed: $e', s);
395404
}

lib/data/model/server/time_seq.dart

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,18 @@ abstract class TimeSeq<T extends List<TimeSeqIface>> extends Fifo<T> {
5656
add(new_);
5757

5858
if (pre.length != now.length) {
59-
pre.removeWhere((e) => now.any((el) => e.same(el)));
60-
pre.addAll(now.where((e) => pre.every((el) => !e.same(el))));
59+
final sizeDiff = (pre.length - now.length).abs();
60+
final isSignificantChange = sizeDiff > 1;
61+
if (isSignificantChange) {
62+
// Replace the pre entry with a new empty list instead of clearing it
63+
// to avoid mutating the historical FIFO data
64+
_list[length - 2] = <TimeSeqIface>[] as T;
65+
} else {
66+
final newPre = List<TimeSeqIface>.from(pre);
67+
newPre.removeWhere((e) => now.any((el) => e.same(el)));
68+
newPre.addAll(now.where((e) => newPre.every((el) => !e.same(el))));
69+
_list[length - 2] = newPre as T;
70+
}
6171
}
6272

6373
onUpdate();

0 commit comments

Comments
 (0)