diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs deleted file mode 100644 index 4a05c7d..0000000 --- a/AssemblyInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Windows; - -[assembly: ThemeInfo( - ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located - //(used if a resource is not found in the page, - // or application resource dictionaries) - ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located - //(used if a resource is not found in the page, - // app, or any theme specific resource dictionaries) -)] \ No newline at end of file diff --git a/Profiles/MappingProfile.cs b/Profiles/MappingProfile.cs new file mode 100644 index 0000000..10aa32b --- /dev/null +++ b/Profiles/MappingProfile.cs @@ -0,0 +1,32 @@ +using System; +using AutoMapper; +using PMSWPF.Data.Entities; +using PMSWPF.Models; + +namespace PMSWPF.Profiles; + +/// +/// AutoMapper 的配置类,用于定义对象之间的映射规则。 +/// +public class MappingProfile : Profile +{ + public MappingProfile() + { + // --- 用户映射 --- + + + // --- 设备映射 (包含List的父对象) --- + // AutoMapper 会自动使用上面的规则来处理 VariableTables 属性 + CreateMap().ReverseMap(); + + // --- 变量表映射 (List中的元素) --- + CreateMap().ReverseMap(); + + CreateMap().ReverseMap(); + // --- MQTT 和 变量数据 映射 --- + CreateMap().ReverseMap(); + + CreateMap().ReverseMap(); + + } +} diff --git a/Services/DataProcessingService.cs b/Services/DataProcessingService.cs new file mode 100644 index 0000000..ed8a603 --- /dev/null +++ b/Services/DataProcessingService.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Channels; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using PMSWPF.Models; + +namespace PMSWPF.Services; + +/// +/// 核心数据处理服务,作为后台服务运行。 +/// 它维护一个无界通道(Channel)作为处理队列,并按顺序执行已注册的数据处理器。 +/// +public class DataProcessingService : BackgroundService, IDataProcessingService +{ + private readonly ILogger _logger; + // 使用 Channel 作为高性能的生产者/消费者队列 + private readonly Channel _queue; + // 存储数据处理器的链表 + private readonly List _processors; + + /// + /// 构造函数,注入日志记录器。 + /// + /// 日志记录器实例。 + public DataProcessingService(ILogger logger) + { + _logger = logger; + // 创建一个无边界的 Channel,允许生产者快速写入而不会被阻塞。 + _queue = Channel.CreateUnbounded(); + _processors = new List(); + } + + /// + /// 向处理链中添加一个数据处理器。 + /// 处理器将按照添加的顺序执行。 + /// + /// 要添加的数据处理器实例。 + public void AddProcessor(IVariableDataProcessor processor) + { + _processors.Add(processor); + } + + /// + /// 将一个变量数据项异步推入处理队列。 + /// + /// 要入队的变量数据。 + public async ValueTask EnqueueAsync(VariableData data) + { + if (data == null) + { + return; + } + + // 将数据项写入 Channel,供后台服务处理。 + await _queue.Writer.WriteAsync(data); + } + + /// + /// 后台服务的核心执行逻辑。 + /// 此方法会持续运行,从队列中读取数据并交由处理器链处理。 + /// + /// 用于通知服务停止的取消令牌。 + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + _logger.LogInformation("数据处理服务已启动。"); + + // 当服务未被请求停止时,持续循环 + while (!stoppingToken.IsCancellationRequested) + { + try + { + // 从队列中异步读取一个数据项,如果队列为空,则等待。 + var data = await _queue.Reader.ReadAsync(stoppingToken); + + // 依次调用处理链中的每一个处理器 + foreach (var processor in _processors) + { + await processor.ProcessAsync(data); + } + } + catch (OperationCanceledException) + { + // 当 stoppingToken 被触发时,ReadAsync 会抛出此异常,属正常停止流程,无需处理。 + } + catch (Exception ex) + { + _logger.LogError(ex, "处理变量数据时发生错误。"); + } + } + + _logger.LogInformation("数据处理服务已停止。"); + } +} diff --git a/Services/IDataProcessingService.cs b/Services/IDataProcessingService.cs new file mode 100644 index 0000000..2ba1133 --- /dev/null +++ b/Services/IDataProcessingService.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using PMSWPF.Models; + +namespace PMSWPF.Services; + +/// +/// 定义了数据处理服务的接口。 +/// 该服务负责管理数据处理队列和处理器链。 +/// +public interface IDataProcessingService +{ + /// + /// 向处理链中添加一个数据处理器。 + /// + /// 要添加的数据处理器实例。 + void AddProcessor(IVariableDataProcessor processor); + + /// + /// 将一个变量数据项异步推入处理队列。 + /// + /// 要入队的变量数据。 + /// 一个表示入队操作的 ValueTask。 + ValueTask EnqueueAsync(VariableData data); +} diff --git a/Services/IVariableDataProcessor.cs b/Services/IVariableDataProcessor.cs new file mode 100644 index 0000000..6517de1 --- /dev/null +++ b/Services/IVariableDataProcessor.cs @@ -0,0 +1,18 @@ +using System.Threading.Tasks; +using PMSWPF.Models; + +namespace PMSWPF.Services; + +/// +/// 定义了变量数据处理器的通用接口。 +/// 任何需要加入数据处理链的类都必须实现此接口。 +/// +public interface IVariableDataProcessor +{ + /// + /// 异步处理单个变量数据。 + /// + /// 要处理的变量数据。 + /// 一个表示异步操作的任务。 + Task ProcessAsync(VariableData data); +} diff --git a/Services/Processors/LoggingDataProcessor.cs b/Services/Processors/LoggingDataProcessor.cs new file mode 100644 index 0000000..747ef49 --- /dev/null +++ b/Services/Processors/LoggingDataProcessor.cs @@ -0,0 +1,35 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using PMSWPF.Helper; +using PMSWPF.Models; + +namespace PMSWPF.Services.Processors; + +/// +/// 一个简单的数据处理器实现,用于演示。 +/// 其主要功能是记录接收到的变量数据的名称和值。 +/// +public class LoggingDataProcessor : IVariableDataProcessor +{ + + /// + /// 构造函数,注入日志记录器。 + /// + /// 日志记录器实例。 + public LoggingDataProcessor() + { + } + + /// + /// 实现处理逻辑,此处为记录日志。 + /// + /// 要处理的变量数据。 + /// 一个表示完成的异步任务。 + public Task ProcessAsync(VariableData data) + { + // 使用日志记录器输出变量的名称和值 + NlogHelper.Info($"处理数据: {data.Name}, 值: {data.DataValue}"); + // 由于此操作是同步的,直接返回一个已完成的任务。 + return Task.CompletedTask; + } +} diff --git a/Services/S7BackgroundService.cs b/Services/S7BackgroundService.cs index 99dc928..d2fa762 100644 --- a/Services/S7BackgroundService.cs +++ b/Services/S7BackgroundService.cs @@ -14,6 +14,8 @@ namespace PMSWPF.Services { // 数据服务实例,用于访问和操作应用程序数据,如设备配置。 private readonly DataServices _dataServices; + // 数据处理服务实例,用于将读取到的数据推入处理队列。 + private readonly IDataProcessingService _dataProcessingService; // 存储 S7设备,键为设备Id,值为会话对象。 private readonly Dictionary _deviceDic; @@ -44,13 +46,14 @@ namespace PMSWPF.Services private Thread _serviceMainThread; /// - /// 构造函数,注入ILogger和DataServices。 + /// 构造函数,注入数据服务和数据处理服务。 /// - /// 日志记录器实例。 /// 数据服务实例。 - public S7BackgroundService(DataServices dataServices) + /// 数据处理服务实例。 + public S7BackgroundService(DataServices dataServices, IDataProcessingService dataProcessingService) { _dataServices = dataServices; + _dataProcessingService = dataProcessingService; _deviceDic = new(); _pollVariableDic = new(); _s7PlcClientDic = new(); @@ -205,17 +208,17 @@ namespace PMSWPF.Services } /// - /// 读取变量数据 + /// 从 PLC 读取变量数据,并将其推送到数据处理队列。 /// - /// - /// - /// - private void ReadVariableData(VariableData variable, + /// 要读取的变量。 + /// S7 PLC 客户端实例。 + /// 关联的设备。 + private async void ReadVariableData(VariableData variable, Plc plcClient, Device device) { try { - _readVariableDic.Add(variable.Id, DataItem.FromAddress(variable.S7Address)); + _readVariableDic[variable.Id]=DataItem.FromAddress(variable.S7Address); if (_readVariableDic.Count == S7PollOnceReadMultipleVars) { // 批量读取 @@ -232,7 +235,8 @@ namespace PMSWPF.Services // 更新变量的原始数据值和显示值。 variableData.DataValue = dataItem.Value.ToString(); variableData.UpdateTime = DateTime.Now; - Console.WriteLine($"S7轮询变量:{variableData.Name},值:{variableData.DataValue}"); + // 将更新后的数据推入处理队列,而不是直接在控制台输出。 + await _dataProcessingService.EnqueueAsync(variableData); } _readVariableDic.Clear(); @@ -241,7 +245,7 @@ namespace PMSWPF.Services } catch (Exception ex) { - NlogHelper.Warn($"从设备 {device.Name} 读取变量 {variable.Name} 失败:{ex.Message}"); + NlogHelper.Error($"从设备 {device.Name} 读取变量 {variable.Name} 失败:{ex.Message}",ex); } }