C#Modbus帧分割和存储程序

C#Modbus帧分割和存储程序,c#,validation,types,bytebuffer,modbus,C#,Validation,Types,Bytebuffer,Modbus,前提 我使用我的c#wpf应用程序以较低的间隔不断轮询来自嵌入式设备的自定义modbus帧。此时,我接收的帧中的字节每次都位于相同的位置(例如帧[15]+帧[16]=0x0532=errorcode2) 问题 是否有智能程序(除了简单地硬编码bytestorage)可以用于从帧接收到拆分/解析 什么样的数据结构更适合使帧能够被进一步动态处理 方法1: 您可以在C#中定义一个显式布局的结构,并像下面的代码一样将数据封送到它。然而,若您并没有在big-endian硬件上运行(Modbus数据以big

前提

我使用我的c#wpf应用程序以较低的间隔不断轮询来自嵌入式设备的自定义modbus帧。此时,我接收的帧中的字节每次都位于相同的位置(例如帧[15]+帧[16]=0x0532=errorcode2)

问题

  • 是否有智能程序(除了简单地硬编码bytestorage)可以用于从帧接收到拆分/解析

  • 什么样的数据结构更适合使帧能够被进一步动态处理

  • 方法1:

    您可以在C#中定义一个显式布局的结构,并像下面的代码一样将数据封送到它。然而,若您并没有在big-endian硬件上运行(Modbus数据以big-endian发送),那个么由于endian排序,您将遇到数据问题。这也不会处理可变长度Modbus数据包(即读取输入寄存器)。注意:假设帧已经过CRC检查,因此不需要声明和读取它

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    struct MyPacket
    {
        public Byte Address;        // Byte 0
        public Byte FunctionCode;   // Byte 1
        public Byte ByteField;      // Byte 2
        public UInt32 UInt32Field;  // Bytes 3-6
        public UInt64 UInt64Field;  // Byte 7-14
        public UInt16 ErrorCode;    // Bytes 15-16
    }
    
    public static bool TryParse<T>(byte[] array, out T packet) where T : struct
    {
        try
        {
            var size = Marshal.SizeOf(typeof(T));
            var p = Marshal.AllocHGlobal(size);
            Marshal.Copy(array, 0, p, size);
            packet = (T)Marshal.PtrToStructure(p, typeof(T));
            Marshal.FreeHGlobal(p);
            return true;
        }
        catch
        {
            packet = default(T);
            return false;
        }
    }
    
    static int Main(string[] args)
    {
        // Create a sample frame
        byte[] bytes = new byte[] { 0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 };
    
        MyPacket packet;
    
        if (TryParse<MyPacket>(bytes, out packet))
        {
            // Process packet here...
        }
    
        return 0;
    }
    
    // VERY SIMPLE BigEndianReader class
    public class BigEndianReader
    {
        private Stream _stream;
    
        public BigEndianReader(Stream stream)
        {
            if (stream == null) throw new ArgumentNullException(nameof(stream));
            if (!stream.CanRead) throw new ArgumentException(nameof(stream));
            _stream = stream;
        }
        public byte ReadByte()
        {
            int b = _stream.ReadByte();
            if (b < 0) throw new EndOfStreamException();
            return (byte)b;
        }
    
        public UInt16 ReadUInt16()
        {
            UInt16 v = 0;
            for (int i = 0; i < sizeof(UInt16); i++){v <<= 8;v |= ReadByte();}
            return v;
        }
    
        public UInt32 ReadUInt32()
        {
            UInt32 v = 0;
            for (int i = 0; i < sizeof(UInt32); i++){v <<= 8;v |= ReadByte();}
            return v;
        }
    
        public UInt64 ReadUInt64()
        {
            UInt64 v = 0;
            for (int i = 0; i < sizeof(UInt64); i++) { v <<= 8; v |= ReadByte(); }
            return v;
        }
    }
    
    class MyPacket
    {
        public Byte Address { get; private set; }
        public Byte FunctionCode { get; private set; }
        public Byte ByteField { get; private set; }
        public UInt32 UInt32Field { get; private set; }
        public UInt64 UInt64Field { get; private set; }
        public UInt16 ErrorCode { get; private set; }
    
        public static bool TryParse(byte[] bytes, out MyPacket result)
        {
            result = null;
    
            try
            {
                BigEndianReader r = new BigEndianReader(new MemoryStream(bytes));
                MyPacket p = new MyPacket();
                p.Address = r.ReadByte();
                p.FunctionCode = r.ReadByte();
                p.ByteField = r.ReadByte();
                p.UInt32Field = r.ReadUInt32();
                p.UInt64Field = r.ReadUInt64();
                p.ErrorCode = r.ReadUInt16();
                result = p;
                return true;
            }
            catch
            {
                return false;
            }
        }
    }
    
    static int Main(string[] args)
    {
        // Create a sample frame
        byte[] bytes = new byte[] { 0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19 };
    
        MyPacket packet;
    
        if (MyPacket.TryParse(bytes, out packet))
        {
            // Process packet here...
        }
    
        //==================================================================
        Console.Write("(press any key to exit)");
        Console.ReadKey(true);
        return 0;
    }
    
    [StructLayout(LayoutKind.Sequential,Pack=1)]
    结构MyPacket
    {
    公共字节地址;//字节0
    公共字节FunctionCode;//字节1
    公共字节ByteField;//字节2
    公共UInt32 UInt32字段;//字节3-6
    公共UInt64 UInt64字段;//字节7-14
    公共UInt16错误代码;//字节15-16
    }
    公共静态bool TryParse(字节[]数组,out T数据包),其中T:struct
    {
    尝试
    {
    var size=Marshal.SizeOf(typeof(T));
    var p=封送。AllocHGlobal(大小);
    封送处理副本(数组,0,p,大小);
    packet=(T)Marshal.PtrToStructure(p,typeof(T));
    弗里赫全球元帅(p);
    返回true;
    }
    抓住
    {
    数据包=默认值(T);
    返回false;
    }
    }
    静态int Main(字符串[]args)
    {
    //创建一个示例框架
    字节[]字节=新字节[]{0x0、0x01、0x02、0x03、0x04、0x05、0x06、0x07、0x08、0x09、0x10、0x11、0x12、0x13、0x14、0x15、0x16};
    我的包;
    if(TryParse(字节,输出数据包))
    {
    //在这里处理数据包。。。
    }
    返回0;
    }
    
    方法2:

    我认为更好的解决方案是为您使用的每个函数类型定义一个类,并单独读取每个字段,如下面的代码所示。注意:假设帧已经过CRC检查,因此不需要声明和读取它

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    struct MyPacket
    {
        public Byte Address;        // Byte 0
        public Byte FunctionCode;   // Byte 1
        public Byte ByteField;      // Byte 2
        public UInt32 UInt32Field;  // Bytes 3-6
        public UInt64 UInt64Field;  // Byte 7-14
        public UInt16 ErrorCode;    // Bytes 15-16
    }
    
    public static bool TryParse<T>(byte[] array, out T packet) where T : struct
    {
        try
        {
            var size = Marshal.SizeOf(typeof(T));
            var p = Marshal.AllocHGlobal(size);
            Marshal.Copy(array, 0, p, size);
            packet = (T)Marshal.PtrToStructure(p, typeof(T));
            Marshal.FreeHGlobal(p);
            return true;
        }
        catch
        {
            packet = default(T);
            return false;
        }
    }
    
    static int Main(string[] args)
    {
        // Create a sample frame
        byte[] bytes = new byte[] { 0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 };
    
        MyPacket packet;
    
        if (TryParse<MyPacket>(bytes, out packet))
        {
            // Process packet here...
        }
    
        return 0;
    }
    
    // VERY SIMPLE BigEndianReader class
    public class BigEndianReader
    {
        private Stream _stream;
    
        public BigEndianReader(Stream stream)
        {
            if (stream == null) throw new ArgumentNullException(nameof(stream));
            if (!stream.CanRead) throw new ArgumentException(nameof(stream));
            _stream = stream;
        }
        public byte ReadByte()
        {
            int b = _stream.ReadByte();
            if (b < 0) throw new EndOfStreamException();
            return (byte)b;
        }
    
        public UInt16 ReadUInt16()
        {
            UInt16 v = 0;
            for (int i = 0; i < sizeof(UInt16); i++){v <<= 8;v |= ReadByte();}
            return v;
        }
    
        public UInt32 ReadUInt32()
        {
            UInt32 v = 0;
            for (int i = 0; i < sizeof(UInt32); i++){v <<= 8;v |= ReadByte();}
            return v;
        }
    
        public UInt64 ReadUInt64()
        {
            UInt64 v = 0;
            for (int i = 0; i < sizeof(UInt64); i++) { v <<= 8; v |= ReadByte(); }
            return v;
        }
    }
    
    class MyPacket
    {
        public Byte Address { get; private set; }
        public Byte FunctionCode { get; private set; }
        public Byte ByteField { get; private set; }
        public UInt32 UInt32Field { get; private set; }
        public UInt64 UInt64Field { get; private set; }
        public UInt16 ErrorCode { get; private set; }
    
        public static bool TryParse(byte[] bytes, out MyPacket result)
        {
            result = null;
    
            try
            {
                BigEndianReader r = new BigEndianReader(new MemoryStream(bytes));
                MyPacket p = new MyPacket();
                p.Address = r.ReadByte();
                p.FunctionCode = r.ReadByte();
                p.ByteField = r.ReadByte();
                p.UInt32Field = r.ReadUInt32();
                p.UInt64Field = r.ReadUInt64();
                p.ErrorCode = r.ReadUInt16();
                result = p;
                return true;
            }
            catch
            {
                return false;
            }
        }
    }
    
    static int Main(string[] args)
    {
        // Create a sample frame
        byte[] bytes = new byte[] { 0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19 };
    
        MyPacket packet;
    
        if (MyPacket.TryParse(bytes, out packet))
        {
            // Process packet here...
        }
    
        //==================================================================
        Console.Write("(press any key to exit)");
        Console.ReadKey(true);
        return 0;
    }
    
    //非常简单的BigEndianReader类
    公共类BigEndianReader
    {
    私有流(u流),;
    公共BigEndianReader(流)
    {
    如果(stream==null)抛出新的ArgumentNullException(nameof(stream));
    如果(!stream.CanRead)抛出新的ArgumentException(nameof(stream));
    _溪流=溪流;
    }
    公共字节ReadByte()
    {
    int b=_stream.ReadByte();
    如果(b<0)抛出新的EndOfStreamException();
    返回(字节)b;
    }
    公共UInt16 ReadUInt16()
    {
    UInt16 v=0;
    对于(inti=0;i