From 9d1660270272180cd71147d6ddd53d1382643d2e Mon Sep 17 00:00:00 2001
From: wenxiangchao <wenxiangchao@ihengxin.com.cn>
Date: 星期一, 02 十一月 2026 18:00:14 +0800
Subject: [PATCH] merge: 并入远程 Initial commit 作为历史根节点

---
 Plugin.DeviceConfig/BatchGenerateModal.xaml.cs |  252 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 252 insertions(+), 0 deletions(-)

diff --git a/Plugin.DeviceConfig/BatchGenerateModal.xaml.cs b/Plugin.DeviceConfig/BatchGenerateModal.xaml.cs
new file mode 100644
index 0000000..b5eb211
--- /dev/null
+++ b/Plugin.DeviceConfig/BatchGenerateModal.xaml.cs
@@ -0,0 +1,252 @@
+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
+{
+    /// <summary>
+    /// BatchGenerateModal.xaml 鐨勪氦浜掗�昏緫
+    /// </summary>
+    public partial class BatchGenerateModal : WindowX
+    {
+        private DeviceConfigModel _model;
+        private ObservableCollection<BatchPreviewItem> _previewItems = new();
+
+        public BatchGenerateModal()
+        {
+            DataContext = this;
+            InitializeComponent();
+        }
+
+        /// <summary>
+        /// 鎵撳紑寮圭獥
+        /// </summary>
+        public bool? Open(DeviceConfigModel model)
+        {
+            _model = model;
+
+            // 鍒濆鍖栨灇涓句笅鎷�
+            cbDeviceType.ItemsSource = Enum.GetValues(typeof(DeviceKind)).Cast<DeviceKind>().ToList();
+            cbTransport.ItemsSource = Enum.GetValues(typeof(TransportType)).Cast<TransportType>().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;
+
+            // 妫�娴嬪凡鏈夎澶嘔D
+            var existingDevices = _model.GetAllDevices();
+            var existingIds = new HashSet<string>(existingDevices.Select(d => d.DeviceId));
+            var existingEndpoints = new HashSet<string>(existingDevices.Select(d => d.Endpoint));
+
+            var conflicts = new List<string>();
+            var generatedIds = new HashSet<string>();
+            var generatedEndpoints = new HashSet<string>();
+
+            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}";
+                }
+
+                // 瑙f瀽 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
+    }
+
+    /// <summary>
+    /// 鎵归噺鐢熸垚棰勮椤�
+    /// </summary>
+    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;
+    }
+}

--
Gitblit v1.9.1