| | |
| | | |
| | | LogWarning($"将在 {delay}ms 后尝试第 {_retryCount} 次重连"); |
| | | |
| | | Task.Delay(delay, _cancellationTokenSource.Token).ContinueWith(async _ => |
| | | Task.Delay(delay, _cancellationTokenSource?.Token ?? CancellationToken.None).ContinueWith(async _ => |
| | | { |
| | | if (!_cancellationTokenSource.Token.IsCancellationRequested) |
| | | if (!(_cancellationTokenSource?.Token.IsCancellationRequested ?? true)) |
| | | { |
| | | await ConnectAsync(); |
| | | } |
| | | }, _cancellationTokenSource.Token); |
| | | }, _cancellationTokenSource?.Token ?? CancellationToken.None); |
| | | } |
| | | |
| | | #endregion |
| | |
| | | // 读取原始寄存器数据 |
| | | 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}')"); |
| | | } |
| | | |
| | | // 验证数据长度 |
| | |
| | | |
| | | #endregion |
| | | |
| | | #region 数据验证方法 |
| | | |
| | | /// <summary> |
| | | /// 验证寄存器数据的有效性 |
| | | /// </summary> |
| | | /// <param name="fieldName">字段名</param> |
| | | /// <param name="fieldConfig">字段配置</param> |
| | | /// <param name="registers">读取的寄存器数据</param> |
| | | 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); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 检测特殊的数据模式 |
| | | /// </summary> |
| | | /// <param name="fieldName">字段名</param> |
| | | /// <param name="registers">寄存器数据</param> |
| | | 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字符数据"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 检查寄存器值是否可能包含ASCII字符 |
| | | /// </summary> |
| | | /// <param name="register">寄存器值</param> |
| | | /// <returns>是否可能是ASCII数据</returns> |
| | | 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 数据发送方法 |
| | | |
| | | /// <summary> |
| | |
| | | /// <summary> |
| | | /// 处理连接丢失 |
| | | /// </summary> |
| | | private async Task HandleConnectionLoss() |
| | | private Task HandleConnectionLoss() |
| | | { |
| | | if (_isConnected) |
| | | { |
| | |
| | | ScheduleReconnect(); |
| | | } |
| | | } |
| | | |
| | | return Task.CompletedTask; |
| | | } |
| | | |
| | | #endregion |