96 lines
3.4 KiB
Dart
96 lines
3.4 KiB
Dart
|
|
import 'package:iperf3gui/domain/models/iperf3_event.dart';
|
||
|
|
|
||
|
|
/// Renders parsed iperf3 events as the kind of line iperf3 itself would print.
|
||
|
|
///
|
||
|
|
/// The app consumes `--json-stream`, so the familiar text layout has to be
|
||
|
|
/// rebuilt here. Keeping it in one pure function makes it testable and leaves
|
||
|
|
/// the structured events untouched for anything else that wants them.
|
||
|
|
String? formatIperf3Event(Iperf3Event event) {
|
||
|
|
return switch (event) {
|
||
|
|
Iperf3Start e => _formatStart(e),
|
||
|
|
Iperf3Interval e => _formatInterval(e),
|
||
|
|
Iperf3End e => _formatEnd(e),
|
||
|
|
Iperf3Error e => 'iperf3 error: ${e.message}',
|
||
|
|
Iperf3Stopped _ => 'Stopped by user.',
|
||
|
|
Iperf3LogLine e => e.text.isEmpty ? null : e.text,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
String _formatStart(Iperf3Start e) {
|
||
|
|
final parts = <String>[];
|
||
|
|
if (e.targetHost != null) {
|
||
|
|
final port = e.targetPort != null ? ', port ${e.targetPort}' : '';
|
||
|
|
parts.add('Connecting to host ${e.targetHost}$port');
|
||
|
|
}
|
||
|
|
final detail = <String>[
|
||
|
|
if (e.protocol != null) e.protocol!,
|
||
|
|
if (e.streamCount != null)
|
||
|
|
'${e.streamCount} stream${e.streamCount == 1 ? '' : 's'}',
|
||
|
|
if (e.durationSeconds != null) '${e.durationSeconds}s',
|
||
|
|
];
|
||
|
|
if (detail.isNotEmpty) parts.add(detail.join(' · '));
|
||
|
|
return parts.isEmpty ? '' : parts.join('\n');
|
||
|
|
}
|
||
|
|
|
||
|
|
String _formatInterval(Iperf3Interval e) {
|
||
|
|
final window = '${_seconds(e.start)}-${_seconds(e.end)} sec';
|
||
|
|
final line = StringBuffer('[SUM] $window '
|
||
|
|
'${formatBytes(e.bytes)} ${formatBitrate(e.bitsPerSecond)}');
|
||
|
|
if (e.retransmits != null) {
|
||
|
|
line.write(' ${e.retransmits} retr');
|
||
|
|
}
|
||
|
|
if (e.omitted) line.write(' (omitted)');
|
||
|
|
return line.toString();
|
||
|
|
}
|
||
|
|
|
||
|
|
String _formatEnd(Iperf3End e) {
|
||
|
|
final lines = <String>['- ' * 20];
|
||
|
|
final sent = e.sent;
|
||
|
|
final received = e.received;
|
||
|
|
if (sent != null) {
|
||
|
|
lines.add('[SUM] 0.00-${_seconds(sent.seconds)} sec '
|
||
|
|
'${formatBytes(sent.bytes)} ${formatBitrate(sent.bitsPerSecond)}'
|
||
|
|
'${sent.retransmits != null ? ' ${sent.retransmits} retr' : ''}'
|
||
|
|
' sender');
|
||
|
|
}
|
||
|
|
if (received != null) {
|
||
|
|
lines.add('[SUM] 0.00-${_seconds(received.seconds)} sec '
|
||
|
|
'${formatBytes(received.bytes)} '
|
||
|
|
'${formatBitrate(received.bitsPerSecond)} receiver');
|
||
|
|
}
|
||
|
|
if (e.hostCpuPercent != null) {
|
||
|
|
lines.add('CPU: local ${e.hostCpuPercent!.toStringAsFixed(1)}%'
|
||
|
|
'${e.remoteCpuPercent != null ? ', remote ${e.remoteCpuPercent!.toStringAsFixed(1)}%' : ''}');
|
||
|
|
}
|
||
|
|
return lines.join('\n');
|
||
|
|
}
|
||
|
|
|
||
|
|
String _seconds(double value) => value.toStringAsFixed(2);
|
||
|
|
|
||
|
|
/// Byte totals the way iperf3 prints them — `93.8 MBytes`.
|
||
|
|
String formatBytes(int bytes) {
|
||
|
|
const units = ['Bytes', 'KBytes', 'MBytes', 'GBytes', 'TBytes'];
|
||
|
|
var value = bytes.toDouble();
|
||
|
|
var unit = 0;
|
||
|
|
while (value >= 1024 && unit < units.length - 1) {
|
||
|
|
value /= 1024;
|
||
|
|
unit++;
|
||
|
|
}
|
||
|
|
final precision = value >= 100 ? 0 : (value >= 10 ? 1 : 2);
|
||
|
|
return '${value.toStringAsFixed(precision)} ${units[unit]}';
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Bitrates the way iperf3 prints them — `786 Mbits/sec`. Decimal units, since
|
||
|
|
/// network rates are powers of ten while transfer totals are powers of two.
|
||
|
|
String formatBitrate(double bitsPerSecond) {
|
||
|
|
const units = ['bits/sec', 'Kbits/sec', 'Mbits/sec', 'Gbits/sec'];
|
||
|
|
var value = bitsPerSecond;
|
||
|
|
var unit = 0;
|
||
|
|
while (value >= 1000 && unit < units.length - 1) {
|
||
|
|
value /= 1000;
|
||
|
|
unit++;
|
||
|
|
}
|
||
|
|
final precision = value >= 100 ? 0 : (value >= 10 ? 1 : 2);
|
||
|
|
return '${value.toStringAsFixed(precision)} ${units[unit]}';
|
||
|
|
}
|