串口 - 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 });
}
}
我知道我的数据是在几个包中接收的,我需要等到响应完成,但根据上面的代码我不知道。我应该如何检查数据是否完整以确定是否等待?(附注:收到的响应大小各不相同。
没有完整性或数据包大小的内置概念。
您必须追加到缓冲区,直到您看到您(或其他人)定义为协议规范一部分的一些可识别的数据包结束模式。 - 然后可能会在一段时间后超时,如果你还没有看到你要找的东西。
旧项目的例子,注意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()。