diff --git a/DMS.Infrastructure/Configuration/OpcUaServiceOptions.cs b/DMS.Infrastructure/Configuration/OpcUaServiceOptions.cs
new file mode 100644
index 0000000..902b477
--- /dev/null
+++ b/DMS.Infrastructure/Configuration/OpcUaServiceOptions.cs
@@ -0,0 +1,43 @@
+namespace DMS.Infrastructure.Configuration
+{
+ ///
+ /// OPC UA服务配置
+ ///
+ public class OpcUaServiceOptions
+ {
+ ///
+ /// 配置节名称
+ ///
+ public const string SectionName = "OpcUaService";
+
+ ///
+ /// 最大并发连接数
+ ///
+ public int MaxConcurrentConnections { get; set; } = 10;
+
+ ///
+ /// 重连延迟(毫秒)
+ ///
+ public int ReconnectDelayMs { get; set; } = 5000;
+
+ ///
+ /// 订阅发布间隔(毫秒)
+ ///
+ public int SubscriptionPublishingIntervalMs { get; set; } = 1000;
+
+ ///
+ /// 订阅采样间隔(毫秒)
+ ///
+ public int SubscriptionSamplingIntervalMs { get; set; } = 1000;
+
+ ///
+ /// 连接超时时间(毫秒)
+ ///
+ public int ConnectionTimeoutMs { get; set; } = 30000;
+
+ ///
+ /// 是否自动接受不受信任的证书
+ ///
+ public bool AutoAcceptUntrustedCertificates { get; set; } = true;
+ }
+}
\ No newline at end of file
diff --git a/DMS.Infrastructure/Extensions/OpcUaServiceExtensions.cs b/DMS.Infrastructure/Extensions/OpcUaServiceExtensions.cs
new file mode 100644
index 0000000..4e04c57
--- /dev/null
+++ b/DMS.Infrastructure/Extensions/OpcUaServiceExtensions.cs
@@ -0,0 +1,33 @@
+using DMS.Infrastructure.Configuration;
+using DMS.Infrastructure.Interfaces.Services;
+using DMS.Infrastructure.Services;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace DMS.Infrastructure.Extensions
+{
+ ///
+ /// OPC UA服务扩展方法
+ ///
+ public static class OpcUaServiceExtensions
+ {
+ ///
+ /// 添加OPC UA服务
+ ///
+ public static IServiceCollection AddOpcUaServices(this IServiceCollection services)
+ {
+ // 注册配置选项
+ services.Configure(
+ options => {
+ // 可以从配置文件或其他来源加载配置
+ });
+
+ // 注册服务
+ services.AddSingleton();
+
+ // 注册后台服务
+ services.AddHostedService();
+
+ return services;
+ }
+ }
+}
\ No newline at end of file
diff --git a/DMS.Infrastructure/Interfaces/Services/IOpcUaServiceManager.cs b/DMS.Infrastructure/Interfaces/Services/IOpcUaServiceManager.cs
new file mode 100644
index 0000000..5d4b458
--- /dev/null
+++ b/DMS.Infrastructure/Interfaces/Services/IOpcUaServiceManager.cs
@@ -0,0 +1,70 @@
+using DMS.Application.DTOs;
+using DMS.Infrastructure.Models;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace DMS.Infrastructure.Interfaces.Services
+{
+ ///
+ /// 定义OPC UA服务管理器的接口
+ ///
+ public interface IOpcUaServiceManager : IDisposable
+ {
+ ///
+ /// 初始化服务管理器
+ ///
+ Task InitializeAsync(CancellationToken cancellationToken = default);
+
+ ///
+ /// 添加设备到监控列表
+ ///
+ void AddDevice(DeviceDto device);
+
+ ///
+ /// 移除设备监控
+ ///
+ Task RemoveDeviceAsync(int deviceId, CancellationToken cancellationToken = default);
+
+ ///
+ /// 更新设备变量
+ ///
+ void UpdateVariables(int deviceId, List variables);
+
+ ///
+ /// 获取设备连接状态
+ ///
+ bool IsDeviceConnected(int deviceId);
+
+ ///
+ /// 重新连接设备
+ ///
+ Task ReconnectDeviceAsync(int deviceId, CancellationToken cancellationToken = default);
+
+ ///
+ /// 获取所有监控的设备ID
+ ///
+ IEnumerable GetMonitoredDeviceIds();
+
+ ///
+ /// 连接指定设备
+ ///
+ Task ConnectDeviceAsync(int deviceId, CancellationToken cancellationToken = default);
+
+ ///
+ /// 断开指定设备连接
+ ///
+ Task DisconnectDeviceAsync(int deviceId, CancellationToken cancellationToken = default);
+
+ ///
+ /// 批量连接设备
+ ///
+ Task ConnectDevicesAsync(IEnumerable deviceIds, CancellationToken cancellationToken = default);
+
+ ///
+ /// 批量断开设备连接
+ ///
+ Task DisconnectDevicesAsync(IEnumerable deviceIds, CancellationToken cancellationToken = default);
+ }
+}
\ No newline at end of file
diff --git a/DMS.Infrastructure/Services/OpcUaBackgroundService.cs b/DMS.Infrastructure/Services/OpcUaBackgroundService.cs
index 1126b01..2cf05c2 100644
--- a/DMS.Infrastructure/Services/OpcUaBackgroundService.cs
+++ b/DMS.Infrastructure/Services/OpcUaBackgroundService.cs
@@ -244,49 +244,6 @@ public class OpcUaBackgroundService : BackgroundService
}
}
- // ///
- // /// 读取单个 OPC UA 变量并处理其数据。
- // ///
- // /// OPC UA 会话。
- // /// 要读取的变量。
- // /// 取消令牌。
- // private async Task ReadAndProcessOpcUaVariableAsync(Session session, Variable variable,
- // CancellationToken stoppingToken)
- // {
- // var nodesToRead = new ReadValueIdCollection
- // {
- // new ReadValueId
- // {
- // NodeId = new NodeId(variable.OpcUaNodeId),
- // AttributeId = Attributes.Value
- // }
- // };
- //
- // try
- // {
- // var readResponse = await session.ReadAsync(null, 0, TimestampsToReturn.Both, nodesToRead, stoppingToken);
- // var result = readResponse.Results?.FirstOrDefault();
- // if (result == null) return;
- //
- // if (!StatusCode.IsGood(result.StatusCode))
- // {
- // _logger.LogWarning($"读取 OPC UA 变量 {variable.Name} ({variable.OpcUaNodeId}) 失败: {result.StatusCode}");
- // return;
- // }
- //
- // await UpdateAndEnqueueVariable(variable, result.Value);
- // }
- // catch (ServiceResultException ex) when (ex.StatusCode == StatusCodes.BadSessionIdInvalid)
- // {
- // _logger.LogError(ex, $"OPC UA会话ID无效,变量: {variable.Name} ({variable.OpcUaNodeId})。正在尝试重新连接...");
- //
- // }
- // catch (Exception ex)
- // {
- // _logger.LogError(ex, $"轮询OPC UA变量 {variable.Name} ({variable.OpcUaNodeId}) 时发生未知错误: {ex.Message}");
- // }
- // }
-
///
/// 更新变量数据,并将其推送到数据处理队列。
///
diff --git a/DMS.Infrastructure/Services/OpcUaServiceManager.cs b/DMS.Infrastructure/Services/OpcUaServiceManager.cs
new file mode 100644
index 0000000..b7e5d66
--- /dev/null
+++ b/DMS.Infrastructure/Services/OpcUaServiceManager.cs
@@ -0,0 +1,358 @@
+using System.Collections.Concurrent;
+using System.Diagnostics;
+using DMS.Application.DTOs;
+using DMS.Application.Interfaces;
+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;
+
+namespace DMS.Infrastructure.Services
+{
+ ///
+ /// OPC UA服务管理器,负责管理OPC UA连接、订阅和变量监控
+ ///
+ public class OpcUaServiceManager : IOpcUaServiceManager
+ {
+ private readonly ILogger _logger;
+ private readonly IDataProcessingService _dataProcessingService;
+ private readonly OpcUaServiceOptions _options;
+ private readonly ConcurrentDictionary _deviceContexts;
+ private readonly SemaphoreSlim _semaphore;
+ private bool _disposed = false;
+
+ public OpcUaServiceManager(
+ ILogger logger,
+ IDataProcessingService dataProcessingService,
+ IOptions options)
+ {
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ _dataProcessingService = dataProcessingService ?? throw new ArgumentNullException(nameof(dataProcessingService));
+ _options = options?.Value ?? throw new ArgumentNullException(nameof(options));
+ _deviceContexts = new ConcurrentDictionary();
+ _semaphore = new SemaphoreSlim(_options.MaxConcurrentConnections, _options.MaxConcurrentConnections);
+ }
+
+ ///
+ /// 初始化服务管理器
+ ///
+ public async Task InitializeAsync(CancellationToken cancellationToken = default)
+ {
+ _logger.LogInformation("OPC UA服务管理器正在初始化...");
+ // 初始化逻辑可以在需要时添加
+ _logger.LogInformation("OPC UA服务管理器初始化完成");
+ }
+
+ ///
+ /// 添加设备到监控列表
+ ///
+ public void AddDevice(DeviceDto device)
+ {
+ if (device == null)
+ throw new ArgumentNullException(nameof(device));
+
+ if (device.Protocol != ProtocolType.OpcUa)
+ {
+ _logger.LogWarning("设备 {DeviceId} 不是OPC UA协议,跳过添加", device.Id);
+ return;
+ }
+
+ var context = new DeviceContext
+ {
+ Device = device,
+ OpcUaService = new OpcUaService(),
+ Variables = new ConcurrentDictionary(),
+ IsConnected = false
+ };
+
+ _deviceContexts.AddOrUpdate(device.Id, context, (key, oldValue) => context);
+ _logger.LogInformation("已添加设备 {DeviceId} 到监控列表", device.Id);
+ }
+
+ ///
+ /// 移除设备监控
+ ///
+ public async Task RemoveDeviceAsync(int deviceId, CancellationToken cancellationToken = default)
+ {
+ if (_deviceContexts.TryRemove(deviceId, out var context))
+ {
+ await DisconnectDeviceAsync(context, cancellationToken);
+ _logger.LogInformation("已移除设备 {DeviceId} 的监控", deviceId);
+ }
+ }
+
+ ///
+ /// 更新设备变量
+ ///
+ public void UpdateVariables(int deviceId, List variables)
+ {
+ if (_deviceContexts.TryGetValue(deviceId, out var context))
+ {
+ context.Variables.Clear();
+ foreach (var variable in variables)
+ {
+ context.Variables.AddOrUpdate(variable.OpcUaNodeId, variable, (key, oldValue) => variable);
+ }
+ _logger.LogInformation("已更新设备 {DeviceId} 的变量列表,共 {Count} 个变量", deviceId, variables.Count);
+ }
+ }
+
+ ///
+ /// 获取设备连接状态
+ ///
+ public bool IsDeviceConnected(int deviceId)
+ {
+ return _deviceContexts.TryGetValue(deviceId, out var context) && context.IsConnected;
+ }
+
+ ///
+ /// 重新连接设备
+ ///
+ public async Task ReconnectDeviceAsync(int deviceId, CancellationToken cancellationToken = default)
+ {
+ if (_deviceContexts.TryGetValue(deviceId, out var context))
+ {
+ await DisconnectDeviceAsync(context, cancellationToken);
+ await ConnectDeviceAsync(context, cancellationToken);
+ }
+ }
+
+ ///
+ /// 获取所有监控的设备ID
+ ///
+ public IEnumerable GetMonitoredDeviceIds()
+ {
+ return _deviceContexts.Keys.ToList();
+ }
+
+ ///
+ /// 连接设备
+ ///
+ public async Task ConnectDeviceAsync(DeviceContext context, CancellationToken cancellationToken = default)
+ {
+ if (context == null || string.IsNullOrEmpty(context.Device.OpcUaServerUrl))
+ return;
+
+ await _semaphore.WaitAsync(cancellationToken);
+ try
+ {
+ _logger.LogInformation("正在连接设备 {DeviceName} ({EndpointUrl})",
+ context.Device.Name, context.Device.OpcUaServerUrl);
+
+ var stopwatch = Stopwatch.StartNew();
+
+ // 设置连接超时
+ using var timeoutToken = new CancellationTokenSource(_options.ConnectionTimeoutMs);
+ using var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutToken.Token);
+
+ await context.OpcUaService.ConnectAsync(context.Device.OpcUaServerUrl);
+
+ stopwatch.Stop();
+ _logger.LogInformation("设备 {DeviceName} 连接耗时 {ElapsedMs} ms",
+ context.Device.Name, stopwatch.ElapsedMilliseconds);
+
+ if (context.OpcUaService.IsConnected)
+ {
+ context.IsConnected = true;
+ await SetupSubscriptionsAsync(context, cancellationToken);
+ _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
+ {
+ _semaphore.Release();
+ }
+ }
+
+ ///
+ /// 断开设备连接
+ ///
+ private async Task DisconnectDeviceAsync(DeviceContext context, CancellationToken cancellationToken = default)
+ {
+ if (context == null)
+ return;
+
+ try
+ {
+ _logger.LogInformation("正在断开设备 {DeviceName} 的连接", context.Device.Name);
+ await context.OpcUaService.DisconnectAsync();
+ context.IsConnected = false;
+ _logger.LogInformation("设备 {DeviceName} 连接已断开", context.Device.Name);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "断开设备 {DeviceName} 连接时发生错误: {ErrorMessage}",
+ context.Device.Name, ex.Message);
+ }
+ }
+
+ ///
+ /// 设置订阅
+ ///
+ private async Task SetupSubscriptionsAsync(DeviceContext context, CancellationToken cancellationToken = default)
+ {
+ if (!context.IsConnected || !context.Variables.Any())
+ return;
+
+ try
+ {
+ _logger.LogInformation("正在为设备 {DeviceName} 设置订阅,变量数: {VariableCount}",
+ context.Device.Name, context.Variables.Count);
+
+ var opcUaNodes = context.Variables.Values
+ .Select(v => new OpcUaNode { NodeId = v.OpcUaNodeId })
+ .ToList();
+
+ context.OpcUaService.SubscribeToNode(opcUaNodes, HandleDataChanged,
+ _options.SubscriptionPublishingIntervalMs, _options.SubscriptionSamplingIntervalMs);
+
+ _logger.LogInformation("设备 {DeviceName} 订阅设置完成", context.Device.Name);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "为设备 {DeviceName} 设置订阅时发生错误: {ErrorMessage}",
+ context.Device.Name, ex.Message);
+ }
+ }
+
+ ///
+ /// 处理数据变化
+ ///
+ private async void HandleDataChanged(OpcUaNode opcUaNode)
+ {
+ if (opcUaNode?.Value == null)
+ return;
+
+ try
+ {
+ // 查找对应的变量
+ foreach (var context in _deviceContexts.Values)
+ {
+ if (context.Variables.TryGetValue(opcUaNode.NodeId.ToString(), out var variable))
+ {
+ // 更新变量值
+ variable.DataValue = opcUaNode.Value.ToString();
+ variable.DisplayValue = opcUaNode.Value.ToString();
+ variable.UpdatedAt = DateTime.Now;
+
+ // 推送到数据处理队列
+ await _dataProcessingService.EnqueueAsync(variable);
+ break;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "处理数据变化时发生错误: {ErrorMessage}", ex.Message);
+ }
+ }
+
+ ///
+ /// 连接指定设备
+ ///
+ public async Task ConnectDeviceAsync(int deviceId, CancellationToken cancellationToken = default)
+ {
+ if (_deviceContexts.TryGetValue(deviceId, out var context))
+ {
+ await ConnectDeviceAsync(context, cancellationToken);
+ }
+ }
+
+ ///
+ /// 批量连接设备
+ ///
+ public async Task ConnectDevicesAsync(IEnumerable deviceIds, CancellationToken cancellationToken = default)
+ {
+ var connectTasks = new List();
+
+ foreach (var deviceId in deviceIds)
+ {
+ connectTasks.Add(ConnectDeviceAsync(deviceId, cancellationToken));
+ }
+
+ await Task.WhenAll(connectTasks);
+ }
+
+ ///
+ /// 断开指定设备连接
+ ///
+ public async Task DisconnectDeviceAsync(int deviceId, CancellationToken cancellationToken = default)
+ {
+ if (_deviceContexts.TryGetValue(deviceId, out var context))
+ {
+ await DisconnectDeviceAsync(context, cancellationToken);
+ }
+ }
+
+ ///
+ /// 批量断开设备连接
+ ///
+ public async Task DisconnectDevicesAsync(IEnumerable deviceIds, CancellationToken cancellationToken = default)
+ {
+ var disconnectTasks = new List();
+
+ foreach (var deviceId in deviceIds)
+ {
+ if (_deviceContexts.TryGetValue(deviceId, out var context))
+ {
+ disconnectTasks.Add(DisconnectDeviceAsync(deviceId, cancellationToken));
+ }
+ }
+
+ await Task.WhenAll(disconnectTasks);
+ }
+
+ ///
+ /// 释放资源
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// 释放资源
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposed && disposing)
+ {
+ _logger.LogInformation("正在释放OPC UA服务管理器资源...");
+
+ // 断开所有设备连接
+ var deviceIds = _deviceContexts.Keys.ToList();
+ DisconnectDevicesAsync(deviceIds).Wait(TimeSpan.FromSeconds(10));
+
+ // 释放其他资源
+ _semaphore?.Dispose();
+
+ _disposed = true;
+ _logger.LogInformation("OPC UA服务管理器资源已释放");
+ }
+ }
+ }
+
+ ///
+ /// 设备上下文
+ ///
+ public class DeviceContext
+ {
+ public DeviceDto Device { get; set; }
+ public OpcUaService OpcUaService { get; set; }
+ public ConcurrentDictionary Variables { get; set; }
+ public bool IsConnected { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/DMS.Infrastructure/Services/OptimizedOpcUaBackgroundService.cs b/DMS.Infrastructure/Services/OptimizedOpcUaBackgroundService.cs
new file mode 100644
index 0000000..0c0a477
--- /dev/null
+++ b/DMS.Infrastructure/Services/OptimizedOpcUaBackgroundService.cs
@@ -0,0 +1,156 @@
+using DMS.Application.DTOs;
+using DMS.Application.DTOs.Events;
+using DMS.Application.Interfaces;
+using DMS.Core.Enums;
+using DMS.Infrastructure.Interfaces.Services;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace DMS.Infrastructure.Services
+{
+ ///
+ /// 优化后的OPC UA后台服务
+ ///
+ public class OptimizedOpcUaBackgroundService : BackgroundService
+ {
+ private readonly IDataCenterService _dataCenterService;
+ private readonly IOpcUaServiceManager _opcUaServiceManager;
+ private readonly ILogger _logger;
+ private readonly SemaphoreSlim _reloadSemaphore = new SemaphoreSlim(0);
+
+ public OptimizedOpcUaBackgroundService(
+ IDataCenterService dataCenterService,
+ IOpcUaServiceManager opcUaServiceManager,
+ ILogger logger)
+ {
+ _dataCenterService = dataCenterService ?? throw new ArgumentNullException(nameof(dataCenterService));
+ _opcUaServiceManager = opcUaServiceManager ?? throw new ArgumentNullException(nameof(opcUaServiceManager));
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+
+ _dataCenterService.DataLoadCompleted += OnDataLoadCompleted;
+ }
+
+ private void OnDataLoadCompleted(object sender, DataLoadCompletedEventArgs e)
+ {
+ _logger.LogInformation("收到数据加载完成通知,触发OPC UA服务重新加载");
+ _reloadSemaphore.Release();
+ }
+
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ _logger.LogInformation("优化后的OPC UA后台服务正在启动...");
+
+ try
+ {
+ // 初始化服务管理器
+ await _opcUaServiceManager.InitializeAsync(stoppingToken);
+
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ await _reloadSemaphore.WaitAsync(stoppingToken);
+
+ if (stoppingToken.IsCancellationRequested)
+ break;
+
+ if (_dataCenterService.Devices.IsEmpty)
+ {
+ _logger.LogInformation("没有可用的OPC UA设备,等待设备列表更新...");
+ continue;
+ }
+
+ await LoadAndConnectDevicesAsync(stoppingToken);
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ _logger.LogInformation("OPC UA后台服务已收到停止请求");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "OPC UA后台服务运行时发生未处理的异常: {ErrorMessage}", ex.Message);
+ }
+ finally
+ {
+ _logger.LogInformation("正在清理OPC UA后台服务资源...");
+ // 服务管理器会在Dispose时自动清理资源
+ }
+
+ _logger.LogInformation("优化后的OPC UA后台服务已停止");
+ }
+
+ ///
+ /// 加载并连接设备
+ ///
+ private async Task LoadAndConnectDevicesAsync(CancellationToken stoppingToken)
+ {
+ _logger.LogInformation("开始加载和连接OPC UA设备...");
+
+ try
+ {
+ // 获取所有活动的OPC UA设备
+ var opcUaDevices = _dataCenterService.Devices.Values
+ .Where(d => d.Protocol == ProtocolType.OpcUa && d.IsActive)
+ .ToList();
+
+ _logger.LogInformation("找到 {DeviceCount} 个活动的OPC UA设备", opcUaDevices.Count);
+
+ if (opcUaDevices.Count == 0)
+ return;
+
+ // 添加设备到监控列表
+ foreach (var device in opcUaDevices)
+ {
+ _opcUaServiceManager.AddDevice(device);
+
+ // 获取设备变量
+ var variables = device.VariableTables?
+ .SelectMany(vt => vt.Variables)
+ .Where(v => v.IsActive && v.Protocol == ProtocolType.OpcUa)
+ .ToList() ?? new List();
+
+ _opcUaServiceManager.UpdateVariables(device.Id, variables);
+ }
+
+ // 批量连接设备
+ var deviceIds = opcUaDevices.Select(d => d.Id).ToList();
+ await _opcUaServiceManager.ConnectDevicesAsync(deviceIds, stoppingToken);
+
+
+ _logger.LogInformation("OPC UA设备加载和连接完成");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "加载和连接OPC UA设备时发生错误: {ErrorMessage}", ex.Message);
+ }
+ }
+
+ public override async Task StopAsync(CancellationToken cancellationToken)
+ {
+ _logger.LogInformation("正在停止OPC UA后台服务...");
+
+ // 释放信号量以确保ExecuteAsync可以退出
+ _reloadSemaphore.Release();
+
+ await base.StopAsync(cancellationToken);
+
+ _logger.LogInformation("OPC UA后台服务停止完成");
+ }
+
+ public override void Dispose()
+ {
+ _logger.LogInformation("正在释放OPC UA后台服务资源...");
+
+ _dataCenterService.DataLoadCompleted -= OnDataLoadCompleted;
+ _reloadSemaphore?.Dispose();
+
+ base.Dispose();
+
+ _logger.LogInformation("OPC UA后台服务资源已释放");
+ }
+ }
+}
\ No newline at end of file
diff --git a/DMS.WPF/App.xaml.cs b/DMS.WPF/App.xaml.cs
index 2c16552..0ab78d9 100644
--- a/DMS.WPF/App.xaml.cs
+++ b/DMS.WPF/App.xaml.cs
@@ -28,32 +28,29 @@ using LogLevel = Microsoft.Extensions.Logging.LogLevel;
namespace DMS.WPF;
///
-/// Interaction logic for App.xaml
+/// Interaction logic for App.xaml
///
public partial class App : System.Windows.Application
{
public IServiceProvider Services { get; }
- // public AppSettings Settings { get; private set; }
-
+ public new static App Current => (App)System.Windows.Application.Current;
+ public IHost Host { get; }
public App()
{
Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder()
- .ConfigureServices((context, services) =>
- {
- ConfigureServices(services);
- })
- .ConfigureLogging(loggingBuilder =>
- {
- ConfigureLogging(loggingBuilder);
- })
- .Build();
+ .ConfigureServices((context, services) =>
+ {
+ ConfigureServices(services);
+ })
+ .ConfigureLogging(loggingBuilder =>
+ {
+ ConfigureLogging(loggingBuilder);
+ })
+ .Build();
Services = Host.Services;
}
- public new static App Current => (App)System.Windows.Application.Current;
- public IHost Host { get; }
-
protected override async void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
@@ -81,17 +78,6 @@ public partial class App : System.Windows.Application
var splashWindow = Host.Services.GetRequiredService();
splashWindow.Show();
-
- // 根据配置启动服务
- // var connectionSettings = DMS.Config.AppSettings.Load();
- // if (connectionSettings.EnableMqttService)
- // {
- // Host.Services.GetRequiredService().StartService();
- // }
- // if (connectionSettings.EnableOpcUaService)
- // {
- // Host.Services.GetRequiredService().StartService();
- // }
}
protected override async void OnExit(ExitEventArgs e)
@@ -107,14 +93,10 @@ public partial class App : System.Windows.Application
{
// 注册NLogLogger作为Microsoft.Extensions.Logging.ILogger的实现
services.AddSingleton();
- //
services.AddSingleton();
services.AddSingleton();
- // services.AddHostedService();
- // services.AddHostedService();
- // services.AddHostedService();
- // --- 核心配置 ---
+ // 注册核心服务
services.AddAutoMapper(cfg =>
{
// 最终解决方案:根据异常信息的建议,设置此标记以忽略重复的Profile加载。
@@ -127,8 +109,9 @@ public partial class App : System.Windows.Application
});
// 注册数据处理服务和处理器
-
services.AddHostedService();
+ services.Configure(options => { });
+ services.AddSingleton();
services.AddSingleton();
services.AddHostedService(provider => (DataProcessingService)provider.GetRequiredService());
services.AddSingleton();
@@ -139,8 +122,6 @@ public partial class App : System.Windows.Application
// 注册Core中的仓库
services.AddSingleton();
- // services.AddSingleton();
- // 2. 配置数据库上下文 (在测试中通常使用单例)
services.AddSingleton(_ =>
{
var appSettings = new AppSettings { Database = { Database = "dms_test" } };
@@ -161,18 +142,19 @@ public partial class App : System.Windows.Application
services.AddTransient();
-
// 注册App服务
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
- //注册WPF中的服务
+
+ // 注册WPF中的服务
services.AddSingleton();
+
// 注册视图模型
services.AddSingleton();
services.AddSingleton();
@@ -180,18 +162,16 @@ public partial class App : System.Windows.Application
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
- services.AddSingleton();
services.AddTransient();
- //services.AddScoped();
services.AddSingleton();
services.AddSingleton();
+
// 注册对话框模型
services.AddTransient();
services.AddTransient();
services.AddTransient();
- // 注册对话框
- services.AddSingleton();
- //注册View视图
+
+ // 注册View视图
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
@@ -207,7 +187,6 @@ public partial class App : System.Windows.Application
LogManager.Setup().LoadConfigurationFromFile("Configurations/nlog.config");
loggingBuilder.ClearProviders();
loggingBuilder.SetMinimumLevel(LogLevel.Trace);
- // loggingBuilder.AddNLog();
// 捕获未处理的异常并记录
AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
@@ -217,7 +196,7 @@ public partial class App : System.Windows.Application
{
// 可以使用一个专用的 Logger 来记录未处理异常
LogManager.GetLogger("UnhandledExceptionLogger")
- .Fatal($"应用程序发生未处理的异常:{ex}");
+ .Fatal($"应用程序发生未处理的异常:{ex}");
}
};
@@ -225,7 +204,7 @@ public partial class App : System.Windows.Application
this.DispatcherUnhandledException += (sender, args) =>
{
LogManager.GetLogger("DispatcherExceptionLogger")
- .Fatal($"UI 线程发生未处理的异常:{args.Exception}");
+ .Fatal($"UI 线程发生未处理的异常:{args.Exception}");
// 标记为已处理,防止应用程序崩溃 (生产环境慎用,可能掩盖问题)
// args.Handled = true;
};
@@ -235,10 +214,8 @@ public partial class App : System.Windows.Application
TaskScheduler.UnobservedTaskException += (sender, args) =>
{
LogManager.GetLogger("UnobservedTaskExceptionLogger")
- .Fatal($"异步任务发生未观察到的异常:{args.Exception}");
+ .Fatal($"异步任务发生未观察到的异常:{args.Exception}");
// args.SetObserved(); // 标记为已观察,防止进程终止
};
}
-
-
}
\ No newline at end of file