|
|
@@ -27,6 +27,8 @@ public partial class DeviceDetailsWindow : Window
|
|
|
private bool _suppressConfigChangeHandling;
|
|
|
private CancellationTokenSource? _statusMessageCts;
|
|
|
|
|
|
+ private readonly record struct ConfigLoadOutcome(bool RequestSucceeded, int FailedCount);
|
|
|
+
|
|
|
public DeviceDetailsWindow(string baseAddress, string localIPv4, string password)
|
|
|
{
|
|
|
InitializeComponent();
|
|
|
@@ -75,13 +77,26 @@ public partial class DeviceDetailsWindow : Window
|
|
|
{
|
|
|
var editor = new InterfaceEditor(interfaces.Interfaces[i], i + 1);
|
|
|
_interfaces.Add(editor);
|
|
|
- await LoadRemoteInterfaceConfigAsync(editor);
|
|
|
}
|
|
|
|
|
|
+ var configLoadOutcome = await LoadRemoteInterfaceConfigsAsync();
|
|
|
+
|
|
|
_configValidated = false;
|
|
|
_configDirty = false;
|
|
|
- SetConfigStateMessage("已读取全部网口配置。", false);
|
|
|
- ShowStatusMessage("已读取全部网口配置。", StatusMessageType.Success);
|
|
|
+ if (!configLoadOutcome.RequestSucceeded)
|
|
|
+ {
|
|
|
+ SetConfigStateMessage("读取全部网口配置失败。", true);
|
|
|
+ }
|
|
|
+ else if (configLoadOutcome.FailedCount > 0)
|
|
|
+ {
|
|
|
+ SetConfigStateMessage($"已读取部分网口配置,{configLoadOutcome.FailedCount} 个网口读取失败。", true);
|
|
|
+ ShowStatusMessage($"已读取部分网口配置,{configLoadOutcome.FailedCount} 个网口读取失败。", StatusMessageType.Warning);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ SetConfigStateMessage("已读取全部网口配置。", false);
|
|
|
+ ShowStatusMessage("已读取全部网口配置。", StatusMessageType.Success);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private void ClearDetails()
|
|
|
@@ -104,57 +119,42 @@ public partial class DeviceDetailsWindow : Window
|
|
|
return Uri.TryCreate(baseAddress, UriKind.Absolute, out var uri) ? uri.Host : baseAddress;
|
|
|
}
|
|
|
|
|
|
- private async Task LoadRemoteInterfaceConfigAsync(InterfaceEditor editor, bool useBusyState = false)
|
|
|
+ private async Task<ConfigLoadOutcome> LoadRemoteInterfaceConfigsAsync(bool useBusyState = false)
|
|
|
{
|
|
|
if (useBusyState)
|
|
|
{
|
|
|
- SetBusyState(true, "正在读取 Linux 端 IP 配置...");
|
|
|
+ SetBusyState(true, "正在读取全部网口配置...");
|
|
|
}
|
|
|
|
|
|
try
|
|
|
{
|
|
|
- var result = await _serverApiService.GetInterfaceConfigAsync(_baseAddress, _password, _localIPv4, editor.SystemName);
|
|
|
+ var result = await _serverApiService.GetInterfaceConfigsAsync(_baseAddress, _password, _localIPv4);
|
|
|
if (!result.Success || result.Data is null)
|
|
|
{
|
|
|
- ShowStatusMessage($"读取目标网口 {editor.DisplayLabel} 配置失败:{result.Message}", StatusMessageType.Error);
|
|
|
- return;
|
|
|
+ ShowStatusMessage($"读取全部网口配置失败:{result.Message}", StatusMessageType.Error);
|
|
|
+ return new ConfigLoadOutcome(false, _interfaces.Count);
|
|
|
}
|
|
|
|
|
|
- var config = result.Data;
|
|
|
- _suppressConfigChangeHandling = true;
|
|
|
- editor.Dhcp4 = config.Dhcp4;
|
|
|
- editor.Addresses.Clear();
|
|
|
- foreach (var address in config.EffectiveAddresses)
|
|
|
- {
|
|
|
- editor.Addresses.Add(new EditableAddress(editor) { IP = address.IP, Mask = PrefixToMask(address.Prefix) });
|
|
|
- }
|
|
|
- editor.Routes.Clear();
|
|
|
- editor.DefaultGatewayEnabled = false;
|
|
|
- editor.DefaultGateway = string.Empty;
|
|
|
- foreach (var route in config.EffectiveRoutes)
|
|
|
- {
|
|
|
- if (route.To.Equals("default", StringComparison.OrdinalIgnoreCase))
|
|
|
- {
|
|
|
- editor.DefaultGatewayEnabled = true;
|
|
|
- editor.DefaultGateway = route.Via;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- editor.Routes.Add(CreateEditableRoute(editor, route));
|
|
|
- }
|
|
|
- }
|
|
|
- editor.CustomRoutesEnabled = editor.Routes.Count > 0;
|
|
|
- editor.Dns.Clear();
|
|
|
- if (config.Dns is not null)
|
|
|
+ var configsByInterface = result.Data.Configs.ToDictionary(config => config.Interface, StringComparer.OrdinalIgnoreCase);
|
|
|
+ var errorsByInterface = result.Data.Errors.ToDictionary(error => error.Interface, StringComparer.OrdinalIgnoreCase);
|
|
|
+ var failedConfigCount = 0;
|
|
|
+ foreach (var editor in _interfaces)
|
|
|
{
|
|
|
- foreach (var dns in config.Dns)
|
|
|
+ if (configsByInterface.TryGetValue(editor.SystemName, out var config))
|
|
|
{
|
|
|
- editor.Dns.Add(new EditableDns(editor) { Address = dns });
|
|
|
+ ApplyRemoteInterfaceConfig(editor, config);
|
|
|
+ continue;
|
|
|
}
|
|
|
+
|
|
|
+ failedConfigCount++;
|
|
|
+ var message = errorsByInterface.TryGetValue(editor.SystemName, out var error)
|
|
|
+ ? error.Message
|
|
|
+ : "Server 未返回该网口配置。";
|
|
|
+ ShowStatusMessage($"读取目标网口 {editor.DisplayLabel} 配置失败:{message}", StatusMessageType.Error);
|
|
|
}
|
|
|
- editor.CaptureOriginalConfiguration();
|
|
|
- _suppressConfigChangeHandling = false;
|
|
|
+
|
|
|
UpdateButtonStates();
|
|
|
+ return new ConfigLoadOutcome(true, failedConfigCount);
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
@@ -166,6 +166,62 @@ public partial class DeviceDetailsWindow : Window
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private void ApplyRemoteInterfaceConfig(InterfaceEditor editor, RemoteInterfaceConfig config)
|
|
|
+ {
|
|
|
+ _suppressConfigChangeHandling = true;
|
|
|
+ editor.Dhcp4 = config.Dhcp4;
|
|
|
+ editor.Addresses.Clear();
|
|
|
+ foreach (var address in config.EffectiveAddresses)
|
|
|
+ {
|
|
|
+ editor.Addresses.Add(new EditableAddress(editor) { IP = address.IP, Mask = PrefixToMask(address.Prefix) });
|
|
|
+ }
|
|
|
+ editor.Routes.Clear();
|
|
|
+ editor.DefaultGatewayEnabled = false;
|
|
|
+ editor.DefaultGateway = string.Empty;
|
|
|
+ foreach (var route in config.EffectiveRoutes)
|
|
|
+ {
|
|
|
+ if (route.To.Equals("default", StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ editor.DefaultGatewayEnabled = true;
|
|
|
+ editor.DefaultGateway = route.Via;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ editor.Routes.Add(CreateEditableRoute(editor, route));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ editor.CustomRoutesEnabled = editor.Routes.Count > 0;
|
|
|
+ editor.Dns.Clear();
|
|
|
+ if (config.Dns is not null)
|
|
|
+ {
|
|
|
+ foreach (var dns in config.Dns)
|
|
|
+ {
|
|
|
+ editor.Dns.Add(new EditableDns(editor) { Address = dns });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ editor.CaptureOriginalConfiguration();
|
|
|
+ _suppressConfigChangeHandling = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void ShowConfigLoadStatus(ConfigLoadOutcome outcome, string successMessage, string partialMessagePrefix, string failureMessage)
|
|
|
+ {
|
|
|
+ if (!outcome.RequestSucceeded)
|
|
|
+ {
|
|
|
+ SetConfigStateMessage(failureMessage, true);
|
|
|
+ }
|
|
|
+ else if (outcome.FailedCount > 0)
|
|
|
+ {
|
|
|
+ var message = $"{partialMessagePrefix},{outcome.FailedCount} 个网口读取失败。";
|
|
|
+ SetConfigStateMessage(message, true);
|
|
|
+ ShowStatusMessage(message, StatusMessageType.Warning);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ SetConfigStateMessage(successMessage, false);
|
|
|
+ ShowStatusMessage(successMessage, StatusMessageType.Success);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private async void ReloadInterfaceConfigButton_OnClick(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
if (!ConfirmDiscardPendingChanges("当前配置已修改但尚未保存,重新获取会丢失未保存内容。是否继续重新获取?", "确认重新获取配置"))
|
|
|
@@ -176,15 +232,10 @@ public partial class DeviceDetailsWindow : Window
|
|
|
SetBusyState(true, "正在重新获取全部网口配置...");
|
|
|
try
|
|
|
{
|
|
|
- foreach (var editor in _interfaces)
|
|
|
- {
|
|
|
- await LoadRemoteInterfaceConfigAsync(editor);
|
|
|
- }
|
|
|
-
|
|
|
+ var configLoadOutcome = await LoadRemoteInterfaceConfigsAsync();
|
|
|
_configValidated = false;
|
|
|
_configDirty = false;
|
|
|
- SetConfigStateMessage("已重新获取全部网口配置。", false);
|
|
|
- ShowStatusMessage("已重新获取全部网口配置。", StatusMessageType.Success);
|
|
|
+ ShowConfigLoadStatus(configLoadOutcome, "已重新获取全部网口配置。", "已重新获取部分网口配置", "重新获取全部网口配置失败。");
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
@@ -334,10 +385,7 @@ public partial class DeviceDetailsWindow : Window
|
|
|
_configValidated = false;
|
|
|
_configDirty = false;
|
|
|
SetConfigStateMessage("配置已保存,当前显示为设备最新配置。", false);
|
|
|
- foreach (var editor in _interfaces)
|
|
|
- {
|
|
|
- await LoadRemoteInterfaceConfigAsync(editor);
|
|
|
- }
|
|
|
+ await LoadRemoteInterfaceConfigsAsync();
|
|
|
}
|
|
|
|
|
|
ShowTaskCompletionDialog(task);
|