diff --git a/Data/Entities/DbDevice.cs b/Data/Entities/DbDevice.cs
index d3745d4..cd28670 100644
--- a/Data/Entities/DbDevice.cs
+++ b/Data/Entities/DbDevice.cs
@@ -2,6 +2,7 @@ using PMSWPF.Enums;
using SqlSugar;
using SqlSugar.DbConvert;
using ProtocolType = PMSWPF.Enums.ProtocolType;
+using S7.Net; // Add this using directive
namespace PMSWPF.Data.Entities;
@@ -54,6 +55,24 @@ public class DbDevice
///
public int Prot { get; set; }
+ ///
+ /// PLC的CPU类型。
+ ///
+ [SugarColumn(ColumnDataType = "varchar(20)", IsNullable = true, SqlParameterDbType = typeof(EnumToStringConvert))]
+ public CpuType CpuType { get; set; }
+
+ ///
+ /// PLC的机架号。
+ ///
+ [SugarColumn(IsNullable = true)]
+ public short Rack { get; set; }
+
+ ///
+ /// PLC的槽号。
+ ///
+ /// [SugarColumn(IsNullable = true)]
+ public short Slot { get; set; }
+
///
/// 设备的通信协议类型。
///
diff --git a/Helper/SiemensHelper.cs b/Helper/SiemensHelper.cs
index 15a13ca..333e44b 100644
--- a/Helper/SiemensHelper.cs
+++ b/Helper/SiemensHelper.cs
@@ -51,4 +51,20 @@ public static class SiemensHelper
return "object";
}
}
+
+ ///
+ /// 将S7读取到的值转换为显示值
+ ///
+ /// S7读取到的原始值
+ /// 变量的数据类型
+ /// 转换规则
+ /// 显示值
+ public static string ConvertS7Value(object value, string dataType, string conversion)
+ {
+ if (value == null) return string.Empty;
+
+ // For now, a simple conversion to string. More complex logic can be added here.
+ // Based on dataType and conversion, you might parse, format, or apply formulas.
+ return value.ToString();
+ }
}
\ No newline at end of file
diff --git a/Models/Device.cs b/Models/Device.cs
index edda0e3..bf99298 100644
--- a/Models/Device.cs
+++ b/Models/Device.cs
@@ -2,6 +2,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
using PMSWPF.Enums;
using SqlSugar;
using SqlSugar.DbConvert;
+using S7.Net; // Add this using directive
namespace PMSWPF.Models;
@@ -64,6 +65,24 @@ public partial class Device : ObservableObject
[ObservableProperty]
private int prot;
+ ///
+ /// PLC的CPU类型。
+ ///
+ [ObservableProperty]
+ private CpuType cpuType;
+
+ ///
+ /// PLC的机架号。
+ ///
+ [ObservableProperty]
+ private short rack;
+
+ ///
+ /// PLC的槽号。
+ ///
+ [ObservableProperty]
+ private short slot;
+
///
/// 设备的通信协议类型。
///
diff --git a/PMSWPF.csproj b/PMSWPF.csproj
index 8e7d34a..728ad5f 100644
--- a/PMSWPF.csproj
+++ b/PMSWPF.csproj
@@ -23,6 +23,7 @@
+
diff --git a/Services/S7BackgroundService.cs b/Services/S7BackgroundService.cs
new file mode 100644
index 0000000..8d2af46
--- /dev/null
+++ b/Services/S7BackgroundService.cs
@@ -0,0 +1,184 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using S7.Net;
+using PMSWPF.Models;
+using PMSWPF.Enums;
+using PMSWPF.Helper;
+
+namespace PMSWPF.Services
+{
+ public class S7BackgroundService : BackgroundService
+ {
+ private readonly ILogger _logger;
+ private readonly DataServices _dataServices;
+ private readonly Dictionary _s7PlcClients = new Dictionary();
+ private readonly TimeSpan _pollingInterval = TimeSpan.FromSeconds(1); // 轮询间隔
+
+ public S7BackgroundService(ILogger logger, DataServices dataServices)
+ {
+ _logger = logger;
+ _dataServices = dataServices;
+ _dataServices.OnDeviceListChanged += HandleDeviceListChanged;
+ }
+
+ private void HandleDeviceListChanged(List devices)
+ {
+ // 当设备列表变化时,更新PLC客户端
+ // 这里需要更复杂的逻辑来处理连接的关闭和新连接的建立
+ // 简单起见,这里只做日志记录
+ _logger.LogInformation("Device list changed. S7 clients might need to be reinitialized.");
+ }
+
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ _logger.LogInformation("S7 Background Service is starting.");
+
+ stoppingToken.Register(() => _logger.LogInformation("S7 Background Service is stopping."));
+
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ _logger.LogDebug("S7 Background Service is doing background work.");
+
+ await PollS7Devices(stoppingToken);
+
+ try
+ {
+ await Task.Delay(_pollingInterval, stoppingToken);
+ }
+ catch (TaskCanceledException)
+ {
+ // When the stopping token is canceled, a TaskCanceledException is thrown.
+ // We should catch it to exit gracefully.
+ }
+ }
+
+ _logger.LogInformation("S7 Background Service has stopped.");
+ }
+
+ private async Task PollS7Devices(CancellationToken stoppingToken)
+ {
+ var s7Devices = _dataServices.Devices?.Where(d => d.ProtocolType == ProtocolType.S7 && d.IsActive).ToList();
+
+ if (s7Devices == null || !s7Devices.Any())
+ {
+ _logger.LogDebug("No active S7 devices found to poll.");
+ return;
+ }
+
+ foreach (var device in s7Devices)
+ {
+ if (stoppingToken.IsCancellationRequested) return;
+
+ if (!_s7PlcClients.ContainsKey(device.Id))
+ {
+ // Initialize Plc client for the device
+ 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})");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, $"Failed to connect to S7 PLC: {device.Name} ({device.Ip})");
+ continue;
+ }
+ }
+
+ 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 s7Variables = device.VariableTables
+ ?.SelectMany(vt => vt.DataVariables)
+ .Where(vd => vd.ProtocolType == ProtocolType.S7 && vd.IsActive)
+ .ToList();
+
+ 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}");
+ }
+ }
+ }
+
+ public override async Task StopAsync(CancellationToken stoppingToken)
+ {
+ _logger.LogInformation("S7 Background Service is stopping.");
+
+ // Close all active PLC connections
+ foreach (var plcClient in _s7PlcClients.Values)
+ {
+ if (plcClient.IsConnected)
+ {
+ plcClient.Close();
+ _logger.LogInformation($"Closed S7 PLC connection: {plcClient.IP}");
+ }
+ }
+ _s7PlcClients.Clear();
+
+ await base.StopAsync(stoppingToken);
+ }
+ }
+}
diff --git a/ViewModels/Dialogs/DeviceDialogViewModel.cs b/ViewModels/Dialogs/DeviceDialogViewModel.cs
index 278e143..e7c6958 100644
--- a/ViewModels/Dialogs/DeviceDialogViewModel.cs
+++ b/ViewModels/Dialogs/DeviceDialogViewModel.cs
@@ -1,6 +1,7 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using PMSWPF.Models;
+using S7.Net; // Add this using directive
namespace PMSWPF.ViewModels.Dialogs;
@@ -8,6 +9,13 @@ public partial class DeviceDialogViewModel : ObservableObject
{
[ObservableProperty]
private Device _device;
+ partial void OnDeviceChanged(Device value)
+ {
+ if (value != null)
+ {
+ System.Diagnostics.Debug.WriteLine($"Device ProtocolType changed to: {value.ProtocolType}");
+ }
+ }
[ObservableProperty] private string title ;
[ObservableProperty] private string primaryButContent ;
@@ -17,6 +25,9 @@ public partial class DeviceDialogViewModel : ObservableObject
_device = device;
}
+ // Add a property to expose CpuType enum values for ComboBox
+ public Array CpuTypes => Enum.GetValues(typeof(CpuType));
+
[RelayCommand]
public void AddDevice()
diff --git a/Views/Dialogs/DeviceDialog.xaml b/Views/Dialogs/DeviceDialog.xaml
index 9e01efc..3a80ae4 100644
--- a/Views/Dialogs/DeviceDialog.xaml
+++ b/Views/Dialogs/DeviceDialog.xaml
@@ -97,7 +97,38 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+