2015-10-14 337 views
1

我有一個讀取數據並將其添加到緩衝區的設備。這是在一個單獨的線程中完成的。一旦這些數據出列,我就設置了一個引發事件OnDataRead()的委託。C#在單獨的線程中實時繪製多個圖表

在我的信號監視器中,當收到事件時,我想繪製多個圖表中的數據(共16個)。由於我有16個圖表,而不是每次收到新數據時更新圖表,所以我將它們添加到數據和時間戳記的緩衝區中。通過讀取存儲在緩衝區中的數據和時間戳,每隔100ms在一個單獨的線程中更新圖表。但是,當繪製數據時,有些圖表會停止添加數據,並且在所有圖表中,很多值不會顯示。這是一個壞方法嗎?什麼是更好的方法,或者我應該改變以使其工作?

我有256samples /秒* 16channels。

這是我得到 signal monitor (16 channels)

這是我所期望的,但對所有16個信道

enter image description here

public void OnDataRead(object source, DataEventArgs e) 
    { 
     if ((e.rawData.Length > 0) && (!_shouldStop)) 
      { 
       for (int sampleIdx = 0; sampleIdx < e.rawData.Length; sampleIdx++) 
       { 
        lock (_bufferRawData) 
         // Append data 
         _bufferRawData.Add(e.rawData[sampleIdx]); 

        lock (_bufferXValues) 
         _bufferXValues.Add(DateTime.Now); 

       } 
    } 

private void AddDataThreadLoop() 
     { 
      while (!_shouldStop) 
      { 
       chChannels[1].Invoke(addDataDel); 

       Thread.Sleep(100); //sleeps for 100ms 
      } 
     } 

private void AddData() 
     { 

      // Copy data stored in lists to arrays 
      if (_bufferRawData.Count > 0) 
      { 
       float[] rawData; 
       lock (_bufferRawData) 
       { 
        rawData = _bufferRawData.ToArray(); 
        _bufferRawData.Clear(); 
       } 
       DateTime[] xValues; 
       lock (_bufferXValues) 
       { 
        xValues = _bufferXValues.ToArray(); 
        _bufferXValues.Clear(); 
       } 

       // Add new data points for the selected channel chart 
       int channelIdx = 0; 

       for (int sampleIdx = 0; sampleIdx < rawData.Length -1; sampleIdx++) 
       { 
        // Calculate the channel where the smaple corersponds 
        channelIdx = sampleIdx % (_numChannels + 1); 

        foreach (Series ptSeries in chChannels[channelIdx].Series) 
          // Add new datapoint to the corresponding chart (x, y, chartIndex, seriesIndex) 
          AddNewPoint(xValues[sampleIdx], rawData[sampleIdx], channelIdx, ptSeries); 

       } 
      } 
     } 

public void AddNewPoint(DateTime timeStamp, float yValue, int chartIDx, System.Windows.Forms.DataVisualization.Charting.Series ptSeries) 
    { 

     //Add datapoint 
     ptSeries.Points.AddXY(timeStamp.ToOADate(), yValue); 

     // Remove old datapoints if needed 
     double removeBefore = timeStamp.AddSeconds((double)(8) * (-1)).ToOADate(); 
     while (ptSeries.Points[0].XValue < removeBefore) 
     { 
      ptSeries.Points.RemoveAt(0); 
     } 

     // Modify minimum and maximum for new samples 
     chChannels[chartIDx].ChartAreas[0].AxisX.Minimum = ptSeries.Points[0].XValue; 
     chChannels[chartIDx].ChartAreas[0].AxisX.Maximum = DateTime.FromOADate(ptSeries.Points[0].XValue).AddSeconds(10).ToOADate(); 
     chChannels[chartIDx].ChartAreas[0].AxisY.Maximum = _yMax; 
     chChannels[chartIDx].ChartAreas[0].AxisY.Minimum = -_yMax; 

     chChannels[chartIDx].Invalidate(); 
    } 

private void btnPlay_Click(object sender, EventArgs e) 
{ 
    //Create thread 
    //define a thread to add values into chart 
    ThreadStart addDataThreadObj = new ThreadStart(AddDataThreadLoop); 
    addDataRunner = new Thread(addDataThreadObj); 
    addDataDel += new AddDataDelegate(AddData); 

    //Start thread 
    addDataRunner.Start(); 
    } 

EDIT1: chChanels是圖表,其中的列表每個元素對應於其中一個圖表。 public List chChannels;

EDIT2 爲此更改鎖定後,將更新所有圖表。然而,每個圖表的很多樣本仍未更新。

lock (_bufferRawData) { 
     for (int sampleIdx = 0; sampleIdx < e.rawData.Length; sampleIdx++) 
     { 
      // Append data 
      _bufferRawData.Add(e.rawData[sampleIdx]); 
      _bufferXValues.Add(DateTime.Now); 
     } 
    } 

enter image description here

+0

什麼是chChannels?恐怕除非你提供一個實際可以編譯和測試的例子,否則任何試圖幫助你都很困難。 –

回答

0

有在代碼兩個問題:

1)使用在非有效的方式鎖定。我用普通鎖取代了兩個單獨的鎖。我也將它鎖定在所有迭代中,而不是像每個迭代中那樣做,正如其中一個答案中指出的那樣。這應該是怎樣的

lock (_bufferRawData) { 
     for (int sampleIdx = 0; sampleIdx < e.rawData.Length; sampleIdx++) 
     { 
      // Append data 
      _bufferRawData.Add(e.rawData[sampleIdx]); 
      _bufferXValues.Add(DateTime.Now); 
     } 
    } 

2)我生成的時間戳與收集數據時的時間戳不一致。因此,幾個Y樣本具有相同或相似的時間戳。

一個解決方案是爲每個樣本生成確切的時間戳。這應該在樣品被採集時完成。但是,由於頻率爲256Hz,因此每4ms對應1個採樣。 DateTime.Now的精度約爲15ms,這使得這種方法不適用於256Hz的頻率。但是,對於較低的頻率,這將是一個好方法。

正確的方法是在繪製數據時確定圖形的X軸值。由於頻率固定在256Hz,很容易知道Y採樣的X值。

0

這很難說,如果這是你唯一的問題,但至少該站出來對我說:

  1. 使用兩個單獨的鎖的時間戳和原始數據緩衝區。這意味着緩衝區可能不表示相同的一系列數據。你可能想用一個鎖來訪問這兩個緩衝區:

    lock (_bufferRawData) { 
        // Append data 
        _bufferRawData.Add(e.rawData[sampleIdx]); 
        _bufferXValues.Add(DateTime.Now); 
    } 
    

其實,我也建議移動鎖定了for循環的。這是超級低效鎖定在每次迭代:

lock (_bufferRawData) { 
     for (int sampleIdx = 0; sampleIdx < e.rawData.Length; sampleIdx++) 
     { 
      // Append data 
      _bufferRawData.Add(e.rawData[sampleIdx]); 
      _bufferXValues.Add(DateTime.Now); 
     } 
    } 
  • ,你這樣做的另一件事,可以改善(但不一定解釋爲什麼數據正在奇怪)你是否在安排更新的線程中休眠。你可能應該用定期觸發的定時器替換它,並執行更新。而且,由於您正在UI線程上調用更新,因此您可能會從使用UI定時器中受益。
  • +0

    我改變了鎖定,現在圖表總是被更新。但是,很多值仍然被忽略。它可能與DateTime.Now有關嗎?或者按照你的建議讓睡眠恢復正常。 – nabrugir

    +0

    @nabrugir - 是的,它很有可能是DateTime.Now導致麻煩。 DateTime.Now在該級別上不可靠。您可能想要使用高精度定時器(請參閱System.Diagnostics.StopWatch),或者甚至可以重新評估您的時間戳策略。 –