Files
DMS/DMS.Infrastructure/Services/S7/S7ServiceManager.cs
David P.G 6e9f7c4a44 feat: 为多个服务添加日志记录功能并改进批量导入变量处理
- 为OpcUaServiceManager添加日志记录功能,并实现OnBatchImportVariables事件处理器
  - 为OpcUaService添加全面的日志记录,包括连接、断开、读写节点等操作
  - 为DataEventService添加日志记录功能,跟踪数据加载和变量值变更事件
  - 为S7ServiceManager添加OnBatchImportVariables事件处理器,处理批量导入变量事件
  - 在VariableTableViewModel中改进变量添加逻辑,确保变量被正确添加到数据存储中
  - 在VariableManagementService中完善变量表信息的填充逻辑
2025-10-02 11:26:50 +08:00

422 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections.Concurrent;
using System.Diagnostics;
using DMS.Application.DTOs;
using DMS.Application.Events;
using DMS.Application.Interfaces;
using DMS.Core.Enums;
using DMS.Core.Events;
using DMS.Infrastructure.Interfaces.Services;
using Microsoft.Extensions.Logging;
using NPOI.HSSF.Record;
namespace DMS.Infrastructure.Services.S7
{
/// <summary>
/// S7服务管理器负责管理S7连接和变量监控
/// </summary>
public class S7ServiceManager : IS7ServiceManager
{
private readonly ILogger<S7ServiceManager> _logger;
private readonly IEventService _eventService;
private readonly IDataProcessingService _dataProcessingService;
private readonly IAppDataCenterService _appDataCenterService;
private readonly IAppDataStorageService _appDataStorageService;
private readonly IS7ServiceFactory _s7ServiceFactory;
private readonly ConcurrentDictionary<int, S7DeviceContext> _deviceContexts;
private readonly SemaphoreSlim _semaphore;
private bool _disposed = false;
public S7ServiceManager(
ILogger<S7ServiceManager> logger,
IEventService eventService,
IDataProcessingService dataProcessingService,
IAppDataCenterService appDataCenterService,
IAppDataStorageService appDataStorageService,
IS7ServiceFactory s7ServiceFactory)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_eventService = eventService;
_dataProcessingService
= dataProcessingService ?? throw new ArgumentNullException(nameof(dataProcessingService));
_appDataCenterService
= appDataCenterService ?? throw new ArgumentNullException(nameof(appDataCenterService));
_appDataStorageService = appDataStorageService;
_s7ServiceFactory = s7ServiceFactory ?? throw new ArgumentNullException(nameof(s7ServiceFactory));
_deviceContexts = new ConcurrentDictionary<int, S7DeviceContext>();
_semaphore = new SemaphoreSlim(10, 10); // 默认最大并发连接数为10
_eventService.OnVariableActiveChanged += OnVariableActiveChanged;
_eventService.OnBatchImportVariables += OnBatchImportVariables;
}
private void OnVariableActiveChanged(object? sender, VariablesActiveChangedEventArgs e)
{
if (_deviceContexts.TryGetValue(e.DeviceId, out var s7DeviceContext))
{
var variables = _appDataStorageService.Variables.Values.Where(v => e.VariableIds.Contains(v.Id))
.ToList();
foreach (var variable in variables)
{
if (e.NewStatus)
{
// 变量启用,从轮询列表中添加变量
s7DeviceContext.Variables.AddOrUpdate(variable.S7Address,variable, (key, oldValue) => variable);
}
else
{
// 变量停用,从轮询列表中移除变量
s7DeviceContext.Variables.Remove(variable.S7Address, out _);
}
}
}
}
/// <summary>
/// 初始化服务管理器
/// </summary>
public async Task InitializeAsync(CancellationToken cancellationToken = default)
{
_logger.LogInformation("S7服务管理器正在初始化...");
// 初始化逻辑可以在需要时添加
_logger.LogInformation("S7服务管理器初始化完成");
}
/// <summary>
/// 添加设备到监控列表
/// </summary>
public void AddDevice(DeviceDto device)
{
if (device == null)
throw new ArgumentNullException(nameof(device));
if (device.Protocol != ProtocolType.S7)
{
_logger.LogWarning("设备 {DeviceId} 不是S7协议跳过添加", device.Id);
return;
}
var context = new S7DeviceContext
{
Device = device,
S7Service = _s7ServiceFactory.CreateService(),
Variables = new ConcurrentDictionary<string, VariableDto>(),
IsConnected = false
};
_deviceContexts.AddOrUpdate(device.Id, context, (key, oldValue) => context);
_logger.LogInformation("已添加设备 {DeviceId} 到监控列表", device.Id);
}
/// <summary>
/// 移除设备监控
/// </summary>
public async Task RemoveDeviceAsync(int deviceId, CancellationToken cancellationToken = default)
{
if (_deviceContexts.TryRemove(deviceId, out var context))
{
await DisconnectDeviceAsync(context, cancellationToken);
_logger.LogInformation("已移除设备 {DeviceId} 的监控", deviceId);
}
}
/// <summary>
/// 更新设备变量
/// </summary>
public void UpdateVariables(int deviceId, List<VariableDto> variables)
{
if (_deviceContexts.TryGetValue(deviceId, out var context))
{
context.Variables.Clear();
foreach (var variable in variables)
{
context.Variables.AddOrUpdate(variable.S7Address, variable, (key, oldValue) => variable);
}
_logger.LogInformation("已更新设备 {DeviceId} 的变量列表,共 {Count} 个变量", deviceId, variables.Count);
}
}
/// <summary>
/// 获取设备连接状态
/// </summary>
public bool IsDeviceConnected(int deviceId)
{
return _deviceContexts.TryGetValue(deviceId, out var context) && context.IsConnected;
}
/// <summary>
/// 重新连接设备
/// </summary>
public async Task ReconnectDeviceAsync(int deviceId, CancellationToken cancellationToken = default)
{
if (_deviceContexts.TryGetValue(deviceId, out var context))
{
await DisconnectDeviceAsync(context, cancellationToken);
await ConnectDeviceAsync(context, cancellationToken);
}
}
/// <summary>
/// 获取所有监控的设备ID
/// </summary>
public IEnumerable<int> GetMonitoredDeviceIds()
{
return _deviceContexts.Keys.ToList();
}
/// <summary>
/// 获取所有监控的设备ID
/// </summary>
public List<S7DeviceContext> GetAllDeviceContexts()
{
return _deviceContexts.Values.ToList();
}
/// <summary>
/// 连接设备
/// </summary>
public async Task ConnectDeviceAsync(S7DeviceContext context, CancellationToken cancellationToken = default)
{
if (context == null)
return;
await _semaphore.WaitAsync(cancellationToken);
try
{
_logger.LogInformation("正在连接设备 {DeviceName} ({IpAddress}:{Port})",
context.Device.Name, context.Device.IpAddress, context.Device.Port);
var stopwatch = Stopwatch.StartNew();
// 设置连接超时
using var timeoutToken = new CancellationTokenSource(5000); // 5秒超时
using var linkedToken
= CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutToken.Token);
var cpuType = ConvertCpuType(context.Device.CpuType);
await context.S7Service.ConnectAsync(
context.Device.IpAddress,
context.Device.Port,
(short)context.Device.Rack,
(short)context.Device.Slot,
cpuType);
stopwatch.Stop();
_logger.LogInformation("设备 {DeviceName} 连接耗时 {ElapsedMs} ms",
context.Device.Name, stopwatch.ElapsedMilliseconds);
if (context.S7Service.IsConnected)
{
context.IsConnected = true;
_logger.LogInformation("设备 {DeviceName} 连接成功", context.Device.Name);
}
else
{
_logger.LogWarning("设备 {DeviceName} 连接失败", context.Device.Name);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "连接设备 {DeviceName} 时发生错误: {ErrorMessage}",
context.Device.Name, ex.Message);
context.IsConnected = false;
}
finally
{
_eventService.RaiseDeviceStateChanged(
this,
new DeviceStateChangedEventArgs(context.Device.Id, context.Device.Name, context.IsConnected, Core.Enums.DeviceStateType.Connection));
_semaphore.Release();
}
}
/// <summary>
/// 断开设备连接
/// </summary>
private async Task DisconnectDeviceAsync(S7DeviceContext context, CancellationToken cancellationToken = default)
{
if (context == null)
return;
try
{
_logger.LogInformation("正在断开设备 {DeviceName} 的连接", context.Device.Name);
await context.S7Service.DisconnectAsync();
context.IsConnected = false;
_eventService.RaiseDeviceStateChanged(
this,
new DeviceStateChangedEventArgs(context.Device.Id, context.Device.Name, context.IsConnected, Core.Enums.DeviceStateType.Connection));
_logger.LogInformation("设备 {DeviceName} 连接已断开", context.Device.Name);
}
catch (Exception ex)
{
_logger.LogError(ex, "断开设备 {DeviceName} 连接时发生错误: {ErrorMessage}",
context.Device.Name, ex.Message);
}
}
/// <summary>
/// 将字符串形式的CPU类型转换为S7.Net.CpuType枚举
/// </summary>
private global::S7.Net.CpuType ConvertCpuType(CpuType cpuType)
{
return cpuType switch
{
CpuType.S7200 => global::S7.Net.CpuType.S7200,
CpuType.S7300 => global::S7.Net.CpuType.S7300,
CpuType.S7400 => global::S7.Net.CpuType.S7400,
CpuType.S71200 => global::S7.Net.CpuType.S71200,
CpuType.S71500 => global::S7.Net.CpuType.S71500,
_ => global::S7.Net.CpuType.S71200 // 默认值
};
}
/// <summary>
/// 连接指定设备
/// </summary>
public async Task ConnectDeviceAsync(int deviceId, CancellationToken cancellationToken = default)
{
if (_deviceContexts.TryGetValue(deviceId, out var context))
{
await ConnectDeviceAsync(context, cancellationToken);
}
}
/// <summary>
/// 批量连接设备
/// </summary>
public async Task ConnectDevicesAsync(IEnumerable<int> deviceIds, CancellationToken cancellationToken = default)
{
var connectTasks = new List<Task>();
foreach (var deviceId in deviceIds)
{
if (_deviceContexts.TryGetValue(deviceId, out var context))
{
connectTasks.Add(ConnectDeviceAsync(context, cancellationToken));
}
}
await Task.WhenAll(connectTasks);
}
/// <summary>
/// 断开指定设备连接
/// </summary>
public async Task DisconnectDeviceAsync(int deviceId, CancellationToken cancellationToken = default)
{
if (_deviceContexts.TryGetValue(deviceId, out var context))
{
await DisconnectDeviceAsync(context, cancellationToken);
}
}
/// <summary>
/// 批量断开设备连接
/// </summary>
public async Task DisconnectDevicesAsync(IEnumerable<int> deviceIds,
CancellationToken cancellationToken = default)
{
var disconnectTasks = new List<Task>();
foreach (var deviceId in deviceIds)
{
disconnectTasks.Add(DisconnectDeviceAsync(deviceId, cancellationToken));
}
await Task.WhenAll(disconnectTasks);
}
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// 释放资源
/// </summary>
protected virtual void Dispose(bool disposing)
{
if (!_disposed && disposing)
{
_logger.LogInformation("正在释放S7服务管理器资源...");
// 断开所有设备连接
var deviceIds = _deviceContexts.Keys.ToList();
DisconnectDevicesAsync(deviceIds)
.Wait(TimeSpan.FromSeconds(10));
// 释放其他资源
_semaphore?.Dispose();
_disposed = true;
_logger.LogInformation("S7服务管理器资源已释放");
}
}
/// <summary>
/// 处理批量导入变量事件
/// </summary>
private void OnBatchImportVariables(object? sender, BatchImportVariablesEventArgs e)
{
if (e?.Variables == null || !e.Variables.Any())
{
_logger.LogWarning("OnBatchImportVariables: 接收到空变量列表");
return;
}
try
{
_logger.LogInformation("处理批量导入变量事件,共 {Count} 个变量", e.Count);
// 更新相关设备的变量表
var deviceIds = e.Variables.Select(v => v.VariableTable.DeviceId).Distinct();
foreach (var deviceId in deviceIds)
{
// 获取设备的变量表信息
var variablesForDevice = e.Variables.Where(v => v.VariableTable.DeviceId == deviceId).ToList();
if (variablesForDevice.Any())
{
// 更新设备上下文中的变量
if (_deviceContexts.TryGetValue(deviceId, out var context))
{
// 将新导入的变量添加到设备上下文
foreach (var variable in variablesForDevice)
{
if (!context.Variables.ContainsKey(variable.S7Address))
{
context.Variables.AddOrUpdate(variable.S7Address, variable, (key, oldValue) => variable);
}
}
}
}
}
_logger.LogInformation("批量导入变量事件处理完成,更新了 {DeviceCount} 个设备的变量信息", deviceIds.Count());
}
catch (Exception ex)
{
_logger.LogError(ex, "处理批量导入变量事件时发生错误");
}
}
}
/// <summary>
/// S7设备上下文
/// </summary>
public class S7DeviceContext
{
public DeviceDto Device { get; set; }
public IS7Service S7Service { get; set; }
public ConcurrentDictionary<string, VariableDto> Variables { get; set; }
public bool IsConnected { get; set; }
}
}