New Ping, use servers to ping

This commit is contained in:
Junyuan Feng
2022-05-03 11:42:30 +08:00
parent fd1b2fc7b0
commit 9e2d49773f
6 changed files with 143 additions and 59 deletions

View File

@@ -0,0 +1,79 @@
final parseFailed = Exception('Parse failed');
final seqReg = RegExp(r'icmp_seq=(.+) ttl=(.+) time=(.+) ms');
final packetReg =
RegExp(r'(.+) packets transmitted, (.+) received, (.+)% packet loss');
final timeReg = RegExp(r'min/avg/max/mdev = (.+)/(.+)/(.+)/(.+) ms');
final ipReg = RegExp(r' \((\S+)\) ');
class PingResult {
String serverName;
String? ip;
List<PingSeqResult>? results;
PingStatistics? statistic;
PingResult.parse(this.serverName, String raw) {
final lines = raw.split('\n');
lines.removeWhere((element) => element.isEmpty);
final statisticIndex =
lines.indexWhere((element) => element.startsWith('---'));
if (statisticIndex == -1) {
throw parseFailed;
}
final statisticRaw = lines.sublist(statisticIndex + 1);
statistic = PingStatistics.parse(statisticRaw);
results = lines
.sublist(1, statisticIndex)
.map((e) => PingSeqResult.parse(e))
.toList();
ip = ipReg.firstMatch(lines[0])?.group(1);
}
}
class PingSeqResult {
int? seq;
int? ttl;
double? time;
PingSeqResult.parse(String raw) {
final seqMatched = seqReg.firstMatch(raw);
if (seqMatched == null) {
throw parseFailed;
}
seq = int.tryParse(seqMatched.group(1)!);
ttl = int.tryParse(seqMatched.group(2)!);
time = double.tryParse(seqMatched.group(3)!);
}
@override
String toString() {
return 'seq: $seq, ttl: $ttl, time: $time';
}
}
class PingStatistics {
int? total;
int? received;
double? loss;
double? min;
double? max;
double? avg;
double? stddev;
PingStatistics.parse(List<String> lines) {
if (lines.isEmpty || lines.length != 2) {
return;
}
final packetMatched = packetReg.firstMatch(lines[0]);
final timeMatched = timeReg.firstMatch(lines[1]);
if (packetMatched == null || timeMatched == null) {
return;
}
total = int.tryParse(packetMatched.group(1)!);
received = int.tryParse(packetMatched.group(2)!);
loss = double.tryParse(packetMatched.group(3)!);
min = double.tryParse(timeMatched.group(1)!);
avg = double.tryParse(timeMatched.group(2)!);
max = double.tryParse(timeMatched.group(3)!);
stddev = double.tryParse(timeMatched.group(4)!);
}
}

View File

@@ -2,9 +2,9 @@
class BuildData { class BuildData {
static const String name = "ServerBox"; static const String name = "ServerBox";
static const int build = 115; static const int build = 117;
static const String engine = static const String engine =
"Flutter 2.10.4 • channel stable • https://github.com/flutter/flutter.git\nFramework • revision c860cba910 (6 weeks ago) • 2022-03-25 00:23:12 -0500\nEngine • revision 57d3bac3dd\nTools • Dart 2.16.2 • DevTools 2.9.2\n"; "Flutter 2.10.4 • channel stable • https://github.com/flutter/flutter.git\nFramework • revision c860cba910 (6 weeks ago) • 2022-03-25 00:23:12 -0500\nEngine • revision 57d3bac3dd\nTools • Dart 2.16.2 • DevTools 2.9.2\n";
static const String buildAt = "2022-05-03 08:33:56.199910"; static const String buildAt = "2022-05-03 11:39:22.853075";
static const int modifications = 3; static const int modifications = 6;
} }

View File

@@ -49,7 +49,11 @@ class _AptManagePageState extends State<AptManagePage>
Navigator.of(context).pop(); Navigator.of(context).pop();
return; return;
} }
locator<AptProvider>().init(si.client!, si.status.sysVer.dist, () => scrollController.jumpTo(scrollController.position.maxScrollExtent)); locator<AptProvider>().init(
si.client!,
si.status.sysVer.dist,
() =>
scrollController.jumpTo(scrollController.position.maxScrollExtent));
} }
@override @override

View File

@@ -1,10 +1,10 @@
import 'dart:io';
import 'package:dart_ping/dart_ping.dart';
import 'package:dart_ping_ios/dart_ping_ios.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:toolbox/core/extension/uint8list.dart';
import 'package:toolbox/core/utils.dart'; import 'package:toolbox/core/utils.dart';
import 'package:toolbox/data/model/server/ping_result.dart';
import 'package:toolbox/data/provider/server.dart';
import 'package:toolbox/data/res/color.dart'; import 'package:toolbox/data/res/color.dart';
import 'package:toolbox/locator.dart';
import 'package:toolbox/view/widget/input_field.dart'; import 'package:toolbox/view/widget/input_field.dart';
import 'package:toolbox/view/widget/round_rect_card.dart'; import 'package:toolbox/view/widget/round_rect_card.dart';
@@ -18,16 +18,13 @@ class PingPage extends StatefulWidget {
class _PingPageState extends State<PingPage> class _PingPageState extends State<PingPage>
with AutomaticKeepAliveClientMixin { with AutomaticKeepAliveClientMixin {
late TextEditingController _textEditingController; late TextEditingController _textEditingController;
Ping? _ping;
late MediaQueryData _media; late MediaQueryData _media;
final List<PingResult> _results = [];
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_textEditingController = TextEditingController(text: ''); _textEditingController = TextEditingController(text: '');
if (Platform.isIOS) {
DartPingIOS.register();
}
} }
@override @override
@@ -47,25 +44,61 @@ class _PingPageState extends State<PingPage>
const SizedBox(height: 13), const SizedBox(height: 13),
buildInput(context, _textEditingController, maxLines: 1), buildInput(context, _textEditingController, maxLines: 1),
_buildControl(), _buildControl(),
RoundRectCard(ConstrainedBox( SizedBox(
constraints: BoxConstraints( width: double.infinity,
minWidth: double.infinity, height: _media.size.height * 0.6,
minHeight: _media.size.height * 0.6, child: ListView.builder(
), itemCount: _results.length,
)), itemBuilder: (context, index) {
final result = _results[index];
return _buildResultItem(result);
}),
),
])), ])),
onTap: () => FocusScope.of(context).requestFocus(FocusNode()), onTap: () => FocusScope.of(context).requestFocus(FocusNode()),
), ),
); );
} }
void doPing() { Widget _buildResultItem(PingResult result) {
_ping = Ping(_textEditingController.text.trim()); return RoundRectCard(ListTile(
_ping!.stream.listen((event) { contentPadding: const EdgeInsets.symmetric(vertical: 7, horizontal: 17),
final resp = event.response.toString(); title: Text(result.serverName,
if (resp == 'null') return; style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold, color: primaryColor)),
subtitle: Text(_buildPingSummary(result)),
trailing: Text(
'Avg: ' +
(result.statistic?.avg?.toStringAsFixed(2) ?? 'unkown') +
' ms',
style: TextStyle(fontSize: 14, color: primaryColor)),
));
}
String _buildPingSummary(PingResult result) {
final ip = result.ip ?? 'unkown';
final ttl = result.results?.first.ttl ?? 'unkown';
final loss = result.statistic?.loss ?? 'unkown';
final min = result.statistic?.min ?? 'unkown';
final max = result.statistic?.max ?? 'unkown';
return '$ip\nttl: $ttl, loss: $loss%\nmin: $min ms, max: $max ms';
}
Future<void> doPing() async {
_results.clear();
final target = _textEditingController.text.trim();
if (target.isEmpty) {
showSnackBar(context, const Text('Please input a target'));
return;
}
for (var si in locator<ServerProvider>().servers) {
if (si.client == null) {
continue;
}
final result = await si.client!.run('ping -c 3 $target').string;
_results.add(PingResult.parse(si.info.name, result));
setState(() {}); setState(() {});
}); }
} }
Widget _buildControl() { Widget _buildControl() {
@@ -86,11 +119,11 @@ class _PingPageState extends State<PingPage>
SizedBox( SizedBox(
width: 7, width: 7,
), ),
Text('Stop') Text('Clear')
], ],
), ),
onPressed: () { onPressed: () {
if (_ping != null) _ping!.stop(); _results.clear();
}, },
), ),
TextButton( TextButton(

View File

@@ -117,20 +117,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.0" version: "1.0.0"
dart_ping:
dependency: "direct main"
description:
name: dart_ping
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.2"
dart_ping_ios:
dependency: "direct main"
description:
name: dart_ping_ios
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
dartssh2: dartssh2:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -206,13 +192,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.2" version: "1.3.2"
flutter_icmp_ping:
dependency: transitive
description:
name: flutter_icmp_ping
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.2"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
@@ -356,13 +335,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.0" version: "1.0.0"
open_file:
dependency: "direct main"
description:
name: open_file
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.1"
path: path:
dependency: transitive dependency: transitive
description: description:
@@ -371,7 +343,7 @@ packages:
source: hosted source: hosted
version: "1.8.0" version: "1.8.0"
path_provider: path_provider:
dependency: "direct main" dependency: transitive
description: description:
name: path_provider name: path_provider
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"

View File

@@ -54,12 +54,8 @@ dependencies:
r_upgrade: ^0.3.6 r_upgrade: ^0.3.6
pull_to_refresh: ^2.0.0 pull_to_refresh: ^2.0.0
marquee: ^2.2.0 marquee: ^2.2.0
dart_ping: ^6.1.1
dart_ping_ios: ^1.0.0
dropdown_button2: ^1.1.1 dropdown_button2: ^1.1.1
flutter_advanced_drawer: ^1.3.0 flutter_advanced_drawer: ^1.3.0
open_file: ^3.2.1
path_provider: ^2.0.9
dev_dependencies: dev_dependencies: