串行端口;几小时后丢失数据包

时间:2011-02-03 08:47:47

标签: c# serial-port

我是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

4 个答案:

答案 0 :(得分:2)

串行端口输入数据必须被视为流,而不是数据包系列。例如,当设备发送0xbf,0x13,0x97,0x74数据包时,DataRec函数可以用整个数据包调用一次,或者用0xbf,0x13和0x97,0x74数据包调用两次,或者用一个字节调用4次等等。程序必须足够灵活,可以使用一些解析器处理输入流。您当前的程序不会这样做,它可能会错过在多个函数调用中收到的逻辑数据包。另一种情况是可能的,当在一个DataRec函数调用中接收到几个数据包时 - 您的程序也没有准备好用于这种情况。

编辑。

典型的串口输入流处理算法应如下所示:

  1. DataRec函数将接收的数据添加到输入队列并调用解析器。

  2. 输入队列是一些字节数组,其中包含已接收但尚未解析的数据。新数据添加到最后,解析的数据包将从此队列的开头删除。

  3. 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。同样,你可以把它放在一个工作线程中,