【问题标题】:C# Drawing multiple charts in real timeC#实时绘制多个图表
【发布时间】:2015-09-30 18:55:26
【问题描述】:

我有一个读取数据并将其添加到缓冲区的采集设备。这是在一个单独的线程中完成的。一旦这些数据出队,我就会设置一个委托来引发 OnDataRead() 事件。

在我的信号监视器中,当收到事件时,我想在多个图表中绘制数据(总共 16 个)。我成功地绘制了第一个样本,但随后图表停止绘制数据,从 16 号开始并逐渐停止所有这些样本。这样做的正确方法是什么?我需要一个单独的线程吗?

接收带有数据的事件

    public void OnDataRead(object source, DataEventArgs e)
    {
        if (e.rawData.Length > 0)
        {
            AddData(e.rawData, e.numChannels, e.triggerEnabled);
        }
    }

在适当的图表中添加数据点

 private void AddData(float[] rawData, int numChannels, bool triggerEnabled)
    {
        //for (int i=0; i<chChannels.Count; i++)
       // {
            if (this.InvokeRequired)
            {
                SetAddDataCallback d = new SetAddDataCallback(AddData);
                this.Invoke(d, new object[] {rawData, numChannels, triggerEnabled})
                    ;
            }
            else
            {
                DateTime timeStamp = DateTime.Now;

                // Find the channel selected
                int channelIdx = 0; //boxChannelSelection.SelectedIndex;
                // Add new data points for the selected channel
                for (int sampleIdx = 0; sampleIdx < rawData.Length; sampleIdx++)
                {
                    channelIdx = sampleIdx %(numChannels + 1);

                    if (triggerEnabled && (channelIdx != numChannels))
                    {
                        foreach (Series ptSeries in chChannels[channelIdx].Series)
                        {
                            AddNewPoint(timeStamp, rawData[sampleIdx], channelIdx, ptSeries);
                        }
                    }
                    else if (!triggerEnabled)
                    {
                        foreach (Series ptSeries in chChannels[channelIdx].Series)
                        {
                            AddNewPoint(timeStamp, rawData[sampleIdx], channelIdx, ptSeries);
                        }

                    }
                    }
                }
           // }
    }

在图表中绘制新数据点

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

            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);
            }

            chChannels[channel].ChartAreas[0].AxisX.Minimum = ptSeries.Points[0].XValue;
            chChannels[channel].ChartAreas[0].AxisX.Maximum = DateTime.FromOADate(ptSeries.Points[0].XValue).AddSeconds(10).ToOADate();

            chChannels[channel].Invalidate();

【问题讨论】:

  • 绘图何时停止:删除第一批点之前还是之后?你为什么写(double)(8) * (-1)?删除后移动 addxy 会改变什么吗?
  • 我在删除第一批点之前停下来。 (double)(8) * (-1) 是删除当前点前8秒的点,不断更新情节。
  • 我认为一个好的方法是当收到新数据时,将其添加到缓冲区(链表),然后有一个单独的线程,每 100 毫秒使用存储在缓冲区中的数据更新图表。导致它崩溃的原因是每次收到新数据时都会更新 16 个图表。你怎么看?

标签: c# forms charts real-time


【解决方案1】:

这是一种在不同线程中绘制图表中数据的好方法。

读取新数据时,数据存储在列表或数组中。由于是实时数据,所以这里也会生成时间戳。使用采集数据的采样率:timeStamp = timeStamp + sampleIdx/sampleRate;

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

                       // Calculate corresponding timestamp
                      secondsToAdd = (float) sampleIdx/e.sampleRate;

                    // Append corresponding timestamp
                    _bufferXValues.Add( e.timeStamp.AddSeconds(secondsToAdd));
                    }
                }

然后,创建一个每N ms休眠的线程(100ms适合我显示2秒的数据,但是如果我想显示10秒,我需要增加线程的休眠时间到500ms)

 //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();

最后,更新图表并让线程每 N 毫秒休眠一次

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

            // Sleeep thread for 100ms
            Thread.Sleep(100); 
        }
    }

数据将每 100 毫秒添加到图表中

private void AddData()
    {
        // Copy data stored in lists to arrays
        float[] rawData;
        DateTime[] xValues;

            if (_bufferRawData.Count > 0)
            {
                // Copy buffered data in thread-safe manner
                lock (_bufferRawData)
                {
                    rawData = _bufferRawData.ToArray();
                    _bufferRawData.Clear();
                    xValues = _bufferXValues.ToArray();
                    _bufferXValues.Clear();
                }

                for (int sampleIdx = 0; sampleIdx < rawData.Length; sampleIdx++)
                {
                        foreach (Series ptSeries in chChannels[channelIdx].Series)
                            // Add new datapoint to the corresponding chart (x, y, chartIndex, seriesIndex)
                            AddNewPoint(xValues[sampleIdx], rawData[sampleIdx], ptSeries);
           }
      }
 }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    • 2012-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多