我是Visual C#的新手。我必须每隔一秒从串行设备接收一个468字节的数据包。数据包的标头是0xbf, 0x13, 0x97, 0x74
。检查验证数据包标题后,我保存此数据包,处理它,并以图形方式显示它。
问题是我在几个小时后开始丢失数据包。 (其他软件整整一周都记录了相同的数据并且运行良好)。
代码在这里......
private void DataRec(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
rtTotBytes = comport.BytesToRead;
rtTotBytesRead = comport.Read(rtSerBuff, 0, rtTotBytes);
this.Invoke(new ComportDelegate(ComportDlgtCallback), rtSerBuff, rtTotBytesRead);
}
//Delegate
delegate void ComportDelegate(byte[] sBuff, int sByte);
//Callback Function to Delegate
private void ComportDlgtCallback(byte[] SerBuff, int TotBytes)
{
for (int k = 0; k < TotBytes; k++)
{
switch (rtState)
{
case 0:
if (SerBuff[k] == 0xbf) { rtState = 1; TempBuff[0] = 0xbf; }
else rtState = 0;
break;
case 1:
if (SerBuff[k] == 0x13) { rtState = 2; TempBuff[1] = 0x13; }
else rtState = 0;
break;
case 2:
if (SerBuff[k] == 0x97) { rtState = 3; TempBuff[2] = 0x97; }
else rtState = 0;
break;
case 3:
if (SerBuff[k] == 0x74) { rtState = 4; TempBuff[3] = 0x74; rtCnt = 4; }
else rtState = 0;
break;
case 4:
if (rtCnt == 467)
{
TempBuff[rtCnt] = SerBuff[k];
TempBuff.CopyTo(PlotBuff, 0);
ProcessPacket(PlotBuff);
rtState = 0; rtCnt = 0;
}
else
TempBuff[rtCnt++] = SerBuff[k];
break;
}
}
}
另一个问题:如果发生DataReceivedEvent,BytesToRead
可以为零吗?您是否需要查看(BytesToRead>0)
中的DataRecievedEvent
?
答案 0 :(得分:2)
串行端口输入数据必须被视为流,而不是数据包系列。例如,当设备发送0xbf,0x13,0x97,0x74数据包时,DataRec函数可以用整个数据包调用一次,或者用0xbf,0x13和0x97,0x74数据包调用两次,或者用一个字节调用4次等等。程序必须足够灵活,可以使用一些解析器处理输入流。您当前的程序不会这样做,它可能会错过在多个函数调用中收到的逻辑数据包。另一种情况是可能的,当在一个DataRec函数调用中接收到几个数据包时 - 您的程序也没有准备好用于这种情况。
编辑。
典型的串口输入流处理算法应如下所示:
DataRec函数将接收的数据添加到输入队列并调用解析器。
输入队列是一些字节数组,其中包含已接收但尚未解析的数据。新数据添加到最后,解析的数据包将从此队列的开头删除。
Parser读取输入队列,处理所有已识别的数据包并将其从队列中删除,留下所有未识别的数据以供下次调用。
答案 1 :(得分:1)
我认为问题可能是您无法确定在DataReceived事件中是否收到完整的包。您可能只得到了数据包的前半部分,而下半部分则有半秒。
因此,您应该实现另一个将数据放入缓冲区的层。进一步的程序取决于数据格式。
如果您收到额外的信息,如结束标记或数据长度,您可以检查缓冲区是否已包含这些信息。如果是,请将此完整包装推进到您的日常工作中。
如果您没有此信息,则必须等到收到下一个标题并将缓冲区中的数据转发到此新标题。
答案 2 :(得分:0)
你检查过程序的内存使用情况了吗? 也许你有一个小的互操作类,内存或者没有正确释放的东西,几个小时后加起来使程序运行缓慢,导致它丢失数据。
我会使用进程资源管理器来检查几个小时后内存和CPU使用情况的变化情况。也许检查硬盘活动。
如果这不会导致结果,请使用像ANTS这样的完整分析器,并尝试在分析器下运行程序以检查问题。
答案 3 :(得分:0)
正如Alex Farber指出的那样,无法保证在调用DataReceived
处理程序时,所有字节都在那里。
如果您的缓冲区始终是固定大小且速率较低,则可以直接使用Read
函数,而不是依赖于DataReceived
事件。从概念上讲:
packetSize = 468;
...initialization...
comport.ReadTimeout = 2000; //packets expected every 1000 milliseconds, so give it some slack
while (captureFlag) {
comport.Read(rtSerBuff, 0, packetSize);
...do stuff...
}
如果需要,可以将其放入自己的工作线程中。
另一种方法是使用ReadLine
方法。您提到数据包具有已知的起始签名。他们是否还有一个已知的保证的结尾签名,不会在数据包中重复?如果是这样,您可以将NewLine
属性设置为此结束签名并使用ReadLine
。同样,你可以把它放在一个工作线程中,