我正在从串口读取数据值,并使用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标志来开始和停止绘图。 任何帮助表示赞赏!提前致谢!
答案 0 :(得分:2)
调用txtUSBLoad.Invoke()会将处理切换到GUI线程(用于处理用户输入和绘制控件的同一线程)。通过将所有处理置于内部调用Invoke(),您实际上正在淹没GUI线程。
如果您的串行更新频繁,则会导致GUI的交互/重新绘制延迟。
这里有几个选项,第一件事是将所有处理保留在从串口接收消息的线程中。查看您正在更新的所有可视化控件,并确定需要更新哪些数据,并在调用Invoke()之前处理的所有数据。创建一个类来保存数据。
这可能太慢了,这意味着您将积累大量的串行数据,您无法及时处理。如果是这样,如果您对丢失更新感到满意,则可以混合传入的数据。您必须看看这是否适用于您的情况。
如果您想尝试并行处理传入数据,请查看TPL Dataflow,以获取如何为数据创建处理管道的示例。