Modbus 读写转换

2 阅读2分钟

1. 按ushort多字节写入

NModbus 写寄存器API:

    public void WriteSingleRegister(byte slaveAddress, ushort registerAddress, ushort value)
    {
        WriteSingleRegisterRequestResponse message = new WriteSingleRegisterRequestResponse(slaveAddress, registerAddress, value);
        base.Transport.UnicastMessage<WriteSingleRegisterRequestResponse>(message);
    }


    public void WriteMultipleRegisters(byte slaveAddress, ushort startAddress, ushort[] data)
    {
        ValidateData("data", data, 123);
        WriteMultipleRegistersRequest message = new WriteMultipleRegistersRequest(slaveAddress, startAddress, new RegisterCollection(data));
        base.Transport.UnicastMessage<WriteMultipleRegistersResponse>(message);
    }

根据不同的设备信号解析类型,大致可以分为:

   /// <summary>
   /// 浮点数解析字节序格式
   /// </summary>
   public enum ParseFormat
   {
       /// <summary>
       /// 标准32位:字节顺序 1 2 3 4 = AB CD
       /// </summary>
       ABCD,
       /// <summary>
       /// 字节反转:BA DC
       /// </summary>
       BADC,
       /// <summary>
       /// 高低字交换:CD AB
       /// </summary>
       CDAB,
       /// <summary>
       /// 完全反转:DC BA
       /// </summary>
       DCBA
   }

1.1 float 类型写入

这里主要是把一个 float 转换为对应的 ushort[].

        /// <summary>
        /// 将 float 按照指定字节序转换为两个 ushort
        /// </summary>
        /// <param name="value">float值</param>
        /// <param name="parseFormat">解析格式ABCD/BADC/CDAB/DCBA</param>
        /// <returns>[高16位, 低16位]</returns>
        private static ushort[] ConvertFloatToTwoUShorts(float value, ParseFormat parseFormat)
        {
            byte[] bytes = BitConverter.GetBytes(value);
            ushort[] result = new ushort[2];

            switch (parseFormat)
            {
                case ParseFormat.ABCD:
                    // 标准:AB  CD
                    result[0] = BitConverter.ToUInt16(bytes, 2);
                    result[1] = BitConverter.ToUInt16(bytes, 0);
                    break;

                case ParseFormat.BADC:
                    // 低16位内部反转:BA  CD
                    result[0] = BitConverter.ToUInt16(bytes, 2);
                    result[1] = ReverseUShort(BitConverter.ToUInt16(bytes, 0));
                    break;

                case ParseFormat.CDAB:
                    // 高低16位互换:CD  AB
                    result[0] = BitConverter.ToUInt16(bytes, 0);
                    result[1] = BitConverter.ToUInt16(bytes, 2);
                    break;

                case ParseFormat.DCBA:
                    // 完全反转:DC  BA
                    result[0] = ReverseUShort(BitConverter.ToUInt16(bytes, 0));
                    result[1] = ReverseUShort(BitConverter.ToUInt16(bytes, 2));
                    break;
            }

            return result;
        }

        /// <summary>
        /// 反转 ushort 的高低字节(B A ↔ A B)
        /// </summary>
        private static ushort ReverseUShort(ushort value)
        {
            return (ushort)((value >> 8) | (value << 8));
        }

1.2 bool 类型写入

这里涉及一点思路转换,把ushort转换为16bits,每个bit对应一个bool状态,更改对应的bit后,得到更新后的ushort值,按照int转换为 ushort[]。

        private static ushort[] ConvertIntToTwoUShorts(int value)
        {
            byte[] bytes = BitConverter.GetBytes(value);
            return new ushort[]
            {
                BitConverter.ToUInt16(bytes, 0),  // 高16位
                BitConverter.ToUInt16(bytes, 2) // 低16位
            };
        }

2. 按ushort读取解析

NModbus 读寄存器API:

 ushort[] ReadHoldingRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints);

2.1 解析float类型

一个float占用4个bytes,也就是两个ushort,所以将两个ushort转换为一个float

        /// <summary>
        /// 【反向】将两个 ushort 根据指定格式 还原为 float
        /// </summary>
        /// <param name="values">两个ushort:[高16位, 低16位]</param>
        /// <param name="parseFormat">解析格式 ABCD/BADC/CDAB/DCBA</param>
        /// <returns>还原后的float</returns>
        private static float ConvertTwoUShortsToFloat(ushort[] values, ParseFormat parseFormat)
        {
            if (values == null || values.Length != 2)
                throw new ArgumentException("必须传入长度为2的ushort数组");

            ushort high = values[0];
            ushort low = values[1];
            byte[] bytes = new byte[4];

            switch (parseFormat)
            {
                case ParseFormat.ABCD:
                    // AB  CD → 字节顺序 [CD][AB]
                    Buffer.BlockCopy(BitConverter.GetBytes(low), 0, bytes, 0, 2);
                    Buffer.BlockCopy(BitConverter.GetBytes(high), 0, bytes, 2, 2);
                    break;

                case ParseFormat.BADC:
                    // BA  CD → 低字反转,高字不变
                    Buffer.BlockCopy(BitConverter.GetBytes(ReverseUShort(low)), 0, bytes, 0, 2);
                    Buffer.BlockCopy(BitConverter.GetBytes(high), 0, bytes, 2, 2);
                    break;

                case ParseFormat.CDAB:
                    // CD  AB → 高低字互换
                    Buffer.BlockCopy(BitConverter.GetBytes(high), 0, bytes, 0, 2);
                    Buffer.BlockCopy(BitConverter.GetBytes(low), 0, bytes, 2, 2);
                    break;

                case ParseFormat.DCBA:
                    // DC  BA → 高低字都反转
                    Buffer.BlockCopy(BitConverter.GetBytes(ReverseUShort(low)), 0, bytes, 0, 2);
                    Buffer.BlockCopy(BitConverter.GetBytes(ReverseUShort(high)), 0, bytes, 2, 2);
                    break;

                default:
                    throw new NotSupportedException("不支持的解析格式");
            }

            return BitConverter.ToSingle(bytes, 0);
        }

2.2 解析bool类型

这里拿到的依然是一个ushort,然后看该bool量对应的bit在第几位。下面仅列出ushort对应到int的逆向转换。

        private static int ConvertTwoUShortsToInt(ushort[] values)
        {
            if (values == null || values.Length != 2)
                throw new ArgumentException("必须传入长度为2的ushort数组");

            byte[] bytes = new byte[4];

            Buffer.BlockCopy(BitConverter.GetBytes(values[0]), 0, bytes, 0, 2);
            Buffer.BlockCopy(BitConverter.GetBytes(values[1]), 0, bytes, 2, 2);

            return BitConverter.ToInt32(bytes, 0);
        }