11
啊鑫
2025-08-01 4df72b089c81867796f33cf64834725070810aa5
config/ModbusDataParser.cs
@@ -75,6 +75,7 @@
        /// <summary>
        /// 解析整数数据(支持多寄存器和缩放)
        /// 根据PLC数据格式:按字节读取,高低字节交换组合
        /// </summary>
        private static object ParseInteger(int[] registers, DataField config)
        {
@@ -84,36 +85,62 @@
            if (config.Length == 1)
            {
                // 单寄存器
                // 单寄存器:直接使用寄存器值
                value = registers[0];
                Console.WriteLine($"[PARSER-DEBUG] 单寄存器: {registers[0]} → {value}");
            }
            else if (config.Length == 2)
            {
                // 双寄存器,根据字节序组合
                if (config.Encoding?.ToLower() == "littleendian")
                {
                    // 小端:低位在前
                    value = (registers[0] & 0xFFFF) | ((long)(registers[1] & 0xFFFF) << 16);
                }
                else
                {
                    // 大端:高位在前(默认)
                    value = ((long)(registers[0] & 0xFFFF) << 16) | (registers[1] & 0xFFFF);
                }
                // 双寄存器:按字节读取,相邻字节交换组合
                // 根据用户说明:"数值类型要按字节读取,高低字节交换在组合"
                // 以及"整型是奇地址"的特殊处理
                
                // 检查是否为无效数据标识(如负值组合)
                if (value == 0xE2400001 || value > 0x7FFFFFFF)
                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规则
                {
                    // 可能是无效数据,返回0或使用特殊标识
                    return 0;
                    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}");
                }
            }
@@ -121,9 +148,10 @@
            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;
        }
@@ -135,23 +163,27 @@
            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
                {
                    // 大端:高字节在前(默认)
                    // 注意:根据实际PLC数据,字节顺序是反的
                    var highByte = (byte)((register >> 8) & 0xFF);
                    var lowByte = (byte)(register & 0xFF);
                    // PLC字符串存储格式:寄存器值需要字节反序处理
                    // 例如:寄存器0x6261中,0x62='b', 0x61='a',但期望输出"ab"
                    // 因此需要先输出低字节'a',再输出高字节'b'
                    var highByte = (byte)((register >> 8) & 0xFF);  // 实际是第二个字符
                    var lowByte = (byte)(register & 0xFF);           // 实际是第一个字符
                    
                    // 先添加低字节,再添加高字节(适应PLC的字节序)
                    Console.WriteLine($"[PARSER-DEBUG] 寄存器0x{register:X4} → 低字节:'{(char)lowByte}' 高字节:'{(char)highByte}'");
                    // 按期望顺序:先添加低字节,再添加高字节
                    if (lowByte != 0) bytes.Add(lowByte);
                    if (highByte != 0) bytes.Add(highByte);
                }
@@ -163,7 +195,9 @@
                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>
@@ -208,20 +242,28 @@
        {
            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>
@@ -241,12 +283,14 @@
        /// <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))
            {
@@ -256,8 +300,12 @@
                var highByte = (byte)((register >> 8) & 0xFF);
                var lowByte = (byte)(register & 0xFF);
                // 按PLC数据格式添加字符(先低字节,后高字节)
                // 检查是否为数字字符(ASCII 48-57, '0'-'9')
                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);
@@ -269,6 +317,7 @@
            }
            var result = new string(chars.ToArray());
            Console.WriteLine($"[PARSER-DEBUG] 时间戳解析结果: '{result}'");
            return result;
        }
@@ -327,6 +376,33 @@
        #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>