Ivan I. Ovchinnikov
Ivan I. Ovchinnikov

Reputation: 109

C#: Chart: fastest way to put data from received UDP packet

I'm trying to write a simple UDP-to-Chart app (for windows forms) that will get raw data from ethernet, and put it in a specific way to a chart. Here's what i have so far: Form with two charts, a Thread to receive UDP packets:

public void serverThread()
    {
        UdpClient udpClient = new UdpClient(Convert.ToInt16(tbEthPort.Text));
        while (_serverWork)
        {
            try
            {
                IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, Convert.ToInt16(tbEthPort.Text));
                Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint);
                if (receiveBytes.Length > 0)
                    for (i = 0; i < receiveBytes.Length; i++)
                    {
                        bigDataIn[i] = receiveBytes[i];
                    }

and draw second chart (that shows all packet contents in a specific way):

if (graphicOn)
{
    for (i = 0; i < 32; i++)
    {
        graphicData[i + (graphicStep * 32)] = bigDataIn[i * 32 + graphicChan];
    }
    graphicStep++;
    if (graphicStep == 32) graphicStep = 0; 
    try
    {
        Invoke(new Action(() => { chartGraphic.Series["DataGraphic"].Points.DataBindXY(graphicEnum, graphicData);
        }));
    }
    catch
    {
    }
}

and a main thread with timer to draw first chart.

private void tmrHisto_Tick(object sender, EventArgs e)
    {
        int[] slice = new int[32];
        for (int i = 0; i < 32; i++)
            slice[i] = bigDataIn[i + 32 * histogramArrayStep];

        histogramArrayStep++;
        if (histogramArrayStep == 32) histogramArrayStep = 0;

        chartHistogram.Series["DataHistogram"].Points.Clear();
        for (int i = 0; i < HISTO_XMAX; i++)
        {
            chartHistogram.Series["DataHistogram"].Points.AddXY(i, slice[i]);
        }
    }

Everything works fine, on my PC, and several others, but when I launch my app on old PC's, my app starts to loose packets. Packet loss starts when I begin to Invoke chartGraphic. I can see all packets (about 20 in a second) in WireShark without any losses. I experienced same problems, when timer interval was set to 50ms instead of 150ms, but I can't increase interval anymore.

So that's my question - can I increase the speed of graphic drawing, and stop packet losses on low-end PC's. Or how can I emulate low-end PC during debug?

Upvotes: 4

Views: 890

Answers (1)

Jeroen van Langen
Jeroen van Langen

Reputation: 22073

Invoke is blocking, so your receivethread will wait until the drawing (DataBindXY) is complete. Try to move it outside the receive thread.

A circular buffer looks fast, but it looks that it only holds references. This doesn't improve much. Besides, you are getting new buffers from the udpClient: Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint); Circular buffers are only usefull when 'old' memory is reused.

Try a concurrent queue to pass data from the receive thread to the gui-thread/timer. Be sure that rebinding/drawing is outside the lock of the concurrent queue. Now you're getting performance to receive datagrams.


UPDATE:

Some pseudo code:

Back:

private List<byte[]> datagrams = new List<byte[]>();

public void serverThread()
{
    UdpClient udpClient = new UdpClient(Convert.ToInt16(tbEthPort.Text));
    while (_serverWork)
    {
        try
        {
            IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, Convert.ToInt16(tbEthPort.Text));
            Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint);

            // add to the queue
            lock (datagrams)
                datagrams.Add(receiveBytes);
        }
    }
}

GUI:

private Timer timer = new Timer();

public void timer_Tick(object sender, EventArgs e)
{
    byte[][] data;

    // lock the queue as short as possible. (create a copy with ToArray())
    // this way the receive thread can run again..
    // this is also know as bulk processing..
    lock (datagrams)
    {
        data = datagrams.ToArray();
        datagrams.Clear();
    }

    // if no packets received, don't update anything
    if(data.Length == 0)
        return;

    // process the received data (multiple datagrams)
    for(byte[] item in data)
    {
        ...
    }
    // Update chart
}

You might check if the queue doesn't grow too large. When this happens, your queue processing is too slow. You could limit the itemcount.

Upvotes: 2

Related Questions