|
|
@@ -1,4 +1,5 @@
|
|
|
using System.Collections.Generic;
|
|
|
+using System.Collections.ObjectModel;
|
|
|
using System.Windows;
|
|
|
using System.Windows.Controls;
|
|
|
using System.Windows.Input;
|
|
|
@@ -18,8 +19,10 @@ public partial class MainWindow : Window
|
|
|
private readonly ServerApiService _serverApiService = new();
|
|
|
private IReadOnlyList<AdapterInfo> _allAdapters = [];
|
|
|
private IReadOnlyList<AdapterInfo> _adapters = [];
|
|
|
- private IReadOnlyList<DiscoveredDevice> _discoveredDevices = [];
|
|
|
+ private readonly ObservableCollection<DiscoveredDevice> _discoveredDevices = [];
|
|
|
private bool _isBusy;
|
|
|
+ private bool _isSearchingDevices;
|
|
|
+ private CancellationTokenSource? _deviceSearchCts;
|
|
|
private CancellationTokenSource? _statusMessageCts;
|
|
|
|
|
|
public MainWindow()
|
|
|
@@ -50,17 +53,19 @@ public partial class MainWindow : Window
|
|
|
AdapterComboBox.ItemsSource = _adapters;
|
|
|
|
|
|
var selected = selectedAdapterId is null
|
|
|
- ? _networkAdapterService.GetRecommendedAdapter(_adapters)
|
|
|
- : _adapters.FirstOrDefault(adapter => adapter.Id == selectedAdapterId) ?? _networkAdapterService.GetRecommendedAdapter(_adapters);
|
|
|
+ ? null
|
|
|
+ : _adapters.FirstOrDefault(adapter => adapter.Id == selectedAdapterId);
|
|
|
|
|
|
if (selected is not null)
|
|
|
{
|
|
|
AdapterComboBox.SelectedItem = selected;
|
|
|
}
|
|
|
- else if (_adapters.Count > 0)
|
|
|
+ else
|
|
|
{
|
|
|
- AdapterComboBox.SelectedIndex = 0;
|
|
|
+ AdapterComboBox.SelectedIndex = -1;
|
|
|
}
|
|
|
+
|
|
|
+ UpdateAdapterPlaceholder();
|
|
|
}
|
|
|
|
|
|
private static bool IsUsableAdapter(AdapterInfo adapter)
|
|
|
@@ -72,14 +77,18 @@ public partial class MainWindow : Window
|
|
|
{
|
|
|
if (AdapterComboBox.SelectedItem is not AdapterInfo adapter)
|
|
|
{
|
|
|
+ CancelDeviceSearch();
|
|
|
ClearDiscoveredDevices();
|
|
|
+ UpdateAdapterPlaceholder();
|
|
|
SetStatus("请选择一块网卡。", StatusMessageType.Warning, false);
|
|
|
UpdateButtonStates();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ UpdateAdapterPlaceholder();
|
|
|
if (!adapter.HasLink)
|
|
|
{
|
|
|
+ CancelDeviceSearch();
|
|
|
ClearDiscoveredDevices();
|
|
|
SetStatus("当前网卡未检测到链路,请检查网线连接。", StatusMessageType.Warning, true);
|
|
|
UpdateButtonStates();
|
|
|
@@ -90,12 +99,18 @@ public partial class MainWindow : Window
|
|
|
_ = SearchDevicesAsync(adapter);
|
|
|
}
|
|
|
|
|
|
+ private void UpdateAdapterPlaceholder()
|
|
|
+ {
|
|
|
+ AdapterPlaceholderTextBlock.Visibility = AdapterComboBox.SelectedItem is null ? Visibility.Visible : Visibility.Collapsed;
|
|
|
+ }
|
|
|
+
|
|
|
private async void RefreshAdaptersButton_OnClick(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
SetBusyState(true, "正在刷新本机网卡...");
|
|
|
try
|
|
|
{
|
|
|
- RefreshAdapters();
|
|
|
+ var selectedAdapterId = (AdapterComboBox.SelectedItem as AdapterInfo)?.Id;
|
|
|
+ RefreshAdapters(selectedAdapterId);
|
|
|
SetStatus("已刷新本机网卡。", StatusMessageType.Success, true);
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
@@ -147,6 +162,7 @@ public partial class MainWindow : Window
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(adapter.IPv4Address))
|
|
|
{
|
|
|
+ CancelDeviceSearch();
|
|
|
ClearDiscoveredDevices();
|
|
|
SetStatus("当前网卡没有可用 IPv4,无法搜索设备。", StatusMessageType.Error, true);
|
|
|
return;
|
|
|
@@ -154,19 +170,25 @@ public partial class MainWindow : Window
|
|
|
|
|
|
if (!adapter.HasLink)
|
|
|
{
|
|
|
+ CancelDeviceSearch();
|
|
|
ClearDiscoveredDevices();
|
|
|
SetStatus("当前网卡未检测到链路,请检查网线连接。", StatusMessageType.Warning, true);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- SetBusyState(true, "正在搜索设备...");
|
|
|
+ _deviceSearchCts?.Cancel();
|
|
|
+ var searchCts = new CancellationTokenSource();
|
|
|
+ _deviceSearchCts = searchCts;
|
|
|
+ SetDeviceSearchState(true);
|
|
|
ClearDiscoveredDevices();
|
|
|
await Dispatcher.InvokeAsync(() => { }, System.Windows.Threading.DispatcherPriority.Render);
|
|
|
|
|
|
try
|
|
|
{
|
|
|
- _discoveredDevices = await _discoveryService.DiscoverManyAsync(adapter.IPv4Address);
|
|
|
- DiscoveredDevicesListView.ItemsSource = _discoveredDevices;
|
|
|
+ await _discoveryService.DiscoverManyAsync(
|
|
|
+ adapter.IPv4Address,
|
|
|
+ device => Dispatcher.Invoke(() => UpsertDiscoveredDevice(device)),
|
|
|
+ searchCts.Token);
|
|
|
if (_discoveredDevices.Count == 0)
|
|
|
{
|
|
|
SetStatus("未发现 169.254 开头的设备 IP,请确认网卡、网线、远端服务和维护网段配置。", StatusMessageType.Warning, true);
|
|
|
@@ -175,6 +197,9 @@ public partial class MainWindow : Window
|
|
|
|
|
|
SetStatus($"已发现 {_discoveredDevices.Count} 台设备,请双击 IP 连接。", StatusMessageType.Success, true);
|
|
|
}
|
|
|
+ catch (OperationCanceledException)
|
|
|
+ {
|
|
|
+ }
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
SetStatus($"搜索设备失败:{ex.Message}", StatusMessageType.Error, true);
|
|
|
@@ -182,7 +207,10 @@ public partial class MainWindow : Window
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
- SetBusyState(false);
|
|
|
+ if (ReferenceEquals(_deviceSearchCts, searchCts))
|
|
|
+ {
|
|
|
+ SetDeviceSearchState(false);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -200,7 +228,7 @@ public partial class MainWindow : Window
|
|
|
var adapter = AdapterComboBox.SelectedItem as AdapterInfo;
|
|
|
var hasAdapter = adapter is not null;
|
|
|
RefreshAdaptersButton.IsEnabled = !_isBusy;
|
|
|
- SearchDevicesButton.IsEnabled = !_isBusy && hasAdapter && adapter!.HasLink;
|
|
|
+ SearchDevicesButton.IsEnabled = !_isBusy && !_isSearchingDevices && hasAdapter && adapter!.HasLink;
|
|
|
}
|
|
|
|
|
|
private void RefreshAdapters(string? selectedAdapterId = null)
|
|
|
@@ -211,10 +239,30 @@ public partial class MainWindow : Window
|
|
|
|
|
|
private void ClearDiscoveredDevices()
|
|
|
{
|
|
|
- _discoveredDevices = [];
|
|
|
+ _discoveredDevices.Clear();
|
|
|
DiscoveredDevicesListView.ItemsSource = _discoveredDevices;
|
|
|
}
|
|
|
|
|
|
+ private void UpsertDiscoveredDevice(DiscoveredDevice device)
|
|
|
+ {
|
|
|
+ var existing = _discoveredDevices.FirstOrDefault(value => string.Equals(value.Lan2Ip, device.Lan2Ip, StringComparison.OrdinalIgnoreCase));
|
|
|
+ if (existing is not null)
|
|
|
+ {
|
|
|
+ var index = _discoveredDevices.IndexOf(existing);
|
|
|
+ _discoveredDevices[index] = device;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var insertIndex = 0;
|
|
|
+ while (insertIndex < _discoveredDevices.Count && string.Compare(_discoveredDevices[insertIndex].Lan2Ip, device.Lan2Ip, StringComparison.OrdinalIgnoreCase) < 0)
|
|
|
+ {
|
|
|
+ insertIndex++;
|
|
|
+ }
|
|
|
+
|
|
|
+ _discoveredDevices.Insert(insertIndex, device);
|
|
|
+ SetStatus($"已发现 {_discoveredDevices.Count} 台设备,搜索仍在继续。", StatusMessageType.Success, true);
|
|
|
+ }
|
|
|
+
|
|
|
private async void DiscoveredDevicesListView_OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
|
|
{
|
|
|
if (!_isBusy && DiscoveredDevicesListView.SelectedItem is DiscoveredDevice device)
|
|
|
@@ -408,6 +456,21 @@ public partial class MainWindow : Window
|
|
|
BusyMessageTextBlock.Text = string.IsNullOrWhiteSpace(message) ? "正在处理,请稍候..." : message;
|
|
|
AdapterComboBox.IsEnabled = !isBusy;
|
|
|
RefreshAdaptersButton.IsEnabled = !isBusy;
|
|
|
- SearchDevicesButton.IsEnabled = !isBusy && AdapterComboBox.SelectedItem is AdapterInfo adapter && adapter.HasLink;
|
|
|
+ SearchDevicesButton.IsEnabled = !isBusy && !_isSearchingDevices && AdapterComboBox.SelectedItem is AdapterInfo adapter && adapter.HasLink;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void SetDeviceSearchState(bool isSearching)
|
|
|
+ {
|
|
|
+ _isSearchingDevices = isSearching;
|
|
|
+ SearchProgressBar.Visibility = isSearching ? Visibility.Visible : Visibility.Collapsed;
|
|
|
+ SearchProgressTextBlock.Visibility = isSearching ? Visibility.Visible : Visibility.Collapsed;
|
|
|
+ SearchDevicesButton.Content = isSearching ? "搜索中..." : "重新搜索设备";
|
|
|
+ UpdateButtonStates();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void CancelDeviceSearch()
|
|
|
+ {
|
|
|
+ _deviceSearchCts?.Cancel();
|
|
|
+ SetDeviceSearchState(false);
|
|
|
}
|
|
|
}
|