修复S7服务轮询问题
This commit is contained in:
@@ -14,34 +14,6 @@ public class VariableDto
|
||||
public string DataValue { get; set; }
|
||||
public double NumericValue { get; set; }
|
||||
public string DisplayValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新数值属性,根据DataValue的值进行转换。
|
||||
/// </summary>
|
||||
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<VariableMqttAliasDto>? MqttAliases { get; set; } = new List<VariableMqttAliasDto>();
|
||||
public SignalType SignalType { get; set; }
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace DMS.Application.Events;
|
||||
|
||||
/// <summary>
|
||||
/// 变量值改变事件参数
|
||||
/// </summary>
|
||||
public class VariableValueChangedEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// 变量ID
|
||||
/// </summary>
|
||||
public int VariableId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 变量名称
|
||||
/// </summary>
|
||||
public string VariableName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 旧值
|
||||
/// </summary>
|
||||
public string OldValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 新值
|
||||
/// </summary>
|
||||
public string NewValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 值改变时间
|
||||
/// </summary>
|
||||
public DateTime ChangeTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 初始化VariableValueChangedEventArgs类的新实例
|
||||
/// </summary>
|
||||
/// <param name="variableId">变量ID</param>
|
||||
/// <param name="variableName">变量名称</param>
|
||||
/// <param name="oldValue">旧值</param>
|
||||
/// <param name="newValue">新值</param>
|
||||
public VariableValueChangedEventArgs(int variableId, string variableName, string oldValue, string newValue)
|
||||
{
|
||||
VariableId = variableId;
|
||||
VariableName = variableName;
|
||||
OldValue = oldValue;
|
||||
NewValue = newValue;
|
||||
ChangeTime = DateTime.Now;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
/// </summary>
|
||||
/// <param name="data">要入队的变量数据。</param>
|
||||
/// <returns>一个表示入队操作的 ValueTask。</returns>
|
||||
ValueTask EnqueueAsync(VariableDto data);
|
||||
ValueTask EnqueueAsync(VariableContext data);
|
||||
}
|
||||
@@ -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
|
||||
/// <summary>
|
||||
/// 变量值改变事件
|
||||
/// </summary>
|
||||
event EventHandler<VariableValueChangedEventArgs> VariableValueChanged;
|
||||
event EventHandler<VariableValueChangedEventArgs> OnVariableValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 触发变量值改变事件
|
||||
@@ -67,4 +69,16 @@ public interface IEventService
|
||||
/// <param name="sender">事件发送者</param>
|
||||
/// <param name="e">设备状态改变事件参数</param>
|
||||
void RaiseDeviceConnectChanged(object sender, DeviceConnectChangedEventArgs e);
|
||||
|
||||
/// <summary>
|
||||
/// 变量值改变事件
|
||||
/// </summary>
|
||||
event EventHandler<VariableChangedEventArgs> OnVariableChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 触发变量值改变事件
|
||||
/// </summary>
|
||||
/// <param name="sender">事件发送者</param>
|
||||
/// <param name="e">变量值改变事件参数</param>
|
||||
void RaiseVariableChanged(object sender, VariableChangedEventArgs e);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 变量事件
|
||||
/// <summary>
|
||||
/// 变量值改变事件
|
||||
/// </summary>
|
||||
public event EventHandler<VariableChangedEventArgs> OnVariableChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 触发变量值改变事件
|
||||
/// </summary>
|
||||
/// <param name="sender">事件发送者</param>
|
||||
/// <param name="e">变量值改变事件参数</param>
|
||||
public void RaiseVariableChanged(object sender, VariableChangedEventArgs e)
|
||||
{
|
||||
OnVariableChanged?.Invoke(sender, e);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 变量值改变事件
|
||||
/// </summary>
|
||||
public event EventHandler<VariableValueChangedEventArgs> VariableValueChanged;
|
||||
public event EventHandler<VariableValueChangedEventArgs> OnVariableValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 触发变量值改变事件
|
||||
@@ -73,7 +90,7 @@ public class EventService : IEventService
|
||||
/// <param name="e">变量值改变事件参数</param>
|
||||
public void RaiseVariableValueChanged(object sender, VariableValueChangedEventArgs e)
|
||||
{
|
||||
VariableValueChanged?.Invoke(sender, e);
|
||||
OnVariableValueChanged?.Invoke(sender, e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -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
|
||||
/// 在内存中删除变量
|
||||
/// </summary>
|
||||
void RemoveVariableFromMemory(int variableId, ConcurrentDictionary<int, VariableTableDto> variableTables);
|
||||
|
||||
void VariableValueChanged(VariableValueChangedEventArgs eventArgs);
|
||||
|
||||
/// <summary>
|
||||
/// 当变量数据发生变化时触发
|
||||
/// </summary>
|
||||
event EventHandler<VariableChangedEventArgs> OnVariableChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 当变量数据发生变化时触发
|
||||
/// </summary>
|
||||
event EventHandler<VariableValueChangedEventArgs> OnVariableValueChanged;
|
||||
}
|
||||
@@ -50,14 +50,13 @@ public class DataProcessingService : BackgroundService, IDataProcessingService
|
||||
/// 将一个变量数据项异步推入处理队列。
|
||||
/// </summary>
|
||||
/// <param name="data">要入队的变量数据。</param>
|
||||
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);
|
||||
}
|
||||
|
||||
34
DMS.Application/Services/Processors/TriggerProcessor.cs
Normal file
34
DMS.Application/Services/Processors/TriggerProcessor.cs
Normal file
@@ -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<TriggerProcessor> _logger;
|
||||
|
||||
public TriggerProcessor(ITriggerEvaluationService triggerEvaluationService, ILogger<TriggerProcessor> 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);
|
||||
// // 不抛出异常,避免影响其他处理器
|
||||
// }
|
||||
}
|
||||
}
|
||||
29
DMS.Application/Services/Processors/UpdateViewProcessor.cs
Normal file
29
DMS.Application/Services/Processors/UpdateViewProcessor.cs
Normal file
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
136
DMS.Application/Services/Processors/ValueConvertProcessor.cs
Normal file
136
DMS.Application/Services/Processors/ValueConvertProcessor.cs
Normal file
@@ -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<ValueConvertProcessor> _logger;
|
||||
|
||||
public ValueConvertProcessor(ILogger<ValueConvertProcessor> 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;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 将从 S7 读取的对象值转换为字符串表示和数值表示
|
||||
/// </summary>
|
||||
/// <param name="variable">关联的变量 DTO</param>
|
||||
/// <param name="value">从 S7 读取的原始对象值</param>
|
||||
/// <returns>(字符串表示, 数值表示)</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 当变量数据发生变化时触发
|
||||
/// </summary>
|
||||
public event EventHandler<VariableChangedEventArgs> OnVariableChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 当变量数据发生变化时触发
|
||||
/// </summary>
|
||||
public event EventHandler<VariableValueChangedEventArgs> 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);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace DMS.Core.Models
|
||||
namespace DMS.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// 变量值变更事件参数
|
||||
@@ -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
|
||||
/// 批量断开设备连接
|
||||
/// </summary>
|
||||
Task DisconnectDevicesAsync(IEnumerable<int> deviceIds, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有监控的设备ID
|
||||
/// </summary>
|
||||
List<S7DeviceContext> GetAllDeviceContexts();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<int, Dictionary<int, List<VariableDto>>> _variablesByPollingInterval = new();
|
||||
|
||||
// 模拟 PollingIntervals,实际应用中可能从配置或数据库加载
|
||||
private static readonly Dictionary<int, TimeSpan> PollingIntervals = new Dictionary<int, TimeSpan>
|
||||
{
|
||||
{ 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
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数,注入数据服务和数据处理服务。
|
||||
/// </summary>
|
||||
@@ -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<VariableDto>();
|
||||
|
||||
var variables = new List<VariableDto>();
|
||||
|
||||
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
|
||||
/// </summary>
|
||||
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设备");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按轮询间隔轮询S7变量
|
||||
/// </summary>
|
||||
private async Task PollS7VariablesByPollingIntervalAsync(CancellationToken stoppingToken)
|
||||
private async Task PollS7VariablesAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pollTasks = new List<Task>();
|
||||
|
||||
// 为每个设备创建轮询任务
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否应该轮询变量
|
||||
/// </summary>
|
||||
private bool ShouldPollVariables(List<VariableDto> variables, int pollingInterval)
|
||||
{
|
||||
if (!PollingIntervals.TryGetValue(pollingInterval, out var interval))
|
||||
return false;
|
||||
|
||||
// 检查是否有任何一个变量需要轮询
|
||||
return variables.Any(v => (DateTime.Now - v.UpdatedAt) >= interval);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 轮询设备的变量
|
||||
/// </summary>
|
||||
private async Task PollVariablesForDeviceAsync(int deviceId, List<VariableDto> variables, CancellationToken stoppingToken)
|
||||
private async Task PollVariablesForDeviceAsync(S7DeviceContext context, List<VariableDto> 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
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新变量数据,并将其推送到数据处理队列。
|
||||
/// </summary>
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 断开所有 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 会话");
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,8 @@ namespace DMS.Infrastructure.Services
|
||||
/// </summary>
|
||||
public class S7Service : IS7Service
|
||||
{
|
||||
private const int ReadMultipleVarsCount = 10;
|
||||
|
||||
private Plc _plc;
|
||||
private readonly ILogger<S7Service> _logger;
|
||||
|
||||
@@ -91,13 +93,16 @@ namespace DMS.Infrastructure.Services
|
||||
{
|
||||
throw new InvalidOperationException("PLC未连接");
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
{
|
||||
var result = new Dictionary<string, object>();
|
||||
var dataItems = addresses.Select(DataItem.FromAddress).ToList();
|
||||
|
||||
await _plc.ReadMultipleVarsAsync(dataItems);
|
||||
|
||||
var result = new Dictionary<string, object>();
|
||||
|
||||
for (int i = 0; i < addresses.Count; i++)
|
||||
{
|
||||
result[addresses[i]] = dataItems[i].Value;
|
||||
|
||||
@@ -135,6 +135,13 @@ namespace DMS.Infrastructure.Services
|
||||
{
|
||||
return _deviceContexts.Keys.ToList();
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取所有监控的设备ID
|
||||
/// </summary>
|
||||
public List<S7DeviceContext> GetAllDeviceContexts()
|
||||
{
|
||||
return _deviceContexts.Values.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 连接设备
|
||||
|
||||
@@ -72,6 +72,8 @@ public partial class App : System.Windows.Application
|
||||
DeviceItemViewModel.EventService = Host.Services.GetRequiredService<IEventService>();
|
||||
// 初始化数据处理链
|
||||
var dataProcessingService = Host.Services.GetRequiredService<IDataProcessingService>();
|
||||
dataProcessingService.AddProcessor(Host.Services.GetRequiredService<ValueConvertProcessor>());
|
||||
dataProcessingService.AddProcessor(Host.Services.GetRequiredService<UpdateViewProcessor>());
|
||||
dataProcessingService.AddProcessor(Host.Services.GetRequiredService<CheckValueChangedProcessor>());
|
||||
dataProcessingService.AddProcessor(Host.Services.GetRequiredService<LoggingProcessor>());
|
||||
dataProcessingService.AddProcessor(Host.Services.GetRequiredService<HistoryProcessor>());
|
||||
@@ -194,6 +196,8 @@ public partial class App : System.Windows.Application
|
||||
services.AddSingleton<IDataProcessingService, DataProcessingService>();
|
||||
services.AddHostedService(provider =>
|
||||
(DataProcessingService)provider.GetRequiredService<IDataProcessingService>());
|
||||
services.AddSingleton<ValueConvertProcessor>();
|
||||
services.AddSingleton<UpdateViewProcessor>();
|
||||
services.AddSingleton<CheckValueChangedProcessor>();
|
||||
services.AddSingleton<LoggingProcessor>();
|
||||
services.AddSingleton<UpdateDbVariableProcessor>();
|
||||
|
||||
@@ -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
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user