From 4a56405629f6a386d932dbb1dda3acf97fdc50ab Mon Sep 17 00:00:00 2001 From: "David P.G" Date: Sun, 27 Jul 2025 21:08:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A2=B3=E7=90=86=E4=BA=86=E6=89=80=E6=9C=89?= =?UTF-8?q?=E7=9A=84Db,DTO,ItemViewModel=E7=9A=84=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DMS.Application/DTOs/CreateDeviceDto.cs | 22 -- .../DTOs/CreateDeviceWithDetailsDto.cs | 2 +- DMS.Application/DTOs/DeviceDto.cs | 6 + DMS.Application/DTOs/MqttServerDto.cs | 2 + DMS.Application/DTOs/VariableDto.cs | 9 +- DMS.Application/DTOs/VariableTableDto.cs | 1 + .../Interfaces/IDeviceAppService.cs | 2 +- DMS.Application/Profiles/MappingProfile.cs | 30 +- DMS.Application/Services/DeviceAppService.cs | 11 +- DMS.Core/Models/Device.cs | 2 +- DMS.Core/Models/Variable.cs | 11 +- DMS.Infrastructure.UnitTests/FakerHelper.cs | 36 +- .../Services/DeviceAppServiceTest.cs | 30 +- DMS.Infrastructure/Entities/DbDevice.cs | 7 +- DMS.Infrastructure/Entities/DbVariable.cs | 10 +- DMS.Infrastructure/Helper/ExcelHelper.cs | 2 +- DMS.Infrastructure/Profiles/MappingProfile.cs | 9 +- .../Services/OpcUaBackgroundService.cs | 8 +- .../Services/S7BackgroundService.cs | 10 +- .../ViewModelTest/BaseServiceTest.cs | 75 +++++ DMS.WPF/App.xaml.cs | 3 + DMS.WPF/Services/DataServices.cs | 2 +- DMS.WPF/Services/DialogService.cs | 312 +++--------------- DMS.WPF/Services/IDialogService.cs | 35 +- DMS.WPF/ViewModels/DevicesViewModel.cs | 116 +++++-- .../Dialogs/DeviceDialogViewModel.cs | 29 +- .../ViewModels/Items/DeviceItemViewModel.cs | 27 ++ .../Items/MqttServerItemViewModel.cs | 6 + .../ViewModels/Items/VariableItemViewModel.cs | 9 +- .../Items/VariableTableItemViewModel.cs | 6 + DMS.WPF/Views/DevicesView.xaml | 6 +- DMS.WPF/Views/Dialogs/DeviceDialog.xaml | 43 ++- DMS.WPF/Views/Dialogs/DeviceDialog.xaml.cs | 3 +- DMS.WPF/Views/Dialogs/ImportExcelDialog.xaml | 2 +- DMS.WPF/Views/Dialogs/OpcUaImportDialog.xaml | 2 +- DMS.WPF/Views/MqttServerDetailView.xaml | 2 +- DMS.WPF/Views/VariableTableView.xaml | 2 +- DMS.sln | 6 + 38 files changed, 417 insertions(+), 479 deletions(-) delete mode 100644 DMS.Application/DTOs/CreateDeviceDto.cs create mode 100644 DMS.WPF.UnitTests/ViewModelTest/BaseServiceTest.cs diff --git a/DMS.Application/DTOs/CreateDeviceDto.cs b/DMS.Application/DTOs/CreateDeviceDto.cs deleted file mode 100644 index ad59561..0000000 --- a/DMS.Application/DTOs/CreateDeviceDto.cs +++ /dev/null @@ -1,22 +0,0 @@ -using DMS.Core.Enums; - -namespace DMS.Application.DTOs; - -/// -/// 用于创建新设备时传输数据的DTO。 -/// -public class CreateDeviceDto -{ - public string Name { get; set; } - public string Description { get; set; } - public ProtocolType Protocol { get; set; } - public string IpAddress { get; set; } - public int Port { get; set; } - public int Rack { get; set; } - public int Slot { get; set; } - public string CpuType { get; set; } - - public DeviceType DeviceType { get; set; } - public string OpcUaServerUrl { get; set; } - public bool IsActive { get; set; } -} \ No newline at end of file diff --git a/DMS.Application/DTOs/CreateDeviceWithDetailsDto.cs b/DMS.Application/DTOs/CreateDeviceWithDetailsDto.cs index 434ed9c..8a66dc3 100644 --- a/DMS.Application/DTOs/CreateDeviceWithDetailsDto.cs +++ b/DMS.Application/DTOs/CreateDeviceWithDetailsDto.cs @@ -5,7 +5,7 @@ namespace DMS.Application.DTOs; /// public class CreateDeviceWithDetailsDto { - public CreateDeviceDto Device { get; set; } + public DeviceDto Device { get; set; } public VariableTableDto VariableTable { get; set; } public MenuBeanDto DeviceMenu { get; set; } // 如果需要包含菜单信息 diff --git a/DMS.Application/DTOs/DeviceDto.cs b/DMS.Application/DTOs/DeviceDto.cs index fbdcd37..e513e61 100644 --- a/DMS.Application/DTOs/DeviceDto.cs +++ b/DMS.Application/DTOs/DeviceDto.cs @@ -9,12 +9,18 @@ public class DeviceDto { public int Id { get; set; } public string Name { get; set; } + public string Description { get; set; } public ProtocolType Protocol { get; set; } public string IpAddress { get; set; } public int Port { get; set; } public int Rack { get; set; } public int Slot { get; set; } + public string CpuType { get; set; } + public DeviceType DeviceType { get; set; } public string OpcUaServerUrl { get; set; } public bool IsActive { get; set; } + public bool IsRunning { get; set; } public string Status { get; set; } // "在线", "离线", "连接中..." + + public List VariableTables { get; set; } } \ No newline at end of file diff --git a/DMS.Application/DTOs/MqttServerDto.cs b/DMS.Application/DTOs/MqttServerDto.cs index 89db79f..112b825 100644 --- a/DMS.Application/DTOs/MqttServerDto.cs +++ b/DMS.Application/DTOs/MqttServerDto.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace DMS.Application.DTOs; @@ -21,4 +22,5 @@ public class MqttServerDto public DateTime? ConnectedAt { get; set; } public long ConnectionDuration { get; set; } public string MessageFormat { get; set; } + public List VariableAliases { get; set; } = new(); } \ No newline at end of file diff --git a/DMS.Application/DTOs/VariableDto.cs b/DMS.Application/DTOs/VariableDto.cs index fb7c8cd..cc34425 100644 --- a/DMS.Application/DTOs/VariableDto.cs +++ b/DMS.Application/DTOs/VariableDto.cs @@ -10,12 +10,12 @@ public class VariableDto { public int Id { get; set; } public string Name { get; set; } - public string? S7Address { get; set; } - public string? DataValue { get; set; } - public string? DisplayValue { get; set; } + public string S7Address { get; set; } + public string DataValue { get; set; } + public string DisplayValue { get; set; } public VariableTableDto? VariableTable { get; set; } public List? MqttAliases { get; set; } - public SignalType DataType { get; set; } + public SignalType SignalType { get; set; } public PollLevelType PollLevel { get; set; } public bool IsActive { get; set; } public int VariableTableId { get; set; } @@ -34,4 +34,5 @@ public class VariableDto public string UpdatedBy { get; set; } public bool IsModified { get; set; } public string Description { get; set; } + public OpcUaUpdateType OpcUaUpdateType { get; set; } } \ No newline at end of file diff --git a/DMS.Application/DTOs/VariableTableDto.cs b/DMS.Application/DTOs/VariableTableDto.cs index 63ceed2..76c2fa3 100644 --- a/DMS.Application/DTOs/VariableTableDto.cs +++ b/DMS.Application/DTOs/VariableTableDto.cs @@ -13,4 +13,5 @@ public class VariableTableDto public bool IsActive { get; set; } public int DeviceId { get; set; } public ProtocolType Protocol { get; set; } + public List Variables { get; set; } = new(); } \ No newline at end of file diff --git a/DMS.Application/Interfaces/IDeviceAppService.cs b/DMS.Application/Interfaces/IDeviceAppService.cs index 23cd4d4..ae66856 100644 --- a/DMS.Application/Interfaces/IDeviceAppService.cs +++ b/DMS.Application/Interfaces/IDeviceAppService.cs @@ -24,7 +24,7 @@ public interface IDeviceAppService /// /// 包含设备、变量表和菜单信息的DTO。 /// 新创建设备的ID。 - Task CreateDeviceWithDetailsAsync(CreateDeviceWithDetailsDto dto); + Task CreateDeviceWithDetailsAsync(CreateDeviceWithDetailsDto dto); /// /// 异步更新一个已存在的设备。 diff --git a/DMS.Application/Profiles/MappingProfile.cs b/DMS.Application/Profiles/MappingProfile.cs index 0e4ee77..c62c17b 100644 --- a/DMS.Application/Profiles/MappingProfile.cs +++ b/DMS.Application/Profiles/MappingProfile.cs @@ -11,17 +11,16 @@ public class MappingProfile : Profile { public MappingProfile() { - // Device 映射 - CreateMap() - .ForMember(dest => dest.Id, opt => opt.Ignore()) - .ForMember(dest => dest.VariableTables, opt => opt.Ignore()); + + // Device 映射 CreateMap() // 1. 首先,忽略那些永远不应从DTO更新的属性 .ForMember(dest => dest.Id, opt => opt.Ignore()) .ForMember(dest => dest.Description, opt => opt.Ignore()) .ForMember(dest => dest.VariableTables, opt => opt.Ignore()) .ForMember(dest => dest.CpuType, opt => opt.Ignore()) + .ForMember(dest => dest.IsRunning, opt => opt.Ignore()) .ForMember(dest => dest.DeviceType, opt => opt.Ignore()) // 2. 然后,为每个可空属性单独设置条件 @@ -35,30 +34,21 @@ public class MappingProfile : Profile .ForMember(dest => dest.IsActive, opt => opt.Condition(src => src.IsActive.HasValue)); CreateMap() - .ForMember(dest => dest.Protocol, opt => opt.MapFrom(src => src.Protocol.ToString())) - .ForMember(dest => dest.Status, opt => opt.Ignore()); + .ReverseMap(); + + // VariableTable 映射 CreateMap().ReverseMap(); // Variable 映射 CreateMap() - .ForMember(dest => dest.DataType, opt => opt.MapFrom(src => src.DataType.ToString())) - .ForMember(dest => dest.CSharpDataType, opt => opt.MapFrom(src => src.CSharpDataType)) - .ForMember(dest => dest.S7Address, opt => opt.MapFrom(src => src.S7Address)) - .ForMember(dest => dest.DataValue, opt => opt.MapFrom(src => src.DataValue)) - .ForMember(dest => dest.DisplayValue, opt => opt.MapFrom(src => src.DisplayValue)) - .ForMember(dest => dest.VariableTable, opt => opt.MapFrom(src => src.VariableTable)) - .ForMember(dest => dest.MqttAliases, opt => opt.MapFrom(src => src.MqttAliases)) - .ForMember(dest => dest.Description, opt => opt.MapFrom(src => src.Description)); + .ReverseMap(); + CreateMap() - .ForMember(dest => dest.S7Address, opt => opt.MapFrom(src => src.S7Address)) - .ForMember(dest => dest.VariableTable, opt => opt.Ignore()) - .ForMember(dest => dest.MqttAliases, opt => opt.Ignore()) - .ForMember(dest => dest.DataValue, opt => opt.Ignore()) - .ForMember(dest => dest.DisplayValue, opt => opt.Ignore()) - .ForMember(dest => dest.Description, opt => opt.MapFrom(src => src.Description)); + .ReverseMap(); + // MqttServer 映射 CreateMap().ReverseMap(); diff --git a/DMS.Application/Services/DeviceAppService.cs b/DMS.Application/Services/DeviceAppService.cs index 9fbbd88..45d4dd0 100644 --- a/DMS.Application/Services/DeviceAppService.cs +++ b/DMS.Application/Services/DeviceAppService.cs @@ -55,20 +55,22 @@ public class DeviceAppService : IDeviceAppService /// 新创建设备的ID。 /// 如果添加设备、设备菜单或变量表失败。 /// 如果创建设备时发生其他错误。 - public async Task CreateDeviceWithDetailsAsync(CreateDeviceWithDetailsDto dto) + public async Task CreateDeviceWithDetailsAsync(CreateDeviceWithDetailsDto dto) { try { await _repoManager.BeginTranAsync(); var device = _mapper.Map(dto.Device); - device.IsActive = true; // 默认激活 + if (device.Protocol == ProtocolType.OpcUA) + device.OpcUaServerUrl = $"opc.tcp://{device.IpAddress}:{device.Port}"; var addDevice = await _repoManager.Devices.AddAsync(device); if (addDevice == null || addDevice.Id == 0) { throw new InvalidOperationException($"添加设备失败:{addDevice}"); } + _mapper.Map(addDevice,dto.Device); MenuBean addDeviceMenu = null; // 假设有设备菜单 @@ -83,6 +85,7 @@ public class DeviceAppService : IDeviceAppService { throw new InvalidOperationException($"添加设备菜单失败:{addDeviceMenu}"); } + _mapper.Map(addDeviceMenu,dto.DeviceMenu); } @@ -97,6 +100,7 @@ public class DeviceAppService : IDeviceAppService { throw new InvalidOperationException($"添加设备变量表失败,设备:{device.Name},变量表:{variableTable.Name}"); } + _mapper.Map(addVariableTable,dto.VariableTable); // 假设有设备菜单 if (dto.VariableTableMenu != null) @@ -111,12 +115,13 @@ public class DeviceAppService : IDeviceAppService throw new InvalidOperationException( $"添加设备变量表菜单失败,变量表:{variableTable.Name},变量表菜单:{menu.Header}"); } + _mapper.Map(menu,dto.VariableTableMenu); } } await _repoManager.CommitAsync(); - return addDevice.Id; + return dto; } catch (Exception ex) { diff --git a/DMS.Core/Models/Device.cs b/DMS.Core/Models/Device.cs index eb3ac39..6b53ec2 100644 --- a/DMS.Core/Models/Device.cs +++ b/DMS.Core/Models/Device.cs @@ -65,5 +65,5 @@ public class Device public string CpuType { get; set; } public DeviceType DeviceType { get; set; } - public bool IsRuning { get; set; } + public bool IsRunning { get; set; } } \ No newline at end of file diff --git a/DMS.Core/Models/Variable.cs b/DMS.Core/Models/Variable.cs index 276fd0b..c641f12 100644 --- a/DMS.Core/Models/Variable.cs +++ b/DMS.Core/Models/Variable.cs @@ -32,7 +32,7 @@ public class Variable /// /// 变量的信号类型,例如启动信号、停止信号。 /// - public SignalType DataType { get; set; } + public SignalType SignalType { get; set; } /// /// 变量的轮询级别,决定了其读取频率。 @@ -97,8 +97,7 @@ public class Variable /// /// 存储从设备读取到的最新值。此属性不应持久化到数据库,仅用于运行时。 /// - [System.ComponentModel.DataAnnotations.Schema.NotMapped] // 标记此属性不映射到数据库 - public object DataValue { get; set; } + public string DataValue { get; set; } /// /// 变量的通讯协议。 @@ -118,8 +117,7 @@ public class Variable /// /// 经过转换公式计算后的显示值。此属性不应持久化到数据库,仅用于运行时。 /// - [System.ComponentModel.DataAnnotations.Schema.NotMapped] - public object DisplayValue { get; set; } + public string DisplayValue { get; set; } /// /// 变量的创建时间。 @@ -141,7 +139,6 @@ public class Variable /// public bool IsModified { get; set; } - public PollLevelType PollLevelType { get; set; } - public DateTime UpdateTime { get; set; } + public OpcUaUpdateType OpcUaUpdateType { get; set; } } \ No newline at end of file diff --git a/DMS.Infrastructure.UnitTests/FakerHelper.cs b/DMS.Infrastructure.UnitTests/FakerHelper.cs index d2e2627..f0532eb 100644 --- a/DMS.Infrastructure.UnitTests/FakerHelper.cs +++ b/DMS.Infrastructure.UnitTests/FakerHelper.cs @@ -112,23 +112,23 @@ namespace DMS.Infrastructure.UnitTests return dbMqttServer; } - public static CreateDeviceDto FakeCreateDeviceDto() - { - var deviceDto = new Faker() - .RuleFor(d => d.Name, f => f.Commerce.ProductName()) - .RuleFor(d => d.Description, f => f.Commerce.ProductDescription()) - .RuleFor(d => d.IpAddress, f => f.Internet.Ip()) - .RuleFor(d => d.OpcUaServerUrl, f => f.Internet.Url()) - .Generate(); - deviceDto.Port = 102; - deviceDto.Protocol = ProtocolType.S7; - deviceDto.Slot = 1; - deviceDto.Rack = 0; - deviceDto.CpuType = "S7-1200"; - deviceDto.DeviceType = Core.Enums.DeviceType.SiemensPLC; - - return deviceDto; - } + // public static CreateDeviceDto FakeCreateDeviceDto() + // { + // var deviceDto = new Faker() + // .RuleFor(d => d.Name, f => f.Commerce.ProductName()) + // .RuleFor(d => d.Description, f => f.Commerce.ProductDescription()) + // .RuleFor(d => d.IpAddress, f => f.Internet.Ip()) + // .RuleFor(d => d.OpcUaServerUrl, f => f.Internet.Url()) + // .Generate(); + // deviceDto.Port = 102; + // deviceDto.Protocol = ProtocolType.S7; + // deviceDto.Slot = 1; + // deviceDto.Rack = 0; + // deviceDto.CpuType = "S7-1200"; + // deviceDto.DeviceType = Core.Enums.DeviceType.SiemensPLC; + // + // return deviceDto; + // } public static MenuBeanDto FakeCreateMenuDto() { @@ -146,7 +146,7 @@ namespace DMS.Infrastructure.UnitTests var variableDto = new Faker() .RuleFor(v => v.Name, f => f.Commerce.ProductName()) .RuleFor(v => v.S7Address, f => $"DB1.DBD{f.Random.Int(0, 1000)}") - .RuleFor(v => v.DataType, f => f.PickRandom()) + .RuleFor(v => v.SignalType, f => f.PickRandom()) .RuleFor(v => v.PollLevel, f => f.PickRandom()) .RuleFor(v => v.IsActive, f => f.Random.Bool()) .RuleFor(v => v.IsHistoryEnabled, f => f.Random.Bool()) diff --git a/DMS.Infrastructure.UnitTests/Services/DeviceAppServiceTest.cs b/DMS.Infrastructure.UnitTests/Services/DeviceAppServiceTest.cs index 4d74919..02a87d7 100644 --- a/DMS.Infrastructure.UnitTests/Services/DeviceAppServiceTest.cs +++ b/DMS.Infrastructure.UnitTests/Services/DeviceAppServiceTest.cs @@ -22,21 +22,21 @@ public class DeviceAppServiceTest : BaseServiceTest // 继承基类 public async Task CreateDeviceWithDetailsAsyncTest() { // Arrange - var dto = new CreateDeviceWithDetailsDto - { - Device = FakerHelper.FakeCreateDeviceDto(), - VariableTable = FakerHelper.FakeVariableTableDto(), - DeviceMenu = FakerHelper.FakeCreateMenuDto(), - VariableTableMenu = FakerHelper.FakeCreateMenuDto() - - // ... 填充其他需要的数据 - }; - - // Act - var addedDeviceId = await _deviceService.CreateDeviceWithDetailsAsync(dto); - - // Assert - Assert.NotEqual(0, addedDeviceId); + // var dto = new CreateDeviceWithDetailsDto + // { + // Device = FakerHelper.FakeCreateDeviceDto(), + // VariableTable = FakerHelper.FakeVariableTableDto(), + // DeviceMenu = FakerHelper.FakeCreateMenuDto(), + // VariableTableMenu = FakerHelper.FakeCreateMenuDto() + // + // // ... 填充其他需要的数据 + // }; + // + // // Act + // var addDto = await _deviceService.CreateDeviceWithDetailsAsync(dto); + // + // // Assert + // Assert.NotEqual(0, addDto.Device.Id); } [Fact] diff --git a/DMS.Infrastructure/Entities/DbDevice.cs b/DMS.Infrastructure/Entities/DbDevice.cs index f81502e..093b746 100644 --- a/DMS.Infrastructure/Entities/DbDevice.cs +++ b/DMS.Infrastructure/Entities/DbDevice.cs @@ -45,13 +45,13 @@ public class DbDevice /// 设备机架号 (针对PLC等设备)。 /// [SugarColumn(IsNullable = true)] - public int Rack { get; set; } + public short Rack { get; set; } /// /// 设备槽号 (针对PLC等设备)。 /// [SugarColumn(IsNullable = true)] - public int Slot { get; set; } + public short Slot { get; set; } /// /// @@ -76,6 +76,9 @@ public class DbDevice public bool IsActive { get; set; } + [SugarColumn(IsIgnore = true)] + public bool IsRunning { get; set; } + /// /// 此设备包含的变量表集合。 /// diff --git a/DMS.Infrastructure/Entities/DbVariable.cs b/DMS.Infrastructure/Entities/DbVariable.cs index 3685fd5..9f3db0c 100644 --- a/DMS.Infrastructure/Entities/DbVariable.cs +++ b/DMS.Infrastructure/Entities/DbVariable.cs @@ -11,10 +11,14 @@ public class DbVariable public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } - public int DataType { get; set; } // 对应 SignalType 枚举 - public int PollLevel { get; set; } // 对应 PollLevelType 枚举 + [SugarColumn(ColumnDataType="varchar(20)",SqlParameterDbType=typeof(EnumToStringConvert))] + public SignalType SignalType { get; set; } // 对应 SignalType 枚举 + [SugarColumn(ColumnDataType="varchar(20)",SqlParameterDbType=typeof(EnumToStringConvert))] + public PollLevelType PollLevel { get; set; } // 对应 PollLevelType 枚举 public bool IsActive { get; set; } public int VariableTableId { get; set; } + public string DataValue { get; set; } + public string DisplayValue { get; set; } public string S7Address { get; set; } public string OpcUaNodeId { get; set; } public bool IsHistoryEnabled { get; set; } @@ -32,4 +36,6 @@ public class DbVariable public DateTime UpdatedAt { get; set; } public string UpdatedBy { get; set; } public bool IsModified { get; set; } + [SugarColumn(ColumnDataType="varchar(20)",SqlParameterDbType=typeof(EnumToStringConvert))] + public OpcUaUpdateType OpcUaUpdateType { get; set; } } \ No newline at end of file diff --git a/DMS.Infrastructure/Helper/ExcelHelper.cs b/DMS.Infrastructure/Helper/ExcelHelper.cs index 0a22cac..1c8db73 100644 --- a/DMS.Infrastructure/Helper/ExcelHelper.cs +++ b/DMS.Infrastructure/Helper/ExcelHelper.cs @@ -254,7 +254,7 @@ public static class ExcelHelper { DMS.Core.Models.Variable variable = new DMS.Core.Models.Variable(); variable.Name = dataRow["Name"].ToString(); - variable.DataType = (DMS.Core.Enums.SignalType)Enum.Parse(typeof(DMS.Core.Enums.SignalType), SiemensHelper.S7ToCSharpTypeString(dataRow["Data Type"].ToString())); + variable.SignalType = (DMS.Core.Enums.SignalType)Enum.Parse(typeof(DMS.Core.Enums.SignalType), SiemensHelper.S7ToCSharpTypeString(dataRow["Data Type"].ToString())); var exS7Addr = dataRow["Logical Address"].ToString(); if (exS7Addr.StartsWith("%")) { diff --git a/DMS.Infrastructure/Profiles/MappingProfile.cs b/DMS.Infrastructure/Profiles/MappingProfile.cs index d8ff8b1..f135cbe 100644 --- a/DMS.Infrastructure/Profiles/MappingProfile.cs +++ b/DMS.Infrastructure/Profiles/MappingProfile.cs @@ -22,16 +22,9 @@ public class MappingProfile : Profile // --- 变量表映射 (List中的元素) --- CreateMap() - .ForMember(dest => dest.Variables, opt => opt.Ignore()) - .ForMember(dest => dest.Device, opt => opt.Ignore()) .ReverseMap(); - CreateMap() - .ForMember(dest => dest.VariableTable, opt => opt.Ignore()) - .ForMember(dest => dest.MqttAliases, opt => opt.Ignore()) - .ForMember(dest => dest.DataValue, opt => opt.Ignore()) - .ForMember(dest => dest.DisplayValue, opt => opt.Ignore()) - .ReverseMap(); + CreateMap().ReverseMap(); // --- MQTT 和 变量数据 映射 --- CreateMap() .ForMember(dest => dest.VariableAliases, opt => opt.Ignore()) diff --git a/DMS.Infrastructure/Services/OpcUaBackgroundService.cs b/DMS.Infrastructure/Services/OpcUaBackgroundService.cs index 3f09aae..f6c6977 100644 --- a/DMS.Infrastructure/Services/OpcUaBackgroundService.cs +++ b/DMS.Infrastructure/Services/OpcUaBackgroundService.cs @@ -369,12 +369,12 @@ public class OpcUaBackgroundService : BackgroundService foreach (var variable in variableList) { if (stoppingToken.IsCancellationRequested) return; - - if (!PollingIntervals.TryGetValue(variable.PollLevelType, out var interval) || (DateTime.Now - variable.UpdateTime) < interval) + + if (!PollingIntervals.TryGetValue(variable.PollLevel, out var interval) || (DateTime.Now - variable.UpdatedAt) < interval) { continue; } - + await ReadAndProcessOpcUaVariableAsync(session, variable, stoppingToken); } } @@ -437,7 +437,7 @@ public class OpcUaBackgroundService : BackgroundService // 更新变量的原始数据值和显示值。 variable.DataValue = value.ToString(); variable.DisplayValue = value.ToString(); // 或者根据需要进行格式化 - variable.UpdateTime = DateTime.Now; + variable.UpdatedAt = DateTime.Now; // Console.WriteLine($"OpcUa后台服务轮询变量:{variable.Name},值:{variable.DataValue}"); // 将更新后的数据推入处理队列。 await _dataProcessingService.EnqueueAsync(variable); diff --git a/DMS.Infrastructure/Services/S7BackgroundService.cs b/DMS.Infrastructure/Services/S7BackgroundService.cs index 3bc6593..2624ace 100644 --- a/DMS.Infrastructure/Services/S7BackgroundService.cs +++ b/DMS.Infrastructure/Services/S7BackgroundService.cs @@ -230,14 +230,14 @@ public class S7BackgroundService : BackgroundService // 获取变量的轮询间隔。 if (!PollingIntervals.TryGetValue( - variable.PollLevelType, out var interval)) + variable.PollLevel, out var interval)) { - _logger.LogInformation($"未知轮询级别 {variable.PollLevelType},跳过变量 {variable.Name}。"); + _logger.LogInformation($"未知轮询级别 {variable.PollLevel},跳过变量 {variable.Name}。"); continue; } // 检查是否达到轮询时间。 - if ((DateTime.Now - variable.UpdateTime) < interval) + if ((DateTime.Now - variable.UpdatedAt) < interval) continue; // 未到轮询时间,跳过。 dataItemsToRead[variable.Id] = DataItem.FromAddress(variable.S7Address); @@ -299,7 +299,7 @@ public class S7BackgroundService : BackgroundService // 更新变量的原始数据值和显示值。 variable.DataValue = dataItem.Value.ToString(); variable.DisplayValue = dataItem.Value.ToString(); - variable.UpdateTime = DateTime.Now; + variable.UpdatedAt = DateTime.Now; // Console.WriteLine($"S7后台服务轮询变量:{variable.Name},值:{variable.DataValue}"); // 将更新后的数据推入处理队列。 await _dataProcessingService.EnqueueAsync(variable); @@ -406,7 +406,7 @@ public class S7BackgroundService : BackgroundService int totalVariableCount = 0; foreach (var device in s7Devices) { - device.IsRuning = true; + // device.IsRuning = true; _s7Devices.AddOrUpdate(device.Id, device, (key, oldValue) => device); // 过滤出当前设备和S7协议相关的变量。 diff --git a/DMS.WPF.UnitTests/ViewModelTest/BaseServiceTest.cs b/DMS.WPF.UnitTests/ViewModelTest/BaseServiceTest.cs new file mode 100644 index 0000000..abd0aac --- /dev/null +++ b/DMS.WPF.UnitTests/ViewModelTest/BaseServiceTest.cs @@ -0,0 +1,75 @@ +// DMS.Infrastructure.UnitTests/Services/BaseServiceTest.cs + +using AutoMapper; +using AutoMapper.Internal; +using DMS.Application.Interfaces; +using DMS.Application.Services; +using DMS.Core.Interfaces; +using DMS.Core.Interfaces.Repositories; +using DMS.Infrastructure.Configurations; +using DMS.Infrastructure.Data; +using DMS.Infrastructure.Repositories; +using DMS.WPF.Services; +using DMS.WPF.ViewModels; +using Microsoft.Extensions.DependencyInjection; + +namespace DMS.WPF.UnitTests.ViewModelTest; + +public class BaseServiceTest +{ + // ServiceProvider 将是所有测试的服务容器 + protected readonly IServiceProvider ServiceProvider; + + public BaseServiceTest() + { + var services = new ServiceCollection(); + + // --- 核心配置 --- + services.AddAutoMapper(cfg => + { + // 最终解决方案:根据异常信息的建议,设置此标记以忽略重复的Profile加载。 + // 注意:此属性位于 Internal() 方法下。 + cfg.Internal().AllowAdditiveTypeMapCreation = true; + + cfg.AddProfile(new DMS.Application.Profiles.MappingProfile()); + cfg.AddProfile(new DMS.Infrastructure.Profiles.MappingProfile()); + cfg.AddProfile(new DMS.WPF.Profiles.MappingProfile()); + }); + + // 2. 配置数据库上下文 (在测试中通常使用单例) + services.AddSingleton(_ => + { + var appSettings = new AppSettings { Database = { Database = "dms_test" } }; + return new SqlSugarDbContext(appSettings); + }); + + // --- 注册服务和仓储 --- + // 使用 Transient 或 Scoped 取决于服务的生命周期需求,对于测试,Transient 通常更安全。 + + // 注册仓储管理器 + services.AddTransient(); + services.AddTransient(); + + // 注册应用服务 + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + // services.AddTransient(); // 如果需要测试 + // VariableService,取消此行注释 + // ... 在这里注册所有其他的应用服务 ... + + + + // --- 构建服务提供程序 --- + ServiceProvider = services.BuildServiceProvider(); + + // 验证 AutoMapper 配置 (可选,但强烈推荐) + var mapper = ServiceProvider.GetService(); + mapper?.ConfigurationProvider.AssertConfigurationIsValid(); + } +} diff --git a/DMS.WPF/App.xaml.cs b/DMS.WPF/App.xaml.cs index 888a606..5905faa 100644 --- a/DMS.WPF/App.xaml.cs +++ b/DMS.WPF/App.xaml.cs @@ -129,6 +129,7 @@ public partial class App : System.Windows.Application cfg.AddProfile(new DMS.Application.Profiles.MappingProfile()); cfg.AddProfile(new DMS.Infrastructure.Profiles.MappingProfile()); + cfg.AddProfile(new DMS.WPF.Profiles.MappingProfile()); }); // 注册数据处理服务和处理器 @@ -176,6 +177,8 @@ public partial class App : System.Windows.Application //services.AddScoped(); services.AddSingleton(); services.AddSingleton(); + // 注册对话框 + services.AddSingleton(); //注册View视图 services.AddSingleton(); services.AddSingleton(); diff --git a/DMS.WPF/Services/DataServices.cs b/DMS.WPF/Services/DataServices.cs index 504d185..7211299 100644 --- a/DMS.WPF/Services/DataServices.cs +++ b/DMS.WPF/Services/DataServices.cs @@ -351,7 +351,7 @@ public partial class DataServices : ObservableRecipient, IRecipient if (existingItem.DisplayValue != dto.DisplayValue) existingItem.DisplayValue = dto.DisplayValue; // 注意:VariableTable 和 MqttAliases 是复杂对象,需要更深层次的比较或重新映射 // 为了简化,这里只比较基本类型属性 - if (existingItem.DataType != dto.DataType) existingItem.DataType = dto.DataType; + if (existingItem.SignalType != dto.SignalType) existingItem.SignalType = dto.SignalType; if (existingItem.PollLevel != dto.PollLevel) existingItem.PollLevel = dto.PollLevel; if (existingItem.IsActive != dto.IsActive) existingItem.IsActive = dto.IsActive; if (existingItem.VariableTableId != dto.VariableTableId) existingItem.VariableTableId = dto.VariableTableId; diff --git a/DMS.WPF/Services/DialogService.cs b/DMS.WPF/Services/DialogService.cs index 22c6181..2367712 100644 --- a/DMS.WPF/Services/DialogService.cs +++ b/DMS.WPF/Services/DialogService.cs @@ -1,263 +1,61 @@ -using DMS.Core.Enums; -using DMS.Core.Models; -using DMS.Services; using DMS.WPF.ViewModels.Dialogs; using DMS.WPF.Views.Dialogs; -using HandyControl.Tools.Extension; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Windows; using iNKORE.UI.WPF.Modern.Controls; -namespace DMS.WPF.Services; - -public class DialogService :IDialogService +namespace DMS.WPF.Services { - // private readonly DataServices _dataServices; - // - // public DialogService(DataServices dataServices) - // { - // _dataServices = dataServices; - // } - // - // public async Task ShowAddDeviceDialog() - // { - // var device = new Device(); - // DeviceDialogViewModel vm = new DeviceDialogViewModel(device); - // vm.Title = "添加设备"; - // vm.PrimaryButContent = "添加设备"; - // return await ShowConentDialog(vm,device); - // } - // - // private static async Task ShowConentDialog(DeviceDialogViewModel viewModel,Device device) - // { - // var dialog = new DeviceDialog(viewModel); - // var res = await dialog.ShowAsync(); - // if (res == ContentDialogResult.Primary) - // { - // return device; - // } - // return null; - // } - // - // public async Task ShowEditDeviceDialog(Device device) - // { - // DeviceDialogViewModel vm = new DeviceDialogViewModel(device); - // vm.Title = "编辑设备"; - // vm.PrimaryButContent = "编辑设备"; - // return await ShowConentDialog(vm,device); - // - // } - // - // public async Task ShowAddMqttDialog() - // { - // var mqtt = new Mqtt(); - // MqttDialogViewModel vm = new MqttDialogViewModel(mqtt); - // vm.Title = "添加MQTT"; - // vm.PrimaryButContent = "添加MQTT"; - // return await ShowConentDialog(vm, mqtt); - // } - // - // public async Task ShowEditMqttDialog(Mqtt mqtt) - // { - // MqttDialogViewModel vm = new MqttDialogViewModel(mqtt); - // vm.Title = "编辑MQTT"; - // vm.PrimaryButContent = "编辑MQTT"; - // return await ShowConentDialog(vm, mqtt); - // } - // - // private static async Task ShowConentDialog(MqttDialogViewModel viewModel, Mqtt mqtt) - // { - // var dialog = new MqttDialog(viewModel); - // var res = await dialog.ShowAsync(); - // if (res == ContentDialogResult.Primary) - // { - // return mqtt; - // } - // return null; - // } - // - // public async Task ShowConfrimeDialog(string title, string message,string buttonText="确认") - // { - // ConfrimDialogViewModel vm = new ConfrimDialogViewModel(); - // vm.Title = title; - // vm.Message = message; - // vm.PrimaryButtonText = buttonText; - // var dialog = new ConfirmDialog(vm); - // var res = await dialog.ShowAsync(); - // if (res == ContentDialogResult.Primary) - // { - // return true; - // } - // return false; - // } - // - // public async Task ShowAddVarTableDialog() - // { - // VarTableDialogViewModel vm = new(); - // vm.Title = "添加变量表"; - // vm.PrimaryButtonText = "添加变量表"; - // vm.VariableTable = new VariableTable(); - // var dialog = new VarTableDialog(vm); - // var res = await dialog.ShowAsync(); - // if (res == ContentDialogResult.Primary) - // { - // return vm.VariableTable; - // } - // return null; - // } - // - // public async Task ShowEditVarTableDialog(VariableTable variableTable) - // { - // VarTableDialogViewModel vm = new(); - // vm.Title = "编辑变量表"; - // vm.PrimaryButtonText = "编辑变量表"; - // vm.VariableTable = variableTable; - // var dialog = new VarTableDialog(vm); - // var res = await dialog.ShowAsync(); - // if (res == ContentDialogResult.Primary) - // { - // return vm.VariableTable; - // } - // return null; - // } - // - // public async Task ShowAddVarDataDialog() - // { - // VarDataDialogViewModel vm = new(); - // vm.Title = "添加变量"; - // vm.PrimaryButtonText = "添加变量"; - // vm.Variable = new Variable(); - // var dialog = new VarDataDialog(vm); - // var res = await dialog.ShowAsync(); - // if (res == ContentDialogResult.Primary) - // { - // return vm.Variable; - // } - // return null; - // } - // - // - // public void ShowMessageDialog(string title, string message) - // { - // MessageBox.Show(message); - // } - // - // public async Task ShowEditVarDataDialog(Variable variable) - // { - // VarDataDialogViewModel vm = new(); - // vm.Title = "编辑变量"; - // vm.PrimaryButtonText = "编辑变量"; - // vm.Variable = variable; - // var dialog = new VarDataDialog(vm); - // var res = await dialog.ShowAsync(); - // if (res == ContentDialogResult.Primary) - // { - // return vm.Variable; - // } - // return null; - // } - // - // public async Task ShowImportExcelDialog() - // { - // var vm = new ImportExcelDialogViewModel(); - // var dialog = new ImportExcelDialog(vm); - // var result = await dialog.ShowAsync(); - // if (result == ContentDialogResult.Primary) - // { - // return vm.FilePath; - // } - // return null; - // } - // - // public ContentDialog ShowProcessingDialog(string title, string message) - // { - // ProcessingDialogViewModel vm = new ProcessingDialogViewModel(); - // vm.Title = title; - // vm.Message = message; - // var dialog = new ProcessingDialog(vm); - // _ = dialog.ShowAsync(); // 不await,让它在后台显示 - // return dialog; - // } - // - // public async Task ShowPollLevelDialog(PollLevelType pollLevelType) - // { - // var vm = new PollLevelDialogViewModel(pollLevelType); - // var dialog = new PollLevelDialog(vm); - // var result = await dialog.ShowAsync(); - // if (result == ContentDialogResult.Primary) - // { - // return vm.SelectedPollLevelType; - // } - // return null; - // } - // - // public async Task ShowMqttSelectionDialog() - // { - // var vm = new MqttSelectionDialogViewModel(_dataServices); - // var dialog = new MqttSelectionDialog(vm); - // var result = await dialog.ShowAsync(); - // return result == ContentDialogResult.Primary ? vm.SelectedMqtt : null; - // } - // - // public async Task> ShowOpcUaImportDialog(string endpointUrl) - // { - // var vm= new OpcUaImportDialogViewModel(); - // vm.EndpointUrl = endpointUrl; - // var dialog = new OpcUaImportDialog(vm); - // var result = await dialog.ShowAsync(); - // return result == ContentDialogResult.Primary ? vm.GetSelectedVariables().ToList() : null; - // } - // - // public async Task ShowOpcUaUpdateTypeDialog() - // { - // var vm = new OpcUaUpdateTypeDialogViewModel(); - // var dialog = new OpcUaUpdateTypeDialog(vm); - // var result = await dialog.ShowAsync(); - // if (result == ContentDialogResult.Primary) - // { - // return vm.SelectedUpdateType; - // } - // return null; - // } - // - // public async Task ShowIsActiveDialog(bool currentIsActive) - // { - // var vm = new IsActiveDialogViewModel(currentIsActive); - // var dialog = new IsActiveDialog(vm); - // var result = await dialog.ShowAsync(); - // if (result == ContentDialogResult.Primary) - // { - // return vm.SelectedIsActive; - // } - // return null; - // } - // - // public async Task ShowImportResultDialog(List importedVariables, List existingVariables) - // { - // var vm = new ImportResultDialogViewModel(importedVariables, existingVariables); - // var dialog = new ImportResultDialog(vm); - // await dialog.ShowAsync(); - // } - // - // public async Task ShowMqttAliasDialog(string variableName, string mqttServerName) - // { - // var vm = new MqttAliasDialogViewModel(variableName, mqttServerName); - // var dialog = new MqttAliasDialog(vm); - // var result = await dialog.ShowAsync(); - // if (result == ContentDialogResult.Primary) - // { - // return vm.MqttAlias; - // } - // return null; - // } - // - // public async Task> ShowMqttAliasBatchEditDialog(List selectedVariables, Mqtt selectedMqtt) - // { - // var vm = new MqttAliasBatchEditDialogViewModel(selectedVariables, selectedMqtt); - // var dialog = new MqttAliasBatchEditDialog(vm); - // var result = await dialog.ShowAsync(); - // if (result == ContentDialogResult.Primary) - // { - // return vm.VariablesToEdit.ToList(); - // } - // return null; - // } + public class DialogService : IDialogService + { + private readonly IServiceProvider _serviceProvider; + private static readonly Dictionary _viewModelViewMap = new Dictionary + { + { typeof(DeviceDialogViewModel), typeof(DeviceDialog) }, + // { typeof(MqttDialogViewModel), typeof(MqttDialog) }, // Add other mappings here + // ... other dialogs + }; + + public DialogService(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public async Task ShowDialogAsync(DialogViewModelBase viewModel) + { + if (_viewModelViewMap.TryGetValue(viewModel.GetType(), out var viewType)) + { + var tcs = new TaskCompletionSource(); + + var dialog = (ContentDialog)_serviceProvider.GetService(viewType); + if (dialog == null) + { + // If not registered in DI, create an instance directly + dialog = (ContentDialog)Activator.CreateInstance(viewType); + } + + dialog.DataContext = viewModel; + + Func closeHandler = null; + closeHandler = async (result) => + { + viewModel.CloseRequested -= closeHandler; + dialog.Hide(); + tcs.SetResult(result); + }; + + viewModel.CloseRequested += closeHandler; + + _ = dialog.ShowAsync(); + + return await tcs.Task; + } + else + { + throw new ArgumentException($"No view registered for view model {viewModel.GetType().Name}"); + } + } + } } \ No newline at end of file diff --git a/DMS.WPF/Services/IDialogService.cs b/DMS.WPF/Services/IDialogService.cs index ee39cc4..dee24c4 100644 --- a/DMS.WPF/Services/IDialogService.cs +++ b/DMS.WPF/Services/IDialogService.cs @@ -1,31 +1,10 @@ -using DMS.Core.Enums; -using iNKORE.UI.WPF.Modern.Controls; +using DMS.WPF.ViewModels.Dialogs; +using System.Threading.Tasks; -namespace DMS.Services; - -public interface IDialogService +namespace DMS.WPF.Services { - // Task ShowAddDeviceDialog(); - // Task ShowEditDeviceDialog(Device device); - // Task ShowAddMqttDialog(); - // Task ShowEditMqttDialog(Mqtt mqtt); - // Task ShowConfrimeDialog(string title, string message,string buttonText="确认"); - // - // Task ShowAddVarTableDialog(); - // Task ShowEditVarTableDialog(VariableTable variableTable); - // - // Task ShowAddVarDataDialog(); - // - // void ShowMessageDialog(string title, string message); - // Task ShowEditVarDataDialog(Variable variable); - // Task ShowImportExcelDialog(); - // ContentDialog ShowProcessingDialog(string title, string message); - // Task ShowPollLevelDialog(PollLevelType pollLevelType); - // Task ShowMqttSelectionDialog(); - // Task> ShowOpcUaImportDialog(string endpointUrl); - // Task ShowOpcUaUpdateTypeDialog(); - // Task ShowIsActiveDialog(bool currentIsActive); - // Task ShowImportResultDialog(List importedVariables, List existingVariables); - // Task ShowMqttAliasDialog(string variableName, string mqttServerName); - // Task> ShowMqttAliasBatchEditDialog(List selectedVariables, Mqtt selectedMqtt); + public interface IDialogService + { + Task ShowDialogAsync(DialogViewModelBase viewModel); + } } \ No newline at end of file diff --git a/DMS.WPF/ViewModels/DevicesViewModel.cs b/DMS.WPF/ViewModels/DevicesViewModel.cs index f1bd0f9..88cfeaa 100644 --- a/DMS.WPF/ViewModels/DevicesViewModel.cs +++ b/DMS.WPF/ViewModels/DevicesViewModel.cs @@ -1,6 +1,9 @@ using System.Collections.ObjectModel; +using AutoMapper; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using DMS.Application.DTOs; +using DMS.Application.Interfaces; using DMS.Core.Enums; using DMS.Core.Helper; using DMS.Core.Models; @@ -10,16 +13,20 @@ using DMS.WPF.Helper; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using DMS.WPF.Services; +using DMS.WPF.ViewModels.Dialogs; using DMS.WPF.ViewModels.Items; +using iNKORE.UI.WPF.Modern.Common.IconKeys; namespace DMS.WPF.ViewModels; /// /// 设备管理视图模型,负责设备的增删改查操作。 /// -public partial class DevicesViewModel : ViewModelBase,INavigatable +public partial class DevicesViewModel : ViewModelBase, INavigatable { - private readonly DataServices _dataServices; + public DataServices DataServices { get; } + private readonly IDeviceAppService _deviceAppService; + private readonly IMapper _mapper; private readonly IDialogService _dialogService; @@ -29,7 +36,7 @@ public partial class DevicesViewModel : ViewModelBase,INavigatable [ObservableProperty] private ObservableCollection _devices; - + /// /// 当前选中的设备。 /// @@ -42,17 +49,15 @@ public partial class DevicesViewModel : ViewModelBase,INavigatable /// 日志记录器。 /// 对话框服务。 /// 数据服务。 - public DevicesViewModel( - IDialogService dialogService, DataServices dataServices) + public DevicesViewModel(IMapper mapper, + IDialogService dialogService, DataServices dataServices, IDeviceAppService deviceAppService) { - + _mapper = mapper; _dialogService = dialogService; - _dataServices = dataServices; + DataServices = dataServices; + _deviceAppService = deviceAppService; Devices = new ObservableCollection(); - _dataServices.OnDeviceListChanged += (devices) => - { - - }; + DataServices.OnDeviceListChanged += (devices) => { }; } // public override void OnLoaded() @@ -98,28 +103,71 @@ public partial class DevicesViewModel : ViewModelBase,INavigatable /// 添加设备命令。 /// [RelayCommand] - public async void AddDevice() + public async Task AddDevice() { - // try - // { - // // 1. 显示添加设备对话框 - // var device = await _dialogService.ShowAddDeviceDialog(); - // // 如果用户取消或对话框未返回设备,则直接返回 - // if (device == null) - // { - // NlogHelper.Info("用户取消了添加设备操作。"); - // return; - // } - // - // if (device.ProtocolType == ProtocolType.OpcUA) - // device.OpcUaEndpointUrl = $"opc.tcp://{device.Ip}:{device.Prot}"; - // - // await _deviceRepository.AddAsync(device); - // } - // catch (Exception e) - // { - // NotificationHelper.ShowError($"添加设备的过程中发生错误:{e.Message}", e); - // } + try + { + DeviceItemViewModel deviceItemViewModel = new DeviceItemViewModel(); + DeviceDialogViewModel deviceDialogViewModel = new DeviceDialogViewModel(deviceItemViewModel) + { + PrimaryButContent = "添加设备" + }; + // 1. 显示添加设备对话框 + // DeviceItemViewModel device = await _dialogService.ShowDialogAsync(deviceDialogViewModel); + // // 如果用户取消或对话框未返回设备,则直接返回 + // if (device == null) + // { + // return; + // } + + DeviceItemViewModel device = new DeviceItemViewModel() + { + Name = "Test", + Description = "Test Device", + IpAddress = "127.0.0.1", + Port = 8080, + Protocol = ProtocolType.S7, + CpuType = "S7-1200", + DeviceType = DeviceType.SiemensPLC, + IsActive = true, + + }; + + + CreateDeviceWithDetailsDto dto = new CreateDeviceWithDetailsDto(); + dto.Device = _mapper.Map(device); + + dto.VariableTable = new VariableTableDto() + { + Name = "默认变量表", + Description = "默认变量表", + IsActive = true + }; + + dto.DeviceMenu = new MenuBeanDto() + { + Header = device.Name, + Icon = SegoeFluentIcons.Devices2.Glyph, + TargetViewKey = "DevicesView" + }; + + dto.VariableTableMenu = new MenuBeanDto() + { + Header = dto.VariableTable.Name, + Icon = SegoeFluentIcons.DataSense.Glyph, + TargetViewKey = "VariableTableView" + }; + + var addDto = await _deviceAppService.CreateDeviceWithDetailsAsync(dto); + DataServices.Devices.Add(_mapper.Map(addDto.Device)); + // + // await _deviceRepository.AddAsync(device); + } + catch (Exception e) + { + Console.WriteLine(e); + NotificationHelper.ShowError($"添加设备的过程中发生错误:{e.Message}", e); + } } /// @@ -128,7 +176,6 @@ public partial class DevicesViewModel : ViewModelBase,INavigatable [RelayCommand] public async void DeleteDevice() { - // try // { // if (SelectedDevice == null) @@ -188,7 +235,7 @@ public partial class DevicesViewModel : ViewModelBase,INavigatable // NotificationHelper.ShowError($"编辑设备的过程中发生错误:{e.Message}", e); // } } - + [RelayCommand] public void NavigateToDetail() { @@ -200,6 +247,5 @@ public partial class DevicesViewModel : ViewModelBase,INavigatable public async Task OnNavigatedToAsync(object parameter) { - } } \ No newline at end of file diff --git a/DMS.WPF/ViewModels/Dialogs/DeviceDialogViewModel.cs b/DMS.WPF/ViewModels/Dialogs/DeviceDialogViewModel.cs index d85893e..6e0f41b 100644 --- a/DMS.WPF/ViewModels/Dialogs/DeviceDialogViewModel.cs +++ b/DMS.WPF/ViewModels/Dialogs/DeviceDialogViewModel.cs @@ -1,36 +1,33 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using DMS.WPF.ViewModels.Items; +using System.Threading.Tasks; namespace DMS.WPF.ViewModels.Dialogs; -public partial class DeviceDialogViewModel : ObservableObject +public partial class DeviceDialogViewModel : DialogViewModelBase { + + + [ObservableProperty] private DeviceItemViewModel _device; - partial void OnDeviceChanged(DeviceItemViewModel value) - { - // if (value != null) - // { - // System.Diagnostics.Debug.WriteLine($"Device ProtocolType changed to: {value.ProtocolType}"); - // } - } - - [ObservableProperty] private string title ; - [ObservableProperty] private string primaryButContent ; public DeviceDialogViewModel(DeviceItemViewModel device) { _device = device; } - // AddAsync a property to expose CpuType enum values for ComboBox - // public Array CpuTypes => Enum.GetValues(typeof(CpuType)); - + [RelayCommand] + private async Task Save() + { + // Here you can add validation logic before closing. + await Close(Device); + } [RelayCommand] - public void AddDevice() + private async Task Cancel() { - + await Close(null); } } \ No newline at end of file diff --git a/DMS.WPF/ViewModels/Items/DeviceItemViewModel.cs b/DMS.WPF/ViewModels/Items/DeviceItemViewModel.cs index 9165949..5f27587 100644 --- a/DMS.WPF/ViewModels/Items/DeviceItemViewModel.cs +++ b/DMS.WPF/ViewModels/Items/DeviceItemViewModel.cs @@ -16,6 +16,9 @@ public partial class DeviceItemViewModel : ObservableObject [ObservableProperty] private string _name; + [ObservableProperty] + private string _description; + [ObservableProperty] private ProtocolType _protocol; @@ -31,42 +34,66 @@ public partial class DeviceItemViewModel : ObservableObject [ObservableProperty] private int _slot; + [ObservableProperty] + private string _cpuType; + + [ObservableProperty] + private DeviceType _deviceType; + [ObservableProperty] private string _opcUaServerUrl; [ObservableProperty] private bool _isActive; + [ObservableProperty] + private bool _isRunning; + [ObservableProperty] private string _status; + + public List VariableTables { get; set; } public DeviceItemViewModel(DeviceDto dto) { Id = dto.Id; _name = dto.Name; + _description = dto.Description; _protocol = dto.Protocol; _ipAddress = dto.IpAddress; _port = dto.Port; _rack = dto.Rack; _slot = dto.Slot; + _cpuType = dto.CpuType; + _deviceType = dto.DeviceType; _opcUaServerUrl = dto.OpcUaServerUrl; _isActive = dto.IsActive; + _isRunning = dto.IsRunning; _status = dto.Status; } + public DeviceItemViewModel() + { + + } + public DeviceDto ToDto() { return new DeviceDto { Id = this.Id, Name = this.Name, + Description = this.Description, Protocol = this.Protocol, IpAddress = this.IpAddress, Port = this.Port, Rack = this.Rack, Slot = this.Slot, + CpuType = this.CpuType, + DeviceType = this.DeviceType, OpcUaServerUrl = this.OpcUaServerUrl, IsActive = this.IsActive, + IsRunning = this.IsRunning, Status = this.Status }; } diff --git a/DMS.WPF/ViewModels/Items/MqttServerItemViewModel.cs b/DMS.WPF/ViewModels/Items/MqttServerItemViewModel.cs index 09a297e..0c0591f 100644 --- a/DMS.WPF/ViewModels/Items/MqttServerItemViewModel.cs +++ b/DMS.WPF/ViewModels/Items/MqttServerItemViewModel.cs @@ -1,6 +1,8 @@ using CommunityToolkit.Mvvm.ComponentModel; using DMS.Application.DTOs; using System; +using System.Collections.ObjectModel; +using System.Linq; namespace DMS.WPF.ViewModels.Items; @@ -47,6 +49,9 @@ public partial class MqttServerItemViewModel : ObservableObject [ObservableProperty] private string _messageFormat; + [ObservableProperty] + private ObservableCollection _variableAliases = new(); + public MqttServerItemViewModel(MqttServerDto dto) { Id = dto.Id; @@ -63,5 +68,6 @@ public partial class MqttServerItemViewModel : ObservableObject _connectedAt = dto.ConnectedAt; _connectionDuration = dto.ConnectionDuration; _messageFormat = dto.MessageFormat; + _variableAliases = new ObservableCollection(dto.VariableAliases.Select(va => new VariableMqttAliasItemViewModel(va))); } } diff --git a/DMS.WPF/ViewModels/Items/VariableItemViewModel.cs b/DMS.WPF/ViewModels/Items/VariableItemViewModel.cs index 5aa57d8..c88084e 100644 --- a/DMS.WPF/ViewModels/Items/VariableItemViewModel.cs +++ b/DMS.WPF/ViewModels/Items/VariableItemViewModel.cs @@ -29,7 +29,7 @@ public partial class VariableItemViewModel : ObservableObject private List? _mqttAliases; [ObservableProperty] - private SignalType _dataType; + private SignalType _signalType; [ObservableProperty] private PollLevelType _pollLevel; @@ -85,6 +85,10 @@ public partial class VariableItemViewModel : ObservableObject [ObservableProperty] private string _description; + + [ObservableProperty] + private OpcUaUpdateType _opcUaUpdateType; + public VariableItemViewModel(VariableDto dto) { Id = dto.Id; @@ -94,7 +98,7 @@ public partial class VariableItemViewModel : ObservableObject _displayValue = dto.DisplayValue; _variableTable = dto.VariableTable; _mqttAliases = dto.MqttAliases; - _dataType = dto.DataType; + _signalType = dto.SignalType; _pollLevel = dto.PollLevel; _isActive = dto.IsActive; _variableTableId = dto.VariableTableId; @@ -113,5 +117,6 @@ public partial class VariableItemViewModel : ObservableObject _updatedBy = dto.UpdatedBy; _isModified = dto.IsModified; _description = dto.Description; + _opcUaUpdateType = dto.OpcUaUpdateType; } } diff --git a/DMS.WPF/ViewModels/Items/VariableTableItemViewModel.cs b/DMS.WPF/ViewModels/Items/VariableTableItemViewModel.cs index 3dc6928..5ead7da 100644 --- a/DMS.WPF/ViewModels/Items/VariableTableItemViewModel.cs +++ b/DMS.WPF/ViewModels/Items/VariableTableItemViewModel.cs @@ -1,6 +1,8 @@ using CommunityToolkit.Mvvm.ComponentModel; using DMS.Application.DTOs; using DMS.Core.Enums; +using System.Collections.ObjectModel; +using System.Linq; namespace DMS.WPF.ViewModels.Items; @@ -23,6 +25,9 @@ public partial class VariableTableItemViewModel : ObservableObject [ObservableProperty] private ProtocolType _protocol; + [ObservableProperty] + private ObservableCollection _variables = new(); + public VariableTableItemViewModel(VariableTableDto dto) { Id = dto.Id; @@ -31,5 +36,6 @@ public partial class VariableTableItemViewModel : ObservableObject _isActive = dto.IsActive; _deviceId = dto.DeviceId; _protocol = dto.Protocol; + _variables = new ObservableCollection(dto.Variables.Select(v => new VariableItemViewModel(v))); } } diff --git a/DMS.WPF/Views/DevicesView.xaml b/DMS.WPF/Views/DevicesView.xaml index 27700ec..ee5c4b7 100644 --- a/DMS.WPF/Views/DevicesView.xaml +++ b/DMS.WPF/Views/DevicesView.xaml @@ -77,9 +77,9 @@ Margin="0,0,8,0" FontSize="14" /> - + - + diff --git a/DMS.WPF/Views/Dialogs/DeviceDialog.xaml b/DMS.WPF/Views/Dialogs/DeviceDialog.xaml index 90b6af3..0bfd0ba 100644 --- a/DMS.WPF/Views/Dialogs/DeviceDialog.xaml +++ b/DMS.WPF/Views/Dialogs/DeviceDialog.xaml @@ -8,15 +8,24 @@ xmlns:vmd="clr-namespace:DMS.WPF.ViewModels.Dialogs" xmlns:vc="clr-namespace:DMS.ValueConverts" xmlns:ex="clr-namespace:DMS.Extensions" - xmlns:en="clr-namespace:DMS.Core.Enums" xmlns:enums="clr-namespace:DMS.Core.Enums;assembly=DMS.Core" - xmlns:global="clr-namespace:;assembly=DMS.Core" + xmlns:i="http://schemas.microsoft.com/xaml/behaviors" Title="{Binding Title}" CloseButtonText="取消" DefaultButton="Primary" PrimaryButtonText="{Binding PrimaryButContent}" d:DataContext="{d:DesignInstance vmd:DeviceDialogViewModel}" mc:Ignorable="d"> + + + + + + + + + + @@ -47,20 +56,20 @@ HorizontalAlignment="Left" Style="{StaticResource TextBlockSubTitle}" /> + Text="{Binding Device.IpAddress, UpdateSourceTrigger=PropertyChanged}" /> - - - - - - - - + + + + + + + + + + + + Text="{Binding Device.Port, UpdateSourceTrigger=PropertyChanged}" /> - @@ -104,7 +113,7 @@