串口 - C#中的串口,数据接收不完整消息

本文关键字:串口 消息 数据 | 更新日期: 2023-09-27 17:56:50

我正在研究Serialport。我面临着一个新问题,一旦我收到数据,我的数据就不完整。如何检查我的数据是否完整然后处理它们,如果不完整,则不要处理它们?

以下是我的数据接收和发送函数:

 private void Send(byte[] cmd)
        {
            bResponse = new byte[0];
            Write(cmd);        
        }
 void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            int iCount = comPort.BytesToRead;
            byte[] bBuffer = new byte[iCount];
            comPort.Read(bBuffer, 0, iCount)
            if (bBuffer.Length == 1 && bBuffer[0] == ACK)
                Write(new byte[] { ENQ });
            else if (bBuffer.Length == 1 && bBuffer[0] == NAK)
            {
                Debug.WriteLine("Incomplete Message detected!");  
            }
            else
            {
                bResponse = bResponse.Concat(bBuffer).ToArray();
                    rResponse = Decode(bResponse);
                    Write(new byte[] { ACK });
            }
        }

我知道我的数据是在几个包中接收的,我需要等到响应完成,但根据上面的代码我不知道。我应该如何检查数据是否完整以确定是否等待?(附注:收到的响应大小各不相同。

串口 - C#中的串口,数据接收不完整消息

没有完整性或数据包大小的内置概念。

您必须追加到缓冲区,直到您看到您(或其他人)定义为协议规范一部分的一些可识别的数据包结束模式。 - 然后可能会在一段时间后超时,如果你还没有看到你要找的东西。

旧项目的例子,注意firstindex,lastindex,你输入一个字符来知道长度,开始/结束字符是预定义的,可以是你选择的任何字符,只是确保不要使用任何常用字符

这是针对tcp/ip的,但相同的原理可以用于串行端口

    public void ReceiveMessage(IAsyncResult ar) 
    {
        int bytesRead;
        try
        {
            lock (client1.GetStream()) 
            {
                bytesRead = client1.GetStream().EndRead(ar);
            }
            string messageReceived = System.Text.Encoding.ASCII.GetString(data, 0, bytesRead);
            received = messageReceived;
            int firstindex = received.IndexOf((char)2);
            int lastindex = received.IndexOf((char)3);
            if (firstindex > 0 && lastindex > 0)
            {
                string first = received.Substring(firstindex, 1);
                string last = received.Substring(lastindex, 1);
            }
            lock (client1.GetStream())
            {
                client1.GetStream().BeginRead(data, 0, System.Convert.ToInt32(client1.ReceiveBufferSize), ReceiveMessage, null);
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
    }

我有一些代码给你。首先,实现 DataReceived 事件(正如您已经完成的那样)。仅当有数据要处理时,才会调用此事件。虽然我不会称它为基于中断的(如"实时能力")绝对不是轮询。这很好。

第二:调用事件时,您可能只有一个字节,但可能有更多的字节。要捕获每个数据包,您需要实现自定义缓冲区。

第三:将一个字节附加到缓冲区后,检查缓冲区是否包含有效的数据包。如果是这样,请处理它。如果没有:附加另一个。如果没有剩余字节,请等待再次调用该事件。

在代码中,它看起来像这样:

const BUFFERLENGTH = 17; // Bytes
byte[] buffer = new byte[BUFFERLENGTH];

private void COM_Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    var port = (SerialPort)sender;
    while (port.BytesToRead > 0)
    {
        var data = (byte)port.ReadByte();
        Read(data);
    }
}
private void Read(byte value)
{
    // Append Byte to buffer
    System.Buffer.BlockCopy(buffer, 1, buffer, 0, BUFFERLENGTH- 1);
    buffer[BUFFERLENGTH - 1] = value;
    // Check for valid Packet
    if (IsValidPacket(buffer))
    {
        // Yeah! Gotcha :-)
        // Now copy your Packet from the Buffer to your struct/whatever
    }
}
private bool IsValidPacket(byte[] buffer)
{
    // Todo: Check whether buffer contains a valid Packet.
    // Some think like:
    // return buffer != null && buffer[0] == 0xF4 && buffer[2] == buffer.length
    throw new NotImplementedException();
}

请注意,我没有"将字节附加到缓冲区"。我丢弃了第一个字节,将每个字节移动了一个位置,并在末尾插入了新字节。如果找到一个有效的数据包,我可以在一个块中将其复制到结构中。因此,缓冲区大小始终保持不变,并且与一个数据包一样长。

这可能不是最快的代码(因为它分别读取每个字节),但它对我来说效果很好:-)

哦,如果你想在你的GUI中显示这些东西,记得使用Begininvoke()。