0

I am trying to plot real time data. A circuit sends data rapidly and I know that the first byte is '$', next 16 bytes as sound and last byte is pulse sensor data.I am using a method that stores data to an array and it continuously adds data inside array starting from the last index.

byte[] Read_Data1 = new byte[100000];
byte[] Read_Data2 = new byte[100000];
byte[] Read_Data3;
private void myPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    if (!myPort.IsOpen)
    return;
    while (myPort.BytesToRead > 0)
    {
        int bytes = myPort.BytesToRead;
        byte[] buffer = new byte[bytes];
        myPort.Read(buffer, 0, bytes);
        bytetobyte(Read_Data1, buffer, buffer.Length, count);
        count += buffer.Length;
    }
}
public void bytetobyte(byte[] Storage, byte[] databyte, int datacount, int count)
{
    //count comes from count += buffer.Lenght
    int abc;
    for (abc = 0; abc < datacount; abc++)
    {
        Storage[abc+count] = databyte[abc];
    }
}

Then I start to process the data using a method that works with timer's tick. I should remove first byte '$' and need to save 16 bytes to a list and 1 byte to another list. Here is the method:

byte[] Read_Data3;
LinkedList<byte> data;
public void DrawingAudioData(byte[] data) //This method works inside timer.
{
    Read_Data2 = Read_Data1;
    int lastCount = count;
    int division = count / 18;
    int remaning = (count - 18 * division);

    Read_Data3 = new byte[count - remaning];

    for (int i = 0; i < count - remaning ; i++)
    {
        Read_Data3[i] = Read_Data2[i];
    }
    count = 0;

    IPointListEdit listAuido = curveAudio.Points as IPointListEdit;
    IPointListEdit listPulse = curvePulse.Points as IPointListEdit;

    XDate time = new XDate(DateTime.Now);

    if (Read_Data3 == null)
    return;
    data= new LinkedList<byte>(Read_Data3);

    if(data.First == null)
    return;
    if (data.Count >= 18 & data.ElementAt(0) == Convert.ToByte('$'))
    {
        data.Remove(veri.ElementAt(0));
        for (int x = 0; x < 16; x++)
        {
            listAuido.Add(time, data.ElementAt(0));
            data.Remove(data.ElementAt(0));
        }
        listPulse.Add(time, data.ElementAt(0));
        data.Remove(data.ElementAt(0));
    }
    lastCount = 0;
}

I think that when timer ticks I should have a new byte because serialport sends data rapidly and data are changed inside of Read_Data1. After that I equalize count to zero. Because data still flows and I don't want to my array goes out of rang. Then I start to process the new Read_Data2. I must process each 18 bytes as I said before so I trim the Read_Data2 and removed the reamaning last few data and I equalize Read_Data3[i]=Read_Data2[i]; Then using a LinkedList that includes Read_Data3, I tried to write a loop that removes the first data after fill it into a list. After processing LinkedList, lastCount is equaled zero because I should set a new size according to new data set. Looks the logic is true but I have a problem. Sometimes the pulse sensor data is listing into the audio data. I mean the data should be inside the listPulse but it goes into the listAudio.

First of all I want to know approach is a correct approach. I mean using a timer is a good idea for rapid data? Please share your opinions. If you have a better way please tell me or give a reference article or samples' link. Thank you.

Noctis
  • 11,507
  • 3
  • 43
  • 82
Blast
  • 955
  • 1
  • 17
  • 40
  • What you have here seems a concurrency problem – Salvatore Sorbello Nov 11 '13 at 12:48
  • i`d agree with @SalvatoreSorbello. how quick do you get the data, and how quick can you process it? what kind of time resolution do you need? – Noctis Nov 11 '13 at 12:54
  • The circuit is 1 khz and baudrate is 38400. I need to plot data each 20 ms or less. @SalvatoreSorbello you mean timer works before the method completes the cycle? – Blast Nov 11 '13 at 13:38
  • @Blast yes. Consider that this is one of the most headache you can take when programming, you should ad last use a `lock()` but the most efficent solution depends on your entire code, not just the method – Salvatore Sorbello Nov 11 '13 at 14:26
  • Now I am trying to write a thread inside the SerialPort_DataRecived event. Actually I have already written it. It trims the data but I could not fill the data to the lists. If you know a better approach instead using threading inside the DataRecieved event or `lock` please share me.And forgot to say thank your for your response. – Blast Nov 11 '13 at 14:39

2 Answers2

0

Try to modify your code in this way:

byte[] Read_Data3;
LinkedList<byte> data;
object w=new object(w)
public void DrawingAudioData(byte[] data) //This method works inside timer.
{
  lock(w}
  {
  //Your previous code here
  }
}

In this way you should not have anymore the problem about mixed data between listPulse, listAuido, Read_Data3, Read_Data2.

For working properly the method must be executed in a period of time < of the timer

0

Instead of a timer I would use double-buffering with a lock or mutex to control access to the buffer which would be a shared resource for reading and writing. Also, the SerialPort.Read(byte[],int,int) method when called from inside the SerialPort.DataReceived event is already automatically executed on a new thread.

The problem comes when you try to read and write to the same memory location at the same time. One way around this is to use 2 buffers (arrays of type byte) and write to one of them while your graphing code reads from the other. In this case the buffers are called shared resources and their access should be regulated. You can use a lock but I usually use one mutex for each buffer. After you instantiate a mutex you call the "WaitOne" method which will force any other code that wants to use the shared resource to wait until the mutex is released. It might look something like this.

  void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        if (writeToBuffer1)
        {
            numToRead1 = SerialPort.BytesToRead;
            mutex1.WaitOne();
            numRead1 = SerialPort.Read(buffer1, 0, numToRead1);
            mutex1.ReleaseMutex();
            writeToBuffer1 = false;
            PlotData(buffer1, numRead1);
        }
        else
        {
            numToRead2 = SerialPort.BytesToRead;
            mutex2.WaitOne();
            numRead2 = SerialPort.Read(buffer2, 0, numToRead2);
            mutex2.ReleaseMutex();
            writeToBuffer1 = true;
            PlotData(buffer2, numRead2);
        }
    }

Your PlotData method will also need access to mutex1 and mutex2. Surround the code where you read the bytes with the WaitOne and ReleaseMutex methods. You will also need to learn how to write a thread-safe call to your plot data method so it can be called by a thread other than the one it was created on. Thread-safe method

Community
  • 1
  • 1
skinnedknuckles
  • 371
  • 3
  • 12
  • Thank you for your suggestions but I need to clear my mind. You mean I should use thread with double-buffering inside the SerialPort_DataReceived event instead of timer? I am asking that because I do not have enough knowledge about double-buffering,lock and mutex. – Blast Nov 16 '13 at 08:13
  • 1
    I have edited my answer and added detail. See above. – skinnedknuckles Nov 27 '13 at 21:24