diff --git a/App.xaml.cs b/App.xaml.cs index 0af85c7..d3ca60f 100644 --- a/App.xaml.cs +++ b/App.xaml.cs @@ -12,6 +12,7 @@ using PMSWPF.Helper; using PMSWPF.Services; using PMSWPF.ViewModels; using PMSWPF.Views; +using Microsoft.Extensions.Hosting; using SqlSugar; using LogLevel = Microsoft.Extensions.Logging.LogLevel; @@ -22,72 +23,71 @@ namespace PMSWPF; /// public partial class App : Application { + public IServiceProvider Services { get; } + public App() { + Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder() + .ConfigureServices((context, services) => { ConfigureServices(services); }) + .ConfigureLogging(loggingBuilder => { ConfigureLogging(loggingBuilder); }) + .Build(); + Services = Host.Services; } public new static App Current => (App)Application.Current; - public IServiceProvider Services { get; set; } + public IHost Host { get; } - protected override void OnStartup(StartupEventArgs e) + protected override async void OnStartup(StartupEventArgs e) { base.OnStartup(e); - InitializeLog(); - InitializeServices(); + await Host.StartAsync(); InitializeDataBase(); - InitializeMenu().Await((e) => { NotificationHelper.ShowMessage($"初始化主菜单失败:{e.Message}"); }, - () => { MessageHelper.SendLoadMessage(LoadTypes.Menu); }); + InitializeMenu() + .Await((e) => { NotificationHelper.ShowMessage($"初始化主菜单失败:{e.Message}"); }, + () => { MessageHelper.SendLoadMessage(LoadTypes.Menu); }); - MainWindow = Services.GetRequiredService(); + MainWindow = Host.Services.GetRequiredService(); MainWindow.Show(); } - protected override void OnExit(ExitEventArgs e) + protected override async void OnExit(ExitEventArgs e) { - // 应用程序退出时,确保 NLog 缓冲区被清空 + await Host.StopAsync(); + Host.Dispose(); LogManager.Shutdown(); base.OnExit(e); } - private void InitializeServices() + private void ConfigureServices(IServiceCollection services) { - var container = new ServiceCollection(); - container.AddLogging(loggingBuilder => - { - loggingBuilder.ClearProviders(); - loggingBuilder.SetMinimumLevel(LogLevel.Trace); - loggingBuilder.AddNLog(); - }); - - container.AddSingleton(); - container.AddSingleton(); - container.AddSingleton(); - container.AddSingleton(); - container.AddSingleton(); - container.AddSingleton(); - container.AddSingleton(); - container.AddSingleton(); - container.AddSingleton(); - container.AddSingleton(); - container.AddSingleton(); - container.AddSingleton(); - container.AddSingleton(); - container.AddSingleton(); - container.AddTransient(); - container.AddSingleton(); - container.AddScoped(); - container.AddScoped(); - container.AddScoped(); - container.AddScoped(); - Services = container.BuildServiceProvider(); - // 启动服务 - Services.GetRequiredService(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddHostedService(); // Register as HostedService + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddTransient(); + services.AddSingleton(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); } - private void InitializeLog() + private void ConfigureLogging(ILoggingBuilder loggingBuilder) { - LogManager.Setup().LoadConfigurationFromFile("Config/nlog.config").GetCurrentClassLogger(); - + loggingBuilder.ClearProviders(); + loggingBuilder.SetMinimumLevel(LogLevel.Trace); + loggingBuilder.AddNLog(); // 捕获未处理的异常并记录 AppDomain.CurrentDomain.UnhandledException += (sender, args) => @@ -96,14 +96,16 @@ public partial class App : Application if (ex != null) { // 可以使用一个专用的 Logger 来记录未处理异常 - LogManager.GetLogger("UnhandledExceptionLogger").Fatal($"应用程序发生未处理的异常:{ex}"); + LogManager.GetLogger("UnhandledExceptionLogger") + .Fatal($"应用程序发生未处理的异常:{ex}"); } }; // 捕获 Dispatcher 线程上的未处理异常 (UI 线程) this.DispatcherUnhandledException += (sender, args) => { - LogManager.GetLogger("DispatcherExceptionLogger").Fatal( $"UI 线程发生未处理的异常:{args.Exception}"); + LogManager.GetLogger("DispatcherExceptionLogger") + .Fatal($"UI 线程发生未处理的异常:{args.Exception}"); // 标记为已处理,防止应用程序崩溃 (生产环境慎用,可能掩盖问题) // args.Handled = true; }; @@ -112,11 +114,10 @@ public partial class App : Application // 可以通过以下方式捕获 Task 中的异常。 TaskScheduler.UnobservedTaskException += (sender, args) => { - LogManager.GetLogger("UnobservedTaskExceptionLogger").Fatal( $"异步任务发生未观察到的异常:{args.Exception}"); + LogManager.GetLogger("UnobservedTaskExceptionLogger") + .Fatal($"异步任务发生未观察到的异常:{args.Exception}"); // args.SetObserved(); // 标记为已观察,防止进程终止 }; - - } /// @@ -127,19 +128,31 @@ public partial class App : Application using (var db = DbContext.GetInstance()) { var homeMenu = new DbMenu() - { Name = "主页", Type = MenuType.MainMenu, Icon = SegoeFluentIcons.Home.Glyph, ParentId = 0 }; + { Name = "主页", Type = MenuType.MainMenu, Icon = SegoeFluentIcons.Home.Glyph, ParentId = 0 }; var deviceMenu = new DbMenu() - { Name = "设备", Type = MenuType.MainMenu, Icon = SegoeFluentIcons.Devices3.Glyph, ParentId = 0 }; + { + Name = "设备", Type = MenuType.MainMenu, Icon = SegoeFluentIcons.Devices3.Glyph, + ParentId = 0 + }; var dataTransfromMenu = new DbMenu() - { Name = "数据转换", Type = MenuType.MainMenu, Icon = SegoeFluentIcons.ChromeSwitch.Glyph, ParentId = 0 }; + { + Name = "数据转换", Type = MenuType.MainMenu, + Icon = SegoeFluentIcons.ChromeSwitch.Glyph, ParentId = 0 + }; var mqttMenu = new DbMenu() - { Name = "Mqtt服务器", Type = MenuType.MainMenu, Icon = SegoeFluentIcons.Cloud.Glyph, ParentId = 0 }; + { + Name = "Mqtt服务器", Type = MenuType.MainMenu, Icon = SegoeFluentIcons.Cloud.Glyph, + ParentId = 0 + }; var settingMenu = new DbMenu() - { Name = "设置", Type = MenuType.MainMenu, Icon = SegoeFluentIcons.Settings.Glyph, ParentId = 0 }; + { + Name = "设置", Type = MenuType.MainMenu, Icon = SegoeFluentIcons.Settings.Glyph, + ParentId = 0 + }; var aboutMenu = new DbMenu() - { Name = "关于", Type = MenuType.MainMenu, Icon = SegoeFluentIcons.Info.Glyph, ParentId = 0 }; + { Name = "关于", Type = MenuType.MainMenu, Icon = SegoeFluentIcons.Info.Glyph, ParentId = 0 }; await CheckMainMenuExist(db, homeMenu); await CheckMainMenuExist(db, deviceMenu); await CheckMainMenuExist(db, dataTransfromMenu); @@ -151,10 +164,12 @@ public partial class App : Application private static async Task CheckMainMenuExist(SqlSugarClient db, DbMenu menu) { - var homeMenuExist = await db.Queryable().FirstAsync(dm => dm.Name == menu.Name); + var homeMenuExist = await db.Queryable() + .FirstAsync(dm => dm.Name == menu.Name); if (homeMenuExist == null) { - await db.Insertable(menu).ExecuteCommandAsync(); + await db.Insertable(menu) + .ExecuteCommandAsync(); } } diff --git a/Services/S7BackgroundService.cs b/Services/S7BackgroundService.cs index 8d2af46..8f94b2e 100644 --- a/Services/S7BackgroundService.cs +++ b/Services/S7BackgroundService.cs @@ -18,6 +18,7 @@ namespace PMSWPF.Services private readonly DataServices _dataServices; private readonly Dictionary _s7PlcClients = new Dictionary(); private readonly TimeSpan _pollingInterval = TimeSpan.FromSeconds(1); // 轮询间隔 + private List? _s7Devices; public S7BackgroundService(ILogger logger, DataServices dataServices) { @@ -28,6 +29,8 @@ namespace PMSWPF.Services private void HandleDeviceListChanged(List devices) { + _s7Devices = devices.Where(d => d.ProtocolType == ProtocolType.S7 && d.IsActive) + .ToList(); // 当设备列表变化时,更新PLC客户端 // 这里需要更复杂的逻辑来处理连接的关闭和新连接的建立 // 简单起见,这里只做日志记录 @@ -40,6 +43,9 @@ namespace PMSWPF.Services stoppingToken.Register(() => _logger.LogInformation("S7 Background Service is stopping.")); + _s7Devices = _dataServices.Devices?.Where(d => d.ProtocolType == ProtocolType.S7 && d.IsActive) + .ToList(); + while (!stoppingToken.IsCancellationRequested) { _logger.LogDebug("S7 Background Service is doing background work."); @@ -62,15 +68,13 @@ namespace PMSWPF.Services private async Task PollS7Devices(CancellationToken stoppingToken) { - var s7Devices = _dataServices.Devices?.Where(d => d.ProtocolType == ProtocolType.S7 && d.IsActive).ToList(); - - if (s7Devices == null || !s7Devices.Any()) + if (_s7Devices == null || !_s7Devices.Any()) { _logger.LogDebug("No active S7 devices found to poll."); return; } - foreach (var device in s7Devices) + foreach (var device in _s7Devices) { if (stoppingToken.IsCancellationRequested) return; @@ -107,10 +111,14 @@ namespace PMSWPF.Services } // Filter variables for the current device and S7 protocol - var s7Variables = device.VariableTables - ?.SelectMany(vt => vt.DataVariables) - .Where(vd => vd.ProtocolType == ProtocolType.S7 && vd.IsActive) - .ToList(); + var s7VariablesTemp = device + .VariableTables.Where(vd => vd.ProtocolType == ProtocolType.S7 && vd.IsActive) + .ToList(); + + var s7Variables = s7VariablesTemp.SelectMany(vt => vt.DataVariables) + .ToList(); + // ?.SelectMany(vt => vt.DataVariables) + if (s7Variables == null || !s7Variables.Any()) { @@ -119,7 +127,8 @@ namespace PMSWPF.Services } // Batch read variables - var addressesToRead = s7Variables.Select(vd => vd.S7Address).ToList(); + var addressesToRead = s7Variables.Select(vd => vd.S7Address) + .ToList(); if (!addressesToRead.Any()) continue; try @@ -146,7 +155,8 @@ namespace PMSWPF.Services { // Update the variable's DataValue and DisplayValue variable.DataValue = value.ToString(); - variable.DisplayValue = SiemensHelper.ConvertS7Value(value, variable.DataType, variable.Converstion); + variable.DisplayValue + = SiemensHelper.ConvertS7Value(value, variable.DataType, variable.Converstion); _logger.LogDebug($"Read {variable.Name}: {variable.DataValue}"); } } @@ -176,9 +186,10 @@ namespace PMSWPF.Services _logger.LogInformation($"Closed S7 PLC connection: {plcClient.IP}"); } } + _s7PlcClients.Clear(); await base.StopAsync(stoppingToken); } } -} +} \ No newline at end of file