我正在以115200的波特率从arduino读取数据。数据以字符串的形式出现在其自己的行中,格式为:<ID,Name/Data>
。
我认为我的代码存在的问题是它没有足够快地处理传入数据,并且强制传入数据等待旧数据的处理。
传入的字符串被拆分为三个单独的类别(ID,名称,数据),并添加到名为dtFromGrid
的数据表中,该数据表绑定到dataGridView1
。
是否有任何关于如何提高代码性能的错误或建议?处理函数的单独线程是否比BeginInvoke
更好?
serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived);
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string inData = serialPort1.ReadLine();
if (PauseButton.Text == "Pause" && inData.StartsWith("<"))
{
try
{
this.BeginInvoke(new SetGridDeleg(DoUpdate), new object[] {inData});
}
catch
{
}
}
}
private void DoUpdate(string inData) //inData passed in so that Serial port read only once
{
if (dtFromGrid == null)
{
dtFromGrid = new DataTable();
dtFromGrid.Columns.Add("Time", typeof(String));
dtFromGrid.Columns.Add("ID", typeof(String));
dtFromGrid.Columns.Add("Name", typeof(String));
dtFromGrid.Columns.Add("Data", typeof(String));
}
DataRow dr = dtFromGrid.NewRow();
TimeSpan ts = stopWatch.Elapsed;
dr["Time"] = String.Format("{0:00}:{1:00}:{2:00}.{3:000}",
ts.Hours, ts.Minutes, ts.Seconds,
ts.Milliseconds);
dr["ID"] = inData.Split(new char[] { '<', ',' })[1];
dr["Name"] = inData.Split(new char[] { ',', '/' })[1];
dr["Data"] = inData.Split(new char[] { '/', '>' })[1];
dtFromGrid.Rows.InsertAt(dr, 0);
//Replace old data with new data if ID's are the same to showo list of only newest data per each ID
if (NewestButton.Text == "Chronological")
{
for (int i = 1; i < dataGridView1.Rows.Count; i++)
{
if (dtFromGrid.Rows[i].ItemArray[1].ToString() == dtFromGrid.Rows[0].ItemArray[1].ToString())
{
dtFromGrid.Rows[i].Delete();
break;
}
}
}
//Keep a maximum of 50 rows of data
if (dtFromGrid.Rows.Count == 51)
{
dtFromGrid.Rows[50].Delete();
}
dtFromGrid.AcceptChanges();
dataGridView1.DataSource = dtFromGrid;
//keep focus of dataGridView on top row
dataGridView1.CurrentCell = dataGridView1.Rows[0].Cells[0];
// add newest row to a logfile if the user has set one
if (logFile != "")
{
using (StreamWriter sw = File.AppendText(logFile))
{
DataRow row = dtFromGrid.Rows[0];
object[] array = row.ItemArray;
int col = 0;
for (col = 0; col < array.Length - 1; col++)
{
sw.Write(array[col].ToString() + "\t|\t");
}
sw.Write(array[col].ToString());
sw.WriteLine();
sw.Close();
}
}
}
更新
我现在正在使用一个单独的线程,但我在调用该线程内部时遇到错误。我随机得到多个错误,但最常见的是“索引超出范围”。我的调用代码如下:
this.Invoke((MethodInvoker) delegate
{
dtFromGrid.AcceptChanges();
dataGridView1.DataSource = dtFromGrid;
dataGridView1.CurrentCell = dataGridView1.Rows[0].Cells[0];
});
答案 0 :(得分:0)
将数据存储在队列中并将工作卸载到辅助线程。这只适用于平均来说,您能够以其进入的速率处理数据。否则,当您落后时,队列的大小将继续增长。
首先,从Queue<T>
周围的包装器开始,它将允许一个线程写入队列,另一个线程以线程安全的方式从中读取。此外,允许读者线程阻止等待数据。
public class ThreadedQueue<T>
{
private readonly Queue<T> _queue = new Queue<T>();
private readonly ManualResetEvent _notEmptyEvt = new ManualResetEvent(false);
public WaitHandle WaitHandle { get { return _notEmptyEvt; } }
public void Enqueue(T obj)
{
lock (_queue)
{
_queue.Enqueue(obj);
_notEmptyEvt.Set();
}
}
public T Dequeue()
{
_notEmptyEvt.WaitOne(Timeout.Infinite);
lock (_queue)
{
var result = _queue.Dequeue();
if (_queue.Count == 0)
_notEmptyEvt.Reset();
return result;
}
}
}
在串口处理程序中,将数据写入队列:
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string inData = serialPort1.ReadLine();
if (PauseButton.Text == "Pause" && inData.StartsWith("<"))
{
_queue.Enqueue(inData);
}
}
在辅助线程中,从队列中读取并调用GUI线程:
private void ThreadProc()
{
while (true)
{
string inData = _queue.Dequeue();
this.Invoke(new SetGridDeleg(DoUpdate), new object[] {inData});
}
}
启动辅助线程,如下所示:
Thread th = new Thread(ThreadProc);
th.IsBackground = true;
th.Start();
当然,您需要创建队列实例:
ThreadedQueue<string> _queue = new ThreadedQueue<string>();
答案 1 :(得分:0)
我通常设计一个SerialService类来管理SerialPort。下面是SerialService类的简单版本。
SerialService类的作用是尽可能快地读取串行缓冲区。这会清除缓冲区并防止出现任何串口错误。然后将此原始数据传递给解析器。
性能的诀窍在于解析器。 YourParser也应该快速将原始数据格式化为您期望的字符串。解析数据后,您可以使用回调或事件。通过回调或事件,您的解析器将继续解析新到达的数据。 YourParse现在是一个可测试的类。
一旦从解析器的回调中获得了良好的数据,就可以使用BeginInvoke将数据发送到主线程,然后你可以在其中显示它。
如果您不在主UI线程中,并且尝试从另一个线程更新UI,则会遇到交叉问题。
祝你好运。class Program
{
private static YourDataParser _parser;
static void Main(string[] args)
{
_parser = new YourDataParser();
var serial = new SerialService("COM1");
serial.DataReceived += serial_DataReceived;
}
static void serial_DataReceived(object sender, DataReceivedEventArgs e)
{
_parser.HandleTheData(e.Data, good =>
{
// here is your good data
// This is not the main thread invoke your UI from here with the good data
// Use BeginInvoke to invoke the main thread
});
}
}
public class YourDataParser
{
private List<byte> _buffer = new List<byte>();
public void HandleTheData(byte[] rawdata, Action<string> goodData)
{
_buffer.AddRange(rawdata);
foreach (var b in _buffer)
{
var thechar = (char) b;
// handle your raw data... like look for the character '<'
// or look for the end of line this would be CR (0x0D) LF (0x0A)
// you can reference the ASCII table for the characters byte values
}
// and return the good data
var data = "your good data after parsing it";
goodData(data);
}
}
public class DataReceivedEventArgs : EventArgs
{
public DataReceivedEventArgs(byte[] data)
{
Data = data;
}
public byte[] Data { get; private set; }
}
class SerialService
{
public event EventHandler<DataReceivedEventArgs> DataReceived;
private SerialPort _port;
public SerialService(string comm)
{
_port = new SerialPort(comm)
{
// YOUR OTHER SETTINGS HERE...
ReceivedBytesThreshold = 1 // I think is better to increase this number if you know the minimum number of bytes that will arrive at the serial port's buffer
};
// Note that the ReceivedBytesThreshold is set to 1.
// That means that the port_DataReceived event will fire with a minimun of 1 byte in the serial buffer
_port.DataReceived += port_DataReceived;
}
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (e.EventType != SerialData.Chars) return;
while (_port.IsOpen & _port.BytesToRead != 0)
{
// important to get all the bytes off the buffer
var size = _port.BytesToRead;
var buffer = new byte[size];
var sizeRead = _port.Read(buffer, 0, size);
OnDataReceived(buffer);
}
}
protected virtual void OnDataReceived(byte[] data)
{
var ev = DataReceived;
if (ev != null) ev(this, new DataReceivedEventArgs(data));
}
}
答案 2 :(得分:-1)
正如您所说,您的代码正在减慢数据接收速度。 您可以通过将数据排队到队列列表来解决您的问题,后台进程将逐个处理此列表。 另一种方法是在接收每个数据批次时创建一个新线程 示例(第二种方法)
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string inData = serialPort1.ReadLine();
System.Threading.Thread T = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(ProcessData));
T.Start(inData);
}
public void ProcessData(Object data)
{
....
}