From 9cf249f10e57a0eb570dc0459766c79d67eeb9cc Mon Sep 17 00:00:00 2001 From: "David P.G" Date: Sat, 5 Jul 2025 12:00:46 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DLog=E4=B8=8D=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=8C=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E4=BA=86S7BackgroundService=E4=B8=AD=E7=9A=84=E8=AF=BB?= =?UTF-8?q?=E5=8F=96=E5=8F=98=E9=87=8F=E7=9A=84=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.xaml.cs | 11 +- Services/S7BackgroundService.cs | 188 ++++++++++++++++++-------------- 2 files changed, 113 insertions(+), 86 deletions(-) diff --git a/App.xaml.cs b/App.xaml.cs index d3ca60f..8b5455c 100644 --- a/App.xaml.cs +++ b/App.xaml.cs @@ -28,8 +28,14 @@ public partial class App : Application public App() { Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder() - .ConfigureServices((context, services) => { ConfigureServices(services); }) - .ConfigureLogging(loggingBuilder => { ConfigureLogging(loggingBuilder); }) + .ConfigureServices((context, services) => + { + ConfigureServices(services); + }) + .ConfigureLogging(loggingBuilder => + { + ConfigureLogging(loggingBuilder); + }) .Build(); Services = Host.Services; } @@ -85,6 +91,7 @@ public partial class App : Application private void ConfigureLogging(ILoggingBuilder loggingBuilder) { + LogManager.Setup().LoadConfigurationFromFile("Config/nlog.config"); loggingBuilder.ClearProviders(); loggingBuilder.SetMinimumLevel(LogLevel.Trace); loggingBuilder.AddNLog(); diff --git a/Services/S7BackgroundService.cs b/Services/S7BackgroundService.cs index 8f94b2e..c80f249 100644 --- a/Services/S7BackgroundService.cs +++ b/Services/S7BackgroundService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -66,6 +67,51 @@ namespace PMSWPF.Services _logger.LogInformation("S7 Background Service has stopped."); } + /// + /// 初始化或重新连接PLC客户端 + /// + /// S7设备 + /// 连接成功的Plc客户端实例,如果连接失败则返回null + private async Task InitializePlcClient(Device device) + { + // 检查字典中是否已存在该设备的PLC客户端 + if (!_s7PlcClients.TryGetValue(device.Id, out var plcClient)) + { + // 如果不存在,则创建新的Plc客户端 + try + { + plcClient = new Plc(device.CpuType, device.Ip, (short)device.Prot, device.Rack, device.Slot); + await plcClient.OpenAsync(); // 尝试打开连接 + _s7PlcClients[device.Id] = plcClient; // 将新创建的客户端添加到字典 + _logger.LogInformation($"已连接到S7 PLC: {device.Name} ({device.Ip})"); + } + catch (Exception ex) + { + _logger.LogError(ex, $"连接S7 PLC失败: {device.Name} ({device.Ip})"); + return null; // 连接失败,返回null + } + } + else if (!plcClient.IsConnected) + { + // 如果存在但未连接,则尝试重新连接 + try + { + await plcClient.OpenAsync(); // 尝试重新打开连接 + _logger.LogInformation($"已重新连接到S7 PLC: {device.Name} ({device.Ip})"); + } + catch (Exception ex) + { + _logger.LogError(ex, $"重新连接S7 PLC失败: {device.Name} ({device.Ip})"); + return null; // 重新连接失败,返回null + } + } + return plcClient; // 返回连接成功的Plc客户端 + } + + /// + /// 轮询S7设备数据 + /// + /// 取消令牌 private async Task PollS7Devices(CancellationToken stoppingToken) { if (_s7Devices == null || !_s7Devices.Any()) @@ -78,98 +124,72 @@ namespace PMSWPF.Services { if (stoppingToken.IsCancellationRequested) return; - if (!_s7PlcClients.ContainsKey(device.Id)) + // 尝试获取或初始化PLC客户端连接 + var plcClient = await InitializePlcClient(device); + if (plcClient == null) { - // Initialize Plc client for the device + continue; // 如果连接失败,则跳过当前设备 + } + + + // 读取设备变量 + await ReadDeviceVariables(plcClient, device, stoppingToken); + + } + } + + /// + /// 读取设备的S7变量并更新其值。 + /// + /// 已连接的Plc客户端实例。 + /// S7设备。 + /// 取消令牌。 + private async Task ReadDeviceVariables(Plc plcClient, Device device, CancellationToken stoppingToken) + { + // 过滤出当前设备和S7协议相关的变量 + var s7Variables = device.VariableTables + .Where(vt => vt.ProtocolType == ProtocolType.S7 && vt.IsActive) + .SelectMany(vt => vt.DataVariables) + .ToList(); + + if (!s7Variables.Any()) + { + _logger.LogDebug($"设备 {device.Name} 没有找到活跃的S7变量。"); + return; + } + + try + { + Stopwatch sw = Stopwatch.StartNew(); + // 遍历并读取每个S7变量 + foreach (var variable in s7Variables) + { + if (stoppingToken.IsCancellationRequested) return; // 如果取消令牌被请求,则停止读取 + try { - var plc = new Plc(device.CpuType, device.Ip, (short)device.Prot, device.Rack, device.Slot); - await plc.OpenAsync(); - _s7PlcClients[device.Id] = plc; - _logger.LogInformation($"Connected to S7 PLC: {device.Name} ({device.Ip})"); + // 从PLC读取变量值 + var value = await plcClient.ReadAsync(variable.S7Address); + if (value != null) + { + // 更新变量的原始数据值和显示值 + variable.DataValue = value.ToString(); + variable.DisplayValue = SiemensHelper.ConvertS7Value(value, variable.DataType, variable.Converstion); + // _logger.LogDebug($"已读取变量 {variable.Name}: {variable.DataValue}"); + } } catch (Exception ex) { - _logger.LogError(ex, $"Failed to connect to S7 PLC: {device.Name} ({device.Ip})"); - continue; + _logger.LogError(ex, $"从设备 {device.Name} 读取变量 {variable.Name} 失败。"); } } - - var plcClient = _s7PlcClients[device.Id]; - if (!plcClient.IsConnected) - { - try - { - await plcClient.OpenAsync(); - _logger.LogInformation($"Reconnected to S7 PLC: {device.Name} ({device.Ip})"); - } - catch (Exception ex) - { - _logger.LogError(ex, $"Failed to reconnect to S7 PLC: {device.Name} ({device.Ip})"); - continue; - } - } - - // Filter variables for the current device and S7 protocol - 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()) - { - _logger.LogDebug($"No active S7 variables found for device: {device.Name}"); - continue; - } - - // Batch read variables - var addressesToRead = s7Variables.Select(vd => vd.S7Address) - .ToList(); - if (!addressesToRead.Any()) continue; - - try - { - // S7.Net.Plus library supports ReadMultiple, but it's more complex for different data types. - // For simplicity, we'll read them one by one for now, or use a more advanced batch read if all are same type. - // A more robust solution would involve grouping by data block and type. - - // Example of reading multiple items (assuming all are of type DWord for simplicity) - // This part needs to be refined based on actual variable types and addresses - // var dataItems = addressesToRead.Select(addr => new DataItem { DataType = DataType.DataBlock, VarType = VarType.DWord, StringLength = 1, DB = 1, StartByteAdr = 0 }).ToList(); - // plcClient.ReadMultiple(dataItems); - - foreach (var variable in s7Variables) - { - if (stoppingToken.IsCancellationRequested) return; - try - { - // This is a simplified read. In a real scenario, you'd parse S7Address - // to get DataType, DB, StartByteAdr, BitAdr, etc. - // For now, assuming S7Address is directly readable by Read method (e.g., "DB1.DBW0") - var value = await plcClient.ReadAsync(variable.S7Address); - if (value != null) - { - // Update the variable's DataValue and DisplayValue - variable.DataValue = value.ToString(); - variable.DisplayValue - = SiemensHelper.ConvertS7Value(value, variable.DataType, variable.Converstion); - _logger.LogDebug($"Read {variable.Name}: {variable.DataValue}"); - } - } - catch (Exception ex) - { - _logger.LogError(ex, $"Failed to read variable {variable.Name} from {device.Name}"); - } - } - } - catch (Exception ex) - { - _logger.LogError(ex, $"Error during batch read for device {device.Name}"); - } + + sw.Stop(); + _logger.LogInformation($"从: {device.Name} ({device.Ip})读取变量总耗时:{sw.ElapsedMilliseconds}ms"); + } + catch (Exception ex) + { + _logger.LogError(ex, $"设备 {device.Name} 批量读取过程中发生错误。"); } }