0

I dont know if this is a good way to work with a stack for this task but I'm sure there is a faster way ... I get data from my microcontroller but the data length is not always the same length. I thought maybe I can push data in my stack and in a thread I can pop it and decode the message. I didnt wanted slow down the DataReceivedHandler so then I created a Thread which can pop the data and write it to my Listview in my decodeMessage() function.

After a short time I get a System.OutOfMemories Exception..

Any ideas how I can do it in a better way ?

I'm reading from my serial port just when data arrives here:

Stack<byte[]> stack = new Stack<byte[]>();

.....

public void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
    SerialPort sp = (SerialPort)sender;
    byte[] data = new byte[sp.BytesToRead];
    sp.Read(data, 0, data.Length);

    stack.Push(data);
}

And this is my Thread:

private void formatData()
{
    try
    {
        while (true)
        {
            byte[] data;
            int i=0;

            Dispatcher.BeginInvoke(new Action(() =>
            {
                while (stack.Count > 0)
                {
                    data = stack.Pop();
                    while (i < data.Length)
                    {
                        decodeMessage(data[i]);
                        i++;
                    }
                }
            }));          
        }
    }
    catch (Exception ex)
    {
        System.Windows.Forms.MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

thx

Mong Zhu
  • 23,309
  • 10
  • 44
  • 76
user2261524
  • 415
  • 3
  • 10
  • 17
  • It seems you need [BlockingCollection](http://msdn.microsoft.com/en-us/library/dd267312.aspx) instead of `Stack` – I4V May 02 '13 at 10:32
  • 6
    I don't think a stack is what you want, a Queue is probably more appropriate for this as it is FIFO. BlockingCollection, as suggested above, uses a Queue by default and is thread safe. Your stack implementation is not thread safe, which might be causing you problems. – Mike Marynowski May 02 '13 at 10:34
  • But how I know ,how many Elements I should read trough the Queue ? – user2261524 May 02 '13 at 10:40
  • 2
    You have several options. `while (blockingCollection.TryTake(out item))` will keep looping until there are no more items in the queue. `blockingCollection.Take()` will block your background thread until there is something to take. `blockingCollection.Take(cancellationToken)` will block until there is something to take or you cancel the operation (i.e. when you stop processing the serial port). – Mike Marynowski May 02 '13 at 10:51
  • 2
    Also, keep in mind that you might not get your whole "message" in one SerialPort.Read() command, it might come in chunks, so your code should handle that as well. If you know the length of a message then you can set your buffer size accordingly and wait until a whole message is available before you call Read(), otherwise you should handle data split between multiple Read calls on your serial port. – Mike Marynowski May 02 '13 at 10:56

1 Answers1

4

This code use a thread safe queue. I simplified some of my own code, so this code is not tested or compiled. If you have problems compiling or it produce errors, add a comment to me and I will help you out.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Windows.Threading;
using System.Collections.Concurrent;

void someRoutine()
{
    // initialize queue before using it
    serialDataQueue = new ConcurrentQueue<char>();

}


/// <summary>
/// data from serialPort is added to the queue as individual chars, 
/// a struct may be better
/// </summary>
public ConcurrentQueue<char> serialDataQueue;

// get data
void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    SerialPort sp = sender as SerialPort;
    int bytesAvailable = sp.BytesToRead;

    // array to store the available data    
    char[] recBuf = new char[bytesAvailable];

    try
    {    
        // get the data
        sp.Read(recBuf, 0, bytesAvailable);

        // put data, char by char into a threadsafe FIFO queue
        // a better aproach maybe is putting the data in a struct and enque the struct        
        for (int index = 0; index < bytesAvailable; index++)
           serialDataQueue.Enqueue(recBuf[index]);

    }
    catch (TimeoutException ex)
    {
        // handle exeption here
    }
}



/// <summary>
/// Check queue that contains serial data, call this 
/// routine at intervals using a timer or button click
/// or raise an event when data is received
/// </summary>
private void readSearialDataQueue()
{
    char ch;

    try
    {
        while (serialDataQueue.TryDequeue(out ch))
        {
            // do something with ch, add it to a textbox 
            // for example to see that it actually works
            textboxDataReceived.Text += ch;
        }

    }
    catch (Exception ex)
    {
        // handle ex here
    }
}
user2019047
  • 749
  • 7
  • 10