views:

462

answers:

3

Hi,

Let's say I want to have a function which reads data from the SerialPort and returns a byte[].

public byte[] RequestData(byte[] data)
{
    //See code below
}

Something as simple as this really doesn't work/perform well and isn't very reliable:

byte[] response = new byte[port.ReadBufferSize];

port.Open();    
port.Write(data, 0, data.Length);

Thread.Sleep(300); //Without this it doesn't even work at all

Console.WriteLine("Bytes to read: {0}", port.BytesToRead);

int count = port.Read(response, 0, port.ReadBufferSize);

Console.WriteLine("Read {0} bytes", count);

port.Close();
port.Dispose();       

return response.GetSubByteArray(0, count);

I also tried replacing the Thread.Sleep with something like:

while (port.BytesToRead < 14)
{
    //Maybe Thread.Sleep(10) here?
}

But that causes problems to. (PS: I know I need at least 14 bytes)

Of course a better way (I think) would be to have something like:

port.ReceivedBytesThreshold = 14;
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
port.Open();

port.Write(data, 0, data.Length);

And then having a handler of course:

void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    var port = (SerialPort)sender;

    while (port.BytesToRead > 0)
    {
        //Read the data here
    }
}

But then I can't return the data as the result of the function I wanted to define? The client code using this would have to subscribe to an event raised by this code, but then how would it know the response is really the response to the request it just made.

(Multiple messages might be sent, and I can imagine one message taking longer to process on the other side than the other, or something).

Any advise would be welcome

UPDATE

The following code works a lot better, but if I remove the Thread.Sleep() statements it once again stops working properly. For example, the serial port monitoring tool clearly indicates 17 bytes have been written on the serial line. The first time BytesToRead = 10 and the next time BytesToRead = 4 , but then BytesToRead remains 0 so where did the last 3 bytes go to ?

void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    Thread.Sleep(100);
    while (port.BytesToRead > 0)
    {
        Console.WriteLine("Bytes to read: {0}", port.BytesToRead);
        var count = port.BytesToRead;

        byte[] buffer = new byte[count];

        var read = port.Read(buffer, 0, count);

        if (count != read)
            Console.WriteLine("Count <> Read : {0} {1}", count, read);

        var collectAction = new Action(() =>
        {
            var response = dataCollector.Collect(buffer);

            if (response != null)
            {
                this.OnDataReceived(response);
            }
        });

        collectAction.BeginInvoke(null, null);
        Thread.Sleep(100);
    }    
}
+3  A: 

Here's how I've done it:

I have a wrapper for the class that accepts the vital data for the connection in the constructor and does the basic setup in that constructor. The consumer of the class calls a Connect method, which fires off another thread to perform the connection (non-blocking).

When the connection is complete, a StateEvent is fired indicating the connection is completed. At this time a Send queue is setup, a thread to work that queue is fired off and a read thread is also setup. The read thread reads 128 characters of data from the SerialPort, converts that to a string and then fires an event to pass along the received data. This is wrapped in a while thread that loops as long as the connection is maintained. When the consumer wants to send something, a Send method simply enqueues the data to be sent.

As far as knowing that the response is in response to something that was sent really isn't the job of a connection class. By abstracting away the connection into something as easy to handle as that, the consumer of the class can cleanly maintain the logic to determine if the response is what it expected.

jasonh
Mind sharing some code?
TimothyP
Unfortunately the proprietary code is too intertwined with the code you'd be interested in.
jasonh
No problem, thnx for the help in any case :)
TimothyP
A: 

Aren't serial ports fun. My only thought is that your fifo, assuming your device has one and its enabled, is being overrun.

kenny
A: 

Problem solved:

void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            var count = port.BytesToRead;

            byte[] buffer = new byte[count];

            var read = port.Read(buffer, 0, count);

            var response = dataCollector.Collect(buffer);

            if (response != null)
            {
                this.OnDataReceived(response);
            }            
        }

It seems the problem wasn't actually this code but the code in the dataCollector.Collect() method.

TimothyP
This is still very much _not_ the way to read a SerialPort.
Henk Holterman
Then what is, because it really works now
TimothyP