From 9e2d49773fdbe1e6239bca81ab6d7775534c3c9f Mon Sep 17 00:00:00 2001 From: Junyuan Feng Date: Tue, 3 May 2022 11:42:30 +0800 Subject: [PATCH] New Ping, use servers to ping --- lib/data/model/server/ping_result.dart | 79 ++++++++++++++++++++++++++ lib/data/res/build_data.dart | 6 +- lib/view/page/apt.dart | 6 +- lib/view/page/ping.dart | 77 ++++++++++++++++++------- pubspec.lock | 30 +--------- pubspec.yaml | 4 -- 6 files changed, 143 insertions(+), 59 deletions(-) create mode 100644 lib/data/model/server/ping_result.dart diff --git a/lib/data/model/server/ping_result.dart b/lib/data/model/server/ping_result.dart new file mode 100644 index 00000000..0495466c --- /dev/null +++ b/lib/data/model/server/ping_result.dart @@ -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? 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 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)!); + } +} diff --git a/lib/data/res/build_data.dart b/lib/data/res/build_data.dart index 2a4721bb..8ed166cb 100644 --- a/lib/data/res/build_data.dart +++ b/lib/data/res/build_data.dart @@ -2,9 +2,9 @@ class BuildData { static const String name = "ServerBox"; - static const int build = 115; + static const int build = 117; 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"; - static const String buildAt = "2022-05-03 08:33:56.199910"; - static const int modifications = 3; + static const String buildAt = "2022-05-03 11:39:22.853075"; + static const int modifications = 6; } diff --git a/lib/view/page/apt.dart b/lib/view/page/apt.dart index 71c0b717..6d1b1674 100644 --- a/lib/view/page/apt.dart +++ b/lib/view/page/apt.dart @@ -49,7 +49,11 @@ class _AptManagePageState extends State Navigator.of(context).pop(); return; } - locator().init(si.client!, si.status.sysVer.dist, () => scrollController.jumpTo(scrollController.position.maxScrollExtent)); + locator().init( + si.client!, + si.status.sysVer.dist, + () => + scrollController.jumpTo(scrollController.position.maxScrollExtent)); } @override diff --git a/lib/view/page/ping.dart b/lib/view/page/ping.dart index 602bfe6b..7ae58bbc 100644 --- a/lib/view/page/ping.dart +++ b/lib/view/page/ping.dart @@ -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:toolbox/core/extension/uint8list.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/locator.dart'; import 'package:toolbox/view/widget/input_field.dart'; import 'package:toolbox/view/widget/round_rect_card.dart'; @@ -18,16 +18,13 @@ class PingPage extends StatefulWidget { class _PingPageState extends State with AutomaticKeepAliveClientMixin { late TextEditingController _textEditingController; - Ping? _ping; late MediaQueryData _media; + final List _results = []; @override void initState() { super.initState(); _textEditingController = TextEditingController(text: ''); - if (Platform.isIOS) { - DartPingIOS.register(); - } } @override @@ -47,25 +44,61 @@ class _PingPageState extends State const SizedBox(height: 13), buildInput(context, _textEditingController, maxLines: 1), _buildControl(), - RoundRectCard(ConstrainedBox( - constraints: BoxConstraints( - minWidth: double.infinity, - minHeight: _media.size.height * 0.6, - ), - )), + SizedBox( + width: double.infinity, + height: _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()), ), ); } - void doPing() { - _ping = Ping(_textEditingController.text.trim()); - _ping!.stream.listen((event) { - final resp = event.response.toString(); - if (resp == 'null') return; + Widget _buildResultItem(PingResult result) { + return RoundRectCard(ListTile( + contentPadding: const EdgeInsets.symmetric(vertical: 7, horizontal: 17), + title: Text(result.serverName, + 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 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().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(() {}); - }); + } } Widget _buildControl() { @@ -86,11 +119,11 @@ class _PingPageState extends State SizedBox( width: 7, ), - Text('Stop') + Text('Clear') ], ), onPressed: () { - if (_ping != null) _ping!.stop(); + _results.clear(); }, ), TextButton( diff --git a/pubspec.lock b/pubspec.lock index f6b63877..ca4c9bb4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -117,20 +117,6 @@ packages: url: "https://pub.dartlang.org" source: hosted 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: dependency: "direct main" description: @@ -206,13 +192,6 @@ packages: url: "https://pub.dartlang.org" source: hosted 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: dependency: "direct dev" description: @@ -356,13 +335,6 @@ packages: url: "https://pub.dartlang.org" source: hosted 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: dependency: transitive description: @@ -371,7 +343,7 @@ packages: source: hosted version: "1.8.0" path_provider: - dependency: "direct main" + dependency: transitive description: name: path_provider url: "https://pub.dartlang.org" diff --git a/pubspec.yaml b/pubspec.yaml index c10586da..2e58241f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -54,12 +54,8 @@ dependencies: r_upgrade: ^0.3.6 pull_to_refresh: ^2.0.0 marquee: ^2.2.0 - dart_ping: ^6.1.1 - dart_ping_ios: ^1.0.0 dropdown_button2: ^1.1.1 flutter_advanced_drawer: ^1.3.0 - open_file: ^3.2.1 - path_provider: ^2.0.9 dev_dependencies: