From 5ab18f95f0924fdb3e1bb8597c350a15bf96fb48 Mon Sep 17 00:00:00 2001 From: "David P.G" Date: Mon, 15 Sep 2025 20:54:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DS7=E6=9C=8D=E5=8A=A1=E8=BD=AE?= =?UTF-8?q?=E8=AF=A2=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DMS.Application/DTOs/VariableDto.cs | 28 --- .../Events/VariableValueChangedEventArgs.cs | 50 ----- .../Interfaces/IDataProcessingService.cs | 3 +- DMS.Application/Interfaces/IEventService.cs | 16 +- DMS.Application/Models/VariableContext.cs | 5 +- .../Services/AppDataCenterService.cs | 1 + DMS.Application/Services/EventService.cs | 21 ++- .../Services/IVariableManagementService.cs | 13 +- .../Processors/DataProcessingService.cs | 5 +- .../Services/Processors/TriggerProcessor.cs | 34 ++++ .../Processors/UpdateViewProcessor.cs | 29 +++ .../Processors/ValueConvertProcessor.cs | 136 ++++++++++++++ .../Services/VariableManagementService.cs | 34 +--- .../VariableValueChangedEventArgs.cs | 2 +- .../Interfaces/Services/IS7ServiceManager.cs | 6 + .../Services/OpcUaServiceManager.cs | 27 +-- .../Services/OptimizedS7BackgroundService.cs | 175 +++++++----------- DMS.Infrastructure/Services/S7Service.cs | 9 +- .../Services/S7ServiceManager.cs | 7 + DMS.WPF/App.xaml.cs | 4 + DMS.WPF/Services/DataEventService.cs | 6 +- 21 files changed, 351 insertions(+), 260 deletions(-) delete mode 100644 DMS.Application/Events/VariableValueChangedEventArgs.cs create mode 100644 DMS.Application/Services/Processors/TriggerProcessor.cs create mode 100644 DMS.Application/Services/Processors/UpdateViewProcessor.cs create mode 100644 DMS.Application/Services/Processors/ValueConvertProcessor.cs rename DMS.Core/{Models => Events}/VariableValueChangedEventArgs.cs (97%) diff --git a/DMS.Application/DTOs/VariableDto.cs b/DMS.Application/DTOs/VariableDto.cs index b39ae39..91085e1 100644 --- a/DMS.Application/DTOs/VariableDto.cs +++ b/DMS.Application/DTOs/VariableDto.cs @@ -14,34 +14,6 @@ public class VariableDto public string DataValue { get; set; } public double NumericValue { get; set; } public string DisplayValue { get; set; } - - /// - /// 更新数值属性,根据DataValue的值进行转换。 - /// - public void UpdateNumericValue() - { - if (string.IsNullOrEmpty(DataValue)) - { - NumericValue = 0.0; - return; - } - - // 尝试将字符串转换为数值 - if (double.TryParse(DataValue, out double numericValue)) - { - NumericValue = numericValue; - } - // 如果是布尔值 - else if (bool.TryParse(DataValue, out bool boolValue)) - { - NumericValue = boolValue ? 1.0 : 0.0; - } - // 如果无法转换,保持为0.0 - else - { - NumericValue = 0.0; - } - } public VariableTableDto? VariableTable { get; set; } public List? MqttAliases { get; set; } = new List(); public SignalType SignalType { get; set; } diff --git a/DMS.Application/Events/VariableValueChangedEventArgs.cs b/DMS.Application/Events/VariableValueChangedEventArgs.cs deleted file mode 100644 index e72e3f2..0000000 --- a/DMS.Application/Events/VariableValueChangedEventArgs.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; - -namespace DMS.Application.Events; - -/// -/// 变量值改变事件参数 -/// -public class VariableValueChangedEventArgs : EventArgs -{ - /// - /// 变量ID - /// - public int VariableId { get; } - - /// - /// 变量名称 - /// - public string VariableName { get; } - - /// - /// 旧值 - /// - public string OldValue { get; } - - /// - /// 新值 - /// - public string NewValue { get; } - - /// - /// 值改变时间 - /// - public DateTime ChangeTime { get; } - - /// - /// 初始化VariableValueChangedEventArgs类的新实例 - /// - /// 变量ID - /// 变量名称 - /// 旧值 - /// 新值 - public VariableValueChangedEventArgs(int variableId, string variableName, string oldValue, string newValue) - { - VariableId = variableId; - VariableName = variableName; - OldValue = oldValue; - NewValue = newValue; - ChangeTime = DateTime.Now; - } -} \ No newline at end of file diff --git a/DMS.Application/Interfaces/IDataProcessingService.cs b/DMS.Application/Interfaces/IDataProcessingService.cs index 06d21ff..9108724 100644 --- a/DMS.Application/Interfaces/IDataProcessingService.cs +++ b/DMS.Application/Interfaces/IDataProcessingService.cs @@ -1,6 +1,7 @@ using DMS.Core.Models; using System.Threading.Tasks; using DMS.Application.DTOs; +using DMS.Application.Models; namespace DMS.Application.Interfaces; @@ -21,5 +22,5 @@ public interface IDataProcessingService /// /// 要入队的变量数据。 /// 一个表示入队操作的 ValueTask。 - ValueTask EnqueueAsync(VariableDto data); + ValueTask EnqueueAsync(VariableContext data); } \ No newline at end of file diff --git a/DMS.Application/Interfaces/IEventService.cs b/DMS.Application/Interfaces/IEventService.cs index a4b95f8..6b96af4 100644 --- a/DMS.Application/Interfaces/IEventService.cs +++ b/DMS.Application/Interfaces/IEventService.cs @@ -1,5 +1,7 @@ using System; +using DMS.Application.DTOs.Events; using DMS.Application.Events; +using DMS.Core.Events; namespace DMS.Application.Interfaces; @@ -29,7 +31,7 @@ public interface IEventService /// /// 变量值改变事件 /// - event EventHandler VariableValueChanged; + event EventHandler OnVariableValueChanged; /// /// 触发变量值改变事件 @@ -67,4 +69,16 @@ public interface IEventService /// 事件发送者 /// 设备状态改变事件参数 void RaiseDeviceConnectChanged(object sender, DeviceConnectChangedEventArgs e); + + /// + /// 变量值改变事件 + /// + event EventHandler OnVariableChanged; + + /// + /// 触发变量值改变事件 + /// + /// 事件发送者 + /// 变量值改变事件参数 + void RaiseVariableChanged(object sender, VariableChangedEventArgs e); } \ No newline at end of file diff --git a/DMS.Application/Models/VariableContext.cs b/DMS.Application/Models/VariableContext.cs index 1efe679..4a81a1d 100644 --- a/DMS.Application/Models/VariableContext.cs +++ b/DMS.Application/Models/VariableContext.cs @@ -6,12 +6,15 @@ namespace DMS.Application.Models public class VariableContext { public VariableDto Data { get; set; } + + public object NewValue { get; set; } public bool IsHandled { get; set; } - public VariableContext(VariableDto data) + public VariableContext(VariableDto data, object newValue=null) { Data = data; IsHandled = false; // 默认未处理 + NewValue = newValue; } } } \ No newline at end of file diff --git a/DMS.Application/Services/AppDataCenterService.cs b/DMS.Application/Services/AppDataCenterService.cs index 5fcc80f..f3b4e47 100644 --- a/DMS.Application/Services/AppDataCenterService.cs +++ b/DMS.Application/Services/AppDataCenterService.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using System; using System.Linq; +using DMS.Core.Events; namespace DMS.Application.Services; diff --git a/DMS.Application/Services/EventService.cs b/DMS.Application/Services/EventService.cs index caf85cf..79b979d 100644 --- a/DMS.Application/Services/EventService.cs +++ b/DMS.Application/Services/EventService.cs @@ -1,6 +1,8 @@ using System; +using DMS.Application.DTOs.Events; using DMS.Application.Events; using DMS.Application.Interfaces; +using DMS.Core.Events; namespace DMS.Application.Services; @@ -60,11 +62,26 @@ public class EventService : IEventService #endregion #region 变量事件 + /// + /// 变量值改变事件 + /// + public event EventHandler OnVariableChanged; + + /// + /// 触发变量值改变事件 + /// + /// 事件发送者 + /// 变量值改变事件参数 + public void RaiseVariableChanged(object sender, VariableChangedEventArgs e) + { + OnVariableChanged?.Invoke(sender, e); + } + /// /// 变量值改变事件 /// - public event EventHandler VariableValueChanged; + public event EventHandler OnVariableValueChanged; /// /// 触发变量值改变事件 @@ -73,7 +90,7 @@ public class EventService : IEventService /// 变量值改变事件参数 public void RaiseVariableValueChanged(object sender, VariableValueChangedEventArgs e) { - VariableValueChanged?.Invoke(sender, e); + OnVariableValueChanged?.Invoke(sender, e); } #endregion diff --git a/DMS.Application/Services/IVariableManagementService.cs b/DMS.Application/Services/IVariableManagementService.cs index c4df21f..517ef62 100644 --- a/DMS.Application/Services/IVariableManagementService.cs +++ b/DMS.Application/Services/IVariableManagementService.cs @@ -1,6 +1,7 @@ using System.Collections.Concurrent; using DMS.Application.DTOs; using DMS.Application.DTOs.Events; +using DMS.Core.Events; using DMS.Core.Models; namespace DMS.Application.Services; @@ -56,16 +57,4 @@ public interface IVariableManagementService /// 在内存中删除变量 /// void RemoveVariableFromMemory(int variableId, ConcurrentDictionary variableTables); - - void VariableValueChanged(VariableValueChangedEventArgs eventArgs); - - /// - /// 当变量数据发生变化时触发 - /// - event EventHandler OnVariableChanged; - - /// - /// 当变量数据发生变化时触发 - /// - event EventHandler OnVariableValueChanged; } \ No newline at end of file diff --git a/DMS.Application/Services/Processors/DataProcessingService.cs b/DMS.Application/Services/Processors/DataProcessingService.cs index 8ad84f4..605c4e3 100644 --- a/DMS.Application/Services/Processors/DataProcessingService.cs +++ b/DMS.Application/Services/Processors/DataProcessingService.cs @@ -50,14 +50,13 @@ public class DataProcessingService : BackgroundService, IDataProcessingService /// 将一个变量数据项异步推入处理队列。 /// /// 要入队的变量数据。 - public async ValueTask EnqueueAsync(VariableDto data) + public async ValueTask EnqueueAsync(VariableContext context) { - if (data == null) + if (context == null) { return; } - var context = new VariableContext(data); // 将数据项写入 Channel,供后台服务处理。 await _queue.Writer.WriteAsync(context); } diff --git a/DMS.Application/Services/Processors/TriggerProcessor.cs b/DMS.Application/Services/Processors/TriggerProcessor.cs new file mode 100644 index 0000000..d828f51 --- /dev/null +++ b/DMS.Application/Services/Processors/TriggerProcessor.cs @@ -0,0 +1,34 @@ +using DMS.Application.Interfaces; +using DMS.Application.Models; +using DMS.Application.Services.Triggers; +using Microsoft.Extensions.Logging; + +namespace DMS.Application.Services.Processors; + +public class TriggerProcessor : IVariableProcessor +{ + private readonly ITriggerEvaluationService _triggerEvaluationService; + private readonly ILogger _logger; + + public TriggerProcessor(ITriggerEvaluationService triggerEvaluationService, ILogger logger) + { + _triggerEvaluationService = triggerEvaluationService; + _logger = logger; + } + + public async Task ProcessAsync(VariableContext context) + { + // try + // { + // // 调用触发器评估服务来评估与变量关联的所有激活状态的触发器 + // await _triggerEvaluationService.EvaluateTriggersAsync(context.Data.Id, context.Data.DataValue); + // + // _logger.LogDebug("触发器评估完成,变量 ID: {VariableId}", context.Data.Id); + // } + // catch (Exception ex) + // { + // _logger.LogError(ex, "评估变量 {VariableId} 的触发器时发生错误", context.Data.Id); + // // 不抛出异常,避免影响其他处理器 + // } + } +} \ No newline at end of file diff --git a/DMS.Application/Services/Processors/UpdateViewProcessor.cs b/DMS.Application/Services/Processors/UpdateViewProcessor.cs new file mode 100644 index 0000000..ae45de2 --- /dev/null +++ b/DMS.Application/Services/Processors/UpdateViewProcessor.cs @@ -0,0 +1,29 @@ +using DMS.Application.Interfaces; +using DMS.Application.Models; +using DMS.Core.Events; + +namespace DMS.Application.Services.Processors; + +public class UpdateViewProcessor: IVariableProcessor +{ + private readonly IEventService _eventService; + + public UpdateViewProcessor(IEventService eventService) + { + _eventService = eventService; + } + + public async Task ProcessAsync(VariableContext context) + { + // 触发变量值变更事件 + var eventArgs = new VariableValueChangedEventArgs( + context.Data.Id, + context.Data.Name, + context.Data.DataValue, + context.NewValue.ToString()??"", + DateTime.Now); + + _eventService.RaiseVariableValueChanged(this,eventArgs); + + } +} \ No newline at end of file diff --git a/DMS.Application/Services/Processors/ValueConvertProcessor.cs b/DMS.Application/Services/Processors/ValueConvertProcessor.cs new file mode 100644 index 0000000..ca8cd42 --- /dev/null +++ b/DMS.Application/Services/Processors/ValueConvertProcessor.cs @@ -0,0 +1,136 @@ +using System.Globalization; +using DMS.Application.DTOs; +using DMS.Application.Interfaces; +using DMS.Application.Models; +using Microsoft.Extensions.Logging; + +namespace DMS.Application.Services.Processors; + +public class ValueConvertProcessor : IVariableProcessor +{ + private readonly ILogger _logger; + + public ValueConvertProcessor(ILogger logger) + { + _logger = logger; + } + public async Task ProcessAsync(VariableContext context) + { + var oldValue = context.Data.DataValue; + ConvertS7ValueToStringAndNumeric(context.Data, context.NewValue); + context.Data.UpdatedAt = DateTime.Now; + // 如何值没有变化则中断处理 + if (context.Data.DataValue!=oldValue) + { + context.IsHandled = true; + } + } + /// + /// 将从 S7 读取的对象值转换为字符串表示和数值表示 + /// + /// 关联的变量 DTO + /// 从 S7 读取的原始对象值 + /// (字符串表示, 数值表示) + private void ConvertS7ValueToStringAndNumeric(VariableDto variable, object value) + { + if (value == null) + return ; + + // 首先根据 value 的实际运行时类型进行匹配和转换 + string directConversion = null; + double numericValue = 0.0; + bool numericParsed = false; + + switch (value) + { + case double d: + directConversion = d.ToString("G17", CultureInfo.InvariantCulture); + numericValue = d; + numericParsed = true; + break; + case float f: + directConversion = f.ToString("G9", CultureInfo.InvariantCulture); + numericValue = f; + numericParsed = true; + break; + case int i: + directConversion = i.ToString(CultureInfo.InvariantCulture); + numericValue = i; + numericParsed = true; + break; + case uint ui: + directConversion = ui.ToString(CultureInfo.InvariantCulture); + numericValue = ui; + numericParsed = true; + break; + case short s: + directConversion = s.ToString(CultureInfo.InvariantCulture); + numericValue = s; + numericParsed = true; + break; + case ushort us: + directConversion = us.ToString(CultureInfo.InvariantCulture); + numericValue = us; + numericParsed = true; + break; + case byte b: + directConversion = b.ToString(CultureInfo.InvariantCulture); + numericValue = b; + numericParsed = true; + break; + case sbyte sb: + directConversion = sb.ToString(CultureInfo.InvariantCulture); + numericValue = sb; + numericParsed = true; + break; + case long l: + directConversion = l.ToString(CultureInfo.InvariantCulture); + numericValue = l; + numericParsed = true; + break; + case ulong ul: + directConversion = ul.ToString(CultureInfo.InvariantCulture); + numericValue = ul; + numericParsed = true; + break; + case bool boolValue: + directConversion = boolValue.ToString().ToLowerInvariant(); + numericValue = boolValue ? 1.0 : 0.0; + numericParsed = true; + break; + case string str: + directConversion = str; + // 尝试从字符串解析数值 + if (double.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out var parsedFromStr)) + { + numericValue = parsedFromStr; + numericParsed = true; + } + break; + default: + // 对于未预期的类型,记录日志 + _logger.LogWarning($"变量 {variable.Name} 读取到未预期的数据类型: {value.GetType().Name}, 值: {value}"); + directConversion = value.ToString() ?? string.Empty; + // 尝试从 ToString() 结果解析数值 + if (double.TryParse(directConversion, NumberStyles.Float, CultureInfo.InvariantCulture, out var parsedFromObj)) + { + numericValue = parsedFromObj; + numericParsed = true; + } + break; + } + + // 如果直接转换成功,直接返回 + + // 如果直接转换未能解析数值,并且变量有明确的 DataType,可以尝试更精细的解析 + // (这部分逻辑在上面的 switch 中已经处理了大部分情况,这里作为后备) + // 在这个实现中,我们主要依赖于 value 的实际类型进行转换,因为这通常更可靠。 + // 如果需要,可以根据 variable.DataType 添加额外的解析逻辑。 + + // 返回最终结果 + variable.DataValue = directConversion ?? value.ToString() ?? string.Empty; + variable.NumericValue = numericValue; + } + + +} \ No newline at end of file diff --git a/DMS.Application/Services/VariableManagementService.cs b/DMS.Application/Services/VariableManagementService.cs index 47e0e9e..493f36c 100644 --- a/DMS.Application/Services/VariableManagementService.cs +++ b/DMS.Application/Services/VariableManagementService.cs @@ -1,14 +1,8 @@ -using AutoMapper; +using System.Collections.Concurrent; using DMS.Application.DTOs; using DMS.Application.DTOs.Events; -using DMS.Core.Models; using DMS.Application.Interfaces; -using DMS.Core.Interfaces; -using DMS.Core.Enums; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Threading.Tasks; -using System; +using DMS.Core.Events; namespace DMS.Application.Services; @@ -18,24 +12,18 @@ namespace DMS.Application.Services; public class VariableManagementService : IVariableManagementService { private readonly IVariableAppService _variableAppService; + private readonly IEventService _eventService; private readonly IAppDataStorageService _appDataStorageService; private readonly IDataProcessingService _dataProcessingService; - /// - /// 当变量数据发生变化时触发 - /// - public event EventHandler OnVariableChanged; - - /// - /// 当变量数据发生变化时触发 - /// - public event EventHandler OnVariableValueChanged; public VariableManagementService(IVariableAppService variableAppService, + IEventService eventService, IAppDataStorageService appDataStorageService, IDataProcessingService dataProcessingService) { _variableAppService = variableAppService; + _eventService = eventService; _appDataStorageService = appDataStorageService; _dataProcessingService = dataProcessingService; } @@ -111,7 +99,7 @@ public class VariableManagementService : IVariableManagementService if (_appDataStorageService.Variables.TryAdd(variableDto.Id, variableDto)) { - OnVariableChanged?.Invoke( + _eventService.RaiseVariableChanged( this, new VariableChangedEventArgs(DataChangeType.Added, variableDto, variableTableDto)); } } @@ -129,7 +117,7 @@ public class VariableManagementService : IVariableManagementService } _appDataStorageService.Variables.AddOrUpdate(variableDto.Id, variableDto, (key, oldValue) => variableDto); - OnVariableChanged?.Invoke( + _eventService.RaiseVariableChanged( this, new VariableChangedEventArgs(DataChangeType.Updated, variableDto, variableTableDto)); } @@ -147,14 +135,8 @@ public class VariableManagementService : IVariableManagementService variableTable.Variables.Remove(variableDto); } - OnVariableChanged?.Invoke( + _eventService.RaiseVariableChanged( this, new VariableChangedEventArgs(DataChangeType.Deleted, variableDto, variableTableDto)); } } - - public void VariableValueChanged(VariableValueChangedEventArgs eventArgs) - { - // 触发事件,通知DataEventService等监听者 - OnVariableValueChanged?.Invoke(this, eventArgs); - } } \ No newline at end of file diff --git a/DMS.Core/Models/VariableValueChangedEventArgs.cs b/DMS.Core/Events/VariableValueChangedEventArgs.cs similarity index 97% rename from DMS.Core/Models/VariableValueChangedEventArgs.cs rename to DMS.Core/Events/VariableValueChangedEventArgs.cs index 76f8376..ed9067e 100644 --- a/DMS.Core/Models/VariableValueChangedEventArgs.cs +++ b/DMS.Core/Events/VariableValueChangedEventArgs.cs @@ -1,4 +1,4 @@ -namespace DMS.Core.Models +namespace DMS.Core.Events { /// /// 变量值变更事件参数 diff --git a/DMS.Infrastructure/Interfaces/Services/IS7ServiceManager.cs b/DMS.Infrastructure/Interfaces/Services/IS7ServiceManager.cs index f1f4ae4..6e490c4 100644 --- a/DMS.Infrastructure/Interfaces/Services/IS7ServiceManager.cs +++ b/DMS.Infrastructure/Interfaces/Services/IS7ServiceManager.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using DMS.Application.DTOs; +using DMS.Infrastructure.Services; namespace DMS.Infrastructure.Interfaces.Services { @@ -65,5 +66,10 @@ namespace DMS.Infrastructure.Interfaces.Services /// 批量断开设备连接 /// Task DisconnectDevicesAsync(IEnumerable deviceIds, CancellationToken cancellationToken = default); + + /// + /// 获取所有监控的设备ID + /// + List GetAllDeviceContexts(); } } \ No newline at end of file diff --git a/DMS.Infrastructure/Services/OpcUaServiceManager.cs b/DMS.Infrastructure/Services/OpcUaServiceManager.cs index 69d40c7..4ed44de 100644 --- a/DMS.Infrastructure/Services/OpcUaServiceManager.cs +++ b/DMS.Infrastructure/Services/OpcUaServiceManager.cs @@ -3,13 +3,14 @@ using System.Diagnostics; using DMS.Application.DTOs; using DMS.Application.Events; using DMS.Application.Interfaces; +using DMS.Application.Models; using DMS.Core.Enums; using DMS.Infrastructure.Configuration; using DMS.Infrastructure.Interfaces.Services; using DMS.Infrastructure.Models; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using VariableValueChangedEventArgs = DMS.Core.Models.VariableValueChangedEventArgs; +using VariableValueChangedEventArgs = DMS.Core.Events.VariableValueChangedEventArgs; namespace DMS.Infrastructure.Services { @@ -329,30 +330,8 @@ namespace DMS.Infrastructure.Services { if (context.Variables.TryGetValue(opcUaNode.NodeId.ToString(), out var variable)) { - // 保存旧值 - var oldValue = variable.DataValue; - var newValue = opcUaNode.Value.ToString(); - - // 更新变量值 - variable.DataValue = newValue; - variable.DisplayValue = newValue; - variable.UpdateNumericValue(); // 更新数值属性 - variable.UpdatedAt = DateTime.Now; - - _logger.LogDebug($"节点:{variable.OpcUaNodeId}值发生了变化:{newValue}"); - - // 触发变量值变更事件 - var eventArgs = new VariableValueChangedEventArgs( - variable.Id, - variable.Name, - oldValue, - newValue, - variable.UpdatedAt); - - _appDataCenterService.VariableManagementService.VariableValueChanged(eventArgs); - // 推送到数据处理队列 - await _dataProcessingService.EnqueueAsync(variable); + await _dataProcessingService.EnqueueAsync(new VariableContext(variable,opcUaNode.Value)); break; } } diff --git a/DMS.Infrastructure/Services/OptimizedS7BackgroundService.cs b/DMS.Infrastructure/Services/OptimizedS7BackgroundService.cs index 3695902..6918c43 100644 --- a/DMS.Infrastructure/Services/OptimizedS7BackgroundService.cs +++ b/DMS.Infrastructure/Services/OptimizedS7BackgroundService.cs @@ -12,6 +12,8 @@ using Microsoft.Extensions.Logging; using DMS.Core.Interfaces; using DMS.Infrastructure.Interfaces.Services; using System.Diagnostics; +using System.Globalization; +using DMS.Application.Models; namespace DMS.Infrastructure.Services; @@ -32,25 +34,8 @@ public class OptimizedS7BackgroundService : BackgroundService // 存储每个设备的变量按轮询间隔分组 private readonly ConcurrentDictionary>> _variablesByPollingInterval = new(); - - // 模拟 PollingIntervals,实际应用中可能从配置或数据库加载 - private static readonly Dictionary PollingIntervals = new Dictionary - { - { 10, TimeSpan.FromMilliseconds(10) }, // TenMilliseconds - { 100, TimeSpan.FromMilliseconds(100) }, // HundredMilliseconds - { 500, TimeSpan.FromMilliseconds(500) }, // FiveHundredMilliseconds - { 1000, TimeSpan.FromMilliseconds(1000) }, // OneSecond - { 5000, TimeSpan.FromMilliseconds(5000) }, // FiveSeconds - { 10000, TimeSpan.FromMilliseconds(10000) }, // TenSeconds - { 20000, TimeSpan.FromMilliseconds(20000) }, // TwentySeconds - { 30000, TimeSpan.FromMilliseconds(30000) }, // ThirtySeconds - { 60000, TimeSpan.FromMilliseconds(60000) }, // OneMinute - { 180000, TimeSpan.FromMilliseconds(180000) }, // ThreeMinutes - { 300000, TimeSpan.FromMilliseconds(300000) }, // FiveMinutes - { 600000, TimeSpan.FromMilliseconds(600000) }, // TenMinutes - { 1800000, TimeSpan.FromMilliseconds(1800000) } // ThirtyMinutes - }; - + + /// /// 构造函数,注入数据服务和数据处理服务。 /// @@ -108,7 +93,7 @@ public class OptimizedS7BackgroundService : BackgroundService // 持续轮询,直到取消请求或需要重新加载 while (!stoppingToken.IsCancellationRequested && _reloadSemaphore.CurrentCount == 0) { - await PollS7VariablesByPollingIntervalAsync(stoppingToken); + await PollS7VariablesAsync(stoppingToken); await Task.Delay(_s7PollOnceSleepTimeMs, stoppingToken); } } @@ -136,28 +121,27 @@ public class OptimizedS7BackgroundService : BackgroundService { _variablesByPollingInterval.Clear(); _logger.LogInformation("开始加载S7变量...."); - + var s7Devices = _appDataStorageService .Devices.Values.Where(d => d.Protocol == ProtocolType.S7 && d.IsActive == true) .ToList(); - + foreach (var s7Device in s7Devices) { _s7ServiceManager.AddDevice(s7Device); - + // 查找设备中所有要轮询的变量 - var variables = s7Device.VariableTables?.SelectMany(vt => vt.Variables) - .Where(v => v.IsActive == true && v.Protocol == ProtocolType.S7) - .ToList() ?? new List(); - + var variables = new List(); + + foreach (var variableTable in s7Device.VariableTables) + { + if (variableTable.IsActive && variableTable.Protocol == ProtocolType.S7) + { + variables.AddRange(variableTable.Variables.Where(v => v.IsActive)); + } + } + _s7ServiceManager.UpdateVariables(s7Device.Id, variables); - - // 按轮询间隔分组变量 - var variablesByPollingInterval = variables - .GroupBy(v => v.PollingInterval) - .ToDictionary(g => g.Key, g => g.ToList()); - - _variablesByPollingInterval.AddOrUpdate(s7Device.Id, variablesByPollingInterval, (key, oldValue) => variablesByPollingInterval); } _logger.LogInformation($"S7 变量加载成功,共加载S7设备:{s7Devices.Count}个"); @@ -175,102 +159,96 @@ public class OptimizedS7BackgroundService : BackgroundService /// private async Task ConnectS7ServiceAsync(CancellationToken stoppingToken) { - var s7Devices = _appDataStorageService .Devices.Values.Where(d => d.Protocol == ProtocolType.S7 && d.IsActive == true) .ToList(); - var deviceIds = s7Devices.Select(d => d.Id).ToList(); + var deviceIds = s7Devices.Select(d => d.Id) + .ToList(); await _s7ServiceManager.ConnectDevicesAsync(deviceIds, stoppingToken); - + _logger.LogInformation("已连接所有S7设备"); } /// /// 按轮询间隔轮询S7变量 /// - private async Task PollS7VariablesByPollingIntervalAsync(CancellationToken stoppingToken) + private async Task PollS7VariablesAsync(CancellationToken stoppingToken) { try { - var pollTasks = new List(); - - // 为每个设备创建轮询任务 - foreach (var deviceEntry in _variablesByPollingInterval) + var s7DeviceContexts = _s7ServiceManager.GetAllDeviceContexts(); + foreach (var context in s7DeviceContexts) { - var deviceId = deviceEntry.Key; - var variablesByPollingInterval = deviceEntry.Value; + if (stoppingToken.IsCancellationRequested) break; - // 为每个轮询间隔创建轮询任务 - foreach (var pollingIntervalEntry in variablesByPollingInterval) + // 收集该设备所有需要轮询的变量 + var variablesToPoll = context.Variables.Values.ToList(); + + if (variablesToPoll.Any()) { - var pollingInterval = pollingIntervalEntry.Key; - var variables = pollingIntervalEntry.Value; - - // 检查是否达到轮询时间 - if (ShouldPollVariables(variables, pollingInterval)) - { - pollTasks.Add(PollVariablesForDeviceAsync(deviceId, variables, stoppingToken)); - } + await PollVariablesForDeviceAsync(context, variablesToPoll, stoppingToken); } } - - await Task.WhenAll(pollTasks); } catch (Exception ex) { _logger.LogError(ex, $"按轮询间隔轮询S7变量时发生错误:{ex.Message}"); } } - - /// - /// 检查是否应该轮询变量 - /// - private bool ShouldPollVariables(List variables, int pollingInterval) - { - if (!PollingIntervals.TryGetValue(pollingInterval, out var interval)) - return false; - - // 检查是否有任何一个变量需要轮询 - return variables.Any(v => (DateTime.Now - v.UpdatedAt) >= interval); - } - + + + /// /// 轮询设备的变量 /// - private async Task PollVariablesForDeviceAsync(int deviceId, List variables, CancellationToken stoppingToken) + private async Task PollVariablesForDeviceAsync(S7DeviceContext context, List variables, + CancellationToken stoppingToken) { - if (!_appDataStorageService.Devices.TryGetValue(deviceId, out var device)) + if (!_appDataStorageService.Devices.TryGetValue(context.Device.Id, out var device)) { - _logger.LogWarning($"轮询时没有找到设备ID:{deviceId}"); + _logger.LogWarning($"轮询时没有找到设备ID:{context.Device.Id}"); return; } - - if (!_s7ServiceManager.IsDeviceConnected(deviceId)) + + var s7Service = context.S7Service; + if (s7Service == null || !s7Service.IsConnected) { - _logger.LogWarning($"轮询时设备 {device.Name} 没有连接"); + _logger.LogWarning($"轮询时设备 {device.Name} 没有连接或服务不可用"); return; } - + try { var stopwatch = Stopwatch.StartNew(); - + // 按地址分组变量以进行批量读取 - var addresses = variables.Select(v => v.S7Address).ToList(); - - // 这里应该使用IS7Service来读取变量 - // 由于接口限制,我们暂时跳过实际读取,仅演示逻辑 - + var addresses = variables.Where(v=>(DateTime.Now-v.UpdatedAt)>=TimeSpan.FromMilliseconds(v.PollingInterval)).Select(v => v.S7Address) + .ToList(); + if (!addresses.Any()) + { + return; + } + + // 批量读取变量值 + var readResults = await s7Service.ReadVariablesAsync(addresses); + stopwatch.Stop(); _logger.LogDebug($"设备 {device.Name} 轮询 {variables.Count} 个变量耗时 {stopwatch.ElapsedMilliseconds} ms"); - + // 更新变量值并推送到处理队列 foreach (var variable in variables) { - // 模拟读取到的值 - var value = DateTime.Now.Ticks.ToString(); - await UpdateAndEnqueueVariable(variable, value); + if (readResults.TryGetValue(variable.S7Address, out var value)) + { + + // 将更新后的数据推入处理队列。 + await _dataProcessingService.EnqueueAsync(new VariableContext(variable, value)); + } + else + { + _logger.LogWarning($"未能从设备 {device.Name} 读取变量 {variable.S7Address} 的值"); + } } } catch (Exception ex) @@ -279,26 +257,7 @@ public class OptimizedS7BackgroundService : BackgroundService } } - /// - /// 更新变量数据,并将其推送到数据处理队列。 - /// - private async Task UpdateAndEnqueueVariable(VariableDto variable, string value) - { - try - { - // 更新变量的原始数据值和显示值。 - variable.DataValue = value; - variable.DisplayValue = value; - variable.UpdatedAt = DateTime.Now; - - // 将更新后的数据推入处理队列。 - await _dataProcessingService.EnqueueAsync(variable); - } - catch (Exception ex) - { - _logger.LogError(ex, $"更新变量 {variable.Name} 并入队失败:{ex.Message}"); - } - } + /// /// 断开所有 S7 会话。 @@ -306,10 +265,10 @@ public class OptimizedS7BackgroundService : BackgroundService private async Task DisconnectAllS7SessionsAsync() { _logger.LogInformation("正在断开所有 S7 会话..."); - + var deviceIds = _s7ServiceManager.GetMonitoredDeviceIds(); await _s7ServiceManager.DisconnectDevicesAsync(deviceIds); - + _logger.LogInformation("已断开所有 S7 会话"); } } \ No newline at end of file diff --git a/DMS.Infrastructure/Services/S7Service.cs b/DMS.Infrastructure/Services/S7Service.cs index 5876afc..8da82f6 100644 --- a/DMS.Infrastructure/Services/S7Service.cs +++ b/DMS.Infrastructure/Services/S7Service.cs @@ -11,6 +11,8 @@ namespace DMS.Infrastructure.Services /// public class S7Service : IS7Service { + private const int ReadMultipleVarsCount = 10; + private Plc _plc; private readonly ILogger _logger; @@ -91,13 +93,16 @@ namespace DMS.Infrastructure.Services { throw new InvalidOperationException("PLC未连接"); } + try - { + { + var result = new Dictionary(); var dataItems = addresses.Select(DataItem.FromAddress).ToList(); + await _plc.ReadMultipleVarsAsync(dataItems); - var result = new Dictionary(); + for (int i = 0; i < addresses.Count; i++) { result[addresses[i]] = dataItems[i].Value; diff --git a/DMS.Infrastructure/Services/S7ServiceManager.cs b/DMS.Infrastructure/Services/S7ServiceManager.cs index a5d4136..fb76c94 100644 --- a/DMS.Infrastructure/Services/S7ServiceManager.cs +++ b/DMS.Infrastructure/Services/S7ServiceManager.cs @@ -135,6 +135,13 @@ namespace DMS.Infrastructure.Services { return _deviceContexts.Keys.ToList(); } + /// + /// 获取所有监控的设备ID + /// + public List GetAllDeviceContexts() + { + return _deviceContexts.Values.ToList(); + } /// /// 连接设备 diff --git a/DMS.WPF/App.xaml.cs b/DMS.WPF/App.xaml.cs index e6b3e9b..57328f5 100644 --- a/DMS.WPF/App.xaml.cs +++ b/DMS.WPF/App.xaml.cs @@ -72,6 +72,8 @@ public partial class App : System.Windows.Application DeviceItemViewModel.EventService = Host.Services.GetRequiredService(); // 初始化数据处理链 var dataProcessingService = Host.Services.GetRequiredService(); + dataProcessingService.AddProcessor(Host.Services.GetRequiredService()); + dataProcessingService.AddProcessor(Host.Services.GetRequiredService()); dataProcessingService.AddProcessor(Host.Services.GetRequiredService()); dataProcessingService.AddProcessor(Host.Services.GetRequiredService()); dataProcessingService.AddProcessor(Host.Services.GetRequiredService()); @@ -194,6 +196,8 @@ public partial class App : System.Windows.Application services.AddSingleton(); services.AddHostedService(provider => (DataProcessingService)provider.GetRequiredService()); + services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/DMS.WPF/Services/DataEventService.cs b/DMS.WPF/Services/DataEventService.cs index bf28dda..b91a760 100644 --- a/DMS.WPF/Services/DataEventService.cs +++ b/DMS.WPF/Services/DataEventService.cs @@ -6,6 +6,7 @@ using DMS.Application.DTOs; using DMS.Application.DTOs.Events; using DMS.Application.Interfaces; using DMS.Core.Enums; +using DMS.Core.Events; using DMS.Core.Models; using DMS.Message; using DMS.WPF.Interfaces; @@ -20,6 +21,7 @@ public class DataEventService : IDataEventService { private readonly IMapper _mapper; private readonly IDataStorageService _dataStorageService; + private readonly IEventService _eventService; private readonly IAppDataCenterService _appDataCenterService; private readonly IWPFDataService _wpfDataService; @@ -28,16 +30,18 @@ public class DataEventService : IDataEventService /// public DataEventService(IMapper mapper, IDataStorageService dataStorageService, + IEventService eventService, IAppDataCenterService appDataCenterService, IWPFDataService wpfDataService) { _mapper = mapper; _dataStorageService = dataStorageService; + _eventService = eventService; _appDataCenterService = appDataCenterService; _wpfDataService = wpfDataService; // 监听变量值变更事件 - _appDataCenterService.VariableManagementService.OnVariableValueChanged += OnVariableValueChanged; + _eventService.OnVariableValueChanged += OnVariableValueChanged; _appDataCenterService.DataLoaderService.OnLoadDataCompleted += OnLoadDataCompleted; // 监听日志变更事件 // _appDataCenterService.OnLogChanged += _logDataService.OnNlogChanged;