using HMI.UI.Util; using Panuon.WPF.UI; using Plugin.DeviceConfig.Entity; using Plugin.DeviceConfig.Model; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Windows; namespace Plugin.DeviceConfig { /// /// BatchGenerateModal.xaml 的交互逻辑 /// public partial class BatchGenerateModal : WindowX { private DeviceConfigModel _model; private ObservableCollection _previewItems = new(); public BatchGenerateModal() { DataContext = this; InitializeComponent(); } /// /// 打开弹窗 /// public bool? Open(DeviceConfigModel model) { _model = model; // 初始化枚举下拉 cbDeviceType.ItemsSource = Enum.GetValues(typeof(DeviceKind)).Cast().ToList(); cbTransport.ItemsSource = Enum.GetValues(typeof(TransportType)).Cast().ToList(); // 默认值 cbDeviceType.SelectedIndex = 0; cbTransport.SelectedIndex = 0; tbAddressRule.Text = "192.168.10.{0}"; tbSlotStart.Text = "Slot001"; tbDeviceIdPrefix.Text = "DEV"; previewListView.ItemsSource = _previewItems; return ShowDialog(); } #region 预览 private void PreviewBtn_Click(object sender, RoutedEventArgs e) { try { GeneratePreview(); } catch (Exception ex) { tbConflictMsg.Text = $"生成预览失败: {ex.Message}"; tbConflictMsg.Visibility = Visibility.Visible; } } private void GeneratePreview() { _previewItems.Clear(); tbConflictMsg.Visibility = Visibility.Collapsed; if (cbDeviceType.SelectedItem is not DeviceKind kind) { ShowConflict("请选择设备类型"); return; } if (cbTransport.SelectedItem is not TransportType transport) { ShowConflict("请选择传输方式"); return; } int count = (int)(niDeviceCount.Value ?? 1); int channelsPerDevice = (int)(niChannelsPerDevice.Value ?? 1); int startPort = (int)(niStartPort.Value ?? 30000); string addressRule = tbAddressRule.Text?.Trim() ?? string.Empty; string slotStart = tbSlotStart.Text?.Trim() ?? string.Empty; string prefix = tbDeviceIdPrefix.Text?.Trim() ?? "DEV"; string driverType = tbDriverType.Text?.Trim() ?? string.Empty; // 检测已有设备ID var existingDevices = _model.GetAllDevices(); var existingIds = new HashSet(existingDevices.Select(d => d.DeviceId)); var existingEndpoints = new HashSet(existingDevices.Select(d => d.Endpoint)); var conflicts = new List(); var generatedIds = new HashSet(); var generatedEndpoints = new HashSet(); for (int i = 1; i <= count; i++) { var deviceId = $"{prefix}{i:D3}"; var endpoint = string.Format(addressRule, i); if (transport == TransportType.Tcp && !string.IsNullOrEmpty(addressRule)) { endpoint = $"{endpoint}:{startPort + i - 1}"; } else if (transport == TransportType.Serial) { endpoint = $"COM{i}"; } // 解析 SlotStart 的序号部分并递增 var slotPrefix = new string(slotStart.TakeWhile(c => !char.IsDigit(c)).ToArray()); var slotNumStr = new string(slotStart.SkipWhile(c => !char.IsDigit(c)).ToArray()); int.TryParse(slotNumStr, out int startNum); var slotId = $"{slotPrefix}{(startNum + i - 1):D3}"; // 检测冲突 if (existingIds.Contains(deviceId) || generatedIds.Contains(deviceId)) conflicts.Add($"设备ID冲突: {deviceId}"); if (existingEndpoints.Contains(endpoint) || generatedEndpoints.Contains(endpoint)) conflicts.Add($"Endpoint冲突: {endpoint}"); generatedIds.Add(deviceId); generatedEndpoints.Add(endpoint); _previewItems.Add(new BatchPreviewItem { DeviceId = deviceId, Kind = kind, DriverType = driverType, Transport = transport, Endpoint = endpoint, ChannelCount = channelsPerDevice, SlotStart = slotId, }); } if (conflicts.Count > 0) { ShowConflict(string.Join("\n", conflicts)); } } private void ShowConflict(string msg) { tbConflictMsg.Text = msg; tbConflictMsg.Visibility = Visibility.Visible; } #endregion #region 保存 private void SaveBtn_Click(object sender, RoutedEventArgs e) { try { if (_previewItems.Count == 0) { ToastHelper.Warning("请先生成预览"); return; } if (tbConflictMsg.Visibility == Visibility.Visible) { if (MsgBoxHelper.WarnMessage(this, "存在冲突项,是否仍要继续保存?") != MessageBoxResult.OK) { return; } } int channelsPerDevice = (int)(niChannelsPerDevice.Value ?? 1); string slotStart = tbSlotStart.Text?.Trim() ?? string.Empty; foreach (var item in _previewItems) { // 保存设备 var device = new DeviceDefinition { DeviceId = item.DeviceId, Kind = item.Kind, DriverType = item.DriverType, Transport = item.Transport, Endpoint = item.Endpoint, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now, }; _model.SaveDevice(device); // 保存通道 for (int ch = 0; ch < channelsPerDevice; ch++) { var channel = new DeviceChannel { DeviceId = item.DeviceId, ChannelId = ch, Capability = $"CH{ch}", MaxVoltage = 0, MaxCurrent = 0, }; _model.SaveChannel(channel); } // 保存 Slot 映射 if (!string.IsNullOrEmpty(item.SlotStart)) { var mapping = new SlotDeviceMapping { SlotId = item.SlotStart, Capability = "Default", Version = "1.0", DeviceId = item.DeviceId, ChannelId = 0, MappingVersion = "1.0", }; _model.SaveSlotMapping(mapping); } } ToastHelper.Success($"成功生成 {_previewItems.Count} 个设备"); DialogResult = true; Close(); } catch (Exception ex) { ToastHelper.Error($"保存失败: {ex.Message}"); } } private void CancelBtn_Click(object sender, RoutedEventArgs e) { DialogResult = false; Close(); } #endregion } /// /// 批量生成预览项 /// public class BatchPreviewItem { public string DeviceId { get; set; } = string.Empty; public DeviceKind Kind { get; set; } public string DriverType { get; set; } = string.Empty; public TransportType Transport { get; set; } public string Endpoint { get; set; } = string.Empty; public int ChannelCount { get; set; } public string SlotStart { get; set; } = string.Empty; } }