using EasyModbus; using GSModbus.Config; using GSModbus.Database; using System; using System.Threading; using System.Threading.Tasks; namespace GSModbus { /// /// 通用Modbus TCP通信管理器 - 支持配置驱动的动态通信 /// public class UniversalModbusManager : IDisposable { #region 私有字段 /// /// Modbus配置 /// private ModbusConfiguration? _configuration; /// /// EasyModbus TCP客户端实例 /// private ModbusClient? _modbusClient; /// /// 心跳定时器 /// private System.Timers.Timer? _heartbeatTimer; /// /// 数据轮询定时器 /// private System.Timers.Timer? _dataPollingTimer; /// /// 心跳包状态 /// private byte _heartbeatState = 0; /// /// 连接状态标志 /// private bool _isConnected = false; /// /// 取消令牌源 /// private CancellationTokenSource? _cancellationTokenSource; /// /// 重试计数器 /// private int _retryCount = 0; /// /// 数据库管理器 /// private DatabaseManager? _databaseManager; #endregion #region 事件定义 /// /// 连接状态改变事件 /// public event EventHandler? ConnectionStatusChanged; /// /// 接收到动态数据事件 /// public event EventHandler? DataReceived; /// /// 错误发生事件 /// public event EventHandler? ErrorOccurred; /// /// 通信统计更新事件 /// public event EventHandler? StatsUpdated; /// /// 数据库日志事件 /// public event EventHandler? DatabaseLogOccurred; /// /// 数据库错误事件 /// public event EventHandler? DatabaseErrorOccurred; #endregion #region 公共属性 /// /// 获取当前连接状态 /// public bool IsConnected => _isConnected; /// /// 获取当前配置 /// public ModbusConfiguration? Configuration => _configuration; /// /// 通信统计信息 /// public CommunicationStats Stats { get; private set; } = new(); #endregion #region 构造函数 /// /// 初始化通用Modbus管理器 /// /// Modbus配置 public UniversalModbusManager(ModbusConfiguration? configuration = null) { _cancellationTokenSource = new CancellationTokenSource(); if (configuration != null) { SetConfiguration(configuration); } } #endregion #region 配置管理方法 /// /// 设置新的配置 /// /// 新配置 public void SetConfiguration(ModbusConfiguration configuration) { // 如果当前已连接,需要先断开 if (_isConnected) { LogWarning("配置更改时自动断开连接"); Disconnect(); } // 释放现有数据库管理器 _databaseManager?.Dispose(); _databaseManager = null; _configuration = configuration; // 释放现有资源 DisposeTimers(); // 初始化新配置 InitializeWithConfiguration(); LogInfo($"配置已更新: {configuration.ProjectName}"); } /// /// 根据配置初始化组件 /// private void InitializeWithConfiguration() { if (_configuration == null) { LogError("无法初始化:配置为空"); return; } try { // 创建Modbus客户端 _modbusClient = new ModbusClient(_configuration.Connection.IpAddress, _configuration.Connection.Port); _modbusClient.ConnectionTimeout = _configuration.Connection.ConnectionTimeoutMs; // 初始化定时器 InitializeTimers(); // 初始化数据库管理器 InitializeDatabaseManager(); LogInfo($"已初始化Modbus客户端: {_configuration.Connection.IpAddress}:{_configuration.Connection.Port}"); } catch (Exception ex) { LogError($"初始化配置时发生错误: {ex.Message}"); throw; } } /// /// 初始化定时器 /// private void InitializeTimers() { if (_configuration == null) return; // 心跳定时器 _heartbeatTimer = new System.Timers.Timer(_configuration.Communication.HeartbeatIntervalMs); _heartbeatTimer.Elapsed += OnHeartbeatTimerElapsed; _heartbeatTimer.AutoReset = true; // 数据轮询定时器 _dataPollingTimer = new System.Timers.Timer(_configuration.Communication.DataPollingIntervalMs); _dataPollingTimer.Elapsed += OnDataPollingTimerElapsed; _dataPollingTimer.AutoReset = true; LogInfo($"定时器已初始化 - 心跳:{_configuration.Communication.HeartbeatIntervalMs}ms, 轮询:{_configuration.Communication.DataPollingIntervalMs}ms"); } /// /// 初始化数据库管理器 /// private void InitializeDatabaseManager() { if (_configuration?.Database?.Enabled == true) { try { _databaseManager = new DatabaseManager( _configuration.Database, _configuration.ProjectName, _configuration.Connection.IpAddress ); // 订阅数据库事件 _databaseManager.DatabaseLogOccurred += (sender, message) => DatabaseLogOccurred?.Invoke(this, message); _databaseManager.DatabaseErrorOccurred += (sender, error) => DatabaseErrorOccurred?.Invoke(this, error); LogInfo("数据库管理器初始化成功"); } catch (Exception ex) { LogError($"初始化数据库管理器失败: {ex.Message}"); } } else { LogInfo("数据库功能未启用"); } } #endregion #region 连接管理方法 /// /// 连接到PLC服务器 /// /// 连接是否成功 public async Task ConnectAsync() { if (_configuration == null) { LogError("无法连接:配置未设置"); return false; } if (_modbusClient == null) { LogError("无法连接:Modbus客户端未初始化"); return false; } try { LogInfo($"正在连接到PLC服务器 {_configuration.Connection.IpAddress}:{_configuration.Connection.Port}..."); // 尝试连接到PLC await Task.Run(() => _modbusClient.Connect()); _isConnected = true; _retryCount = 0; Stats.ConnectionAttempts++; Stats.LastConnectedTime = DateTime.Now; LogInfo("成功连接到PLC服务器"); // 记录数据库日志 await _databaseManager?.LogCommunicationAsync(EventTypes.Connected, "成功连接到PLC服务器", true)!; // 启动定时器 _heartbeatTimer?.Start(); _dataPollingTimer?.Start(); // 触发连接状态改变事件 ConnectionStatusChanged?.Invoke(this, true); return true; } catch (Exception ex) { _isConnected = false; Stats.ConnectionFailures++; LogError($"连接PLC失败: {ex.Message}"); // 记录数据库错误日志 await _databaseManager?.LogErrorAsync("ConnectionError", $"连接PLC失败: {ex.Message}", ex, ErrorSeverity.High, _retryCount)!; await _databaseManager?.LogCommunicationAsync(EventTypes.ConnectionError, ex.Message, false)!; ConnectionStatusChanged?.Invoke(this, false); // 如果启用自动重连,安排重连 if (_configuration.Communication.EnableAutoReconnect && _retryCount < _configuration.Communication.MaxRetryCount) { ScheduleReconnect(); } return false; } } /// /// 断开与PLC的连接 /// public async void Disconnect() { try { // 停止定时器 _heartbeatTimer?.Stop(); _dataPollingTimer?.Stop(); // 断开Modbus连接 if (_modbusClient?.Connected == true) { _modbusClient.Disconnect(); } _isConnected = false; Stats.LastDisconnectedTime = DateTime.Now; LogInfo("已断开与PLC的连接"); // 记录数据库日志 await _databaseManager?.LogCommunicationAsync(EventTypes.Disconnected, "手动断开PLC连接", true)!; // 触发连接状态改变事件 ConnectionStatusChanged?.Invoke(this, false); } catch (Exception ex) { LogError($"断开连接时发生错误: {ex.Message}"); } } /// /// 安排自动重连 /// private void ScheduleReconnect() { if (_configuration == null || _cancellationTokenSource?.Token.IsCancellationRequested == true) return; _retryCount++; var delay = _configuration.Communication.ReconnectDelayMs; LogWarning($"将在 {delay}ms 后尝试第 {_retryCount} 次重连"); Task.Delay(delay, _cancellationTokenSource?.Token ?? CancellationToken.None).ContinueWith(async _ => { if (!(_cancellationTokenSource?.Token.IsCancellationRequested ?? true)) { await ConnectAsync(); } }, _cancellationTokenSource?.Token ?? CancellationToken.None); } #endregion #region 定时器事件处理 /// /// 心跳定时器事件 /// private async void OnHeartbeatTimerElapsed(object? sender, System.Timers.ElapsedEventArgs e) { if (!_isConnected || _configuration == null || _modbusClient == null || _cancellationTokenSource?.Token.IsCancellationRequested == true) return; try { // 在0和1之间交替心跳状态 _heartbeatState = (byte)(_heartbeatState == 0 ? 1 : 0); // 向PLC发送心跳包 var heartbeatAddress = _configuration.OutputAddresses.HeartbeatAddress.Address; await Task.Run(() => _modbusClient.WriteSingleRegister(heartbeatAddress, _heartbeatState)); Stats.HeartbeatsSent++; LogDebug($"发送心跳包到地址 {heartbeatAddress}: {_heartbeatState}"); } catch (Exception ex) { Stats.CommunicationErrors++; LogError($"发送心跳包失败: {ex.Message}"); await HandleConnectionLoss(); } } /// /// 数据轮询定时器事件 /// private async void OnDataPollingTimerElapsed(object? sender, System.Timers.ElapsedEventArgs e) { if (!_isConnected || _configuration == null || _modbusClient == null || _cancellationTokenSource?.Token.IsCancellationRequested == true) return; try { // 读取PLC数据 var dynamicData = await ReadDynamicDataAsync(); if (dynamicData != null) { // 检查是否需要发送确认信号 var dataReadySignal = GetDataReadySignal(dynamicData); if (dataReadySignal == 1) { await SendDataReadConfirmationAsync(); // 保存到数据库 await _databaseManager?.SaveModbusDataAsync(dynamicData)!; await _databaseManager?.LogCommunicationAsync(EventTypes.DataReceived, "成功读取PLC数据", true)!; // 触发数据接收事件 DataReceived?.Invoke(this, dynamicData); Stats.SuccessfulReads++; LogDebug("成功读取PLC数据并发送确认信号"); } } } catch (Exception ex) { Stats.CommunicationErrors++; LogError($"读取PLC数据失败: {ex.Message}"); // 记录数据库错误日志 await _databaseManager?.LogErrorAsync("DataReadError", ex.Message, ex, ErrorSeverity.Medium)!; await _databaseManager?.LogCommunicationAsync(EventTypes.DataReadError, ex.Message, false)!; } finally { // 更新统计信息 StatsUpdated?.Invoke(this, Stats); } } #endregion #region 数据读取方法 /// /// 读取动态配置的数据 /// /// 动态数据对象 private async Task ReadDynamicDataAsync() { if (_configuration?.InputAddresses == null || _modbusClient == null) return null; var dynamicData = new DynamicModbusData { SourceConfiguration = _configuration, ReadTime = DateTime.Now }; try { // 读取控制信号 await ReadFieldGroup(dynamicData, _configuration.InputAddresses.ControlSignals, "控制信号"); // 读取产品数据 await ReadFieldGroup(dynamicData, _configuration.InputAddresses.ProductData, "产品数据"); // 读取测量数据 await ReadFieldGroup(dynamicData, _configuration.InputAddresses.MeasurementData, "测量数据"); return dynamicData; } catch (Exception ex) { LogError($"读取动态数据时发生错误: {ex.Message}"); return null; } } /// /// 读取字段组数据 /// /// 动态数据容器 /// 字段组配置 /// 组名称(用于日志) private async Task ReadFieldGroup(DynamicModbusData dynamicData, Dictionary fieldGroup, string groupName) { if (_modbusClient == null) { LogWarning($"Modbus客户端为null,无法读取 {groupName}"); return; } if (!_isConnected) { LogWarning($"Modbus未连接,无法读取 {groupName}"); return; } if (fieldGroup == null || fieldGroup.Count == 0) { LogDebug($"{groupName} 没有配置字段,跳过读取"); return; } foreach (var kvp in fieldGroup) { var fieldName = kvp.Key; var fieldConfig = kvp.Value; try { // 检查Modbus客户端状态 if (_modbusClient == null || !_modbusClient.Connected) { LogWarning($"Modbus客户端未连接,跳过字段 '{fieldName}'"); continue; } // 读取原始寄存器数据 var registers = await Task.Run(() => _modbusClient.ReadHoldingRegisters(fieldConfig.Address, fieldConfig.Length)); // 验证寄存器读取结果的有效性 ValidateRegisterData(fieldName, fieldConfig, registers); // 检查读取结果是否为null if (registers == null) { LogWarning($"字段 '{fieldName}' 读取结果为null,地址: {fieldConfig.Address}, 长度: {fieldConfig.Length}"); continue; } // 记录原始寄存器数据用于调试 LogDebug($"[RAW-DATA] 字段 '{fieldName}' 原始寄存器: [{string.Join(", ", registers)}] (十六进制: [{string.Join(", ", registers.Select(r => $"0x{r:X4}"))}])"); // 详细记录每个寄存器的字节分解 for (int i = 0; i < registers.Length; i++) { var reg = registers[i]; var highByte = (byte)((reg >> 8) & 0xFF); var lowByte = (byte)(reg & 0xFF); LogDebug($"[RAW-DATA] 寄存器{i}: {reg} (0x{reg:X4}) → 高字节:{highByte}('{(char)highByte}') 低字节:{lowByte}('{(char)lowByte}')"); } // 验证数据长度 if (!ModbusDataParser.ValidateRegisterLength(registers, fieldConfig)) { LogWarning($"字段 '{fieldName}' 的数据长度不足:期望{fieldConfig.Length},实际{registers?.Length ?? 0}"); continue; } // 解析数据 var parsedValue = ModbusDataParser.ParseFieldData(registers, fieldConfig); // 存储到动态数据容器 dynamicData.SetFieldData(fieldName, parsedValue, registers); LogDebug($"成功读取字段 '{fieldName}': {parsedValue}"); } catch (Exception ex) { LogError($"读取字段 '{fieldName}' 失败: {ex.Message}"); // 记录详细错误信息 await _databaseManager?.LogErrorAsync( "FieldReadError", $"读取字段 '{fieldName}' 失败: {ex.Message}", ex, ErrorSeverity.Medium)!; // 继续读取其他字段 } } } /// /// 获取数据就绪信号 /// private byte GetDataReadySignal(DynamicModbusData data) { // 查找数据就绪信号字段 var controlSignals = _configuration?.InputAddresses.ControlSignals; if (controlSignals != null) { foreach (var kvp in controlSignals) { var fieldConfig = kvp.Value; if (fieldConfig.Description?.Contains("数据") == true && fieldConfig.Description?.Contains("就绪") == true) { return data.GetByte(kvp.Key); } } } // 如果没有找到明确的数据就绪信号,返回1(假设数据总是就绪) return 1; } #endregion #region 数据验证方法 /// /// 验证寄存器数据的有效性 /// /// 字段名 /// 字段配置 /// 读取的寄存器数据 private void ValidateRegisterData(string fieldName, DataField fieldConfig, int[]? registers) { if (registers == null) { LogWarning($"[VALIDATE] 字段 '{fieldName}' 寄存器数据为null"); return; } LogDebug($"[VALIDATE] 字段 '{fieldName}' 原始数据验证:"); LogDebug($"[VALIDATE] - 地址: {fieldConfig.Address}, 期望长度: {fieldConfig.Length}, 实际长度: {registers.Length}"); LogDebug($"[VALIDATE] - 数据类型: {fieldConfig.DataType}"); // 检查数据长度 if (registers.Length != fieldConfig.Length) { LogWarning($"[VALIDATE] 字段 '{fieldName}' 数据长度不匹配: 期望{fieldConfig.Length}, 实际{registers.Length}"); } // 检查是否包含有效数据 var hasNonZeroData = registers.Any(r => r != 0); if (!hasNonZeroData) { LogWarning($"[VALIDATE] 字段 '{fieldName}' 所有寄存器都为0,可能是无效数据"); } // 检查数据范围(16位有符号整数范围) var outOfRangeCount = registers.Count(r => r < -32768 || r > 32767); if (outOfRangeCount > 0) { LogWarning($"[VALIDATE] 字段 '{fieldName}' 有{outOfRangeCount}个寄存器值超出16位有符号整数范围"); } // 特殊数据模式检测 DetectSpecialDataPatterns(fieldName, registers); } /// /// 检测特殊的数据模式 /// /// 字段名 /// 寄存器数据 private void DetectSpecialDataPatterns(string fieldName, int[] registers) { // 检测全FF模式(通信错误) if (registers.All(r => r == -1 || r == 0xFFFF)) { LogWarning($"[PATTERN] 字段 '{fieldName}' 检测到全FF模式,可能是通信错误"); } // 检测常见的错误码模式 if (registers.Length >= 2) { var combined = ((long)((uint)registers[0] & 0xFFFF) << 16) | ((uint)registers[1] & 0xFFFF); if (combined == 0xE2400001) { LogInfo($"[PATTERN] 字段 '{fieldName}' 检测到已知的无效数据标识 0xE2400001"); } } // 检测ASCII字符模式(用于字符串和时间戳字段) if (registers.Any(r => IsLikelyAsciiData(r))) { LogDebug($"[PATTERN] 字段 '{fieldName}' 包含可能的ASCII字符数据"); } } /// /// 检查寄存器值是否可能包含ASCII字符 /// /// 寄存器值 /// 是否可能是ASCII数据 private bool IsLikelyAsciiData(int register) { if (register == 0) return false; var highByte = (byte)((register >> 8) & 0xFF); var lowByte = (byte)(register & 0xFF); // 检查是否为可打印ASCII字符范围(32-126) return (highByte >= 32 && highByte <= 126) || (lowByte >= 32 && lowByte <= 126); } #endregion #region 数据发送方法 /// /// 向PLC发送数据读取确认信号 /// private async Task SendDataReadConfirmationAsync() { if (_configuration == null || _modbusClient == null) return; try { var confirmAddress = _configuration.OutputAddresses.DataConfirmationAddress.Address; await Task.Run(() => _modbusClient.WriteSingleRegister(confirmAddress, 1)); Stats.ConfirmationsSent++; LogDebug($"已向地址 {confirmAddress} 发送数据读取确认信号"); } catch (Exception ex) { LogError($"发送数据确认信号失败: {ex.Message}"); throw; } } #endregion #region 错误处理方法 /// /// 处理连接丢失 /// private Task HandleConnectionLoss() { if (_isConnected) { LogWarning("检测到连接丢失"); Disconnect(); // 如果启用自动重连 if (_configuration?.Communication.EnableAutoReconnect == true && _retryCount < _configuration.Communication.MaxRetryCount) { ScheduleReconnect(); } } return Task.CompletedTask; } #endregion #region 资源清理方法 /// /// 释放定时器资源 /// private void DisposeTimers() { _heartbeatTimer?.Stop(); _heartbeatTimer?.Dispose(); _heartbeatTimer = null; _dataPollingTimer?.Stop(); _dataPollingTimer?.Dispose(); _dataPollingTimer = null; } public void Dispose() { _cancellationTokenSource?.Cancel(); DisposeTimers(); Disconnect(); _modbusClient = null; _databaseManager?.Dispose(); _databaseManager = null; _cancellationTokenSource?.Dispose(); } #endregion #region 日志方法 private void LogInfo(string message) { Console.WriteLine($"[UNIVERSAL-INFO] {DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}"); } private void LogWarning(string message) { Console.WriteLine($"[UNIVERSAL-WARN] {DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}"); } private void LogError(string message) { Console.WriteLine($"[UNIVERSAL-ERROR] {DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}"); ErrorOccurred?.Invoke(this, message); } private void LogDebug(string message) { // 只在Debug模式下输出详细日志 #if DEBUG Console.WriteLine($"[UNIVERSAL-DEBUG] {DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}"); #endif } #endregion } #region 通信统计类 /// /// 通信统计信息 /// public class CommunicationStats { /// /// 连接尝试次数 /// public long ConnectionAttempts { get; set; } /// /// 连接失败次数 /// public long ConnectionFailures { get; set; } /// /// 发送的心跳包数量 /// public long HeartbeatsSent { get; set; } /// /// 发送的确认信号数量 /// public long ConfirmationsSent { get; set; } /// /// 成功读取次数 /// public long SuccessfulReads { get; set; } /// /// 通信错误次数 /// public long CommunicationErrors { get; set; } /// /// 最后连接时间 /// public DateTime? LastConnectedTime { get; set; } /// /// 最后断开时间 /// public DateTime? LastDisconnectedTime { get; set; } /// /// 获取连接成功率 /// public double ConnectionSuccessRate => ConnectionAttempts > 0 ? (double)(ConnectionAttempts - ConnectionFailures) / ConnectionAttempts * 100 : 0; /// /// 获取连接成功率 /// public double TotalConnectionAttempts => ConnectionAttempts; /// /// 获取成功连接数 /// public double SuccessfulConnections => ConnectionAttempts - ConnectionFailures; /// /// 获取总读取数 /// public double TotalDataReads => SuccessfulReads + CommunicationErrors; /// /// 获取数据读取成功率 /// public double DataReadSuccessRate => TotalDataReads > 0 ? SuccessfulReads / TotalDataReads * 100 : 0; /// /// 获取总错误数 /// public double TotalErrors => CommunicationErrors + ConnectionFailures; /// /// 平均响应时间(毫秒) /// public double AverageResponseTimeMs { get; set; } = 0; /// /// 最大响应时间(毫秒) /// public int MaxResponseTimeMs { get; set; } = 0; /// /// 最小响应时间(毫秒) /// public int MinResponseTimeMs { get; set; } = int.MaxValue; /// /// 获取统计摘要字符串 /// public string GetSummary() { return $"连接: {ConnectionAttempts - ConnectionFailures}/{ConnectionAttempts} " + $"({ConnectionSuccessRate:F1}%), " + $"读取: {SuccessfulReads}, " + $"错误: {CommunicationErrors}"; } } #endregion }