解码字节数组

时间:2015-07-07 06:56:25

标签: c# modbus

我想解码从modbus通信接收的字节数组。这是表示字节数组的十六进制字符串:

01 11 0C 46 57 35 32 39 37 30 31 52 30 2E 35 FE 27

我想分成3部分:

  1. 01 11 0C
  2. 46 57 35 32 39 37 30 31 52 30 2E 35
  3. FE 27
  4. 为了从字节转换为十六进制,我使用以下方法:

     #region ByteToHex
        /// <summary>
        /// method to convert a byte array into a hex string
        /// </summary>
        /// <param name="comByte">byte array to convert</param>
        /// <returns>a hex string</returns>
        public string ByteToHex(byte[] comByte)
        {
            //create a new StringBuilder object
            StringBuilder builder = new StringBuilder(comByte.Length * 3);
            //loop through each byte in the array
            foreach (byte data in comByte)
                //convert the byte to a string and add to the stringbuilder
                builder.Append(Convert.ToString(data, 16).PadLeft(2, '0').PadRight(3, ' '));
            //return the converted value
            return builder.ToString().ToUpper();
        }
    

    如何从该方法拆分返回的字符串以返回: 前3个字节是:

    1. 服务器ID
    2. 功能代码
    3. 字节数
    4. 接下来的N个字节(见3.)是有效载荷;最后2个是CRC16。

      我需要找到有效载荷;它是一个字符串。

5 个答案:

答案 0 :(得分:3)

部分答案:

如果字节返回表示结构化数据,则将所有字节转换为字符串。而是根据其含义转换字节:

int serverId = Convert.ToInt32(byte[0]);
int functionCode = Convert.ToInt32(byte[1]);
int byteCount = Convert.ToInt32(byte[2]);

只要知道数据部分的长度,就可以解码这些数据:

for(int i = 0; i < byteCount; i++)
{
    // do something with byte[3+i]
}

答案 1 :(得分:3)

将输入转换为十六进制字符串毫无意义。您需要解码字节保存的信息。他们所持有的东西完全取决于您只是转述的文档。我会小心翼翼地说:

public class ModbusRequest
{
    public byte ServerId { get; set; }
    public byte FunctionCode { get; set; }
    public string Payload { get; set; }
}

public ModbusRequest DecodeMessage(byte[] message)
{
    var result = new ModbusRequest();

    // Simply copy bytes 0 and 1 into the destination structure.
    result.ServerId = message[0];
    result.FunctionCode = message[1];

    byte stringLength = message[2];

    // Assuming ASCII encoding, see docs.
    result.Payload = Encoding.ASCII.GetString(message, 3, stringLength);

    // Get the CRC bytes.
    byte[] crc = new byte[2];
    Buffer.BlockCopy(message, 4 + stringLength, crc, 0, 2);

    // TODO: verify CRC.

    return result;

}

要验证CRC,请找出正在使用的格式。我建议使用classless-hasher来实现各种CRC变体。

答案 2 :(得分:0)

string str = ByteToHex(comByte);
string partOne = str.Substring(0, 8);
string partTwo = str.Substring(9, 35);
string partThree = str.Substring(45, 5);

答案 3 :(得分:0)

首先更改现有ByteToHex方法的签名,使输入参数为IEnumerable<byte>而不是byte[]

然后使用:

var str1 = ByteToHex(new ArraySegment<byte>(yourBytes, 0, 3));
var str2 = ByteToHex(new ArraySegment<byte>(yourBytes, 3, 12));
var str3 = ByteToHex(new ArraySegment<byte>(yourBytes, 15, 2));

从.NET 4.5(Visual Studio 2012)开始,可以使用ArraySegment<>

PS!该方法的整个主体可以简化:

public static string ByteToHex(IEnumerable<byte> comByte)
{
  return string.Join(" ", comByte.Select(b => b.ToString("X2")));
}

此处首都X表示带有大写&#34;数字&#34;的十六进制格式A到F,2表示填充零,长度为2。

答案 4 :(得分:0)

如果要拆分字节数组,则直接在数组上执行 - 不要事先转换为字符串!生成的代码将更容易理解。

之后,您仍然可以将部分内容转换为十六进制字符串,或者在您的情况下转换为带编码的文本字符串,而不是十六进制。

对于Subarrays,我有这个小扩展:

// Returns the SubArray starting from index and covering the amount of length items.
public static T[] SubArray<T>(this T[] data, int index, int length)
{
    T[] result = new T[length];
    System.Array.Copy(data, index, result, 0, length);
    return result;
}

正如在另一个答案中提到的那样,从.NET 4.5开始,您也可以使用新的ArraySegment

有了这个,您可以简单地拆分:

const int HEADER_LENGTH = 3; //1 Byte ServerId, 1 Byte Function Code, 1 Byte ByteCount
const int CRC_LENGTH = 2;

var bytes = new byte[]{0x01, 0x11,  0x0C, 0x46, 0x57, 0x35, 0x32, 0x39, 0x37, 0x30, 0x31, 0x52, 0x30, 0x2E, 0x35, 0xFE, 0x27};
int payloadLength = bytes.Count() - HEADER_LENGTH - CRC_LENGTH;

//.NET 4.5 solution
var textbytes = new ArraySegment<byte>(bytes, HEADER_LENGTH, payloadLength).ToArray();

//pre 4.5 solution with extension
var textbytes = bytes.SubArray(HEADER_LENGTH, payloadLength);

var text = Encoding.ASCII.GetString(textbytes); //FW529701R0.5