4

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

  1. 如何提取每个数据包?
  2. 如何删除每个包含头字节的数据包内不必要的 0x00?

对于第一个问题,我有:

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);
        }
    }
}

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

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);
}

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

4

1 回答 1

0

尝试以下方法:

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

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

一旦你有了开始索引,你需要继续将传入的数据附加到缓冲区并检查结束标记(0xFE,0xFE)是否已经到达。捕获缓冲区内结束标记的索引。

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

您需要创建适合此目的的搜索算法。针和干草堆都是字节数组(byte[])。为此,您可以使用Boyer-Moore 字符串搜索算法。

这是 Boyer-Moore 算法的简单 C# 实现,它只实现了 bad character rule。如果您还想实施良好的后缀规则,请阅读 Wikipedia 。

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

查看 Wikipedia 文章以了解有关它的更多信息。它包含一个完整的 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];
}

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

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

希望能帮助到你。查看MJPEGStream 的AForge实现,以获取连续读取传入数据的示例。

回顾一下:

  1. 声明一个实例变量来存储接收到的数据(例如_buffer = new byte[4096])。
  2. 将传入数据附加到 DataReceived 事件处理程序中的缓冲区。
  3. 搜索开始标记。如果找到,请记住实例变量中的起始索引。
  4. 如果您已经知道开始标记的位置,则搜索结束标记的索引。
  5. 当您找到结束标记时,提取数据包并触发事件。将数据包用作事件的 EventArgs 的一部分。
  6. 洗涤,冲洗,重复。

您需要进行一些内务处理以确保缓冲区不会溢出(> 4096 字节)。例如,一旦您找到一个数据包,您就可以清理缓冲区,直到最后收到的结束标记。

于 2012-06-27T07:37:08.010 回答