SECI  1
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Properties Events
Graph.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Drawing;
5 using System.Data;
6 using System.Linq;
7 using System.Text;
8 using System.Windows.Forms;
9 using System.Windows.Forms.DataVisualization.Charting;
10 
11 namespace SeciControls.Graphing
12 {
13  public partial class Graph : UserControl, IUpdate
14  {
15  private Double _maxDoubleValue = Math.Pow(10, 25);
16  private const int _maxPoints = 1000;
17  private DateTime _lastUpdate = new DateTime();
18  private DateTime _minValue;
19  private DateTime _maxValue;
20 
21  private Double _yMin = Double.NaN;
22  private Double _yMax = Double.NaN;
23  private Double _y2Min = Double.NaN;
24  private Double _y2Max = Double.NaN;
25 
26  private Boolean _limitsBeingChanged = false;
27  private Boolean _yLimitsSetManually = false;
28  private Boolean _y2LimitsSetManually = false;
29 
30  private Boolean _yMinAuto = false;
31  private Boolean _yMaxAuto = false;
32  private Boolean _y2MinAuto = false;
33  private Boolean _y2MaxAuto = false;
34 
35  private Double _ySetMin = Double.NaN;
36  private Double _ySetMax = Double.NaN;
37  private Double _y2SetMin = Double.NaN;
38  private Double _y2SetMax = Double.NaN;
39 
41 
42  public Seci.Definitions.GraphDefinition GraphDef { get { return _graphDef; } }
43 
44  public Graph()
45  {
46  InitializeComponent();
47 
48  chart1.ChartAreas[0].CursorX.IsUserEnabled = true;
49  chart1.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
50  chart1.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
51  chart1.ChartAreas[0].AxisX.ScaleView.MinSize = 1;
52  chart1.ChartAreas[0].AxisX.ScaleView.MinSizeType = DateTimeIntervalType.Minutes;
53  chart1.ChartAreas[0].AxisX.ScrollBar.ButtonStyle = ScrollBarButtonStyles.None;
54 
55  chart1.ChartAreas[0].CursorY.IsUserEnabled = true;
56  chart1.ChartAreas[0].CursorY.IsUserSelectionEnabled = true;
57  chart1.ChartAreas[0].AxisY.ScaleView.Zoomable = true;
58  chart1.ChartAreas[0].AxisY.ScaleView.MinSize = 1;
59  chart1.ChartAreas[0].AxisY.ScaleView.MinSizeType = DateTimeIntervalType.Auto;
60  chart1.ChartAreas[0].AxisY.ScrollBar.ButtonStyle = ScrollBarButtonStyles.None;
61 
62  chart1.ChartAreas[0].AxisY2.ScaleView.Zoomable = true;
63  chart1.ChartAreas[0].AxisY2.ScaleView.MinSize = 1;
64  chart1.ChartAreas[0].AxisY2.ScaleView.MinSizeType = DateTimeIntervalType.Auto;
65  chart1.ChartAreas[0].AxisY2.ScrollBar.ButtonStyle = ScrollBarButtonStyles.None;
66 
67  chart1.ChartAreas[0].CursorX.Interval = 0; //Zooming does not work on x-axis without this being set for some reason - y axis is fine without
68  chart1.ChartAreas[0].CursorY.Interval = 0.001; //However, this stops the labels printing to 10 dps
69  }
70 
71  public void SetupGraph(Seci.Definitions.GraphDefinition graphDef)
72  {
73  _lastUpdate = new DateTime();
74  _graphDef = graphDef;
75 
76  // Predefine the viewing area of the chart
77  _minValue = DateTime.Now;
78  _maxValue = _minValue.AddSeconds(120);
79 
80  //Main appearance
81  chart1.Titles.Clear();
82  chart1.Titles.Add(_graphDef.Title);
83  chart1.ChartAreas[0].BackGradientStyle = GradientStyle.TopBottom;
84  chart1.ChartAreas[0].BackColor = Color.AliceBlue;
85  chart1.ChartAreas[0].BorderDashStyle = ChartDashStyle.Solid;
86  chart1.ChartAreas[0].BorderColor = Color.Gray;
87 
88  //Standard axis appearance
89  chart1.ChartAreas[0].AxisX.MajorGrid.Enabled = false;
90  chart1.ChartAreas[0].AxisX.LineColor = Color.Gray;
91  chart1.ChartAreas[0].AxisX.MajorTickMark.LineColor = Color.Gray;
92  chart1.ChartAreas[0].AxisY.MajorGrid.Enabled = false;
93  chart1.ChartAreas[0].AxisY.LineColor = Color.Gray;
94  chart1.ChartAreas[0].AxisY.MajorTickMark.LineColor = Color.Gray;
95  chart1.ChartAreas[0].AxisY2.MajorGrid.Enabled = false;
96  chart1.ChartAreas[0].AxisY2.LineColor = Color.Gray;
97  chart1.ChartAreas[0].AxisY2.MajorTickMark.LineColor = Color.Gray;
98 
99  //Legend appearance
100  chart1.Legends[0].Enabled = true;
101  //chart1.Legends[0].BorderColor = Color.Gray;
102  chart1.Legends[0].LegendStyle = LegendStyle.Row;
103  chart1.Legends[0].Docking = Docking.Top;
104 
105  chart1.ChartAreas[0].AxisX.Minimum = _minValue.ToOADate();
106  chart1.ChartAreas[0].AxisX.Maximum = _maxValue.ToOADate();
107  chart1.ChartAreas[0].AxisX.LabelStyle.Format = "HH:mm:ss";
108  chart1.ChartAreas[0].AxisX.Title = "Time";
109  chart1.ChartAreas[0].AxisY.Title = _graphDef.YLabel;
110 
111  if (_graphDef.ShowZeroY1)
112  {
113  chart1.ChartAreas[0].AxisY.Minimum = 0;
114  chart1.ChartAreas[0].AxisY.Maximum = 1;
115  _yMinAuto = true;
116  _yMaxAuto = true;
117  }
118 
119  if (_graphDef.ShowZeroY2)
120  {
121  chart1.ChartAreas[0].AxisY2.Minimum = 0;
122  chart1.ChartAreas[0].AxisY.Maximum = 1;
123  y2AxisToolStripMenuItem.Enabled = true;
124  _y2MinAuto = true;
125  _y2MaxAuto = true;
126  }
127 
128  if (!String.IsNullOrEmpty(_graphDef.Y2Label))
129  {
130  chart1.ChartAreas[0].AxisY2.Title = _graphDef.Y2Label;
131  chart1.ChartAreas[0].AxisY2.Enabled = AxisEnabled.False; //Only show it if a plot uses it
132  }
133 
134  chart1.Series.Clear();
135 
136  foreach (Seci.Definitions.PlotDefinition plot in _graphDef.Plots)
137  {
138  addPlot(plot.BlockName, plot.UseY2);
139  }
140  }
141 
142  private void addPlot(string blockname, Boolean useY2)
143  {
144  foreach (Series series in chart1.Series)
145  {
146  if (series.Name.ToLower() == blockname.ToLower())
147  {
148  //Don't add duplicates
149  return;
150  }
151  }
152 
153  Series series1 = new Series(blockname);
154  series1.ChartType = SeriesChartType.FastLine;
155  series1.BorderWidth = 3;
156  series1.XValueType = ChartValueType.DateTime;
157  if (useY2)
158  {
159  chart1.ChartAreas[0].AxisY2.Enabled = AxisEnabled.True;
160  series1.YAxisType = AxisType.Secondary;
161  }
162  chart1.Series.Add(series1);
163 
164  setPlotColours();
165  }
166 
167  private void setPlotColours()
168  {
169  for (int i = 0; i < chart1.Series.Count; ++i)
170  {
171  switch (i)
172  {
173  case 0:
174  chart1.Series[i].Color = Color.Red; //Plot 1
175  break;
176  case 1:
177  chart1.Series[i].Color = Color.Blue; //Plot 2
178  break;
179  case 2:
180  chart1.Series[i].Color = Color.Purple; //Plot 3
181  break;
182  case 3:
183  chart1.Series[i].Color = Color.Goldenrod; //Plot 4
184  break;
185  default:
186  chart1.Series[i].Color = Color.Black; //Others
187  break;
188  }
189  }
190  }
191 
192  public void RemoveAllPlots()
193  {
194  chart1.Series.Clear();
195  }
196 
197  private void addDataPoint(Series series, DateTime time, double value)
198  {
199  if (Math.Abs(value) > _maxDoubleValue)
200  {
201  //Do not plot it or the graph will crash
202  return;
203  }
204 
205  try
206  {
207  series.Points.AddXY(time.ToOADate(), value);
208  }
209  catch
210  {
211  }
212 
213  while (series.Points.Count > _maxPoints)
214  {
215  series.Points.RemoveAt(0);
216  }
217  }
218 
219  private void adjustXaxisRange()
220  {
221  double minX = -1;
222  double maxX = -1;
223 
224  foreach (Series series in chart1.Series)
225  {
226  if (series.Points != null && series.Points.Count > 0)
227  {
228  if (minX == -1 || series.Points[0].XValue < minX) minX = series.Points[0].XValue;
229  if (maxX == -1 || series.Points[series.Points.Count - 1].XValue > maxX) maxX = series.Points[series.Points.Count - 1].XValue;
230  }
231  }
232  chart1.ChartAreas[0].AxisX.Minimum = minX;
233  chart1.ChartAreas[0].AxisX.Maximum = maxX;
234  }
235 
236  public void ClearAllPlotData()
237  {
238  foreach (Series series in chart1.Series)
239  {
240  series.Points.Clear();
241  }
242  _yMin = Double.NaN;
243  _yMax = Double.NaN;
244  _y2Min = Double.NaN;
245  _y2Max = Double.NaN;
246  }
247 
248  private void checkYRange(Series series, double value)
249  {
250  if (series.YAxisType == AxisType.Primary)
251  {
252  if (_yLimitsSetManually)
253  {
254  //Still log the max and min even if they are not shown
255  setYRange(chart1.ChartAreas[0].AxisY, ref _yMin, ref _yMax, value, false);
256 
257  ySetManualLimits();
258  }
259  else if (!_graphDef.ShowZeroY1)
260  {
261  setYRange(chart1.ChartAreas[0].AxisY, ref _yMin, ref _yMax, value, !_limitsBeingChanged);
262  }
263  else
264  {
265  setYRange(chart1.ChartAreas[0].AxisY, ref _yMin, ref _yMax, value, !_limitsBeingChanged);
266  if (_yMin != _yMax && !_limitsBeingChanged)
267  {
268  chart1.ChartAreas[0].AxisY.Minimum = Double.NaN;
269  chart1.ChartAreas[0].AxisY.Maximum = Double.NaN;
270  }
271  }
272  }
273  else if (series.YAxisType == AxisType.Secondary)
274  {
275  if (_y2LimitsSetManually)
276  {
277  //Still log the max and min even if they are not shown
278  setYRange(chart1.ChartAreas[0].AxisY2, ref _y2Min, ref _y2Max, value, false);
279 
280  y2SetManualLimits();
281  }
282  else if (!_graphDef.ShowZeroY2)
283  {
284  setYRange(chart1.ChartAreas[0].AxisY2, ref _y2Min, ref _y2Max, value, !_limitsBeingChanged);
285  }
286  else
287  {
288  setYRange(chart1.ChartAreas[0].AxisY2, ref _y2Min, ref _y2Max, value, !_limitsBeingChanged);
289  if (_y2Min != _y2Max && !_limitsBeingChanged)
290  {
291  chart1.ChartAreas[0].AxisY2.Minimum = Double.NaN;
292  chart1.ChartAreas[0].AxisY2.Maximum = Double.NaN;
293  }
294  }
295  }
296  }
297 
298  private void y2SetManualLimits()
299  {
300  if ((_y2MinAuto || _y2MaxAuto) && _y2Min == _y2Max)
301  {
302  chart1.ChartAreas[0].AxisY2.Maximum = _y2Min + 1;
303  chart1.ChartAreas[0].AxisY2.Minimum = _y2Min - 1;
304  }
305  else if (_y2MaxAuto && _y2MinAuto)
306  {
307  chart1.ChartAreas[0].AxisY2.Maximum = Double.NaN;
308  chart1.ChartAreas[0].AxisY2.Minimum = Double.NaN;
309  }
310  else if (_y2MinAuto)
311  {
312  chart1.ChartAreas[0].AxisY2.Minimum = Double.NaN;
313  chart1.ChartAreas[0].AxisY2.Maximum = _y2SetMax;
314  }
315  else if (_y2MaxAuto)
316  {
317  chart1.ChartAreas[0].AxisY2.Maximum = Double.NaN;
318  chart1.ChartAreas[0].AxisY2.Minimum = _y2SetMin;
319  }
320  }
321 
322  private void ySetManualLimits()
323  {
324  if ((_yMinAuto || _yMaxAuto) && _yMin == _yMax)
325  {
326  chart1.ChartAreas[0].AxisY.Maximum = _yMin + 1;
327  chart1.ChartAreas[0].AxisY.Minimum = _yMin - 1;
328  }
329  else if (_yMaxAuto && _yMinAuto)
330  {
331  chart1.ChartAreas[0].AxisY.Maximum = Double.NaN;
332  chart1.ChartAreas[0].AxisY.Minimum = Double.NaN;
333  }
334  else if (_yMinAuto)
335  {
336  chart1.ChartAreas[0].AxisY.Minimum = Double.NaN;
337  chart1.ChartAreas[0].AxisY.Maximum = _ySetMax;
338  }
339  else if (_yMaxAuto)
340  {
341  chart1.ChartAreas[0].AxisY.Maximum = Double.NaN;
342  chart1.ChartAreas[0].AxisY.Minimum = _ySetMin;
343  }
344  }
345 
346  private Double findMinimum(Double min, Double value)
347  {
348  if (Double.IsNaN(min))
349  {
350  return value;
351  }
352  else if (value < min)
353  {
354  return value;
355  }
356 
357  return min;
358  }
359 
360  private Double findMaximum(Double max, Double value)
361  {
362  if (Double.IsNaN(max))
363  {
364  return value;
365  }
366  else if (value > max)
367  {
368  return value;
369  }
370 
371  return max;
372  }
373 
374  private void setYRange(Axis axis, ref Double min, ref Double max, Double value, Boolean setAxis)
375  {
376  min = findMinimum(min, value);
377  max = findMaximum(max, value);
378 
379  if (setAxis)
380  {
381  if (min == max)
382  {
383  axis.Minimum = min - 1;
384  axis.Maximum = max + 1;
385  }
386  else
387  {
388  axis.Minimum = min;
389  axis.Maximum = max;
390  }
391  }
392  }
393 
394  #region IUpdate Members
395 
396  public void UpdateControl()
397  {
398  DateTime time = DateTime.Now;
399 
400  if (time >= _lastUpdate.AddSeconds(_graphDef.UpdateRate))
401  {
402  _lastUpdate = time;
403 
404  foreach (Series series in chart1.Series)
405  {
406  double result;
407 
408  try
409  {
410 
411  if (series.Name.EndsWith(" (Setpoint)"))
412  {
413  if (Double.TryParse(Seci.Managers.BlockMgr.GetBlockSetpoint(series.Name.Substring(0, series.Name.IndexOf(" (Setpoint)"))), out result))
414  {
415  addDataPoint(series, time, result);
416  checkYRange(series, result);
417  }
418  }
419  else
420  {
421  if (Double.TryParse(Seci.Managers.BlockMgr.GetBlockValue(series.Name), out result))
422  {
423  addDataPoint(series, time, result);
424  checkYRange(series, result);
425  }
426  }
427  }
428  catch
429  {
430  //Just incase something goes wrong with the parsing.
431  }
432  }
433 
434  adjustXaxisRange();
435  chart1.Invalidate();
436  }
437  }
438 
439  #endregion
440 
441  private void autoScaleToolStripMenuItem_Click(object sender, EventArgs e)
442  {
443  chart1.ChartAreas[0].AxisX.ScaleView.ZoomReset(0);
444  chart1.ChartAreas[0].AxisY.ScaleView.ZoomReset(0);
445  chart1.ChartAreas[0].AxisY2.ScaleView.ZoomReset(0);
446  }
447 
448  private void clearPointsToolStripMenuItem_Click(object sender, EventArgs e)
449  {
450  ClearAllPlotData();
451  }
452 
453  private void setLimitsXToolStripMenuItem(object sender, EventArgs e)
454  {
455  //Not implemented
456  }
457 
458  private void autoScaleXToolStripMenuItem_Click(object sender, EventArgs e)
459  {
460  //Not implemented
461  }
462 
463  private void setLimitsYToolStripMenuItem_Click(object sender, EventArgs e)
464  {
465  GraphLimitsDialog limits = new GraphLimitsDialog();
466  _limitsBeingChanged = true;
467  limits.Min = chart1.ChartAreas[0].AxisY.Minimum;
468  limits.Max = chart1.ChartAreas[0].AxisY.Maximum;
469  if (_yMinAuto) limits.Min = Double.NaN;
470  if (_yMaxAuto) limits.Max = Double.NaN;
471 
472  if (limits.ShowDialog() == DialogResult.OK)
473  {
474  _yLimitsSetManually = true;
475  _ySetMin = limits.Min;
476  _ySetMax = limits.Max;
477  chart1.ChartAreas[0].AxisY.Minimum = limits.Min;
478  chart1.ChartAreas[0].AxisY.Maximum = limits.Max;
479  _yMinAuto = Double.IsNaN(limits.Min);
480  _yMaxAuto = Double.IsNaN(limits.Max);
481  ySetManualLimits();
482  }
483  _limitsBeingChanged = false;
484  }
485 
486  private void autoScaleYToolStripMenuItem_Click(object sender, EventArgs e)
487  {
488  _yLimitsSetManually = false;
489  _ySetMin = Double.NaN;
490  _ySetMax = Double.NaN;
491  }
492 
493  private void setLimitsY2ToolStripMenuItem_Click(object sender, EventArgs e)
494  {
495  GraphLimitsDialog limits = new GraphLimitsDialog();
496  _limitsBeingChanged = true;
497  limits.Min = chart1.ChartAreas[0].AxisY2.Minimum;
498  limits.Max = chart1.ChartAreas[0].AxisY2.Maximum;
499  if (_y2MinAuto) limits.Min = Double.NaN;
500  if (_y2MaxAuto) limits.Max = Double.NaN;
501 
502  if (limits.ShowDialog() == DialogResult.OK)
503  {
504  _y2LimitsSetManually = true;
505  _y2SetMin = limits.Min;
506  _y2SetMax = limits.Max;
507  chart1.ChartAreas[0].AxisY2.Minimum = limits.Min;
508  chart1.ChartAreas[0].AxisY2.Maximum = limits.Max;
509  _y2MinAuto = Double.IsNaN(limits.Min);
510  _y2MaxAuto = Double.IsNaN(limits.Max);
511  y2SetManualLimits();
512  }
513  _limitsBeingChanged = false;
514  }
515 
516  private void autoScaleY2ToolStripMenuItem_Click(object sender, EventArgs e)
517  {
518  _y2LimitsSetManually = false;
519  _y2SetMin = Double.NaN;
520  _y2SetMax = Double.NaN;
521  }
522  }
523 }
This class is used for serializing the graphs for saving in the configuration.
void setLimitsXToolStripMenuItem(object sender, EventArgs e)
Definition: Graph.cs:453
void addDataPoint(Series series, DateTime time, double value)
Definition: Graph.cs:197
void checkYRange(Series series, double value)
Definition: Graph.cs:248
void autoScaleXToolStripMenuItem_Click(object sender, EventArgs e)
Definition: Graph.cs:458
Double findMaximum(Double max, Double value)
Definition: Graph.cs:360
void autoScaleYToolStripMenuItem_Click(object sender, EventArgs e)
Definition: Graph.cs:486
void autoScaleY2ToolStripMenuItem_Click(object sender, EventArgs e)
Definition: Graph.cs:516
void addPlot(string blockname, Boolean useY2)
Definition: Graph.cs:142
void autoScaleToolStripMenuItem_Click(object sender, EventArgs e)
Definition: Graph.cs:441
Double findMinimum(Double min, Double value)
Definition: Graph.cs:346
void setLimitsYToolStripMenuItem_Click(object sender, EventArgs e)
Definition: Graph.cs:463
void clearPointsToolStripMenuItem_Click(object sender, EventArgs e)
Definition: Graph.cs:448
void SetupGraph(Seci.Definitions.GraphDefinition graphDef)
Definition: Graph.cs:71
void setYRange(Axis axis, ref Double min, ref Double max, Double value, Boolean setAxis)
Definition: Graph.cs:374
Seci.Definitions.GraphDefinition _graphDef
Definition: Graph.cs:40
void setLimitsY2ToolStripMenuItem_Click(object sender, EventArgs e)
Definition: Graph.cs:493