使用Control.Invoke和Delegates延迟从串行端口绘制数据

时间:2014-05-03 20:56:28

标签: c# serial-port zedgraph

我正在从串口读取数据值,并使用invoke和delegate在zedgraph控件上实时绘制数据。我想点击一个将停止绘图的断开按钮,当按下连接按钮时,绘图将恢复。我的问题是当我重新连接时,绘图将不会以每秒1点的速度显示。将出现意外行为,有时包括不会在2秒内绘制的延迟,下一秒会同时绘制2个点。有时它不会绘制3秒和下一秒,它会同时绘制3点,有时它可以很好地绘制每秒1点的绘图。有时它会每秒绘制两次或三次。仅当我断开连接然后重新连接时才会出现此问题。每次断开连接并重新连接时,我的GUI响应也会变得更糟,直到最终没有响应。

当我点击断开连接时,我会清除zedgraph但不要关闭串口。当我再次重新点击连接时,串口将再次开始发送数据并继续绘图,每次在zedgraph上绘制新数据点时调用control.invoke。我调用的问题是否会调用太多次,我将如何解决这个问题?我需要实时绘图,所以每次收到数据点时,都要立即在zedgraph上绘制。

以下是我连接串口的方法:

private void btnConnect_Click_1(object sender, EventArgs e)
    {
        curveUSB = myPaneUSB.AddCurve("Load", listUSB, Color.Black, SymbolType.Circle);
        isConnected = true;

        DateTime startTime = DateTime.Now;
        txtStartTime.Text = startTime.ToString();
        sw.Start();
        createCSVFile(startTime);

        try
        {
            if (!serialPort1.IsOpen)
            {

                serialPort1.PortName = cmbPort.Items[cmbPort.SelectedIndex].ToString();


                //Other Serial Port Property
                serialPort1.Parity = Parity.None;
                serialPort1.StopBits = StopBits.One;
                serialPort1.DataBits = 8;
                serialPort1.BaudRate = 9600;
                //Open our serial port
                serialPort1.Open();
            }
        }
        catch(Exception ex)
        {
            MessageBox.Show(ex.Message);
        }



        Thread.Sleep(100); //Always sleep before reading
        serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceivedHandler);

        // Save the beginning time for reference
        tickStart = Environment.TickCount;  //used the calculate the time 
        setGraphAxis(myPaneUSB, zgControlUSB);
        //Disable Connect button
        btnConnect.Enabled = false;
        //Enable Disconnect button
        btnDisconnect.Enabled = true;        
    }

Invoke和serialPort1_DataReceivedHandler:

private void serialPort1_DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
    {
        if (isConnected == true)
        {
            txtUSBLoad.Invoke(new EventHandler(delegate
            {

                strRawData = serialPort1.ReadLine();

                txtUSBLoad.Text = strRawData + loadUnit;
                Display_Data(strRawData, curveUSB, listUSB, zgControlUSB);


            }));
        }
    }

Display_Data函数显示图:

private void Display_Data(String data, LineItem curve, IPointListEdit list, ZedGraphControl zgControl) 
    {

        ts = sw.Elapsed;
        tsBT = swBT.Elapsed;
        elapsedTime = String.Format("{0:00}:{1:00}:{2:00}",tsBT.Hours, tsBT.Minutes, tsBT.Seconds);
        elapsedTimeBT = String.Format("{0:00}:{1:00}:{2:00}", tsBT.Hours, tsBT.Minutes, tsBT.Seconds);
        txtElapsedTime.Text = elapsedTime;
        txtBTElapsedTime.Text = elapsedTimeBT;


        if (zgControl.GraphPane.CurveList.Count <= 0)                    //Make sure that the curvelist has at least one curve
            return;
        curve = zgControl.GraphPane.CurveList[0] as LineItem;            //Get the first CurveItem in the graph
        if (curve == null)
            return;
        list = curve.Points as IPointListEdit;                                  //Get the PointPairList
        if (list == null)                                                       //If this is null, it means the reference at curve.Points does not
            return;                                                             //support IPointListEdit, so we won't be able to modify it
        time = (Environment.TickCount - tickStart) / 1000.0;             //Time is measured in seconds
                                             //Get current time        
        now = DateTime.Now;
        timestamp = now.ToOADate();
        list.Add(timestamp, Convert.ToDouble(data)); 
        if(fileWriter.BaseStream != null){  //If fileWriter is open, then write to file, else don't write
            fileWriter.WriteLine(String.Format("{0:yyyy-MM-dd_HH-mm-ss}", now) + "," + data);   //writes the timestamp and data to the CSV file
        }
        if (Convert.ToDouble(data) > tempLoad)  //Checks if the current load is the maximum load
        {
            tempLoad = Convert.ToDouble(data);
            txtMaxLoad.Text = tempLoad.ToString() + " " + loadUnit;
            txtBTMaxLoad.Text = tempLoad.ToString() + " " + loadUnit;
        }
        //if ((Convert.ToDouble(data) > dblThreshold) && (emailAlerts == true)) { 
        if ((Convert.ToDouble(data) > 50) && (emailAlerts == true))
        { 
            sendEmail(Convert.ToDouble(data));
        }

        XDate dateTime = curve[0].X;                                   

        zgControl.AxisChange();                                          //changes the x-axis  
        zgControl.Invalidate();                                          //Force a redraw
    }

断开按钮:

 private void btnDisconnect_Click_1(object sender, EventArgs e)
    {
        zgControlUSB.GraphPane.CurveList.Clear();
        if (fileWriter != null)
        {
            fileWriter.Close(); 
        }
        isConnected = false;
        btnConnect.Enabled = true;
        btnDisconnect.Enabled = false;
        txtUSBLoad.Text = initText;
        DateTime endTime = DateTime.Now;
        txtEndTime.Text = endTime.ToString();   //displays the time stamp when the plotting stops
        sw.Stop();  //stops the stop watch 

    }

当我断开连接时,我不关闭串口,因为当我这样做时问题更严重,我读到总是关闭并重新打开串口是不好的。相反,我使用isConnected标志来开始和停止绘图。 任何帮助表示赞赏!提前致谢!

1 个答案:

答案 0 :(得分:2)

调用txtUSBLoad.Invoke()会将处理切换到GUI线程(用于处理用户输入和绘制控件的同一线程)。通过将所有处理置于内部调用Invoke(),您实际上正在淹没GUI线程。

如果您的串行更新频繁,则会导致GUI的交互/重新绘制延迟。

这里有几个选项,第一件事是将所有处理保留在从串口接收消息的线程中。查看您正在更新的所有可视化控件,并确定需要更新哪些数据,并在调用Invoke()之前处理的所有数据。创建一个类来保存数据。

这可能太慢了,这意味着您将积累大量的串行数据,您无法及时处理。如果是这样,如果您对丢失更新感到满意,则可以混合传入的数据。您必须看看这是否适用于您的情况。

如果您想尝试并行处理传入数据,请查看TPL Dataflow,以获取如何为数据创建处理管道的示例。