using System; using System.Collections.Generic; using System.Threading.Tasks; using DMS.Application.DTOs; using DMS.Application.Models; using DMS.Core.Enums; using DMS.Core.Models; using DMS.Infrastructure.Interfaces; using DMS.Infrastructure.Interfaces.Services; using Microsoft.Extensions.Logging; using S7.Net; using S7.Net.Types; using CpuType = DMS.Core.Enums.CpuType; using DateTime = System.DateTime; namespace DMS.Infrastructure.Services { /// /// S7设备代理类,专门负责与单个S7 PLC进行所有通信 /// public class S7DeviceAgent : IAsyncDisposable { private readonly Device _deviceConfig; private readonly IChannelBus _channelBus; private readonly IMessenger _messenger; private readonly ILogger _logger; private Plc _plc; private bool _isConnected; private readonly Dictionary> _variablesByPollingInterval; private readonly Dictionary _lastPollTimes; public S7DeviceAgent(Device device, IChannelBus channelBus, IMessenger messenger, ILogger logger) { _deviceConfig = device ?? throw new ArgumentNullException(nameof(device)); _channelBus = channelBus ?? throw new ArgumentNullException(nameof(channelBus)); _messenger = messenger ?? throw new ArgumentNullException(nameof(messenger)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _variablesByPollingInterval = new Dictionary>(); _lastPollTimes = new Dictionary(); InitializePlc(); } private void InitializePlc() { try { var cpuType = ConvertCpuType(_deviceConfig.CpuType); _plc = new Plc(cpuType, _deviceConfig.IpAddress, (short)_deviceConfig.Port, _deviceConfig.Rack, _deviceConfig.Slot); _logger.LogInformation($"S7DeviceAgent: 初始化PLC客户端 {_deviceConfig.Name} ({_deviceConfig.IpAddress})"); } catch (Exception ex) { _logger.LogError(ex, $"S7DeviceAgent: 初始化PLC客户端 {_deviceConfig.Name} ({_deviceConfig.IpAddress}) 失败。"); throw; } } private S7.Net.CpuType ConvertCpuType(CpuType cpuType) { return cpuType switch { CpuType.S7200 => S7.Net.CpuType.S7200, CpuType.S7300 => S7.Net.CpuType.S7300, CpuType.S7400 => S7.Net.CpuType.S7400, CpuType.S71200 => S7.Net.CpuType.S71200, CpuType.S71500 => S7.Net.CpuType.S71500, _ => S7.Net.CpuType.S71200 }; } /// /// 连接到PLC /// public async Task ConnectAsync() { try { if (_plc != null && !_isConnected) { await _plc.OpenAsync(); _isConnected = _plc.IsConnected; if (_isConnected) { _logger.LogInformation($"S7DeviceAgent: 成功连接到设备 {_deviceConfig.Name} ({_deviceConfig.IpAddress})"); } else { _logger.LogWarning($"S7DeviceAgent: 无法连接到设备 {_deviceConfig.Name} ({_deviceConfig.IpAddress})"); } } } catch (Exception ex) { _logger.LogError(ex, $"S7DeviceAgent: 连接设备 {_deviceConfig.Name} ({_deviceConfig.IpAddress}) 失败。"); _isConnected = false; } } /// /// 断开PLC连接 /// public async Task DisconnectAsync() { try { if (_plc != null && _isConnected) { _plc.Close(); _isConnected = false; _logger.LogInformation($"S7DeviceAgent: 断开设备 {_deviceConfig.Name} ({_deviceConfig.IpAddress}) 连接"); } } catch (Exception ex) { _logger.LogError(ex, $"S7DeviceAgent: 断开设备 {_deviceConfig.Name} ({_deviceConfig.IpAddress}) 连接失败。"); } } /// /// 更新设备变量配置 /// public void UpdateVariables(List variables) { // 清空现有的变量分组 _variablesByPollingInterval.Clear(); _lastPollTimes.Clear(); // 按轮询间隔分组变量 foreach (var variable in variables) { if (!_variablesByPollingInterval.ContainsKey(variable.PollingInterval)) { _variablesByPollingInterval[variable.PollingInterval] = new List(); _lastPollTimes[variable.PollingInterval] = DateTime.MinValue; } _variablesByPollingInterval[variable.PollingInterval].Add(variable); } _logger.LogInformation($"S7DeviceAgent: 更新设备 {_deviceConfig.Name} 的变量配置,共 {variables.Count} 个变量"); } /// /// 轮询设备变量 /// public async Task PollVariablesAsync() { if (!_isConnected || _plc == null) { _logger.LogWarning($"S7DeviceAgent: 设备 {_deviceConfig.Name} ({_deviceConfig.IpAddress}) 未连接,跳过轮询。"); return; } try { // 按轮询间隔依次轮询 foreach (var kvp in _variablesByPollingInterval) { var pollingInterval = kvp.Key; var variables = kvp.Value; // 检查是否到了轮询时间 if (ShouldPoll(pollingInterval)) { await PollVariablesByLevelAsync(variables, pollingInterval); _lastPollTimes[pollingInterval] = DateTime.Now; } } } catch (Exception ex) { // _messenger.Send(new LogMessage(LogLevel.Error, ex, $"S7DeviceAgent: 设备 {_deviceConfig.Name} ({_deviceConfig.IpAddress}) 轮询错误。")); } } private bool ShouldPoll(int PollingInterval) { // 获取轮询间隔 var interval = GetPollingInterval(PollingInterval); // 检查是否到了轮询时间 if (_lastPollTimes.TryGetValue(PollingInterval, out var lastPollTime)) { return DateTime.Now - lastPollTime >= interval; } return true; } private TimeSpan GetPollingInterval(int pollingInterval) { return pollingInterval switch { 10 => TimeSpan.FromMilliseconds(10), // TenMilliseconds 100 => TimeSpan.FromMilliseconds(100), // HundredMilliseconds 500 => TimeSpan.FromMilliseconds(500), // FiveHundredMilliseconds 1000 => TimeSpan.FromMilliseconds(1000), // OneSecond 5000 => TimeSpan.FromMilliseconds(5000), // FiveSeconds 10000 => TimeSpan.FromMilliseconds(10000), // TenSeconds 20000 => TimeSpan.FromMilliseconds(20000), // TwentySeconds 30000 => TimeSpan.FromMilliseconds(30000), // ThirtySeconds 60000 => TimeSpan.FromMilliseconds(60000), // OneMinute 180000 => TimeSpan.FromMilliseconds(180000), // ThreeMinutes 300000 => TimeSpan.FromMilliseconds(300000), // FiveMinutes 600000 => TimeSpan.FromMilliseconds(600000), // TenMinutes 1800000 => TimeSpan.FromMilliseconds(1800000), // ThirtyMinutes _ => TimeSpan.FromMilliseconds(1000) }; } private async Task PollVariablesByLevelAsync(List variables, int pollingInterval) { // 批量读取变量 var dataItems = new List(); var variableLookup = new Dictionary(); // 用于关联DataItem和Variable foreach (var variable in variables) { try { var dataItem = DataItem.FromAddress(variable.S7Address); dataItems.Add(dataItem); variableLookup[dataItems.Count - 1] = variable; // 记录索引和变量的对应关系 } catch (Exception ex) { _logger.LogError(ex, $"S7DeviceAgent: 解析变量 {variable.Name} ({variable.S7Address}) 地址失败。"); } } if (dataItems.Count == 0) return; try { // 执行批量读取 await _plc.ReadMultipleVarsAsync(dataItems); // 处理读取结果 for (int i = 0; i < dataItems.Count; i++) { if (variableLookup.TryGetValue(i, out var variable)) { var dataItem = dataItems[i]; if (dataItem?.Value != null) { // 更新变量值 variable.DataValue = dataItem.Value.ToString(); variable.DisplayValue = dataItem.Value.ToString(); variable.UpdatedAt = DateTime.Now; // 创建VariableDto对象 var variableDto = new VariableDto { Id = variable.Id, Name = variable.Name, S7Address = variable.S7Address, DataValue = variable.DataValue, DisplayValue = variable.DisplayValue, PollingInterval = variable.PollingInterval, Protocol = variable.Protocol, UpdatedAt = variable.UpdatedAt // 可以根据需要添加其他属性 }; // 发送变量更新消息 var variableContext = new VariableContext(variableDto); // 写入通道总线 await _channelBus.GetWriter("DataProcessingQueue").WriteAsync(variableContext); } } } _logger.LogDebug($"S7DeviceAgent: 设备 {_deviceConfig.Name} 完成 {pollingInterval} 毫秒轮询间隔,共处理 {dataItems.Count} 个变量"); } catch (Exception ex) { _logger.LogError(ex, $"S7DeviceAgent: 设备 {_deviceConfig.Name} 批量读取变量失败。"); } } public async ValueTask DisposeAsync() { await DisconnectAsync(); _plc?.Close(); } } }