1 实现 MQTT 变量数据发布功能

2
   3 - 迁移 IMqttServiceManager 接口到 DMS.Core
   4 - 在 DataCenterService 中添加 MQTT 服务器和变量别名的加载逻辑
   5 - 实现 MqttPublishProcessor 的核心处理逻辑
   6 - 为 DTO 和 ViewModel 的 MqttAliases 属性提供默认空列表初始化
   7 - 更新 AutoMapper 映射配置以支持 VariableMqttAliasDto
This commit is contained in:
2025-09-07 08:51:18 +08:00
parent be16e1a5d7
commit 1f9c0a1111
11 changed files with 122 additions and 44 deletions

View File

@@ -14,7 +14,7 @@ public class VariableDto
public string DataValue { get; set; }
public string DisplayValue { get; set; }
public VariableTableDto? VariableTable { get; set; }
public List<VariableMqttAliasDto>? MqttAliases { get; set; }
public List<VariableMqttAliasDto>? MqttAliases { get; set; } = new List<VariableMqttAliasDto>();
public SignalType SignalType { get; set; }
public int PollingInterval { get; set; }
public bool IsActive { get; set; }

View File

@@ -91,7 +91,6 @@ public class DataCenterService : IDataCenterService
public event EventHandler<MqttServerChangedEventArgs> MqttServerChanged;
/// <summary>
/// 当变量值发生变化时触发
/// </summary>
@@ -202,7 +201,6 @@ public class DataCenterService : IDataCenterService
/// </summary>
public void RemoveDeviceFromMemory(int deviceId)
{
if (Devices.TryGetValue(deviceId, out var deviceDto))
{
foreach (var variableTable in deviceDto.VariableTables)
@@ -317,7 +315,6 @@ public class DataCenterService : IDataCenterService
{
deviceDto = device;
device.VariableTables.Remove(variableTableDto);
}
OnVariableTableChanged(new VariableTableChangedEventArgs(
@@ -666,6 +663,8 @@ public class DataCenterService : IDataCenterService
var mqttServers = await LoadAllMqttServersAsync();
var variableMqttAliases = await _repositoryManager.VariableMqttAliases.GetAllAsync();
// 建立设备与变量表的关联
foreach (var deviceDto in deviceDtos)
{
@@ -692,6 +691,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<VariableMqttAliasDto>(alises);
// variableMqttAliasDto.Variable = _mapper.Map<Variable>(variableDto) ;
// if (MqttServers.TryGetValue(variableMqttAliasDto.MqttServerId, out var mqttServerDto))
// {
// variableMqttAliasDto.MqttServer = _mapper.Map<MqttServer>(mqttServerDto) ;
// variableMqttAliasDto.MqttServerName = variableMqttAliasDto.MqttServer.ServerName;
// }
//
// variableDto.MqttAliases.Add(variableMqttAliasDto);
// }
Variables.TryAdd(variableDto.Id, variableDto);
}
@@ -708,11 +729,7 @@ public class DataCenterService : IDataCenterService
Menus.TryAdd(menuDto.Id, menuDto);
}
// 加载MQTT服务器数据到内存
foreach (var mqttServer in mqttServers)
{
MqttServers.TryAdd(mqttServer.Id, mqttServer);
}
// 构建菜单树
BuildMenuTree();

View File

@@ -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;
/// </summary>
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;
}
/// <summary>
/// 处理单个变量上下文如果有关联的MQTT配置则将其推送到发送队列。
@@ -23,25 +27,25 @@ public class MqttPublishProcessor : IVariableProcessor
/// <param name="context">包含变量及其元数据的上下文对象。</param>
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>(variable),
Mqtt = variableMqttAlias.MqttServer,
MqttId = variableMqttAlias.MqttServerId
};
// 发布变量数据到MQTT服务器
await _mqttServiceManager.PublishVariableDataAsync(variableMqtt);
}
}
}

View File

@@ -15,5 +15,11 @@ namespace DMS.Core.Interfaces.Repositories
/// 异步根据变量和服务器获取别名关联。
/// </summary>
Task<VariableMqttAlias> GetByVariableAndServerAsync(int variableId, int mqttServerId);
/// <summary>
/// 异步获取所有变量与MQTT别名关联。
/// </summary>
/// <returns>包含所有变量与MQTT别名关联实体的列表。</returns>
Task<List<VariableMqttAlias>> GetAllAsync();
}
}

View File

@@ -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
{
/// <summary>
/// MQTT服务管理器接口负责管理MQTT连接和变量监控

View File

@@ -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
{

View File

@@ -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
{

View File

@@ -17,6 +17,7 @@ namespace DMS.WPF.Profiles
CreateMap<OpcUaNode, OpcUaNodeItemViewModel>()
.ReverseMap();
CreateMap<VariableItemViewModel, VariableItemViewModel>();
CreateMap<VariableMqttAliasDto, VariableMqttAliasItemViewModel>().ReverseMap();
CreateMap<MenuBeanDto, MenuItemViewModel>()

View File

@@ -123,7 +123,11 @@ public partial class DataServices : ObservableObject, IRecipient<LoadMessage>, I
/// </summary>
private void LoadAllDatas()
{
Devices = _mapper.Map<ObservableCollection<DeviceItemViewModel>>(_dataCenterService.Devices.Values);
foreach (var deviceDto in _dataCenterService.Devices.Values)
{
Devices.Add(_mapper.Map<DeviceItemViewModel>(deviceDto));
}
foreach (var device in Devices)
{
foreach (var variableTable in device.VariableTables)

View File

@@ -60,7 +60,7 @@ public partial class VariableItemViewModel : ObservableObject
/// 一个变量可以有多个MQTT别名。
/// </summary>
[ObservableProperty]
private List<VariableMqttAliasItemViewModel>? _mqttAliases;
private List<VariableMqttAliasItemViewModel>? _mqttAliases=new List<VariableMqttAliasItemViewModel>();
/// <summary>
/// 获取或设置变量的信号类型 (如AI, DI, AO, DO)。

48
QWEN.md Normal file
View File

@@ -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.