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;
|
|
// 检测已有设备ID
|
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}";
|
}
|
|
// 解析 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;
|
}
|
}
|