Chart: Red X Error when trying to display multiple series in an indexed chart - c#

I have an Issue with C# charting.
This is using System.Windows.Forms.DataVisualization.Charting
My series are configured to be indexed as such:
{
Name = name,
Color = color,
IsVisibleInLegend = false,
IsXValueIndexed = true,
ChartType =
System.Windows.Forms.DataVisualization.Charting.SeriesChartType.FastLine
};
I have 3 series in this chart
I am adding data to the series as it arrives via an Event Handler
for (int i = 0; i < samples; i++)
{
double time_point = DData[i * 4]
chart1.Series[0].Points[lastSample + i].XValue = time_point;
chart1.Series[0].Points[lastSample + i].YValues[0] = DData[i * 4 + 1];
chart1.Series[1].Points[lastSample + i].XValue = time_point;
chart1.Series[1].Points[lastSample + i].YValues[0] = DData[i * 4 + 2];
chart1.Series[2].Points[lastSample + i].XValue = time_point;
chart1.Series[2].Points[lastSample + i].YValues[0] = DData[i * 4 + 3];
}
lastSample = (lastSample + samples) % maxPlotPoints;
chart1.Invalidate();
The issue is as follows:
If I turn on each series individually (via chart1.Series[0].Enabled), they all work fine
If I turn on more than 1 series at a time, a big red X appears instead oft eh chart, and I have to restart the application to resume streaming charts. This either happens immediately or after a few seconds.
If I set time_point to some other number, like 0, this issue doesn't happen, and all 3 charts can be displayed simultaneously
Next, I understand that this happens when each series has a different X-value for the same Point[] location. But I am explicitly setting all 3 series to use the same exact time_point
My next assumption was that the event handler was executing the tread before the previous thread finishes.
So I added a lock around the graphing call, it did not help
private Object thisLock = new Object();
lock (thisLock)
{
}
MY questions are:
Does anyone know if there is another reason why this may be caused?
Is it at all possible to use just the X-indexes from the first series for the chart but to display all 3 series simultaneuously?

EDIT: This fix did not work, the problem disappeared, then came back after a while
The comment above by TaW helped me fix this issue.
This really seems to be a problem with Microsoft charts
I originally though it was due to EventHandler firing multiple times before it finishes, and I opted out to use a Queue that would be filled with an EventHandler and Dequeued on a separate thread.
The problem was still there.
The solution to the problem was to simply set IsXValueIndexed to false
then to set it to true before drawing the chart
Below is the code used
In case anyone is wondering what this does:
It graphs 3 Values simultaneously on the chart with increasing time.
The chart updates from left to right instead of scrolling (think EKG machine in a hospital)
There is a fixed total number of points that is set to maxPlotPoints
The EventHandler receives some Data packet (made up here) that contains a certain number of points which = samplePoints.
It constructs a DataPoints struct and adds it to a queue
A separate thread MainTask dequeues an element from DataPoints and uses it to populate a 100 point section of each of the three series. That section then increments to the next 100 points, and so on.
This is useful if you want to continuously chart a fixed number of points
Unfortunately, I can't seem to mark the answer above from TaW as correct because it's in a comment. But I would be happy to try if you can explain how to do this. Thanks
private struct DataPoint
{
public double timePoint;
public double X;
public double Y;
public double Z;
}
private struct DataPoints
{
public DataPoints(int samples)
{
dataPoints = new DataPoint[samples];
}
public DataPoint[] dataPoints;
}
Queue queue = new Queue();
int lastSample = 0;
static int maxPlotPoints = 1000;
static int samplePoints = 100;
public ChartForm(UdpMgrControl RefUdpMgr, byte GyroChartNum, string ConfigFile)
{
for ( Row = 0; Row < 3; Row++)
{
var series1 = new System.Windows.Forms.DataVisualization.Charting.Series
{
Name = name,
Color = Color.Blue,
IsVisibleInLegend = false,
IsXValueIndexed = true,
ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.FastLine
};
//Fill in chart with blank points
for (int i = 0; i < maxPlotPoints; i++)
{
series1.Points.AddY(0);
}
chart1.Series.Add(series1);
}
chart1.Series[0].Enabled = true;
chart1.Series[1].Enabled = true;
chart1.Series[2].Enabled = true;
running = true;
Thread main_thread = new Thread(new ThreadStart(this.MainTask));
}
private void OutputHandler(byte[] DData)
{
if (running)
{
DataPoints data_element = new DataPoints(samplePoints);
for (int i = 0; i < samplePoints; i++)
{
data_element.dataPoints[i].X = DData[i].X;
data_element.dataPoints[i].Y = DData[i].Y;
data_element.dataPoints[i].Z = DData[i].Z;
data_element.dataPoints[i].timePoint = DData[i].timePoint;
}
queue.Enqueue(data_element);
}
}
private void MainTask()
{
while (running)
{
try
{
if (queue.Count > 0)
{
chart1.Series[0].IsXValueIndexed = false;
chart1.Series[1].IsXValueIndexed = false;
chart1.Series[2].IsXValueIndexed = false;
DataPoints data_element = (DataPoints)queue.Dequeue();
for (int i = 0; i < data_element.dataPoints.Length; i++)
{
chart1.Series[0].Points[lastSample + i].XValue = data_element.dataPoints[i].timePoint;
chart1.Series[0].Points[lastSample + i].YValues[0] = data_element.dataPoints[i].X;
chart1.Series[1].Points[lastSample + i].XValue = data_element.dataPoints[i].timePoint;
chart1.Series[1].Points[lastSample + i].YValues[0] = data_element.dataPoints[i].Y;
chart1.Series[2].Points[lastSample + i].XValue = data_element.dataPoints[i].timePoint;
chart1.Series[2].Points[lastSample + i].YValues[0] = data_element.dataPoints[i].Z;
}
//Adjust the next location to end of first
lastSample = (lastSample + samples) % maxPlotPoints;
chart1.Series[0].IsXValueIndexed = true;
chart1.Series[1].IsXValueIndexed = true;
chart1.Series[2].IsXValueIndexed = true;
GC_UpdateDataGrids();
chart1.Invalidate();
}
else
{
Thread.Sleep(10);
}
}
catch (Exception error)
{
LogErrors.AddErrorMsg(error.ToString());
}
}
}

Related

ObservableCollection causing a lot of GC Pressure

I am using Livecharts2 in my WPF Application in order to show a realtime graph (100 data points/s)
The chart is connected to an ObservableCollection.
I found that my Code to add DataPoints is causing a lot of GC Pressure:
The Code to add Data is as follows:
// amount of datapoints to keep in chart per series
// (200 points / 25points per second = 8 seconds)
static int dataLength = 200;
// 4 series in total
private static List<ObservableCollection<ObservableValue>> seriesDataPoints
= new List<ObservableCollection<ObservableValue>>();
private static List<string> seriesNames = new List<string>();
// variable in attempt to reduce GC pressure
private static ObservableValue ReUseShell = null;
// add new datapoint (and remove old datapoint if series > dataLength )
public static void AddDataPoint(double point, string seriesName)
{
// search for the correct series
int seriesIndex = -1;
for (int i = 0; i < seriesNames.Count; i++)
{
if (seriesNames[i] == seriesName)
{
seriesIndex = i;
break;
}
}
// series was not found, create new serries
if (seriesIndex == -1)
{
seriesIndex = CreateNewSeries(seriesName);
}
// check if we have a reusable datapoint (in order to reduce gc)
if (ReUseShell != null)
{
ReUseShell.Value = point;
seriesDataPoints[seriesIndex].Add(ReUseShell);
ReUseShell = null;
}
else
{ // we dont have a empty data point (series < datalengt / ~initialisation)
seriesDataPoints[seriesIndex].Add(new(point));
}
// remove oldest datapoint and reuse it for the next data fill
if (seriesDataPoints[seriesIndex].Count > dataLength)
{
ReUseShell = seriesDataPoints[seriesIndex][0];
seriesDataPoints[seriesIndex].RemoveAt(0);
}
}
and this is how the chart looks:
Is there any issue with my code / Observable collection or does the issue reside with the Library LiveCharts2?

How to Create/Draw Pi Graph in DataGridView Cell using C#.Net

My Target is to implement Pi Graph in DataGridView Cell, which will show renaming time of pending Orders Point of Sale System.
When we place an Order on POS System it can be Delivery or Collection Order.
Delivery and Collection Orders contain time to finish.
develop this scenario in which a renaming time showed in Pi Graph an its decreases when Time closer to its completion time.
When current time reach to renaming time graph must show 0 on the graph
Step 1: How to Create Bar Graph in Datagridview Cell
First of all we have there Parameter or Values
Order Placing time
Order Completion time(Time required to complete the order e.g 120 min).
Current System Time.
Select a DatagridView form toolbox and also a Chart.
This Datagridview have column:
then create a function
private void GenerateGraphOfRemaingTiming()
{
try
{
DateTime moment = DateTime.Now;
chart1.Visible = false;
chart1.ClientSize = new Size(37, 37);
Bitmap bmp = new Bitmap(chart1.ClientSize.Width, chart1.ClientSize.Height);
for (int row = 0; row < dataGridView_RecentOrder.RowCount; row++)
{
chart1.Series.Clear();
chart1.Legends.Clear();
int Hour = int.Parse(moment.Hour.ToString());
int Min = int.Parse(moment.Minute.ToString());
int Sec = int.Parse(moment.Second.ToString());
//Add a new chart-series
string seriesname = "MySeriesName";
chart1.Series.Add(seriesname);
//set the chart-type to "Pie"
chart1.Series[seriesname].ChartType = SeriesChartType.Pie;
//Add some datapoints so the series. in this case you can pass the values to this method
chart1.Series[seriesname].LabelForeColor = Color.White;
chart1.Series[seriesname].BackSecondaryColor = Color.FromArgb(192, 192, 255);
string OrderDateTime = dataGridView_RecentOrder.Rows[row].Cells["Order_PlacingTime"].Value.ToString();
var result = Convert.ToDateTime(OrderDateTime);
int OHour = int.Parse(result.ToString("HH", System.Globalization.CultureInfo.CurrentCulture));
int OMin = int.Parse(result.ToString("mm", System.Globalization.CultureInfo.CurrentCulture));
int OnSec = int.Parse(result.ToString("ss", System.Globalization.CultureInfo.CurrentCulture));
int OrderMinuts =Convert.ToInt32( (OHour * 60) + OMin + OnSec * 0.0166667);
int NowTimeInMinuts = Convert.ToInt32( (Hour * 60) + Min + (Sec * 0.0166667));
int FinalOrderMinutes = int.Parse(dataGridView_RecentOrder.Rows[row].Cells["Order_CompletionTime"].Value.ToString()) - (NowTimeInMinuts - OrderMinuts);
if (FinalOrderMinutes <= 0)
{
FinalOrderMinutes = 0;
}
int OrderCompletionTime = int.Parse(dataGridView_RecentOrder.Rows[row].Cells["Order_CompletionTime"].Value.ToString());
if (OrderCompletionTime == 0)
{
OrderCompletionTime = 1;
}
int OrderTimingDifference = OrderCompletionTime - FinalOrderMinutes;
// MessageBox.Show("Order Min: "+ OrderMinuts.ToString() +"\n Now Time in Min: "+NowTimeInMinuts.ToString());
chart1.Series[seriesname].Points.AddXY("", OrderTimingDifference);
chart1.Series[seriesname].Points.AddXY(FinalOrderMinutes.ToString(), FinalOrderMinutes);
chart1.DrawToBitmap(bmp, chart1.ClientRectangle);
// bmp = OvalImage(bmp);
dataGridView_RecentOrder.Rows[row].Cells["Order_RemaningTime"].Value = bmp.Clone();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
Out put of this will be like.
Step 2: How to automatically graph value decrease when system time increase.
public void StartOrderTimingCounter()
{
System.Windows.Forms.Timer OrderRemaningTimer = new System.Windows.Forms.Timer();
OrderRemaningTimer.Interval = 60000; // specify interval time as you want
OrderRemaningTimer.Tick += new EventHandler(timer_Tick);
OrderRemaningTimer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
// call above implemented function
GenerateGraphOfRemaingTiming();
}

C# delegate multithreading slower than single threaded?

I am currently writing a C# application to demonstrate the speedup of parallel computing over single threaded applications. My case is Median blur of an image. But putting more threads to work slows down the application significantly (60 seconds single threaded vs 75 seconds multithreaded) . Given my current approach, I don't know how I could improve the process for multithreading. Sorry in advance for the long code in this post.
my current approach:
first, I calculate how many pixels each thread needs to process to even out the work, the DateTime calculation is to know how much time is passed single threaded and how much time is passed multithreaded:
public void blurImage(int cores)
{
_startTotal = DateTime.Now;
int numberOfPixels = _originalImage.Width * _originalImage.Height;
if (cores>=numberOfPixels)
{
for (int i = 0; i < numberOfPixels; i++)
{
startThread(0, numberOfPixels);
}
}
else
{
int pixelsPerThread = numberOfPixels / cores;
int threshold = numberOfPixels - (pixelsPerThread * cores);
startThread(0, pixelsPerThread + threshold);
for (int i = 1; i < cores; i++)
{
int startPixel = i * pixelsPerThread + threshold;
startThread(startPixel, startPixel + pixelsPerThread);
}
}
_SeqTime = DateTime.Now.Subtract(_startTotal);
}
the startThread method starts a thread and saves the result into a special class object so it can all be merged into one image, I pass a copy of the input image in each thread.
private void startThread(int startPixel, int numberOfPixels)
{
BlurOperation operation = new BlurOperation(blurPixels);
_operations.Add(operation);
BlurResult result = new BlurResult();
operation.BeginInvoke((Bitmap)_processedImage.Clone(), startPixel, numberOfPixels, _windowSize, result, operation, new AsyncCallback(finish), result);
}
Every thread blurs their set of pixels and saves the result into a new list of colors, the result is saved into the result object as well as the startpixel and the current operation, so the program knows when all threads are finished:
private void blurPixels(Bitmap bitmap, int startPixel, int endPixel, int window, BlurResult result, BlurOperation operation)
{
List<Color> colors = new List<Color>();
for (int i = startPixel; i < endPixel; i++)
{
int x = i % bitmap.Width;
int y = i / bitmap.Width;
colors.Add(PixelBlurrer.ShadePixel(x, y, bitmap, window));
}
result._pixels = colors;
result._startPixel = startPixel;
result._operation = operation;
}
the PixelBlurrer calculates the median of each color channel and returns it:
public static Color ShadePixel(int x, int y, Bitmap image, int window)
{
List<byte> red = new List<byte>();
List<byte> green = new List<byte>();
List<byte> blue = new List<byte>();
int xBegin = Math.Max(x - window, 0);
int yBegin = Math.Max(y - window, 0);
int xEnd = Math.Min(x + window, image.Width - 1);
int yEnd = Math.Min(y + window, image.Height - 1);
for (int tx = xBegin; tx < xEnd; tx++)
{
for (int ty = yBegin; ty < yEnd; ty++)
{
Color c = image.GetPixel(tx, ty);
red.Add(c.R);
green.Add(c.G);
blue.Add(c.B);
}
}
red.Sort();
green.Sort();
blue.Sort();
Color output = Color.FromArgb(red[red.Count / 2], green[green.Count / 2], blue[blue.Count / 2]);
return output;
}
on the callback, we return to the GUI thread and merge all pixels into the resulting image. Lastly an event is called telling my form the process is done:
private void finish(IAsyncResult iar)
{
Application.Current.Dispatcher.BeginInvoke(new AsyncCallback(update), iar);
}
private void update(IAsyncResult iar)
{
BlurResult result = (BlurResult)iar.AsyncState;
updateImage(result._pixels, result._startPixel, result._operation);
}
private void updateImage(List<Color> colors, int startPixel, BlurOperation operation)
{
DateTime updateTime = DateTime.Now;
_operations.Remove(operation);
int end = startPixel + colors.Count;
for (int i = startPixel; i < end; i++)
{
int x = i % _processedImage.Width;
int y = i / _processedImage.Width;
_processedImage.SetPixel(x, y, colors[i - startPixel]);
}
if (_operations.Count==0)
{
done(this, null);
}
_SeqTime += DateTime.Now.Subtract(updateTime);
}
Any thoughts? I tried using Parallel.For instead of delegates, but that made it worse. Is there a way to speedup Median blur by multithreading or is this a failed case?
after some thinking, I figured out my logic is solid, but I didn't send a deep copy to each thread. After changing this line in StartThread:
operation.BeginInvoke((Bitmap)_processedImage.Clone(), startPixel, numberOfPixels, _windowSize, result, operation, new AsyncCallback(finish), result);
to this:
operation.BeginInvoke(new Bitmap(_processedImage), startPixel, numberOfPixels, _windowSize, result, operation, new AsyncCallback(finish), result);
I can see a speedup multithreaded

C# Update chart values

How do I update values that have already been created?
Like, I have this chart:
And then I want to update the chart values with other values. I've already tried many things but I don't manage to make it work...
My code:
int ratio = (int)PlayerStats.Kills - PlayerStats.Deaths;
string[] seriesArray = { "Kills", "Assists", "Deaths", "MVPS" , "Headshots", "Ratio" };
int[] pointsArray = { PlayerStats.Kills, PlayerStats.Assists, PlayerStats.Deaths, PlayerStats.MVPS, PlayerStats.Headshots, ratio };
this.chart1.Titles.Add("Statistics Chart");
for (int i = 0; i < seriesArray.Length; i++)
{
Series series = series = this.chart1.Series.Add(seriesArray[i]);
series.Points.Add(pointsArray[i]);
}
chart1.ChartAreas[0].AxisX.Enabled = AxisEnabled.False;
chart1.ChartAreas[0].AxisY.Enabled = AxisEnabled.False;
double realRatio = Math.Round((double)PlayerStats.Kills / PlayerStats.Deaths, 2);
double hsRatio = Math.Round((double)PlayerStats.Headshots / PlayerStats.Kills, 2);
label6.Text = PlayerStats.Kills.ToString();
label7.Text = PlayerStats.Assists.ToString();
label8.Text = PlayerStats.Deaths.ToString();
label11.Text = PlayerStats.MVPS.ToString();
label10.Text = String.Format("{0:P2}", hsRatio);
label9.Text = realRatio.ToString();
When I try to run the function a second time (to update the values) it gives me a exception: (inside the loop, first line)
Additional information: There is already a chart element with the name 'Kills' in 'SeriesCollection'.
I've managed to that that specific function (chart1.Series.Add(...)) only called once on the startup, but then the other function inside the loop gave me exceptions too.
Well, I hope that you understand my english :P
Are you clearing out the chart elements before looping through to add them again? Something like
this.chart1.Series.Clear();

How do I plot real-time signal in WPF and C# .net 3.5?

I am trying plot real-time signal, signal vs time, with WPF and C# (Dynamic Data Display); Once a signal is generated, it should be showed up on a plot chart instantly; The signal can be generated very fast (on the order of mili-second (0.001 second));
What would be the best way to do it? Any suggestion is appreciated.Thanks.
EDIT: An example code will be highly appreciated.
Here is what I have tried:
Plot initiation:
#region Constructor
public SignalStatsDisplay()
{
InitializeComponent();
plotter.Legend.Remove();
_initialChildrenCount = plotter.Children.Count;
int count = plotter.Children.Count;
//do not remove the initial children
if (count > _initialChildrenCount)
{
for (int i = count - 1; i >= _initialChildrenCount; i--)
{
plotter.Children.RemoveAt(i);
}
}
_nColorChannels = 4;
_lineprofileColor = new Color[4];
_lineprofileColor[0] = Colors.Transparent;
_lineprofileColor[1] = Colors.Red;
_lineprofileColor[2] = Colors.Black;
_lineprofileColor[3] = Colors.Blue;
//LineGraph lg = new LineGraph();
LineAndMarker<MarkerPointsGraph> lg = new LineAndMarker<MarkerPointsGraph>();
CirclePointMarker pm = new CirclePointMarker { Size = 10, Fill = Brushes.Green };
// MarkerPointsGraph mpg = new MarkerPointsGraph();
if (_nColorChannels > 0) // plotter init
{
_dataX = new List<int[]>();
_dataY = new List<int[]>();
int[] dataXOneCh = new int[1];
int[] dataYOneCh = new int[1];
dataXOneCh[0] = 0;
dataYOneCh[0] = 0;
/*CirclePointMarker marker = new CirclePointMarker();
marker.Fill = new SolidColorBrush(_lineprofileColor[0]);
marker.Pen = new Pen(marker.Fill, 0);
marker.Size = 2;
int lineWidth = 1;*/
for (int i = 0; i < 4; i++)
{
_dataX.Add(dataXOneCh); // data x-y mapping init
_dataY.Add(dataYOneCh);
EnumerableDataSource<int> xOneCh = new EnumerableDataSource<int>(dataXOneCh);
EnumerableDataSource<int> yOneCh = new EnumerableDataSource<int>(dataYOneCh);
xOneCh.SetXMapping(xVal => xVal);
yOneCh.SetXMapping(yVal => yVal);
CompositeDataSource dsOneCh = new CompositeDataSource(xOneCh, yOneCh);
// LineProfileColorSetup();
lg = plotter.AddLineGraph(dsOneCh,
(new Pen(Brushes.Green, 2)),
pm,
(new PenDescription("Data")));
// lg = plotter.AddLineGraph(dsOneCh,
// Colors.Transparent,
// 2,
// "Data");
// pm.FilteringEnabled = false;
}
plotter.FitToView();
}
else
{
return;
}
}
Data update:
for (int i = 0; i < 1; i++)
{
if (_dataX[i].Length == _dataY[i].Length)
{
EnumerableDataSource<int> xOneCh = new EnumerableDataSource<int>(_dataX[i]);
xOneCh.SetXMapping(xVal => xVal);
EnumerableDataSource<int> yOneCh = new EnumerableDataSource<int>(_dataY[i]);
yOneCh.SetYMapping(yVal => yVal);
CompositeDataSource ds = new CompositeDataSource(xOneCh, yOneCh);
Action UpdateData = delegate()
{
// ((LineGraph)plotter.Children.ElementAt(startIndex + i)).DataSource = ds;
// ((LineGraph)plotter.Children.ElementAt(startIndex + i)).LinePen = new Pen(new SolidColorBrush(Colors.Green), 1);
// ((PointsGraphBase)plotter.Children.ElementAt(startIndex + i)).DataSource = ds;
((PointsGraphBase)plotter.Children.ElementAt(startIndex + i + 1)).DataSource = ds;
// }
// (plotter.Children.ElementAt(startIndex + i)).DataSource = ds;
};
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, UpdateData);
}
}
The problem I have is when signal comes in very fats, say a new signal is generated every mili-second, it seems the graph only generate a new marker every 1 second, which I don't understand.
The basic idea is to generate a plotter.AddLineGraph, then only update the DataSource. But it seems not working. So I thought maybe I am on the wrong direction.
I am not a C# or WPF expert. When LineAndMarker does not work as expected, I started doubting. So I am wondering what would be the general way for people to display real-time signal (rapidly changing, time span between samples could be on mili-second) using WPF and C#.
I found where I did wrong. Here is the part where problem happens:
_pxlValAllChan[0, signalIndex] = pxlValue;
DateTime currentTime = DateTime.Now; //has to come from real file: Tiff
TimeSpan timeSpan = currentTime - _startTime;
_timeStampAllChan[0, signalIndex] = Convert.ToInt16(timeSpan.TotalSeconds);
_pxlValOneChan = new int[_signalLength]; // no need to new it every time
_timeStampOneChan = new int[_signalLength];
for (int i = 0; i <= signalIndex; i++)
{
_pxlValOneChan[i] = _pxlValAllChan[0, i];
//_timeStampOneChan[i] = _timeStampAllChan[0, i];
_timeStampOneChan[i] = i;
}
_signalDisplay.SetData(_timeStampOneChan, _pxlValOneChan, 0, true);
My x data was based on time. I basically consider the initiation of program as starting point, then I keep track of time elapse by subtracting starting time with current time. Then the time elapse is saved as x-axis data (see the line that is commented out). And this actually causes problem. When I simple change x-axis data as index, the problem is gone. The graph update pretty fast. Although I have not figure out how I should change the logic to still use time elapse as my x-axis data, but that is the culprit of slow updating on the graph.

Categories