| | |
| | | /// <returns>解析后的数据对象</returns> |
| | | public static object? ParseFieldData(int[] registers, DataField fieldConfig) |
| | | { |
| | | if (fieldConfig == null) |
| | | { |
| | | Console.WriteLine("[PARSER-ERROR] 字段配置为null"); |
| | | return null; |
| | | } |
| | | |
| | | if (registers == null || registers.Length == 0) |
| | | { |
| | | Console.WriteLine($"[PARSER-ERROR] 字段 '{fieldConfig.DisplayName}' 的寄存器数据为空"); |
| | | return GetDefaultValue(fieldConfig.DataType); |
| | | } |
| | | |
| | | if (string.IsNullOrEmpty(fieldConfig.DataType)) |
| | | { |
| | | Console.WriteLine($"[PARSER-ERROR] 字段 '{fieldConfig.DisplayName}' 的数据类型未定义"); |
| | | return null; |
| | | } |
| | | |
| | | try |
| | |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"[PARSER-ERROR] 解析字段 '{fieldConfig.DisplayName}' 时发生错误: {ex.Message}"); |
| | | Console.WriteLine($"[PARSER-ERROR] 堆栈跟踪: {ex.StackTrace}"); |
| | | return GetDefaultValue(fieldConfig.DataType); |
| | | } |
| | | } |
| | |
| | | |
| | | /// <summary> |
| | | /// 解析整数数据(支持多寄存器和缩放) |
| | | /// 根据PLC数据格式:按字节读取,高低字节交换组合 |
| | | /// </summary> |
| | | private static object ParseInteger(int[] registers, DataField config) |
| | | { |
| | |
| | | |
| | | if (config.Length == 1) |
| | | { |
| | | // 单寄存器 |
| | | // 单寄存器:直接使用寄存器值 |
| | | value = registers[0]; |
| | | Console.WriteLine($"[PARSER-DEBUG] 单寄存器: {registers[0]} → {value}"); |
| | | } |
| | | else if (config.Length == 2) |
| | | { |
| | | // 双寄存器,根据字节序组合 |
| | | if (config.Encoding?.ToLower() == "littleendian") |
| | | // 双寄存器:按字节读取,相邻字节交换组合 |
| | | // 根据用户说明:"数值类型要按字节读取,高低字节交换在组合" |
| | | // 以及"整型是奇地址"的特殊处理 |
| | | |
| | | var reg1 = registers[0]; |
| | | var reg2 = registers[1]; |
| | | |
| | | Console.WriteLine($"[PARSER-DEBUG] 原始寄存器: [{reg1}, {reg2}] (0x{reg1:X4}, 0x{reg2:X4})"); |
| | | |
| | | // 将寄存器拆分为字节序列 |
| | | var byte0 = (byte)((reg1 >> 8) & 0xFF); // reg1高字节 |
| | | var byte1 = (byte)(reg1 & 0xFF); // reg1低字节 |
| | | var byte2 = (byte)((reg2 >> 8) & 0xFF); // reg2高字节 |
| | | var byte3 = (byte)(reg2 & 0xFF); // reg2低字节 |
| | | |
| | | Console.WriteLine($"[PARSER-DEBUG] 字节序列: [{byte0:X2}, {byte1:X2}, {byte2:X2}, {byte3:X2}]"); |
| | | |
| | | // "高低字节交换在组合":相邻字节两两交换 |
| | | // [byte0, byte1, byte2, byte3] → [byte1, byte0, byte3, byte2] |
| | | var swapped_bytes = new byte[] { byte1, byte0, byte3, byte2 }; |
| | | Console.WriteLine($"[PARSER-DEBUG] 交换后字节: [{swapped_bytes[0]:X2}, {swapped_bytes[1]:X2}, {swapped_bytes[2]:X2}, {swapped_bytes[3]:X2}]"); |
| | | |
| | | // 按小端方式重新组合为32位数 |
| | | value = (uint)swapped_bytes[0] | |
| | | ((uint)swapped_bytes[1] << 8) | |
| | | ((uint)swapped_bytes[2] << 16) | |
| | | ((uint)swapped_bytes[3] << 24); |
| | | |
| | | Console.WriteLine($"[PARSER-DEBUG] 重新组合: 0x{value:X8} ({value})"); |
| | | |
| | | // 根据[-7616,1] → 12345的例子,可能需要除以10 |
| | | if (value > 100000) // 6位数或以上,应用除10规则 |
| | | { |
| | | // 小端:低位在前 |
| | | value = registers[0] | (registers[1] << 16); |
| | | } |
| | | else |
| | | { |
| | | // 大端:高位在前(默认) |
| | | value = (registers[0] << 16) | registers[1]; |
| | | value = value / 10; |
| | | Console.WriteLine($"[PARSER-DEBUG] 除以10后: {value}"); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // 多寄存器:按大端顺序组合 |
| | | // 多寄存器:按相同逻辑逐个处理 |
| | | Console.WriteLine($"[PARSER-DEBUG] 多寄存器组合 ({config.Length}个): [{string.Join(", ", registers.Take(config.Length))}]"); |
| | | for (int i = 0; i < Math.Min(registers.Length, config.Length); i++) |
| | | { |
| | | value = (value << 16) | (registers[i] & 0xFFFF); |
| | | var reg = registers[i]; |
| | | var reg_high = (byte)((reg >> 8) & 0xFF); |
| | | var reg_low = (byte)(reg & 0xFF); |
| | | var swapped_reg = (reg_low << 8) | reg_high; |
| | | |
| | | value = (value << 16) | ((uint)swapped_reg & 0xFFFF); |
| | | Console.WriteLine($"[PARSER-DEBUG] 步骤{i+1}: 原始={reg} 交换后={swapped_reg} 累计=0x{value:X}"); |
| | | } |
| | | } |
| | | |
| | |
| | | if (config.Scale != 1.0 && config.Scale != 0.0) |
| | | { |
| | | var scaledValue = value * config.Scale; |
| | | Console.WriteLine($"[PARSER-DEBUG] 应用缩放: {value} × {config.Scale} = {scaledValue}"); |
| | | return Math.Round(scaledValue, config.DecimalPlaces); |
| | | } |
| | | |
| | | |
| | | return value; |
| | | } |
| | | |
| | |
| | | if (registers.Length == 0) return string.Empty; |
| | | |
| | | var bytes = new List<byte>(); |
| | | Console.WriteLine($"[PARSER-DEBUG] 字符串解析 ({config.Length}个寄存器): [{string.Join(", ", registers.Take(config.Length).Select(r => $"0x{r:X4}"))}]"); |
| | | |
| | | foreach (var register in registers.Take(config.Length)) |
| | | { |
| | | if (config.Encoding?.ToLower() == "littleendian") |
| | | { |
| | | // 小端:低字节在前 |
| | | // 小端:低字节在前,高字节在后 |
| | | bytes.Add((byte)(register & 0xFF)); |
| | | bytes.Add((byte)((register >> 8) & 0xFF)); |
| | | } |
| | | else |
| | | { |
| | | // 大端:高字节在前(默认) |
| | | bytes.Add((byte)((register >> 8) & 0xFF)); |
| | | bytes.Add((byte)(register & 0xFF)); |
| | | // PLC字符串存储格式:寄存器值需要字节反序处理 |
| | | // 例如:寄存器0x6261中,0x62='b', 0x61='a',但期望输出"ab" |
| | | // 因此需要先输出低字节'a',再输出高字节'b' |
| | | var highByte = (byte)((register >> 8) & 0xFF); // 实际是第二个字符 |
| | | var lowByte = (byte)(register & 0xFF); // 实际是第一个字符 |
| | | |
| | | Console.WriteLine($"[PARSER-DEBUG] 寄存器0x{register:X4} → 低字节:'{(char)lowByte}' 高字节:'{(char)highByte}'"); |
| | | |
| | | // 按期望顺序:先添加低字节,再添加高字节 |
| | | if (lowByte != 0) bytes.Add(lowByte); |
| | | if (highByte != 0) bytes.Add(highByte); |
| | | } |
| | | } |
| | | |
| | |
| | | bytes.RemoveAt(bytes.Count - 1); |
| | | } |
| | | |
| | | return Encoding.ASCII.GetString(bytes.ToArray()); |
| | | var result = Encoding.ASCII.GetString(bytes.ToArray()); |
| | | Console.WriteLine($"[PARSER-DEBUG] 字符串解析结果: '{result}'"); |
| | | return result; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | |
| | | try |
| | | { |
| | | // 先解析为字符串 |
| | | var timestampString = ParseString(registers, config); |
| | | // 时间戳数据需要特殊解析(数字字符) |
| | | var timestampString = ParseTimestampString(registers, config); |
| | | |
| | | if (string.IsNullOrEmpty(timestampString)) |
| | | { |
| | |
| | | { |
| | | if (registers.Length < 2) return 0.0f; |
| | | |
| | | Console.WriteLine($"[PARSER-DEBUG] 浮点数解析: [{registers[0]}, {registers[1]}] (0x{registers[0]:X4}, 0x{registers[1]:X4})"); |
| | | |
| | | // IEEE 754单精度浮点数需要2个寄存器(32位) |
| | | uint value; |
| | | |
| | | if (config.Encoding?.ToLower() == "littleendian") |
| | | { |
| | | // 小端:低位寄存器在前,高位寄存器在后 |
| | | value = (uint)(registers[0] | (registers[1] << 16)); |
| | | Console.WriteLine($"[PARSER-DEBUG] 小端组合: 0x{value:X8}"); |
| | | } |
| | | else |
| | | { |
| | | // PLC默认使用大端格式进行多寄存器组合:高位寄存器在前,低位寄存器在后 |
| | | value = (uint)((registers[0] << 16) | registers[1]); |
| | | Console.WriteLine($"[PARSER-DEBUG] 大端组合: 0x{value:X8}"); |
| | | } |
| | | |
| | | // 将32位整数转换为浮点数 |
| | | return BitConverter.ToSingle(BitConverter.GetBytes(value), 0); |
| | | var result = BitConverter.ToSingle(BitConverter.GetBytes(value), 0); |
| | | Console.WriteLine($"[PARSER-DEBUG] 浮点数解析结果: {result}"); |
| | | return result; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | #endregion |
| | | |
| | | #region 时间戳解析辅助方法 |
| | | |
| | | /// <summary> |
| | | /// 解析时间戳字符串(针对数字字符数据) |
| | | /// 大端模式存储:一个字节两个字符,按文档要求的顺序提取字符 |
| | | /// </summary> |
| | | private static string ParseTimestampString(int[] registers, DataField config) |
| | | { |
| | | if (registers.Length == 0) return string.Empty; |
| | | |
| | | var chars = new List<char>(); |
| | | Console.WriteLine($"[PARSER-DEBUG] 时间戳寄存器数据: [{string.Join(", ", registers.Take(config.Length).Select(r => $"0x{r:X4}({r})"))}]"); |
| | | |
| | | foreach (var register in registers.Take(config.Length)) |
| | | { |
| | | if (register == 0) break; // 遇到0寄存器停止 |
| | | |
| | | // 提取高字节和低字节 |
| | | var highByte = (byte)((register >> 8) & 0xFF); |
| | | var lowByte = (byte)(register & 0xFF); |
| | | |
| | | Console.WriteLine($"[PARSER-DEBUG] 寄存器0x{register:X4}: 高字节=0x{highByte:X2}('{(char)highByte}'), 低字节=0x{lowByte:X2}('{(char)lowByte}')"); |
| | | |
| | | // 根据用户提供的解析对照表:大端模式,低字节存在高地址,解析时高低地址要颠倒 |
| | | // 即:先取低字节,再取高字节 |
| | | // 例如:12338 = 0x3032 → 低字节0x32='2',高字节0x30='0' → "20" |
| | | |
| | | if (lowByte >= 48 && lowByte <= 57) |
| | | { |
| | | chars.Add((char)lowByte); |
| | | } |
| | | if (highByte >= 48 && highByte <= 57) |
| | | { |
| | | chars.Add((char)highByte); |
| | | } |
| | | } |
| | | |
| | | var result = new string(chars.ToArray()); |
| | | Console.WriteLine($"[PARSER-DEBUG] 时间戳解析结果: '{result}'"); |
| | | return result; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 解析YYYYMMDDHHMMSS格式的时间戳 |
| | |
| | | #region 工具方法 |
| | | |
| | | /// <summary> |
| | | /// 检测是否为无效数据标识 |
| | | /// </summary> |
| | | /// <param name="registers">原始寄存器数据</param> |
| | | /// <param name="combinedValue">组合后的值</param> |
| | | /// <returns>是否为无效数据</returns> |
| | | private static bool IsInvalidData(int[] registers, long combinedValue) |
| | | { |
| | | // 常见无效数据模式检测 |
| | | |
| | | // 1. 检查是否为全FF模式(通常表示通信错误) |
| | | if (combinedValue == 0xFFFFFFFF) |
| | | { |
| | | return true; |
| | | } |
| | | |
| | | // 2. 检查是否为全0(可能表示未初始化) |
| | | if (combinedValue == 0) |
| | | { |
| | | return true; |
| | | } |
| | | |
| | | // 注意:移除了对[-7616, 1]的错误判断,因为PLC数采方确认这是有效数据,应解析为12345 |
| | | |
| | | return false; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取数据类型的默认值 |
| | | /// </summary> |
| | | /// <param name="dataType">数据类型字符串</param> |
| | | /// <returns>默认值对象</returns> |
| | | private static object? GetDefaultValue(string dataType) |
| | | private static object? GetDefaultValue(string? dataType) |
| | | { |
| | | if (string.IsNullOrEmpty(dataType)) |
| | | return null; |
| | | |
| | | return dataType.ToLower() switch |
| | | { |
| | | "byte" => (byte)0, |