完成S7变量启用和停用更新
This commit is contained in:
@@ -1,18 +1,13 @@
|
||||
using System.Collections.Concurrent;
|
||||
using DMS.Application.Events;
|
||||
using DMS.Application.Interfaces;
|
||||
using DMS.Core.Interfaces.Services;
|
||||
using DMS.Core.Models;
|
||||
using DMS.Infrastructure.Interfaces.Services;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using DMS.Application.DTOs.Events;
|
||||
using DMS.Core.Models;
|
||||
using DMS.Application.Interfaces;
|
||||
using DMS.Core.Interfaces.Services;
|
||||
|
||||
namespace DMS.Infrastructure.Services
|
||||
namespace DMS.Infrastructure.Services.Mqtt
|
||||
{
|
||||
/// <summary>
|
||||
/// MQTT后台服务,负责管理MQTT连接和数据传输
|
||||
@@ -1,10 +1,8 @@
|
||||
using System.Collections.Concurrent;
|
||||
using DMS.Core.Models;
|
||||
using DMS.Infrastructure.Interfaces.Services;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DMS.Infrastructure.Services
|
||||
namespace DMS.Infrastructure.Services.Mqtt
|
||||
{
|
||||
/// <summary>
|
||||
/// MQTT设备上下文,用于存储单个MQTT服务器的连接信息和状态
|
||||
@@ -5,11 +5,8 @@ using MQTTnet.Client;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Disconnecting;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Client.Receiving;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DMS.Infrastructure.Services
|
||||
namespace DMS.Infrastructure.Services.Mqtt
|
||||
{
|
||||
/// <summary>
|
||||
/// MQTT服务实现类,用于与MQTT服务器进行通信
|
||||
@@ -1,8 +1,7 @@
|
||||
using DMS.Infrastructure.Interfaces.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
|
||||
namespace DMS.Infrastructure.Services
|
||||
namespace DMS.Infrastructure.Services.Mqtt
|
||||
{
|
||||
/// <summary>
|
||||
/// MQTT服务工厂实现类,用于创建MQTT服务实例
|
||||
@@ -1,18 +1,13 @@
|
||||
using DMS.Infrastructure.Interfaces.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using DMS.Core.Models;
|
||||
using DMS.Application.Interfaces;
|
||||
using DMS.Core.Interfaces.Services;
|
||||
using DMS.Core.Models;
|
||||
using DMS.Infrastructure.Interfaces.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace DMS.Infrastructure.Services
|
||||
namespace DMS.Infrastructure.Services.Mqtt
|
||||
{
|
||||
/// <summary>
|
||||
/// MQTT服务管理器,负责管理MQTT连接和变量监控
|
||||
@@ -4,12 +4,8 @@ using DMS.Infrastructure.Models;
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Client;
|
||||
using Opc.Ua.Configuration;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DMS.Infrastructure.Services
|
||||
namespace DMS.Infrastructure.Services.OpcUa
|
||||
{
|
||||
public class OpcUaService : IOpcUaService
|
||||
{
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Collections.Concurrent;
|
||||
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.Core.Events;
|
||||
using DMS.Infrastructure.Configuration;
|
||||
using DMS.Infrastructure.Interfaces.Services;
|
||||
using DMS.Infrastructure.Models;
|
||||
@@ -12,7 +12,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using VariableValueChangedEventArgs = DMS.Core.Events.VariableValueChangedEventArgs;
|
||||
|
||||
namespace DMS.Infrastructure.Services
|
||||
namespace DMS.Infrastructure.Services.OpcUa
|
||||
{
|
||||
/// <summary>
|
||||
/// OPC UA服务管理器,负责管理OPC UA连接、订阅和变量监控
|
||||
@@ -1,12 +1,12 @@
|
||||
using DMS.Application.DTOs;
|
||||
using DMS.Application.DTOs.Events;
|
||||
using DMS.Application.Events;
|
||||
using DMS.Application.Interfaces;
|
||||
using DMS.Core.Enums;
|
||||
using DMS.Infrastructure.Interfaces.Services;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace DMS.Infrastructure.Services
|
||||
namespace DMS.Infrastructure.Services.OpcUa
|
||||
{
|
||||
/// <summary>
|
||||
/// 优化后的OPC UA后台服务
|
||||
@@ -1,21 +1,17 @@
|
||||
using System.Collections.Concurrent;
|
||||
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;
|
||||
using DMS.Infrastructure.Interfaces.Services;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using DMS.Application.DTOs;
|
||||
using DMS.Application.Events;
|
||||
using DMS.Application.Interfaces;
|
||||
using DMS.Application.Models;
|
||||
using DMS.Core.Enums;
|
||||
using DMS.Core.Events;
|
||||
using DMS.Infrastructure.Interfaces.Services;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using DateTime = System.DateTime;
|
||||
|
||||
namespace DMS.Infrastructure.Services;
|
||||
namespace DMS.Infrastructure.Services.S7;
|
||||
|
||||
/// <summary>
|
||||
/// 优化的S7后台服务,继承自BackgroundService,用于在后台高效地轮询S7 PLC设备数据。
|
||||
@@ -24,6 +20,7 @@ public class OptimizedS7BackgroundService : BackgroundService
|
||||
{
|
||||
private readonly IAppDataCenterService _appDataCenterService;
|
||||
private readonly IAppDataStorageService _appDataStorageService;
|
||||
private readonly IEventService _eventService;
|
||||
private readonly IDataProcessingService _dataProcessingService;
|
||||
private readonly IS7ServiceManager _s7ServiceManager;
|
||||
private readonly ILogger<OptimizedS7BackgroundService> _logger;
|
||||
@@ -42,18 +39,22 @@ public class OptimizedS7BackgroundService : BackgroundService
|
||||
public OptimizedS7BackgroundService(
|
||||
IAppDataCenterService appDataCenterService,
|
||||
IAppDataStorageService appDataStorageService,
|
||||
IEventService eventService,
|
||||
IDataProcessingService dataProcessingService,
|
||||
IS7ServiceManager s7ServiceManager,
|
||||
ILogger<OptimizedS7BackgroundService> logger)
|
||||
{
|
||||
_appDataCenterService = appDataCenterService;
|
||||
_appDataStorageService = appDataStorageService;
|
||||
_eventService = eventService;
|
||||
_dataProcessingService = dataProcessingService;
|
||||
_s7ServiceManager = s7ServiceManager;
|
||||
_logger = logger;
|
||||
|
||||
_appDataCenterService.DataLoaderService.OnLoadDataCompleted += OnLoadDataCompleted;
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void OnLoadDataCompleted(object? sender, DataLoadCompletedEventArgs e)
|
||||
{
|
||||
@@ -245,10 +246,10 @@ public class OptimizedS7BackgroundService : BackgroundService
|
||||
// 将更新后的数据推入处理队列。
|
||||
await _dataProcessingService.EnqueueAsync(new VariableContext(variable, value));
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning($"未能从设备 {device.Name} 读取变量 {variable.S7Address} 的值");
|
||||
}
|
||||
// else
|
||||
// {
|
||||
// _logger.LogWarning($"未能从设备 {device.Name} 读取变量 {variable.S7Address} 的值");
|
||||
// }
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -1,19 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using DMS.Application.DTOs;
|
||||
using DMS.Application.Models;
|
||||
using DMS.Core.Enums;
|
||||
using DMS.Core.Models;
|
||||
using DMS.Infrastructure.Interfaces;
|
||||
using DMS.Infrastructure.Interfaces.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using S7.Net;
|
||||
using S7.Net.Types;
|
||||
using CpuType = DMS.Core.Enums.CpuType;
|
||||
using DateTime = System.DateTime;
|
||||
|
||||
namespace DMS.Infrastructure.Services
|
||||
namespace DMS.Infrastructure.Services.S7
|
||||
{
|
||||
/// <summary>
|
||||
/// S7设备代理类,专门负责与单个S7 PLC进行所有通信
|
||||
@@ -57,16 +52,16 @@ namespace DMS.Infrastructure.Services
|
||||
}
|
||||
}
|
||||
|
||||
private S7.Net.CpuType ConvertCpuType(CpuType cpuType)
|
||||
private global::S7.Net.CpuType ConvertCpuType(CpuType cpuType)
|
||||
{
|
||||
return cpuType switch
|
||||
{
|
||||
CpuType.S7200 => S7.Net.CpuType.S7200,
|
||||
CpuType.S7300 => S7.Net.CpuType.S7300,
|
||||
CpuType.S7400 => S7.Net.CpuType.S7400,
|
||||
CpuType.S71200 => S7.Net.CpuType.S71200,
|
||||
CpuType.S71500 => S7.Net.CpuType.S71500,
|
||||
_ => S7.Net.CpuType.S71200
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using DMS.Infrastructure.Interfaces.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NLog;
|
||||
using S7.Net;
|
||||
using S7.Net.Types;
|
||||
|
||||
namespace DMS.Infrastructure.Services
|
||||
namespace DMS.Infrastructure.Services.S7
|
||||
{
|
||||
/// <summary>
|
||||
/// S7服务实现类,用于与S7 PLC进行通信
|
||||
@@ -1,7 +1,7 @@
|
||||
using DMS.Infrastructure.Interfaces.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace DMS.Infrastructure.Services
|
||||
namespace DMS.Infrastructure.Services.S7
|
||||
{
|
||||
/// <summary>
|
||||
/// S7服务工厂实现,用于创建S7Service实例
|
||||
@@ -1,18 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
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
|
||||
namespace DMS.Infrastructure.Services.S7
|
||||
{
|
||||
/// <summary>
|
||||
/// S7服务管理器,负责管理S7连接和变量监控
|
||||
@@ -23,6 +19,7 @@ namespace DMS.Infrastructure.Services
|
||||
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;
|
||||
@@ -33,15 +30,44 @@ namespace DMS.Infrastructure.Services
|
||||
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));
|
||||
_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;
|
||||
}
|
||||
|
||||
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>
|
||||
@@ -69,12 +95,12 @@ namespace DMS.Infrastructure.Services
|
||||
}
|
||||
|
||||
var context = new S7DeviceContext
|
||||
{
|
||||
Device = device,
|
||||
S7Service = _s7ServiceFactory.CreateService(),
|
||||
Variables = new ConcurrentDictionary<string, VariableDto>(),
|
||||
IsConnected = false
|
||||
};
|
||||
{
|
||||
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);
|
||||
@@ -104,6 +130,7 @@ namespace DMS.Infrastructure.Services
|
||||
{
|
||||
context.Variables.AddOrUpdate(variable.S7Address, variable, (key, oldValue) => variable);
|
||||
}
|
||||
|
||||
_logger.LogInformation("已更新设备 {DeviceId} 的变量列表,共 {Count} 个变量", deviceId, variables.Count);
|
||||
}
|
||||
}
|
||||
@@ -135,6 +162,7 @@ namespace DMS.Infrastructure.Services
|
||||
{
|
||||
return _deviceContexts.Keys.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有监控的设备ID
|
||||
/// </summary>
|
||||
@@ -155,13 +183,14 @@ namespace DMS.Infrastructure.Services
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("正在连接设备 {DeviceName} ({IpAddress}:{Port})",
|
||||
context.Device.Name, context.Device.IpAddress, context.Device.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);
|
||||
using var linkedToken
|
||||
= CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutToken.Token);
|
||||
|
||||
var cpuType = ConvertCpuType(context.Device.CpuType);
|
||||
await context.S7Service.ConnectAsync(
|
||||
@@ -173,13 +202,13 @@ namespace DMS.Infrastructure.Services
|
||||
|
||||
stopwatch.Stop();
|
||||
_logger.LogInformation("设备 {DeviceName} 连接耗时 {ElapsedMs} ms",
|
||||
context.Device.Name, stopwatch.ElapsedMilliseconds);
|
||||
context.Device.Name, stopwatch.ElapsedMilliseconds);
|
||||
|
||||
if (context.S7Service.IsConnected)
|
||||
{
|
||||
context.IsConnected = true;
|
||||
|
||||
|
||||
|
||||
|
||||
_logger.LogInformation("设备 {DeviceName} 连接成功", context.Device.Name);
|
||||
}
|
||||
else
|
||||
@@ -190,15 +219,14 @@ namespace DMS.Infrastructure.Services
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "连接设备 {DeviceName} 时发生错误: {ErrorMessage}",
|
||||
context.Device.Name, ex.Message);
|
||||
context.Device.Name, ex.Message);
|
||||
context.IsConnected = false;
|
||||
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
_eventService.RaiseDeviceConnectChanged(
|
||||
this, new DeviceConnectChangedEventArgs(context.Device.Id, context.Device.Name, context.IsConnected));
|
||||
this,
|
||||
new DeviceConnectChangedEventArgs(context.Device.Id, context.Device.Name, context.IsConnected));
|
||||
_semaphore.Release();
|
||||
}
|
||||
}
|
||||
@@ -216,32 +244,33 @@ namespace DMS.Infrastructure.Services
|
||||
_logger.LogInformation("正在断开设备 {DeviceName} 的连接", context.Device.Name);
|
||||
await context.S7Service.DisconnectAsync();
|
||||
context.IsConnected = false;
|
||||
|
||||
|
||||
_eventService.RaiseDeviceConnectChanged(
|
||||
this, new DeviceConnectChangedEventArgs(context.Device.Id, context.Device.Name, context.IsConnected));
|
||||
this,
|
||||
new DeviceConnectChangedEventArgs(context.Device.Id, context.Device.Name, context.IsConnected));
|
||||
_logger.LogInformation("设备 {DeviceName} 连接已断开", context.Device.Name);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "断开设备 {DeviceName} 连接时发生错误: {ErrorMessage}",
|
||||
context.Device.Name, ex.Message);
|
||||
context.Device.Name, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字符串形式的CPU类型转换为S7.Net.CpuType枚举
|
||||
/// </summary>
|
||||
private S7.Net.CpuType ConvertCpuType(CpuType cpuType)
|
||||
private global::S7.Net.CpuType ConvertCpuType(CpuType cpuType)
|
||||
{
|
||||
return cpuType switch
|
||||
{
|
||||
CpuType.S7200 => S7.Net.CpuType.S7200,
|
||||
CpuType.S7300 => S7.Net.CpuType.S7300,
|
||||
CpuType.S7400 => S7.Net.CpuType.S7400,
|
||||
CpuType.S71200 => S7.Net.CpuType.S71200,
|
||||
CpuType.S71500 => S7.Net.CpuType.S71500,
|
||||
_ => S7.Net.CpuType.S71200 // 默认值
|
||||
};
|
||||
{
|
||||
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>
|
||||
@@ -287,7 +316,8 @@ namespace DMS.Infrastructure.Services
|
||||
/// <summary>
|
||||
/// 批量断开设备连接
|
||||
/// </summary>
|
||||
public async Task DisconnectDevicesAsync(IEnumerable<int> deviceIds, CancellationToken cancellationToken = default)
|
||||
public async Task DisconnectDevicesAsync(IEnumerable<int> deviceIds,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var disconnectTasks = new List<Task>();
|
||||
|
||||
@@ -319,7 +349,8 @@ namespace DMS.Infrastructure.Services
|
||||
|
||||
// 断开所有设备连接
|
||||
var deviceIds = _deviceContexts.Keys.ToList();
|
||||
DisconnectDevicesAsync(deviceIds).Wait(TimeSpan.FromSeconds(10));
|
||||
DisconnectDevicesAsync(deviceIds)
|
||||
.Wait(TimeSpan.FromSeconds(10));
|
||||
|
||||
// 释放其他资源
|
||||
_semaphore?.Dispose();
|
||||
@@ -1,241 +0,0 @@
|
||||
using System.Collections.Concurrent;
|
||||
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;
|
||||
using DMS.Infrastructure.Interfaces;
|
||||
using DMS.Infrastructure.Interfaces.Services;
|
||||
|
||||
namespace DMS.Infrastructure.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 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;
|
||||
private readonly IChannelBus _channelBus;
|
||||
private readonly IMessenger _messenger;
|
||||
private readonly ILogger<S7BackgroundService> _logger;
|
||||
private readonly SemaphoreSlim _reloadSemaphore = new SemaphoreSlim(0);
|
||||
|
||||
// 存储活动的S7设备代理,键为设备ID,值为代理实例
|
||||
private readonly ConcurrentDictionary<int, S7DeviceAgent> _activeAgents = new();
|
||||
|
||||
// S7轮询一遍后的等待时间
|
||||
private readonly int _s7PollOnceSleepTimeMs = 100;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数,注入所需的服务
|
||||
/// </summary>
|
||||
public S7BackgroundService(
|
||||
IAppDataCenterService appDataCenterService,
|
||||
IAppDataStorageService appDataStorageService,
|
||||
IDataProcessingService dataProcessingService,
|
||||
IChannelBus channelBus,
|
||||
IMessenger messenger,
|
||||
ILogger<S7BackgroundService> logger)
|
||||
{
|
||||
_appDataCenterService = appDataCenterService;
|
||||
_appDataStorageService = appDataStorageService;
|
||||
_dataProcessingService = dataProcessingService;
|
||||
_channelBus = channelBus;
|
||||
_messenger = messenger;
|
||||
_logger = logger;
|
||||
|
||||
_appDataCenterService.DataLoaderService.OnLoadDataCompleted += OnLoadDataCompleted;
|
||||
}
|
||||
|
||||
private void OnLoadDataCompleted(object? sender, DataLoadCompletedEventArgs e)
|
||||
{
|
||||
_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)
|
||||
{
|
||||
_logger.LogInformation("没有可用的S7设备,等待设备列表更新...");
|
||||
continue;
|
||||
}
|
||||
|
||||
await LoadAndInitializeDevicesAsync(stoppingToken);
|
||||
_logger.LogInformation("S7后台服务已启动。");
|
||||
|
||||
// 持续轮询,直到取消请求或需要重新加载
|
||||
while (!stoppingToken.IsCancellationRequested && _reloadSemaphore.CurrentCount == 0)
|
||||
{
|
||||
await PollAllDevicesAsync(stoppingToken);
|
||||
await Task.Delay(_s7PollOnceSleepTimeMs, stoppingToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger.LogInformation("S7后台服务已停止。");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, $"S7后台服务运行中发生了错误:{e.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
await CleanupAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载并初始化所有S7设备
|
||||
/// </summary>
|
||||
private async Task LoadAndInitializeDevicesAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("开始加载S7设备....");
|
||||
|
||||
// 获取所有激活的S7设备
|
||||
var s7Devices = _appDataStorageService
|
||||
.Devices.Values.Where(d => d.Protocol == ProtocolType.S7 && d.IsActive == true)
|
||||
.ToList();
|
||||
|
||||
// 清理已不存在的设备代理
|
||||
var existingDeviceIds = s7Devices.Select(d => d.Id).ToHashSet();
|
||||
var agentKeysToRemove = _activeAgents.Keys.Where(id => !existingDeviceIds.Contains(id)).ToList();
|
||||
|
||||
foreach (var deviceId in agentKeysToRemove)
|
||||
{
|
||||
if (_activeAgents.TryRemove(deviceId, out var agent))
|
||||
{
|
||||
await agent.DisposeAsync();
|
||||
_logger.LogInformation($"已移除设备ID {deviceId} 的代理");
|
||||
}
|
||||
}
|
||||
|
||||
// 为每个设备创建或更新代理
|
||||
foreach (var deviceDto in s7Devices)
|
||||
{
|
||||
if (!_appDataStorageService.Devices.TryGetValue(deviceDto.Id, out var device))
|
||||
continue;
|
||||
|
||||
// 创建或更新设备代理
|
||||
// await CreateOrUpdateAgentAsync(device, stoppingToken);
|
||||
}
|
||||
|
||||
_logger.LogInformation($"S7设备加载成功,共加载S7设备:{s7Devices.Count}个");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, $"加载S7设备的过程中发生了错误:{e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建或更新设备代理
|
||||
/// </summary>
|
||||
private async Task CreateOrUpdateAgentAsync(Device device, CancellationToken stoppingToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 获取设备的变量
|
||||
var variables = device.VariableTables?
|
||||
.SelectMany(vt => vt.Variables)
|
||||
.Where(v => v.IsActive == true && v.Protocol == ProtocolType.S7)
|
||||
.ToList() ?? new List<Variable>();
|
||||
|
||||
// 检查是否已存在代理
|
||||
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}) 创建代理");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"为设备 {device.Name} (ID: {device.Id}) 创建/更新代理时发生错误");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 轮询所有设备
|
||||
/// </summary>
|
||||
private async Task PollAllDevicesAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pollTasks = new List<Task>();
|
||||
|
||||
// 为每个活动代理创建轮询任务
|
||||
foreach (var agent in _activeAgents.Values)
|
||||
{
|
||||
if (stoppingToken.IsCancellationRequested)
|
||||
break;
|
||||
|
||||
pollTasks.Add(agent.PollVariablesAsync());
|
||||
}
|
||||
|
||||
// 并行执行所有轮询任务
|
||||
await Task.WhenAll(pollTasks);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"轮询S7设备时发生错误:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理资源
|
||||
/// </summary>
|
||||
private async Task CleanupAsync()
|
||||
{
|
||||
_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后台服务资源清理完成");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user