George Kerwood George Kerwood - 2 months ago 9
C# Question

WaitFor() - How to wait for a specific buffer to arrive on Steam/SerialPort?

First ever questions so please take it easy on me following protocol, advice welcome...

Requested Behaviour: I would like to hear proposed, generic solutions for suspending a calling thread until a specific buffer is received on a Stream/SerialPort. For the time being, I'm not concerned with timeouts etc, however I need something robust.

Attempted Method:

Class myClass
{
private SerialPort _port; //Assume configured and connected.

public void WaitFor(byte[] buffer)
{
int bufferLength = buffer.Length;
byte[] comparisonBuffer = new byte[bufferLength];

while(true)
{
if(_port.BytesToRead >= bufferLength)
{
_port.Read(comparisonBuffer, 0, bufferLength);
if (comparisonBuffer.SequenceEqual(buffer)) { return; }
}
}
}
{


I've had a reasonable amount of success with this however it just has a "hacky" feel to it. It has quite often caused me trouble. I believe it's due to the fact that I cannot guarantee that other data isn't received either before or after the expected packet, so naturally this method can end up reading off the stream out of sync. In such a case I would not want to loose the leading/trailing data but the method should release the thread.

I need to implement in a procedural nature so event driven methods won't really work for me. In the generic sense I want to be able to implement as;

Do thing;
WaitFor(mybuffer);
Do other thing;


Hope this is all clear! Many Thanks!!!

Answer

Problem

Lets assume you wait for the byte pattern {1,1,1,2,2} and the serial port has buffered {1,1,1,1,2,2,5}.

Your code reads the first 5 bytes {1,1,1,1,2} which will not match the pattern. But after reading from the port the data you read has been removed from the buffer and contains only {2,5} and you will never get a match.

Solution

public void WaitFor( byte[ ] buffer )
{
    if ( buffer.Length == 0 )
        return;

    var q = new List<byte>( buffer.Length );

    while ( true )
    {
        var current = _reader.ReadByte();
        q.Add( (byte)current );
        // sequence match so far
        if ( q.Last == buffer[ q.Count - 1 ] )
        {
            // check for total match
            if ( q.Count == buffer.Length )
                return;
        }
        else
        {
            // shift the data
            while ( q.Any() && !q.SequenceEqual( buffer.Take( q.Count ) ) )
            {
                q.RemoveAt( 0 );
            }
        }
    }
}
Comments