From 1f9c0a111104f06a63a0fa9f322fda21ed723f68 Mon Sep 17 00:00:00 2001 From: "David P.G" Date: Sun, 7 Sep 2025 08:51:18 +0800 Subject: [PATCH] =?UTF-8?q?=20=201=20=E5=AE=9E=E7=8E=B0=20MQTT=20=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E6=95=B0=E6=8D=AE=E5=8F=91=E5=B8=83=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=20=20=20=202=20=20=20=203=20-=20=E8=BF=81=E7=A7=BB=20IMqttServ?= =?UTF-8?q?iceManager=20=E6=8E=A5=E5=8F=A3=E5=88=B0=20DMS.Core=20=20=20=20?= =?UTF-8?q?4=20-=20=E5=9C=A8=20DataCenterService=20=E4=B8=AD=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20MQTT=20=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=92=8C?= =?UTF-8?q?=E5=8F=98=E9=87=8F=E5=88=AB=E5=90=8D=E7=9A=84=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E9=80=BB=E8=BE=91=20=20=20=205=20-=20=E5=AE=9E=E7=8E=B0=20Mqtt?= =?UTF-8?q?PublishProcessor=20=E7=9A=84=E6=A0=B8=E5=BF=83=E5=A4=84?= =?UTF-8?q?=E7=90=86=E9=80=BB=E8=BE=91=20=20=20=206=20-=20=E4=B8=BA=20DTO?= =?UTF-8?q?=20=E5=92=8C=20ViewModel=20=E7=9A=84=20MqttAliases=20=E5=B1=9E?= =?UTF-8?q?=E6=80=A7=E6=8F=90=E4=BE=9B=E9=BB=98=E8=AE=A4=E7=A9=BA=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E5=88=9D=E5=A7=8B=E5=8C=96=20=20=20=207=20-=20?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=20AutoMapper=20=E6=98=A0=E5=B0=84=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E4=BB=A5=E6=94=AF=E6=8C=81=20VariableMqttAliasDto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DMS.Application/DTOs/VariableDto.cs | 2 +- DMS.Application/Services/DataCenterService.cs | 39 ++++++++++---- .../Processors/MqttPublishProcessor.cs | 54 ++++++++++--------- .../IVariableMqttAliasRepository.cs | 6 +++ .../Services/IMqttServiceManager.cs | 6 +-- .../Services/MqttBackgroundService.cs | 1 + .../Services/MqttServiceManager.cs | 1 + DMS.WPF/Profiles/MappingProfile.cs | 1 + DMS.WPF/Services/DataServices.cs | 6 ++- .../ViewModels/Items/VariableItemViewModel.cs | 2 +- QWEN.md | 48 +++++++++++++++++ 11 files changed, 122 insertions(+), 44 deletions(-) rename {DMS.Infrastructure => DMS.Core}/Interfaces/Services/IMqttServiceManager.cs (93%) create mode 100644 QWEN.md diff --git a/DMS.Application/DTOs/VariableDto.cs b/DMS.Application/DTOs/VariableDto.cs index 1461b56..2a0b2c3 100644 --- a/DMS.Application/DTOs/VariableDto.cs +++ b/DMS.Application/DTOs/VariableDto.cs @@ -14,7 +14,7 @@ public class VariableDto public string DataValue { get; set; } public string DisplayValue { get; set; } public VariableTableDto? VariableTable { get; set; } - public List? MqttAliases { get; set; } + public List? MqttAliases { get; set; } = new List(); public SignalType SignalType { get; set; } public int PollingInterval { get; set; } public bool IsActive { get; set; } diff --git a/DMS.Application/Services/DataCenterService.cs b/DMS.Application/Services/DataCenterService.cs index d31dec3..0ab3d79 100644 --- a/DMS.Application/Services/DataCenterService.cs +++ b/DMS.Application/Services/DataCenterService.cs @@ -90,7 +90,6 @@ public class DataCenterService : IDataCenterService /// public event EventHandler MqttServerChanged; - /// /// 当变量值发生变化时触发 @@ -202,10 +201,9 @@ public class DataCenterService : IDataCenterService /// public void RemoveDeviceFromMemory(int deviceId) { - if (Devices.TryGetValue(deviceId, out var deviceDto)) { - foreach (var variableTable in deviceDto.VariableTables) + foreach (var variableTable in deviceDto.VariableTables) { foreach (var variable in variableTable.Variables) { @@ -216,7 +214,7 @@ public class DataCenterService : IDataCenterService } Devices.TryRemove(deviceId, out _); - + OnDeviceChanged(new DeviceChangedEventArgs(DataChangeType.Deleted, deviceDto)); } } @@ -317,7 +315,6 @@ public class DataCenterService : IDataCenterService { deviceDto = device; device.VariableTables.Remove(variableTableDto); - } OnVariableTableChanged(new VariableTableChangedEventArgs( @@ -663,9 +660,11 @@ public class DataCenterService : IDataCenterService // 加载所有菜单 var menus = await _repositoryManager.Menus.GetAllAsync(); var menuDtos = _mapper.Map>(menus); - + var mqttServers = await LoadAllMqttServersAsync(); + var variableMqttAliases = await _repositoryManager.VariableMqttAliases.GetAllAsync(); + // 建立设备与变量表的关联 foreach (var deviceDto in deviceDtos) { @@ -691,6 +690,12 @@ public class DataCenterService : IDataCenterService // 将变量表添加到安全字典 VariableTables.TryAdd(variableTableDto.Id, variableTableDto); } + + // 加载MQTT服务器数据到内存 + foreach (var mqttServer in mqttServers) + { + MqttServers.TryAdd(mqttServer.Id, mqttServer); + } // 将变量添加到安全字典 foreach (var variableDto in variableDtos) @@ -699,6 +704,22 @@ public class DataCenterService : IDataCenterService { variableDto.VariableTable = variableTable; } + + // var alises= variableMqttAliases.FirstOrDefault(vm => vm.VariableId == variableDto.Id); + // if (alises != null) + // { + // + // var variableMqttAliasDto = _mapper.Map(alises); + // variableMqttAliasDto.Variable = _mapper.Map(variableDto) ; + // if (MqttServers.TryGetValue(variableMqttAliasDto.MqttServerId, out var mqttServerDto)) + // { + // variableMqttAliasDto.MqttServer = _mapper.Map(mqttServerDto) ; + // variableMqttAliasDto.MqttServerName = variableMqttAliasDto.MqttServer.ServerName; + // } + // + // variableDto.MqttAliases.Add(variableMqttAliasDto); + // } + Variables.TryAdd(variableDto.Id, variableDto); } @@ -707,12 +728,8 @@ public class DataCenterService : IDataCenterService { Menus.TryAdd(menuDto.Id, menuDto); } + - // 加载MQTT服务器数据到内存 - foreach (var mqttServer in mqttServers) - { - MqttServers.TryAdd(mqttServer.Id, mqttServer); - } // 构建菜单树 BuildMenuTree(); diff --git a/DMS.Application/Services/Processors/MqttPublishProcessor.cs b/DMS.Application/Services/Processors/MqttPublishProcessor.cs index dbefc74..b7a36f5 100644 --- a/DMS.Application/Services/Processors/MqttPublishProcessor.cs +++ b/DMS.Application/Services/Processors/MqttPublishProcessor.cs @@ -1,6 +1,8 @@ using System.Threading.Tasks; +using AutoMapper; using DMS.Application.Interfaces; using DMS.Application.Models; +using DMS.Core.Interfaces.Services; using DMS.Core.Models; namespace DMS.Application.Services.Processors; @@ -10,12 +12,14 @@ namespace DMS.Application.Services.Processors; /// public class MqttPublishProcessor : IVariableProcessor { - // private readonly IMqttServiceManager _mqttServiceManager; + private readonly IMapper _mapper; + private readonly IMqttServiceManager _mqttServiceManager; - // public MqttPublishProcessor(IMqttServiceManager mqttServiceManager) - // { - // // _mqttServiceManager = mqttServiceManager; - // } + public MqttPublishProcessor(IMapper mapper, IMqttServiceManager mqttServiceManager) + { + _mapper = mapper; + _mqttServiceManager = mqttServiceManager; + } /// /// 处理单个变量上下文,如果有关联的MQTT配置,则将其推送到发送队列。 @@ -23,25 +27,25 @@ public class MqttPublishProcessor : IVariableProcessor /// 包含变量及其元数据的上下文对象。 public async Task ProcessAsync(VariableContext context) { - // var variable = context.Data; - // if (variable?.MqttAliases == null || variable.MqttAliases.Count == 0) - // { - // return; // 没有关联的MQTT配置,直接返回 - // } - // - // // 遍历所有关联的MQTT配置,并将其推入发送队列 - // foreach (var variableMqttAlias in variable.MqttAliases) - // { - // // 创建VariableMqtt对象 - // var variableMqtt = new VariableMqtt - // { - // Variable = variable, - // Mqtt = variableMqttAlias.MqttServer, - // MqttId = variableMqttAlias.MqttServerId - // }; - // - // // 发布变量数据到MQTT服务器 - // await _mqttServiceManager.PublishVariableDataAsync(variableMqtt); - // } + var variable = context.Data; + if (variable?.MqttAliases == null || variable.MqttAliases.Count == 0) + { + return; // 没有关联的MQTT配置,直接返回 + } + + // 遍历所有关联的MQTT配置,并将其推入发送队列 + foreach (var variableMqttAlias in variable.MqttAliases) + { + // 创建VariableMqtt对象 + var variableMqtt = new VariableMqtt + { + Variable = _mapper.Map(variable), + Mqtt = variableMqttAlias.MqttServer, + MqttId = variableMqttAlias.MqttServerId + }; + + // 发布变量数据到MQTT服务器 + await _mqttServiceManager.PublishVariableDataAsync(variableMqtt); + } } } \ No newline at end of file diff --git a/DMS.Core/Interfaces/Repositories/IVariableMqttAliasRepository.cs b/DMS.Core/Interfaces/Repositories/IVariableMqttAliasRepository.cs index c4ba73e..f0ae3a7 100644 --- a/DMS.Core/Interfaces/Repositories/IVariableMqttAliasRepository.cs +++ b/DMS.Core/Interfaces/Repositories/IVariableMqttAliasRepository.cs @@ -15,5 +15,11 @@ namespace DMS.Core.Interfaces.Repositories /// 异步根据变量和服务器获取别名关联。 /// Task GetByVariableAndServerAsync(int variableId, int mqttServerId); + + /// + /// 异步获取所有变量与MQTT别名关联。 + /// + /// 包含所有变量与MQTT别名关联实体的列表。 + Task> GetAllAsync(); } } \ No newline at end of file diff --git a/DMS.Infrastructure/Interfaces/Services/IMqttServiceManager.cs b/DMS.Core/Interfaces/Services/IMqttServiceManager.cs similarity index 93% rename from DMS.Infrastructure/Interfaces/Services/IMqttServiceManager.cs rename to DMS.Core/Interfaces/Services/IMqttServiceManager.cs index 58b604d..68b8efc 100644 --- a/DMS.Infrastructure/Interfaces/Services/IMqttServiceManager.cs +++ b/DMS.Core/Interfaces/Services/IMqttServiceManager.cs @@ -1,10 +1,6 @@ using DMS.Core.Models; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -namespace DMS.Infrastructure.Interfaces.Services +namespace DMS.Core.Interfaces.Services { /// /// MQTT服务管理器接口,负责管理MQTT连接和变量监控 diff --git a/DMS.Infrastructure/Services/MqttBackgroundService.cs b/DMS.Infrastructure/Services/MqttBackgroundService.cs index 3167e48..77db7b1 100644 --- a/DMS.Infrastructure/Services/MqttBackgroundService.cs +++ b/DMS.Infrastructure/Services/MqttBackgroundService.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using DMS.Application.DTOs.Events; using DMS.Core.Models; using DMS.Application.Interfaces; +using DMS.Core.Interfaces.Services; namespace DMS.Infrastructure.Services { diff --git a/DMS.Infrastructure/Services/MqttServiceManager.cs b/DMS.Infrastructure/Services/MqttServiceManager.cs index 68a5cd9..2f07106 100644 --- a/DMS.Infrastructure/Services/MqttServiceManager.cs +++ b/DMS.Infrastructure/Services/MqttServiceManager.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using DMS.Core.Models; using DMS.Application.Interfaces; +using DMS.Core.Interfaces.Services; namespace DMS.Infrastructure.Services { diff --git a/DMS.WPF/Profiles/MappingProfile.cs b/DMS.WPF/Profiles/MappingProfile.cs index ec28880..1c5eeb7 100644 --- a/DMS.WPF/Profiles/MappingProfile.cs +++ b/DMS.WPF/Profiles/MappingProfile.cs @@ -17,6 +17,7 @@ namespace DMS.WPF.Profiles CreateMap() .ReverseMap(); CreateMap(); + CreateMap().ReverseMap(); CreateMap() diff --git a/DMS.WPF/Services/DataServices.cs b/DMS.WPF/Services/DataServices.cs index 299d6d3..84fca33 100644 --- a/DMS.WPF/Services/DataServices.cs +++ b/DMS.WPF/Services/DataServices.cs @@ -123,7 +123,11 @@ public partial class DataServices : ObservableObject, IRecipient, I /// private void LoadAllDatas() { - Devices = _mapper.Map>(_dataCenterService.Devices.Values); + + foreach (var deviceDto in _dataCenterService.Devices.Values) + { + Devices.Add(_mapper.Map(deviceDto)); + } foreach (var device in Devices) { foreach (var variableTable in device.VariableTables) diff --git a/DMS.WPF/ViewModels/Items/VariableItemViewModel.cs b/DMS.WPF/ViewModels/Items/VariableItemViewModel.cs index a20545c..469d684 100644 --- a/DMS.WPF/ViewModels/Items/VariableItemViewModel.cs +++ b/DMS.WPF/ViewModels/Items/VariableItemViewModel.cs @@ -60,7 +60,7 @@ public partial class VariableItemViewModel : ObservableObject /// 一个变量可以有多个MQTT别名。 /// [ObservableProperty] - private List? _mqttAliases; + private List? _mqttAliases=new List(); /// /// 获取或设置变量的信号类型 (如:AI, DI, AO, DO)。 diff --git a/QWEN.md b/QWEN.md new file mode 100644 index 0000000..488fd86 --- /dev/null +++ b/QWEN.md @@ -0,0 +1,48 @@ +# Project Overview: DMS (Device Management System) + +This directory contains the source code for the Device Management System (DMS), a C#/.NET 8 application. The system is primarily composed of a WPF desktop application for the user interface, backed by a layered architecture including Core, Application, and Infrastructure projects. It also includes dedicated unit test projects for both the infrastructure and WPF layers. + +A significant feature described in the README is an OPC UA service implementation, suggesting the system is designed for industrial device communication and management. + +## Project Structure + +The solution (`DMS.sln`) is organized into several key projects: + +* **`DMS.Core`**: Contains fundamental, reusable components and shared models. Likely includes utilities, common data transfer objects (DTOs), and core business logic abstractions. It references `Microsoft.Extensions.Logging.Abstractions`, `Newtonsoft.Json`, and `NLog`. +* **`DMS.Application`**: Implements application-specific business logic. It depends on `DMS.Core` and uses libraries like `AutoMapper` for object mapping and `Microsoft.Extensions` for hosting and logging abstractions. +* **`DMS.Infrastructure`**: Handles data access, external integrations, and technical implementations. It references `DMS.Core` and `DMS.Application`. Key dependencies include: + * `SqlSugarCore`: For database interaction (MySQL). + * `MQTTnet`: For MQTT protocol communication. + * `OPCFoundation.NetStandard.Opc.Ua`: For OPC UA communication. + * `S7netplus`: For Siemens S7 PLC communication. + * `NPOI`: For reading/writing Excel files. + * `AutoMapper.Extensions.Microsoft.DependencyInjection`: For dependency injection setup of AutoMapper. +* **`DMS.WPF`**: The main WPF desktop application. It targets `net8.0-windows` and uses WPF-specific libraries like `CommunityToolkit.Mvvm`, `HandyControl` (UI toolkit), `Hardcodet.NotifyIcon.Wpf` (system tray icon), and `iNKORE.UI.WPF.Modern` (modern UI styling). It depends on `DMS.Application` and `DMS.Infrastructure`. +* **`DMS.Infrastructure.UnitTests`**: Unit tests for the `DMS.Infrastructure` project. Uses `xunit`, `Moq`, `Bogus` (for fake data), and `Microsoft.NET.Test.Sdk`. +* **`DMS.WPF.UnitTests`**: Unit tests for the `DMS.WPF` project. Also uses `xunit`, `Moq`, and `Bogus`. + +## Building and Running + +This is a .NET 8 solution. You will need the .NET 8 SDK and an IDE like Visual Studio or JetBrains Rider to build and run it. + +**To Build:** +Use the standard .NET CLI command within the solution directory: +`dotnet build` + +**To Run:** +The main executable is the WPF application (`DMS.WPF`). You can run it using: +`dotnet run --project DMS.WPF` + +**To Test:** +Unit tests are included in `DMS.Infrastructure.UnitTests` and `DMS.WPF.UnitTests`. +Run all tests using: +`dotnet test` + +## Development Conventions + +* **Language & Framework:** C# with .NET 8. +* **Architecture:** Layered architecture (Core, Application, Infrastructure) with a WPF presentation layer. +* **UI Pattern:** MVVM (Model-View-ViewModel), supported by `CommunityToolkit.Mvvm`. +* **DI (Dependency Injection):** Microsoft.Extensions.DependencyInjection is used, configured likely in the `DMS.WPF` project. +* **Logging:** NLog is used for logging, configured via `DMS.WPF\Configurations\nlog.config`. +* **Mapping:** AutoMapper is used for object-to-object mapping. \ No newline at end of file