修复Log不显示的问题,重构了S7BackgroundService中的读取变量的方法

This commit is contained in:
2025-07-05 12:00:46 +08:00
parent 4f4922e07c
commit 9cf249f10e
2 changed files with 113 additions and 86 deletions

View File

@@ -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.");
}
/// <summary>
/// 初始化或重新连接PLC客户端
/// </summary>
/// <param name="device">S7设备</param>
/// <returns>连接成功的Plc客户端实例如果连接失败则返回null</returns>
private async Task<Plc?> 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客户端
}
/// <summary>
/// 轮询S7设备数据
/// </summary>
/// <param name="stoppingToken">取消令牌</param>
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);
}
}
/// <summary>
/// 读取设备的S7变量并更新其值。
/// </summary>
/// <param name="plcClient">已连接的Plc客户端实例。</param>
/// <param name="device">S7设备。</param>
/// <param name="stoppingToken">取消令牌。</param>
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} 批量读取过程中发生错误。");
}
}