如何在队列<byte>中搜索字节并提取数据包?</byte>

时间:2012-06-27 06:33:44

标签: c# list serial-port

我通过COM端口收到数据包。每个数据包以{0xFF,0xFF}开头,以{0xFE,OxFE}结束。所有收到的字节都在Queue<byte>中排队,并在每个void port_DataReceived(object sender, SerialDataReceivedEventArgs e)之后我正在处理该队列。 如果数据包中出现任何0xFF或0xFE,则设备在其后添加0x00。

  1. 如何提取每个数据包?
  2. 如何在每个包含头部字节的数据包中删除不必要的0x00?
  3. 我的第一个问题是:

    void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        byte[] data = new byte[port.BytesToRead];
        try
        {
            port.Read(data, 0, data.Length);    
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
        data.ToList().ForEach(newByte => receivedData.Enqueue(newByte));
        processData();
    }
    
    private void processData()
    {
        // Determine if we have a "packet" in the queue
        if (Enumerable.SequenceEqual(receivedData.Take(2), new List<byte> { 0xFF, 0xFF }))
        {
            // Beginning of new packet in the front of queue is ready!
            if (Enumerable.SequenceEqual(receivedData.Skip(Math.Max(0, receivedData.Count() - 2)).Take(2), new List<byte> { 0xFE, 0xFE }))
            {
                List<byte> tempPacket = new List<byte>();
                // Whole packet in the queue
                while(receivedData.Count > 0)
                    tempPacket.Add(receivedData.Dequeue());
                tempPacket.TrimExcess();
                Packet pack = new Packet(tempPacket, PacketOrigin.Serial);
            }
        }
    }
    

    我正在尝试删除在Queue<byte>内可以找到的任何0xFE和0xFF之后的所有0x00,到目前为止我想出了:

    List<byte> unconvertedPacket = new List<byte> { 0xFF, OxFF, 0x00, 0x00,0x4D, 0xFA 0xFE, 0x00, 0x01, 0x00, 0x03, 0xFE, 0xFE}
    int index = 0;
    while (index != null)
    {
        unconvertedPacket.RemoveAt(index + 1);
        index = unconvertedPacket.IndexOf(0xFE);
    }
    while (index != null)
    {
        unconvertedPacket.RemoveAt(index + 1);
        index = unconvertedPacket.IndexOf(0xFF);
    }
    

    有没有人可能有任何其他解决方案/建议呢?

1 个答案:

答案 0 :(得分:0)

尝试以下方法:

在DataReceived事件处理程序中继续读取传入数据并将其附加到缓冲区(byte [])。

首先,您需要在接收数据的缓冲区中找到开始标记({0xFF,0xFF})。您需要确定缓冲区中此标记的索引。

获得起始索引后,需要继续将输入数据附加到缓冲区,并检查结束标记(0xFE,0xFE)是否已到达。捕获缓冲区内结束标记的索引。

获得开始和结束索引后,您可以在它们之间提取数据包。没关系在它之后添加的额外0x00字节。您知道开始和结束标记的索引及其长度(2)。只需提取它们之间的字节数组。

您需要创建一个适合此目的的搜索算法。 needle和haystack都是一个字节数组(byte [])。您可以使用Boyer-Moore string search algorithm来实现此目的。

这是Boyer-Moore算法的简单C#实现,它只实现the bad character rule。如果您还想实现good suffix rule,请阅读维基百科。

该算法通常用于字符串,但我修改它以使用字节数组。使用IP摄像头在本地进行测试,以提取收到的JPEG图像。

查看维基百科文章,了解有关它的更多信息。它包含一个完整的Java实现,您可以轻松地将其转换为C#。

public class BoyerMoore
{
    public static int IndexOf(byte[] needle, byte[] haystack)
    {
        if (needle == null || needle.Length == 0)
            return -1;

        int[] charTable = CreateCharTable(needle);
        for (int i = needle.Length - 1, j; i < haystack.Length;)
        {
            for (j = needle.Length - 1; needle[j] == haystack[i]; i--, j--)
            {
                if (j == 0)
                    return i;
            }
            i += charTable[haystack[i]];
        }
        return -1;
    }

    private static int[] CreateCharTable(byte[] needle)
    {
        const int ALPHABET_SIZE = 256;
        var table = new int[ALPHABET_SIZE];
        for (int i = 0; i < table.Length; i++)
        {
            table[i] = needle.Length;
        }
        for (int i = 0; i < needle.Length - 1; i++)
        {
            table[needle[i]] = needle.Length - 1 - i;
        }
        return table;
    }
}

使用示例:

var haystack = new byte[] 
  {0xFF, 0xFF, 0x00, 0x00, 0x4D, 0xFA, 0xFE, 0x00, 0x01, 0x00, 0x03, 0xFE, 0xFE};

var startIndexOf = BoyerMoore.IndexOf(new byte[] {0xFF, 0xFF}, haystack);
var endIndexOf = BoyerMoore.IndexOf(new byte[] {0xFE, 0xFE}, haystack);

var packet = new byte[endIndexOf - 2 - startIndexOf];
for (int i = startIndexOf + 2, j = 0; i < endIndexOf - startIndexOf; i++, j++)
{
    packet[j] = haystack[i];
}

Voila,在这个例子中,包字节数组包含9个字节,只包含开始和结束标记之间的字节。您现在可以触发事件并将数据包作为事件arg传递。例如。

备注:从COM端口接收数据是一个连续事件。你需要继续监控它。继续附加收到的数据,并继续检查起始和索引标记,提取包...等。注意你的缓冲区不会溢出。你需要在那里实施一些内务管理。

希望它有所帮助。请查看AForge MJPEGStream的实施情况,以获取持续读取传入数据的示例。

概括说明:

  1. 声明一个实例变量来存储接收到的数据(例如_buffer = new byte [4096])。
  2. 将传入数据附加到DataReceived事件处理程序中的缓冲区。
  3. 搜索开始标记。如果找到,请记住实例变量中的起始索引。
  4. 如果您已经知道开始标记的位置,则搜索结束标记的索引。
  5. 找到结束标记时,提取数据包并触发事件。将数据包用作事件EventArgs的一部分。
  6. 洗涤,冲洗,重复。
  7. 您需要实施一些内务处理以确保缓冲区不会溢出(> 4096字节)。例如,一旦找到数据包,就可以将缓冲区清理到最后收到的结束标记。