【问题标题】:C# real time chart desktop application too slowC#实时图表桌面应用程序太慢
【发布时间】:2014-11-08 02:30:33
【问题描述】:

我从实现桌面应用程序的人手中接过C#代码以从串行端口读取实时数据并显示它在图表中(使用图表类)。

代码似乎可以运行,但是非常慢。它似乎每 4 秒更新一次图表(0.25Hz)。但是,我可以看到它是多线程的,并且没有延迟命令,所以我不明白为什么它运行这么慢。图表的更新会减慢它的速度吗?理想情况下,我希望达到 每秒 1000 个数据点 (1kHz),实时显示并保存到硬盘,其中每个数据点的大小约为 30 字节。

我花了几天时间理解代码,但是太繁琐了,全部写在一个文件中,没有 cmets。 我每秒读取 1000 个数据点的目标是否现实/可实现?

我也在考虑重新编写代码(而不是尝试修复它),因为它只有大约 2,500 行 长。任何提示将不胜感激。另外,如果我重写代码,哪种语言可能更适合这个应用程序

【问题讨论】:

  • 你是认真的吗? 30Bytes*1000data/sec 使用串口?不,一点都不现实!您可以根据需要重新编写代码,但您必须再次考虑您的规格,您能告诉我您的波特率是多少吗?
  • 30 kB/s 真的这么多吗?目前波特率为38400。但会相应增加。我不太担心硬件。我想知道“常规”计算机(i7 3Ghz 8G ram)是否可以跟上实时图表显示和保存。
  • 这是 30,000 字节/秒,即每秒 240,000 位。我见过的串行端口上的最高波特率是每秒 230,400 位,这仅略低于您所需的吞吐量。
  • 顺便说一句,处理 30 字节的数据类型是什么?一定有一些数据处理吧?
  • 您希望一次在图表上显示多少点?如果您想每秒用 1000 个(或很多)新点更新绘图,但删除最旧的 1000 个点,这不是问题。如果您在添加新点的同时保留旧点,最终您的应用程序将窒息。当您有 10k 或 20k 个点时,图表渲染会变得非常慢。

标签: c# multithreading charts serial-port real-time


【解决方案1】:

我开发了一些代码,我的性能得到了显着提升,它可能对你有用。这就是我所做的-

第 1 步:我会首先识别哪个是瓶颈,drawing/rendering of the chart

serial port

第 2 步:如果您发现它的渲染 - 然后将其添加到您的表单/图表设置中,它将绘制得更快。但首先请仔细检查以确保您没有处于远程桌面模式。

 <!-- language: c# -->

    // No double buffering for remote, only local
    public bool OptimizeOfLocalFormsOnly(System.Windows.Forms.Control chartControlForm)
    {
     if (!System.Windows.Forms.SystemInformation.TerminalServerSession)
     {
              SetUpDoubleBuffer(chartControlForm);
              return true;
     }
     return false;

    }

    public static void SetUpDoubleBuffer(System.Windows.Forms.Control chartControlForm)
    {

       System.Reflection.PropertyInfo formProp = 
       typeof(System.Windows.Forms.Control).GetProperty("DoubleBuffered",         System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);    
       formProp.SetValue(chartControlForm, true, null); 
    }

【讨论】:

    【解决方案2】:

    我假设您使用的是 winform 应用程序:

    使用serialPort 组件:

    1. 配置其properties:(BaudRate、DataBits、StopBits、Parity ...)
    2. 利用其event (DataReceived) 收集您的输入。

    您可以循环发送命令并收集输入/在图表组件上绘制它们,大致如下:

    while(/*some condition*/)
    {
      serialPort.Write(/* your command */);
    
      // you have to think of response time
      // so implement something that waits a bit before calling the port again
      // I would do it using timers
    
      int tick= Environment.TickCount;
      while(Environment.TickCount - tick <= 100) // wait 100 milliseconds
      { 
        Application.DoEvents();
      }
    
    }
    
    // collect the data as:
    private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
      // use according to your needs, for example
      string data = "";
      int readByte;
      int available;
    
      available = serialPort.BytesToRead;
      if(available >= 30) // 30 bytes as stated in your question
      {
        for(int i=0; i<30; i++)
        {
          readByte = serialPort.ReadByte();
          data += String.Format("{0:2X}", readByte);
        }
    
        // you can call some methods to save/display your collected data from here
        save_to_file(data);
        display_to_chart(data);
      }
    
    }
    

    【讨论】:

      【解决方案3】:

      我开发了一个类似的应用程序,显示 16 个图表 * 256 个样本/秒。将数据存储在缓冲区中并创建一个单独的线程来更新图表对我有用。

      读取新数据时,数据存储在列表或数组中。由于是实时数据,所以这里也会生成时间戳。使用采集数据的采样率: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); 
              }
          }
      

      AddData 方法,只需读取存储在缓冲区中的 X(时间戳)和 Y 值,并使用 ptSeries.Points.AddXY(xValue, yValue) 将其添加到图表中

      【讨论】:

        猜你喜欢
        • 2012-07-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-18
        • 2012-09-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多