Files
DMS/DMS.Infrastructure/Services/S7BackgroundService.cs

241 lines
8.4 KiB
C#
Raw Normal View History

using System.Collections.Concurrent;
2025-09-05 15:59:14 +08:00
using DMS.Application.DTOs;
using DMS.Application.DTOs.Events;
using DMS.Application.Interfaces;
using DMS.Core.Enums;
using DMS.Core.Models;
using Microsoft.Extensions.Hosting;
using S7.Net;
using S7.Net.Types;
using DateTime = System.DateTime;
using Microsoft.Extensions.Logging;
using DMS.Core.Interfaces;
2025-09-05 15:59:14 +08:00
using DMS.Infrastructure.Interfaces;
using DMS.Infrastructure.Interfaces.Services;
namespace DMS.Infrastructure.Services;
/// <summary>
2025-09-05 15:59:14 +08:00
/// S7后台服务继承自BackgroundService采用"编排者-代理"模式管理所有S7设备。
/// S7BackgroundService作为编排者负责创建、管理和销毁每个设备专属的S7DeviceAgent。
/// 每个S7DeviceAgent作为代理专门负责与一个S7 PLC进行所有交互。
/// </summary>
public class S7BackgroundService : BackgroundService
{
private readonly IAppDataCenterService _appDataCenterService;
private readonly IAppDataStorageService _appDataStorageService;
private readonly IDataProcessingService _dataProcessingService;
2025-09-05 15:59:14 +08:00
private readonly IChannelBus _channelBus;
private readonly IMessenger _messenger;
private readonly ILogger<S7BackgroundService> _logger;
private readonly SemaphoreSlim _reloadSemaphore = new SemaphoreSlim(0);
2025-09-05 15:59:14 +08:00
// 存储活动的S7设备代理键为设备ID值为代理实例
private readonly ConcurrentDictionary<int, S7DeviceAgent> _activeAgents = new();
// S7轮询一遍后的等待时间
private readonly int _s7PollOnceSleepTimeMs = 100;
/// <summary>
2025-09-05 15:59:14 +08:00
/// 构造函数,注入所需的服务
/// </summary>
2025-09-05 15:59:14 +08:00
public S7BackgroundService(
IAppDataCenterService appDataCenterService,
IAppDataStorageService appDataStorageService,
2025-09-05 15:59:14 +08:00
IDataProcessingService dataProcessingService,
IChannelBus channelBus,
IMessenger messenger,
ILogger<S7BackgroundService> logger)
{
_appDataCenterService = appDataCenterService;
_appDataStorageService = appDataStorageService;
_dataProcessingService = dataProcessingService;
2025-09-05 15:59:14 +08:00
_channelBus = channelBus;
_messenger = messenger;
_logger = logger;
2025-09-05 15:59:14 +08:00
_appDataCenterService.DataLoaderService.OnLoadDataCompleted += OnLoadDataCompleted;
2025-09-05 15:59:14 +08:00
}
2025-09-06 15:19:04 +08:00
private void OnLoadDataCompleted(object? sender, DataLoadCompletedEventArgs e)
2025-09-05 15:59:14 +08:00
{
_reloadSemaphore.Release();
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("S7后台服务正在启动。");
try
{
while (!stoppingToken.IsCancellationRequested)
{
await _reloadSemaphore.WaitAsync(stoppingToken); // Wait for a reload signal
if (stoppingToken.IsCancellationRequested)
{
break;
}
if (_appDataStorageService.Devices.IsEmpty)
2025-09-05 15:59:14 +08:00
{
_logger.LogInformation("没有可用的S7设备等待设备列表更新...");
continue;
}
2025-09-05 15:59:14 +08:00
await LoadAndInitializeDevicesAsync(stoppingToken);
_logger.LogInformation("S7后台服务已启动。");
// 持续轮询,直到取消请求或需要重新加载
while (!stoppingToken.IsCancellationRequested && _reloadSemaphore.CurrentCount == 0)
{
2025-09-05 15:59:14 +08:00
await PollAllDevicesAsync(stoppingToken);
await Task.Delay(_s7PollOnceSleepTimeMs, stoppingToken);
}
}
}
catch (OperationCanceledException)
{
_logger.LogInformation("S7后台服务已停止。");
}
catch (Exception e)
{
_logger.LogError(e, $"S7后台服务运行中发生了错误:{e.Message}");
}
finally
{
2025-09-05 15:59:14 +08:00
await CleanupAsync();
}
}
/// <summary>
2025-09-05 15:59:14 +08:00
/// 加载并初始化所有S7设备
/// </summary>
2025-09-05 15:59:14 +08:00
private async Task LoadAndInitializeDevicesAsync(CancellationToken stoppingToken)
{
2025-09-05 15:59:14 +08:00
try
{
2025-09-05 15:59:14 +08:00
_logger.LogInformation("开始加载S7设备....");
// 获取所有激活的S7设备
var s7Devices = _appDataStorageService
.Devices.Values.Where(d => d.Protocol == ProtocolType.S7 && d.IsActive == true)
.ToList();
2025-09-05 15:59:14 +08:00
// 清理已不存在的设备代理
var existingDeviceIds = s7Devices.Select(d => d.Id).ToHashSet();
var agentKeysToRemove = _activeAgents.Keys.Where(id => !existingDeviceIds.Contains(id)).ToList();
foreach (var deviceId in agentKeysToRemove)
{
2025-09-05 15:59:14 +08:00
if (_activeAgents.TryRemove(deviceId, out var agent))
{
2025-09-05 15:59:14 +08:00
await agent.DisposeAsync();
_logger.LogInformation($"已移除设备ID {deviceId} 的代理");
}
}
2025-09-05 15:59:14 +08:00
// 为每个设备创建或更新代理
foreach (var deviceDto in s7Devices)
{
if (!_appDataStorageService.Devices.TryGetValue(deviceDto.Id, out var device))
2025-09-05 15:59:14 +08:00
continue;
2025-09-05 15:59:14 +08:00
// 创建或更新设备代理
// await CreateOrUpdateAgentAsync(device, stoppingToken);
}
2025-09-05 15:59:14 +08:00
_logger.LogInformation($"S7设备加载成功共加载S7设备{s7Devices.Count}个");
}
2025-09-05 15:59:14 +08:00
catch (Exception e)
{
2025-09-05 15:59:14 +08:00
_logger.LogError(e, $"加载S7设备的过程中发生了错误{e.Message}");
}
}
/// <summary>
2025-09-05 15:59:14 +08:00
/// 创建或更新设备代理
/// </summary>
2025-09-05 15:59:14 +08:00
private async Task CreateOrUpdateAgentAsync(Device device, CancellationToken stoppingToken)
{
try
{
2025-09-05 15:59:14 +08:00
// 获取设备的变量
var variables = device.VariableTables?
.SelectMany(vt => vt.Variables)
.Where(v => v.IsActive == true && v.Protocol == ProtocolType.S7)
.ToList() ?? new List<Variable>();
2025-09-05 15:59:14 +08:00
// 检查是否已存在代理
if (_activeAgents.TryGetValue(device.Id, out var existingAgent))
{
// 更新现有代理的变量配置
existingAgent.UpdateVariables(variables);
}
else
{
// 创建新的代理
// // var agent = new S7DeviceAgent(device, _channelBus, _messenger, _logger);
// _activeAgents.AddOrUpdate(device.Id, agent, (key, oldValue) => agent);
//
// // 连接设备
// await agent.ConnectAsync();
//
// // 更新变量配置
// agent.UpdateVariables(variables);
_logger.LogInformation($"已为设备 {device.Name} (ID: {device.Id}) 创建代理");
}
}
2025-09-05 15:59:14 +08:00
catch (Exception ex)
{
2025-09-05 15:59:14 +08:00
_logger.LogError(ex, $"为设备 {device.Name} (ID: {device.Id}) 创建/更新代理时发生错误");
}
}
/// <summary>
2025-09-05 15:59:14 +08:00
/// 轮询所有设备
/// </summary>
2025-09-05 15:59:14 +08:00
private async Task PollAllDevicesAsync(CancellationToken stoppingToken)
{
2025-09-05 15:59:14 +08:00
try
{
2025-09-05 15:59:14 +08:00
var pollTasks = new List<Task>();
// 为每个活动代理创建轮询任务
foreach (var agent in _activeAgents.Values)
{
2025-09-05 15:59:14 +08:00
if (stoppingToken.IsCancellationRequested)
break;
pollTasks.Add(agent.PollVariablesAsync());
}
2025-09-05 15:59:14 +08:00
// 并行执行所有轮询任务
await Task.WhenAll(pollTasks);
}
2025-09-05 15:59:14 +08:00
catch (Exception ex)
{
2025-09-05 15:59:14 +08:00
_logger.LogError(ex, $"轮询S7设备时发生错误{ex.Message}");
}
}
/// <summary>
2025-09-05 15:59:14 +08:00
/// 清理资源
/// </summary>
2025-09-05 15:59:14 +08:00
private async Task CleanupAsync()
{
2025-09-05 15:59:14 +08:00
_logger.LogInformation("正在清理S7后台服务资源...");
// 断开所有代理连接并释放资源
var cleanupTasks = new List<Task>();
foreach (var agent in _activeAgents.Values)
{
cleanupTasks.Add(agent.DisposeAsync().AsTask());
}
await Task.WhenAll(cleanupTasks);
_activeAgents.Clear();
_logger.LogInformation("S7后台服务资源清理完成");
}
}