我有一个采集设备,可以读取数据并将其添加到缓冲区。这是在一个单独的线程中完成的。一旦这个数据出列,我就设置了一个提升事件OnDataRead()的委托。
在我的信号监视器中,当收到事件时,我想在多个图表中绘制数据(总共16个)。由于我有16个图表,而不是每次接收新数据时更新图表,我将它们添加到数据和时间戳的缓冲区中。通过读取存储在缓冲区中的数据和时间戳,每100ms在一个单独的线程中更新图表。但是,当我绘制数据时,某些图表会停止添加数据,而在所有图表中,不显示大量值。这是一个糟糕的方法吗?什么是更好的方法或我应该改变什么才能使它工作?
我有256个样本/秒* 16个频道。
这是我所期望的,但对于所有16个频道
public void OnDataRead(object source, DataEventArgs e)
{
if ((e.rawData.Length > 0) && (!_shouldStop))
{
for (int sampleIdx = 0; sampleIdx < e.rawData.Length; sampleIdx++)
{
lock (_bufferRawData)
// Append data
_bufferRawData.Add(e.rawData[sampleIdx]);
lock (_bufferXValues)
_bufferXValues.Add(DateTime.Now);
}
}
private void AddDataThreadLoop()
{
while (!_shouldStop)
{
chChannels[1].Invoke(addDataDel);
Thread.Sleep(100); //sleeps for 100ms
}
}
private void AddData()
{
// Copy data stored in lists to arrays
if (_bufferRawData.Count > 0)
{
float[] rawData;
lock (_bufferRawData)
{
rawData = _bufferRawData.ToArray();
_bufferRawData.Clear();
}
DateTime[] xValues;
lock (_bufferXValues)
{
xValues = _bufferXValues.ToArray();
_bufferXValues.Clear();
}
// Add new data points for the selected channel chart
int channelIdx = 0;
for (int sampleIdx = 0; sampleIdx < rawData.Length -1; sampleIdx++)
{
// Calculate the channel where the smaple corersponds
channelIdx = sampleIdx % (_numChannels + 1);
foreach (Series ptSeries in chChannels[channelIdx].Series)
// Add new datapoint to the corresponding chart (x, y, chartIndex, seriesIndex)
AddNewPoint(xValues[sampleIdx], rawData[sampleIdx], channelIdx, ptSeries);
}
}
}
public void AddNewPoint(DateTime timeStamp, float yValue, int chartIDx, System.Windows.Forms.DataVisualization.Charting.Series ptSeries)
{
//Add datapoint
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);
}
// Modify minimum and maximum for new samples
chChannels[chartIDx].ChartAreas[0].AxisX.Minimum = ptSeries.Points[0].XValue;
chChannels[chartIDx].ChartAreas[0].AxisX.Maximum = DateTime.FromOADate(ptSeries.Points[0].XValue).AddSeconds(10).ToOADate();
chChannels[chartIDx].ChartAreas[0].AxisY.Maximum = _yMax;
chChannels[chartIDx].ChartAreas[0].AxisY.Minimum = -_yMax;
chChannels[chartIDx].Invalidate();
}
private void btnPlay_Click(object sender, EventArgs e)
{
//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();
}
EDIT1: chChanels是一个图表列表,其中每个元素对应一个图表。 public List chChannels;
EDIT2 更改此锁定后,将更新所有图表。但是,每张图表的大量样本仍未更新。
lock (_bufferRawData) {
for (int sampleIdx = 0; sampleIdx < e.rawData.Length; sampleIdx++)
{
// Append data
_bufferRawData.Add(e.rawData[sampleIdx]);
_bufferXValues.Add(DateTime.Now);
}
}
答案 0 :(得分:0)
很难说这些是你唯一的问题,但至少这对我来说很突出:
您正在为时间戳和原始数据缓冲区使用两个单独的锁。这意味着缓冲区可能不代表同一系列数据。您可能希望使用单个锁来访问两个缓冲区:
lock (_bufferRawData) {
// Append data
_bufferRawData.Add(e.rawData[sampleIdx]);
_bufferXValues.Add(DateTime.Now);
}
事实上,我还建议将锁定移出for-loop。锁定每次迭代是非常低效的:
lock (_bufferRawData) {
for (int sampleIdx = 0; sampleIdx < e.rawData.Length; sampleIdx++)
{
// Append data
_bufferRawData.Add(e.rawData[sampleIdx]);
_bufferXValues.Add(DateTime.Now);
}
}
答案 1 :(得分:0)
代码中存在两个问题:
1)以无效的方式使用锁。我将两个单锁替换为普通锁。我也为所有迭代锁定它,而不是每次迭代都这样做,正如其中一个答案所指出的那样。这是应该如何
lock (_bufferRawData) {
for (int sampleIdx = 0; sampleIdx < e.rawData.Length; sampleIdx++)
{
// Append data
_bufferRawData.Add(e.rawData[sampleIdx]);
_bufferXValues.Add(DateTime.Now);
}
}
2)我生成的时间戳与收集数据时的时间戳不对应。因此,几个Y样本具有相同或相似的时间戳。
一种解决方案是为每个样本生成确切的时间戳。这应该在获得样本时完成。但是,由于频率为256Hz,因此每4ms对应1个样本。 DateTime.Now的精度约为15ms,这使得该方法不适用于256Hz的频率。但是,这对于降低频率率是一种很好的方法。
正确的方法是在绘制数据时确定图表的X轴值。由于频率固定为256Hz,因此很容易知道Y样本的X值。