在 C# 中从通过串口接收的字节组装一个结构体

发布于 2025-01-10 04:26:36 字数 2390 浏览 2 评论 0原文

在 CI 中,只需将数据缓冲区数组中的 memcpy 复制到结构体的地址即可。 我不知道如何在 C# 中为桌面端执行此操作。这是我在 C# 中的结构

 struct frame_type
    {
        public UInt32 start_of_frame;
        public UInt32 frame_id;
        public UInt16 frame_len;
        public UInt32 crc;
        public UInt32 end_of_frame;

    }

,我有一个 Datareceived 串行端口回调,下面的 dataIn 变量是一个字符串,但显然我可以对其进行其他更改,以便更轻松地获取所有这些字节并组装框架。

private void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
    {

        dataIN = port.ReadExisting();

        //increment variable and when i have 34 bytes assemble a frame
        //and checkl if it is an ack frame.
        bytes_received_count++;
        if(bytes_received_count == 34)
        {
            //assemble a frame_type frame
        }

        this.Invoke(new EventHandler(sendFirmware));
    }

所以欢迎任何建议。

更新: 经过一番研究后,我最终得到了这段代码:

     private void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            
            for (int i = 0; i < 34; i++)
            {
                bytes_received[i] = (byte)port.ReadByte();

            }


            assemble_frame_from_port_bytes();
            // this.Invoke(new EventHandler(sendFirmware));
        }
        public void assemble_frame_from_port_bytes()
        {
            frame.start_of_frame = (UInt32)(bytes_received[3] << 24 | bytes_received[2] << 16 | bytes_received[1] << 8 | bytes_received[0] << 0);
            frame.frame_id = (UInt32)(bytes_received[7] << 24 | bytes_received[6] << 16 | bytes_received[5] << 8 | bytes_received[4] << 0);
            frame.frame_len = (UInt16)(bytes_received[9] << 8 | bytes_received[8] << 0);
            int idx = 10;
            for (int i = 0; i < 16; i++)
            {
                payload[i] = bytes_received[idx++];
            }
            frame.crc = (UInt32)(bytes_received[29] << 24 | bytes_received[28] << 16 | bytes_received[27] << 8 | bytes_received[26] << 0);
            frame.end_of_frame = (UInt32)(bytes_received[33] << 24 | bytes_received[32] << 16 | bytes_received[31] << 8 | bytes_received[30] << 0);
        }

它完成了工作,是不是很棒?我不知道,但现阶段它达到了目的。 C# 无疑是一个非常强大的工具,但在傻瓜手中它就会被削弱,哈哈。

In C I simply do a memcpy from my data buffer array to the address of my struct.
I am not sure how to do this in C# for the desktop side of things. This is my struct in C#

 struct frame_type
    {
        public UInt32 start_of_frame;
        public UInt32 frame_id;
        public UInt16 frame_len;
        public UInt32 crc;
        public UInt32 end_of_frame;

    }

And I have a Datareceived serial port callback, the dataIn variable below is a string but obviously I can change it something else to make it easier to grab all those bytes and assemble the frame.

private void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
    {

        dataIN = port.ReadExisting();

        //increment variable and when i have 34 bytes assemble a frame
        //and checkl if it is an ack frame.
        bytes_received_count++;
        if(bytes_received_count == 34)
        {
            //assemble a frame_type frame
        }

        this.Invoke(new EventHandler(sendFirmware));
    }

So any suggestions are welcome.

UPDATE:
I ended up with this code after some research:

     private void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            
            for (int i = 0; i < 34; i++)
            {
                bytes_received[i] = (byte)port.ReadByte();

            }


            assemble_frame_from_port_bytes();
            // this.Invoke(new EventHandler(sendFirmware));
        }
        public void assemble_frame_from_port_bytes()
        {
            frame.start_of_frame = (UInt32)(bytes_received[3] << 24 | bytes_received[2] << 16 | bytes_received[1] << 8 | bytes_received[0] << 0);
            frame.frame_id = (UInt32)(bytes_received[7] << 24 | bytes_received[6] << 16 | bytes_received[5] << 8 | bytes_received[4] << 0);
            frame.frame_len = (UInt16)(bytes_received[9] << 8 | bytes_received[8] << 0);
            int idx = 10;
            for (int i = 0; i < 16; i++)
            {
                payload[i] = bytes_received[idx++];
            }
            frame.crc = (UInt32)(bytes_received[29] << 24 | bytes_received[28] << 16 | bytes_received[27] << 8 | bytes_received[26] << 0);
            frame.end_of_frame = (UInt32)(bytes_received[33] << 24 | bytes_received[32] << 16 | bytes_received[31] << 8 | bytes_received[30] << 0);
        }

It gets the job done, is it great? I dont know but it serves its purpose at this stage. C# surely is a great powerful tool but in the hands of a fool it is crippled haha.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

瑾兮 2025-01-17 04:26:36

在 CI 中,只需从我的数据缓冲区数组到我的结构的地址执行 m​​emcpy。

我想下面是类似的东西,但我无法测试它:

[StructLayout(LayoutKind.Sequential)]
struct frame_type
{
    public UInt32 start_of_frame;
    public UInt32 frame_id;
    public UInt16 frame_len;
    public UInt32 crc;
    public UInt32 end_of_frame;
     

    public static implicit operator frame_type(byte[] data) 
    {
        unsafe
        {
            fixed (byte* b = &data[0])
            {
                return *(frame_type*)b;
            }
        }
  
    }    
}

它应该允许您直接将长度为 18 的字节数组分配给结构

frame_type f = aByteArray18Long;

当然,它是万恶之源和安全的方法可能看起来像:

static public implicit operator frame_type(byte[] b) 
{
    var f = new frame_type();
    f.start_of_frame = (uint)(b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3]);
    f.frame_id =       (uint)(b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7]);
    f.frame_len =    (ushort)(b[8]<<8 | b[9]);
    f.crc =            (uint)(b[10]<<24 | b[11]<<16 | b[12]<<8 | b[13]);
    f.end_of_frame =   (uint)(b[14]<<24 | b[15]<<16 | b[16]<<8 | b[17]);
    return f;
}

ps;通过以下方式获取字节可能是最简单的:

var buf = new byte[18];
for(int x = 0; x<18; x++)
    buf[x] = port.Read();

In C I simply do a memcpy from my data buffer array to the address of my struct.

I suppose the following is something similar, but I'm not in a position to test it:

[StructLayout(LayoutKind.Sequential)]
struct frame_type
{
    public UInt32 start_of_frame;
    public UInt32 frame_id;
    public UInt16 frame_len;
    public UInt32 crc;
    public UInt32 end_of_frame;
     

    public static implicit operator frame_type(byte[] data) 
    {
        unsafe
        {
            fixed (byte* b = &data[0])
            {
                return *(frame_type*)b;
            }
        }
  
    }    
}

It should allow you to just straight assign a byte array of length 18 to the struct

frame_type f = aByteArray18Long;

Of course, it is the root of all evil and a safe approach would perhaps look like:

static public implicit operator frame_type(byte[] b) 
{
    var f = new frame_type();
    f.start_of_frame = (uint)(b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3]);
    f.frame_id =       (uint)(b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7]);
    f.frame_len =    (ushort)(b[8]<<8 | b[9]);
    f.crc =            (uint)(b[10]<<24 | b[11]<<16 | b[12]<<8 | b[13]);
    f.end_of_frame =   (uint)(b[14]<<24 | b[15]<<16 | b[16]<<8 | b[17]);
    return f;
}

ps; it's perhaps easiest to get your bytes by something like:

var buf = new byte[18];
for(int x = 0; x<18; x++)
    buf[x] = port.Read();
冷夜 2025-01-17 04:26:36

好吧,大警告,我没有串行端口,所以无法测试

      MemoryStream buffer = new MemoryStream();

    private void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) {

        for (int i = 0; i < port.BytesToRead; i++) {
            if (buffer.Length == 34)
                break;
             // write needs an array even though its only 1
             byte[] bytes = new bytes[1];
             bytes[0] = port.ReadByte();
             buffer.Write(bytes, 0, 1);
        }
    if (buffer.Length == 34){
        // ok we now have the next 34 bytes in buffer
        var frame = new frame_type();
        // rewind to the beginning
        buffer.Seek(0, SeekOrigin.Begin);
        using (var br = new BinaryReader(buffer)) {
            frame.start_of_frame = br.ReadUInt32();
            frame.frame_id = br.ReadUint32();
            .......
        }
        // rewind to be ready for next block
        buffer.Seek(0, SeekOrigin.Begin);
      }
    }

核心是使用 BinaryReader 从字节流中读取序列化元素,ReadUInt32 拉出接下来的 4 个字节并编组到 UIn32 等。

棘手的一点是将字节从 thr 端口获取到流,因为我们无法将流直接连接到端口(我不知道如何做到这一点),所以我使用 MemoryStream,顾名思义,它是内存中的字节流

ok big caveat, I have no serial port so cannot test

      MemoryStream buffer = new MemoryStream();

    private void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) {

        for (int i = 0; i < port.BytesToRead; i++) {
            if (buffer.Length == 34)
                break;
             // write needs an array even though its only 1
             byte[] bytes = new bytes[1];
             bytes[0] = port.ReadByte();
             buffer.Write(bytes, 0, 1);
        }
    if (buffer.Length == 34){
        // ok we now have the next 34 bytes in buffer
        var frame = new frame_type();
        // rewind to the beginning
        buffer.Seek(0, SeekOrigin.Begin);
        using (var br = new BinaryReader(buffer)) {
            frame.start_of_frame = br.ReadUInt32();
            frame.frame_id = br.ReadUint32();
            .......
        }
        // rewind to be ready for next block
        buffer.Seek(0, SeekOrigin.Begin);
      }
    }

the core is using BinaryReader this reads serialized elements from a byte stream, ReadUInt32 pulls the next 4 bytes and marshalls to a UIn32 etc.

The fiddly bit is getting the bytes from thr port into a stream since we cant connect a stream directly to the port (I coulsnt see how to do it) so I use a MemoryStream which, as the name suggests , is a byte stream in memory

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文