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

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

誰かがそれを行うための他の解決策/アドバイスを持っていますか?

4

1 に答える 1

0

次のアプローチを試してください。

DataReceived イベント ハンドラーで、着信データを読み取り続け、それをバッファー (byte[]) に追加します。

まず、受信データのバッファで開始マーカー ({0xFF, 0xFF}) を見つける必要があります。バッファ内のこのマーカーのインデックスを決定する必要があります。

開始インデックスを取得したら、受信データをバッファに追加し続け、終了マーカー (0xFE、0xFE) が到着したかどうかを確認する必要があります。バッファー内のエンド マーカーのインデックスをキャプチャします。

開始インデックスと終了インデックスを取得したら、それらの間のパケットを抽出できます。その後に追加される余分な 0x00 バイトは気にしないでください。開始マーカーと終了マーカーのインデックスとその長さ (2) はわかっています。それらの間のバイト配列を抽出するだけです。

この目的に合わせて検索アルゴリズムを作成する必要があります。針と干し草の山はどちらもバイト配列 (byte[]) です。この目的には、 Boyer-Moore 文字列検索アルゴリズムを使用できます。

これは、Bad Characterルールのみを実装する Boyer-Moore アルゴリズムの単純な C# 実装です。適切なサフィックス規則も実装したい場合は、ウィキペディアを読んでください。

このアルゴリズムは通常、文字列を対象としていますが、バイト配列で動作するように変更しました。これをローカルで IP カメラを使用してテストし、受信した JPEG 画像を抽出しました。

詳細については、Wikipedia の記事を参照してください。これには、C# に簡単に変換できる完全な Java 実装が含まれています。

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 バイトが含まれており、開始マーカーと終了マーカーの間のバイトのみが含まれています。たとえば、イベントをトリガーして、パケットをイベント引数として渡すことができるようになりました。

備考:COMポートからのデータ受信は連続イベントです。監視し続ける必要があります。受信したデータを追加し続け、開始マーカーとインデックスマーカーをチェックし続け、パッケージを抽出します...など。バッファがオーバーフローしないように注意してください。そこにいくつかのハウスキーピングを実装する必要があります。

それが役に立てば幸い。着信データを継続的に読み取る例については、 MJPEGStreamのAForge実装を確認してください。

要約すると:

  1. 受信したデータを格納するインスタンス変数を宣言します (例: _buffer = new byte[4096])。
  2. DataReceived イベント ハンドラーで受信データをバッファーに追加します。
  3. 開始マーカーを検索します。見つかった場合は、インスタンス変数の開始インデックスを覚えておいてください。
  4. 開始マーカーの位置が既にわかっている場合は、終了マーカーのインデックスを検索します。
  5. エンド マーカーが見つかったら、パケットを抽出してイベントを発生させます。イベントの EventArgs の一部としてパケットを使用します。
  6. 洗って、すすいで、繰り返します。

バッファがオーバーフローしないようにするために、いくつかのハウスキーピングを実装する必要があります (> 4096 バイト)。たとえば、パケットを見つけたら、最後に受信したエンド マーカーまでバッファをクリーンアップできます。

于 2012-06-27T07:37:08.010 に答える