How to search for bytes in a <byte> queue and retrieve a packet?
I receive packets through the COM port. Each packet starts with {0xFF, 0xFF} and ends with {0xFE, OxFE}. All received bytes are queued in Queue<byte> , and after each void port_DataReceived(object sender, SerialDataReceivedEventArgs e) I process this queue. If 0xFF or 0xFE appears in the packet, the device adds 0x00 after it.
- How to extract each package?
- How to remove unnecessary 0x00 inside each packet with internal header byte?
For the first problem, I have:
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); } } } I am trying to remove all 0x00 that are after any 0xFE and 0xFF that can be found inside the Queue<byte> , while I came up with:
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); } Does anyone have any other solutions / tips for this?
Try the following approach:
In the DataReceived event handler, they continue to read incoming data and add it to the buffer (byte []).
First you need to find the start marker ({0xFF, 0xFF}) in the received data buffer. You need to determine the index of this marker in the buffer.
After you have a start index, you need to save the incoming data in the buffer and check if the end marker (0xFE, 0xFE) has arrived. Capturing the index of the final marker in the buffer.
Once you have the start and end index, you can extract the package between them. Nevermind about the extra 0x00 byte that is added after it. You know the index of the start and end markers and their length (2). Just extract an array of bytes between them.
You need to create a search algorithm for this purpose. The needle and haystacks are an array of bytes (bytes []). For this purpose, you can use the Boyer-Moore string search algorithm .
Here's a simple C # implementation of the Boyer-Moore algorithm that implements a bad character rule . Read on Wikipedia if you also want to implement the good suffix rule .
The algorithm is usually intended for strings, but I modified it to work with byte arrays. Tested locally using an IP camera to extract received JPEG images.
Check out the Wikipedia article for more information on this. It contains a complete Java implementation, which you can easily translate into 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; } } Usage example:
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, the packet byte array contains 9 bytes in this example and contains only bytes between the start and end marker. Now you can trigger the event and pass the packet as an argument to the events arg.
Note Receiving data from the COM port is a continuous event. You need to keep track of this. Continue to add the received data and continue to check start-and index markers, extract packets ... etc. Make sure that your buffer does not overflow. You need to implement something economy there.
Hope this helps. Check out the AForge implementation of MJPEGStream for an example of continuous reading of incoming data.
Repeat:
- Declare an instance variable to store the received data (for example, _buffer = new byte [4096]).
- Add incoming data to the buffer in the DataReceived event handler.
- Find the start marker. If found, remember the starting index in the instance variable.
- If you already know the position of the start marker, find the index of the ending marker.
- When you find the end marker, extract the package and fire the event. Use the package as part of the EventArgs event.
- Rinse, rinse, repeat.
You need to do some homework to make sure that the buffer will not overflow (> 4096 bytes). For example, as soon as you find a packet, you can clear the buffer to the last received final marker.