using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Collections.ObjectModel; using System.ComponentModel; using System.Windows.Forms.DataVisualization.Charting; namespace FLY.ControlLibrary { /// <summary> /// GraphRange.xaml 的交互逻辑 /// IsAutoTarget, 自动目标值 /// IsPercent,百分比显示,alarm = curr_target * Alarm/Target, /// </summary> public partial class GraphRange : UserControl, IGraphRange, INotifyPropertyChanged { #region 私有成员变量 int autotarget = 30; protected int AutoTarget { get { return autotarget; } set { if (autotarget != value) { autotarget = value; NotifyPropertyChanged("AutoTarget"); } } } #endregion #region 接口 public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(GraphRange), new PropertyMetadata( "Title", new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) { (d as GraphRange).NotifyPropertyChanged("Title"); }))); public string Title { get { return GetValue(TitleProperty) as string; } set { SetValue(TitleProperty, value); } } public static readonly DependencyProperty Title2Property = DependencyProperty.Register("Title2", typeof(string), typeof(GraphRange), new PropertyMetadata( "", new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) { (d as GraphRange).NotifyPropertyChanged("Title2"); }))); public string Title2 { get { return GetValue(Title2Property) as string; } set { SetValue(Title2Property, value); } } /// <summary> /// 计算结果 /// </summary> public static readonly DependencyProperty CalStateProperty = DependencyProperty.Register("CalState", typeof(string), typeof(GraphRange), new PropertyMetadata( "Max:null Min:null Avg:null 2σ:null", new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) { }))); public string CalState { get { return (string)GetValue(CalStateProperty); } set { SetValue(CalStateProperty, value); } } /// <summary> /// 空的数据,定义某个值为空,默认为 99999998 /// </summary> public static readonly DependencyProperty EmptyValueProperty = DependencyProperty.Register("EmptyValue", typeof(int), typeof(GraphRange), new PropertyMetadata( 99999998, new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) { (d as GraphRange).NotifyPropertyChanged("EmptyValue"); }))); public int EmptyValue { get { return (int)GetValue(EmptyValueProperty); } set { SetValue(EmptyValueProperty, value); } } /// <summary> /// 放大倍数, 目标值= Target/放大倍数 /// </summary> public static readonly DependencyProperty MultiProperty = DependencyProperty.Register("Multi", typeof(int), typeof(GraphRange), new PropertyMetadata( 100, new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) { (d as GraphRange).NotifyPropertyChanged("Multi"); }))); public int Multi { get { return (int)GetValue(MultiProperty); } set { SetValue(MultiProperty, value); } } /// <summary> /// %显示 /// </summary> public static readonly DependencyProperty IsPercentProperty = DependencyProperty.Register("IsPercent", typeof(bool), typeof(GraphRange), new PropertyMetadata( false, new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) { (d as GraphRange).NotifyPropertyChanged("IsPercent"); }))); public bool IsPercent { get { return (bool)GetValue(IsPercentProperty); } set { SetValue(IsPercentProperty, value); } } /// <summary> /// 自动目标值 /// </summary> public static readonly DependencyProperty IsAutoTargetProperty = DependencyProperty.Register("IsAutoTarget", typeof(bool), typeof(GraphRange), new PropertyMetadata( false, new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) { (d as GraphRange).NotifyPropertyChanged("IsAutoTarget"); }))); public bool IsAutoTarget { get { return (bool)GetValue(IsAutoTargetProperty); } set { SetValue(IsAutoTargetProperty, value); } } public static readonly DependencyProperty TypeProperty = DependencyProperty.Register("Type", typeof(GraphRangeType), typeof(GraphRange), new PropertyMetadata( GraphRangeType.Sigma2, new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) { (d as GraphRange).NotifyPropertyChanged("Type"); }))); public GraphRangeType Type { get { return (GraphRangeType)GetValue(TypeProperty); } set { SetValue(TypeProperty, value); } } public static readonly DependencyProperty XAxisTypeProperty = DependencyProperty.Register("XAxisType", typeof(GraphRangeXAxisType), typeof(GraphRange), new PropertyMetadata( GraphRangeXAxisType.Time, new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) { (d as GraphRange).NotifyPropertyChanged("XAxisType"); }))); public GraphRangeXAxisType XAxisType { get { return (GraphRangeXAxisType)GetValue(XAxisTypeProperty); } set { SetValue(XAxisTypeProperty, value); } } /// <summary> /// 目标值 /// </summary> public static readonly DependencyProperty TargetProperty = DependencyProperty.Register("Target", typeof(int), typeof(GraphRange), new PropertyMetadata( 3000, new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) { (d as GraphRange).NotifyPropertyChanged("Target"); }))); public int Target { get { return (int)GetValue(TargetProperty); } set { SetValue(TargetProperty, value); } } /// <summary> /// 报警值 /// </summary> public static readonly DependencyProperty AlarmProperty = DependencyProperty.Register("Alarm", typeof(int), typeof(GraphRange), new PropertyMetadata( 1000, new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) { (d as GraphRange).NotifyPropertyChanged("Alarm"); }))); public int Alarm { get { return (int)GetValue(AlarmProperty); } set { SetValue(AlarmProperty, value); } } /// <summary> /// 控制线 /// </summary> public static readonly DependencyProperty HasCtrlLineProperty = DependencyProperty.Register("HasCtrlLine", typeof(bool), typeof(GraphRange), new PropertyMetadata( false, new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) { (d as GraphRange).NotifyPropertyChanged("HasCtrlLine"); }))); public bool HasCtrlLine { get { return (bool)GetValue(HasCtrlLineProperty); } set { SetValue(HasCtrlLineProperty, value); } } /// <summary> /// 控制线 /// </summary> public static readonly DependencyProperty CtrlLineProperty = DependencyProperty.Register("CtrlLine", typeof(int), typeof(GraphRange), new PropertyMetadata( 1000, new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) { (d as GraphRange).NotifyPropertyChanged("CtrlLine"); }))); public int CtrlLine { get { return (int)GetValue(CtrlLineProperty); } set { SetValue(CtrlLineProperty, value); } } /// <summary> /// Y轴是 Alarm 的N倍 默认是 3倍 /// </summary> public static readonly DependencyProperty YRangePercentProperty = DependencyProperty.Register("YRangePercent", typeof(double), typeof(GraphRange), new PropertyMetadata( 3.0, new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) { (d as GraphRange).NotifyPropertyChanged("YRangePercent"); }))); public double YRangePercent { get { return (double)GetValue(YRangePercentProperty); } set { SetValue(YRangePercentProperty, value); } } public static readonly DependencyProperty DataCntProperty = DependencyProperty.Register("DataCnt", typeof(int), typeof(GraphRange), new PropertyMetadata( 80, new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) { (d as GraphRange).NotifyPropertyChanged("DataCnt"); }))); public int DataCnt { get { return (int)GetValue(DataCntProperty); } set { SetValue(DataCntProperty, value); } } /// <summary> /// 数据源 /// </summary> private ObservableCollection<GraphRangeData> _datasource; public ObservableCollection<GraphRangeData> DataSource { get { return _datasource; } set { if (_datasource != null) _datasource.CollectionChanged -= _dataSource_CollectionChanged; _datasource = value; _datasource.CollectionChanged += _dataSource_CollectionChanged; mRefresh.UpdateData = true; } } System.Collections.Specialized.NotifyCollectionChangedEventHandler _dataSource_CollectionChanged; public event EventHandler ShowSettingDialogEvent; #endregion #region 刷新率控制 class Refresh { /// <summary> /// 控制刷新率 /// </summary> System.Windows.Threading.DispatcherTimer timer; bool updatedata_flag = false; bool updatey_flag = false; bool updatex_flag = false; bool updatecalx_flag = false; /// <summary> /// 最快0.1s 刷新一次 /// </summary> TimeSpan REFRESH_RATE = TimeSpan.FromSeconds(0.1); public Refresh(GraphRange graph) { timer = new System.Windows.Threading.DispatcherTimer(); timer.Tick += new EventHandler(delegate(object sender, EventArgs e) { if (UpdateData) { UpdateData = false; graph.UpdateAutoTarget(); graph.DataBindAll(); } if (UpdateY) { UpdateY = false; graph.UpdateAxisY(); } if (UpdateX) { UpdateX = false; graph.UpdateXRange(); graph.UpdateXType(); } timer.Stop(); }); timer.Interval = REFRESH_RATE; timer.Start(); } public bool UpdateData { get { return updatedata_flag; } set { updatedata_flag = value; if (value) { if (!timer.IsEnabled) timer.Start(); } } } public bool UpdateY { get { return updatey_flag; } set { updatey_flag = value; if (value) { if (!timer.IsEnabled) timer.Start(); } } } public bool UpdateX { get { return updatex_flag; } set { updatex_flag = value; if (value) { if (!timer.IsEnabled) timer.Start(); } } } public bool UpdateCalX { get { return updatecalx_flag; } set { updatecalx_flag = value; if (value) { if (!timer.IsEnabled) timer.Start(); } } } } Refresh mRefresh; #endregion public GraphRange() { InitializeComponent(); InitializeComponent2(); _dataSource_CollectionChanged = new System.Collections.Specialized.NotifyCollectionChangedEventHandler(dataSource_CollectionChanged); this.PropertyChanged += new PropertyChangedEventHandler(GraphRange_PropertyChanged); mRefresh = new Refresh(this); mRefresh.UpdateX = true; mRefresh.UpdateY = true; CheckDataSourceEmpty(); run_title.Text = Title; run_title2.Text = Title2; } void GraphRange_PropertyChanged(object sender, PropertyChangedEventArgs e) { if ((e.PropertyName == "Target") || (e.PropertyName == "Alarm") || (e.PropertyName == "YRangePercent") || (e.PropertyName == "AutoTarget") || (e.PropertyName == "IsAutoTarget") || (e.PropertyName == "IsPercent") || (e.PropertyName == "Type")) { mRefresh.UpdateY = true; mRefresh.UpdateData = true; } else if ((e.PropertyName == "DataCnt") || (e.PropertyName == "XAxisType")) { mRefresh.UpdateX = true; } else if (e.PropertyName == "Title") { run_title.Text = Title; } else if (e.PropertyName == "Title2") { run_title2.Text = Title2; } } void InitializeComponent2() { System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea(); System.Windows.Forms.DataVisualization.Charting.Series series_sigma = new System.Windows.Forms.DataVisualization.Charting.Series(); System.Windows.Forms.DataVisualization.Charting.Series series_avg = new System.Windows.Forms.DataVisualization.Charting.Series(); chart1.Name = "chart1"; chart1.BackColor = System.Drawing.Color.Transparent; // // chart1 //--------------------------------------------------------------------------------------- chartArea1.Name = "chartArea1"; chartArea1.AxisX.LabelStyle.Font = new System.Drawing.Font("Trebuchet MS", 8.25F, System.Drawing.FontStyle.Bold); chartArea1.AxisX.LineColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); chartArea1.AxisX.MajorGrid.LineColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); chartArea1.AxisX.IsMarksNextToAxis = false; chartArea1.AxisX.IsLabelAutoFit = true; chartArea1.AxisX.LabelAutoFitStyle = LabelAutoFitStyles.DecreaseFont; chartArea1.AxisX.IntervalAutoMode = System.Windows.Forms.DataVisualization.Charting.IntervalAutoMode.VariableCount; chartArea1.AxisY.LabelStyle.Font = new System.Drawing.Font("Trebuchet MS", 8.25F, System.Drawing.FontStyle.Bold); chartArea1.AxisY.LineColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); chartArea1.AxisY.MajorGrid.LineColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); chartArea1.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); chartArea1.BorderDashStyle = System.Windows.Forms.DataVisualization.Charting.ChartDashStyle.Solid; //chartArea1.Position.Height = 100;// 42F; //chartArea1.Position.Width = 100;// 88F; //chartArea1.Position.X = 0;// 3F; //chartArea1.Position.Y = 0;// 10F; chart1.ChartAreas.Add(chartArea1); //--------------------------------------------------------------------------------------- series_sigma.Name = "series_sigma"; series_sigma.ChartArea = chartArea1.Name; series_sigma.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.RangeColumn; series_sigma.Color = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(65)))), ((int)(((byte)(140)))), ((int)(((byte)(240))))); series_sigma.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(180)))), ((int)(((byte)(26)))), ((int)(((byte)(59)))), ((int)(((byte)(105))))); series_sigma.BorderWidth = 2; series_sigma.ShadowColor = System.Drawing.Color.Black; series_sigma.ShadowOffset = 2; series_sigma.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Int32; series_sigma.YValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Double; chart1.Series.Add(series_sigma); //--------------------------------------------------------------------------------------- series_avg.Name = "series_avg"; series_avg.ChartArea = chartArea1.Name; series_avg.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Spline; series_avg.Color = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(65)))), ((int)(((byte)(140)))), ((int)(((byte)(240))))); series_avg.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(180)))), ((int)(((byte)(26)))), ((int)(((byte)(59)))), ((int)(((byte)(105))))); series_avg.BorderWidth = 3; series_avg.ShadowColor = System.Drawing.Color.Black; series_avg.ShadowOffset = 2; series_avg.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Int32; series_avg.YValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Double; chart1.Series.Add(series_avg); //chart1.PrePaint += new EventHandler<ChartPaintEventArgs>(Chart1_PrePaint); this.chart1.PrePaint += new System.EventHandler<System.Windows.Forms.DataVisualization.Charting.ChartPaintEventArgs>(Chart1_PrePaint); } public void UpdateCtrlLine() { //chart1.Invalidate(); } protected virtual void Chart1_PrePaint(object sender, System.Windows.Forms.DataVisualization.Charting.ChartPaintEventArgs e) { //if (e.ChartElement is ChartArea) //{ // ChartArea area = (ChartArea)e.ChartElement; // if (area.Name == "chartArea1") // { // if (!HasCtrlLine) // return; // double c1 = ((double)(Target+CtrlLine)) / Multi; // double c2 = ((double)(Target-CtrlLine)) / Multi; // if (c1 > area.AxisY.Maximum || c2 < area.AxisY.Minimum) // return; // // Take Graphics object from chart // System.Drawing.Graphics graph = e.ChartGraphics.Graphics; // // Convert X and Y values to screen position // c1 = e.ChartGraphics.GetPositionFromAxis(area.Name, AxisName.Y, c1); // c2 = e.ChartGraphics.GetPositionFromAxis(area.Name, AxisName.Y, c2); // double XMin = e.ChartGraphics.GetPositionFromAxis(area.Name, AxisName.X, area.AxisX.Minimum); // double XMax = e.ChartGraphics.GetPositionFromAxis(area.Name, AxisName.X, area.AxisX.Maximum); // // Draw Maximum trangle // System.Drawing.Pen darkPen = new System.Drawing.Pen(System.Drawing.Color.DarkRed, 2); // System.Drawing.PointF[] points = new System.Drawing.PointF[2]; // points[0].X = (float)XMin; // points[1].X = (float)XMax; // points[0].Y = points[1].Y = (float)c1; // // Convert relative coordinates to absolute coordinates. // points[0] = e.ChartGraphics.GetAbsolutePoint(points[0]); // points[1] = e.ChartGraphics.GetAbsolutePoint(points[1]); // graph.DrawLine(darkPen, points[0], points[1]); // points[0].X = (float)XMin; // points[1].X = (float)XMax; // points[0].Y = points[1].Y = (float)c2; // // Convert relative coordinates to absolute coordinates. // points[0] = e.ChartGraphics.GetAbsolutePoint(points[0]); // points[1] = e.ChartGraphics.GetAbsolutePoint(points[1]); // graph.DrawLine(darkPen, points[0], points[1]); // } //} } void dataSource_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { mRefresh.UpdateData = true; } #region 绑定画图 void DataBindAll() { CheckDataSourceEmpty(); UpdateAutoTarget(); switch (Type) { case GraphRangeType.All: DataBindAll_all(); break; case GraphRangeType.Avg: DataBindAll_avg(); break; case GraphRangeType.Sigma2: DataBindAll_2sigma(); break; } UpdateXType(); } #endregion void UpdateDataCalRange() { switch (Type) { case GraphRangeType.All: UpdateDataCalRange_avg(); break; case GraphRangeType.Avg: UpdateDataCalRange_avg(); break; case GraphRangeType.Sigma2: UpdateDataCalRange_2sigma(); break; } } /// <summary> /// DataSource 里面的数据都是 EmptyValue 组成, 当修改数据时,应该clear ,再添加 /// </summary> bool DataSourceIsEmpty = false; /// <summary> /// 当一个数据都没有时,需要添加空数据 /// </summary> protected void CheckDataSourceEmpty() { if ((DataSource == null) || (DataSource.Count() == 0)) { chart1.Series["series_avg"].Points.Clear(); chart1.Series["series_sigma"].Points.Clear(); chart1.Series["series_sigma"].Points.AddXY(0, 10,-10); chart1.Series["series_sigma"].Points[0].IsEmpty = true; DataSourceIsEmpty = true; } else { DataSourceIsEmpty = false; } } public void Button_Click(object sender, RoutedEventArgs e) { if (ShowSettingDialogEvent != null) ShowSettingDialogEvent(this, new EventArgs()); } /// <summary> /// 与 FirstBoltNo,FirstIndex,LastIndex 相关 /// </summary> void UpdateXRange() { // Set manual minimum and maximum values. chart1.ChartAreas["chartArea1"].AxisX.Minimum = 0; chart1.ChartAreas["chartArea1"].AxisX.Maximum = DataCnt; } void UpdateXType() { UpdateDataCalRange(); if (DataSource == null) return; switch (XAxisType) { case GraphRangeXAxisType.Index: { for (int i = 0; i < DataSource.Count(); i++) { if (i < chart1.Series["series_sigma"].Points.Count) { DataPoint p = chart1.Series["series_sigma"].Points[i]; p.AxisLabel = null; } if (i < chart1.Series["series_avg"].Points.Count) { DataPoint p = chart1.Series["series_avg"].Points[i]; p.AxisLabel = null; } } }break; case GraphRangeXAxisType.Time: { for (int i = 0; i < DataSource.Count(); i++) { if (i < chart1.Series["series_sigma"].Points.Count) { DataPoint p = chart1.Series["series_sigma"].Points[i]; p.AxisLabel = DataSource[i].Time.ToString("HH:mm"); } if (i < chart1.Series["series_avg"].Points.Count) { DataPoint p = chart1.Series["series_avg"].Points[i]; p.AxisLabel = DataSource[i].Time.ToString("HH:mm"); } } }break; } } void UpdateAutoTarget() { AutoTarget = Target; if (DataSource == null) return; for (int i = 0; i < DataSource.Count(); i++) { if (DataSource[DataSource.Count - 1 - i].Avg != EmptyValue) { AutoTarget = DataSource[DataSource.Count - 1 - i].Avg; break; } } } void UpdateAxisY() { switch(Type) { case GraphRangeType.Sigma2: UpdateAxisY_2sigma(); break; case GraphRangeType.All: UpdateAxisY_all(); break; case GraphRangeType.Avg: UpdateAxisY_avg(); break; } } protected void SetCustomLabel(Axis axisY, double p, string s, GridTickTypes gt, System.Drawing.Color c) { double multi = Multi; double r = axisY.Maximum - axisY.Minimum; double f, t; f = p + r / 2; t = p - r / 2; CustomLabel customLabel = axisY.CustomLabels.Add(f, t, s); customLabel.GridTicks = gt; customLabel.ForeColor = c; } #region 2sigma protected virtual void SetCustomLabel_2sigma(Axis axisY, double p, GridTickTypes gt, System.Drawing.Color c, bool ispercent, string message) { double multi = Multi; double r = axisY.Maximum - axisY.Minimum; double f, t; f = p + r / 2; t = p - r / 2; CustomLabel customLabel; string str; if (!ispercent) { str = p.ToString("N1"); } else { str = p.ToString("N1") + "%"; } if (!string.IsNullOrEmpty(message)) str = message + " " + str; customLabel = axisY.CustomLabels.Add(f, t, str); customLabel.GridTicks = gt; customLabel.ForeColor = c; } protected virtual void UpdateAxisY_2sigma() { double multi = Multi; double alarm; double yrange; if (IsPercent) { alarm = 100.0 * Alarm / Target; yrange = alarm * YRangePercent; } else { alarm = Alarm / multi; yrange = alarm * YRangePercent; } if ((alarm < 0.1) || (yrange<0.3)) { alarm = 0.1; yrange = 0.3; } Axis axisY = chart1.ChartAreas["chartArea1"].AxisY; //TODO axisY.Crossing = 0; // Set manual minimum and maximum values. axisY.Minimum = -yrange; axisY.Maximum = yrange; axisY.CustomLabels.Clear(); //上下限 SetCustomLabel_2sigma(axisY, alarm, GridTickTypes.All, System.Drawing.Color.DarkGoldenrod, IsPercent, null); SetCustomLabel_2sigma(axisY, -alarm, GridTickTypes.All, System.Drawing.Color.DarkGoldenrod, IsPercent, null); //2倍上下限 SetCustomLabel_2sigma(axisY, alarm * 2, GridTickTypes.All, System.Drawing.Color.Red, IsPercent, null); SetCustomLabel_2sigma(axisY, -alarm * 2, GridTickTypes.All, System.Drawing.Color.Red, IsPercent, null); //SetCustomLabel_2sigma(axisY, axisY.Maximum, GridTickTypes.All, System.Drawing.Color.Black, IsPercent, null); //SetCustomLabel_2sigma(axisY, axisY.Minimum, GridTickTypes.All, System.Drawing.Color.Black, IsPercent, null); //显示范围数值 if ((yrange != alarm) && (yrange != 2 * alarm)) { SetCustomLabel_2sigma(axisY, axisY.Minimum, GridTickTypes.TickMark, System.Drawing.Color.Black, IsPercent, null); SetCustomLabel_2sigma(axisY, axisY.Maximum, GridTickTypes.TickMark, System.Drawing.Color.Black, IsPercent, null); } //显示范围太小,多加纵坐标 if (yrange <= alarm) { SetCustomLabel_2sigma(axisY, (axisY.Maximum + axisY.Minimum) / 2 - (axisY.Maximum - axisY.Minimum) / 4, GridTickTypes.All, System.Drawing.Color.Black, IsPercent, null); SetCustomLabel_2sigma(axisY, (axisY.Maximum + axisY.Minimum) / 2 + (axisY.Maximum - axisY.Minimum) / 4, GridTickTypes.All, System.Drawing.Color.Black, IsPercent, null); } } protected virtual void UpdateOnePoint_2sigma(int index) { double multi = Multi; int emptyvalue = EmptyValue; DataPoint point; double sigma2 = 2 * DataSource[index].Sigma / multi; double avg = DataSource[index].Avg / multi; if (IsPercent) { if (avg <= 0) { //异常 sigma2 = 0; } else { sigma2 = sigma2 / avg * 100; } } if (index >= chart1.Series["series_sigma"].Points.Count) { chart1.Series["series_sigma"].Points.AddXY(index, sigma2, -sigma2); point = chart1.Series["series_sigma"].Points[index]; } else { point = chart1.Series["series_sigma"].Points[index]; point.YValues[0] = sigma2; point.YValues[1] = -sigma2; point.XValue = index; } //设定空数据 if (DataSource[index].Avg == emptyvalue) { point.IsEmpty = true; } else { point.IsEmpty = false; double threshold1; double threshold2; if (IsPercent) threshold1 = 100.0 * Alarm / Target; else threshold1 = Alarm / multi; threshold2 = threshold1 * 2; System.Drawing.Color c; if (sigma2 > threshold2) c = System.Drawing.Color.Red; else if (sigma2 > threshold1) c = System.Drawing.Color.Orange; else c = System.Drawing.Color.Green; point.BorderColor = c; point.Color = c; } } void DataBindAll_2sigma() { chart1.Series["series_avg"].Points.Clear(); chart1.Series["series_sigma"].Points.Clear(); if (DataSource != null) { for (int i = 0; i < DataSource.Count(); i++) { UpdateOnePoint_2sigma(i); } } CheckDataSourceEmpty(); } /// <summary> /// 与 FirstBoltNo,DataFirst,DataLast 相关 /// </summary> void UpdateDataCalRange_2sigma() { System.Windows.Forms.DataVisualization.Charting.Axis axisX = chart1.ChartAreas["chartArea1"].AxisX; axisX.CustomLabels.Clear(); } #endregion #region all void UpdateAxisY_all() { chart1.Series["series_avg"].ChartType = SeriesChartType.Spline; UpdateAxisY_avg(); } protected virtual void UpdateOnePoint_all(int index) { double multi = Multi; int emptyvalue = EmptyValue; DataPoint point_sigma; DataPoint point_avg; double sigma2 = 2 * DataSource[index].Sigma / multi; double avg = DataSource[index].Avg / multi; if (index >= chart1.Series["series_sigma"].Points.Count) { chart1.Series["series_sigma"].Points.AddXY(index, avg + sigma2, avg - sigma2); point_sigma = chart1.Series["series_sigma"].Points[index]; chart1.Series["series_avg"].Points.AddXY(index, avg); point_avg = chart1.Series["series_avg"].Points[index]; } else { point_sigma = chart1.Series["series_sigma"].Points[index]; point_sigma.YValues[0] = avg + sigma2; point_sigma.YValues[1] = avg - sigma2; point_sigma.XValue = index; point_avg = chart1.Series["series_avg"].Points[index]; point_avg.YValues[0] = avg; point_avg.XValue = index; } //设定空数据 if (DataSource[index].Avg == emptyvalue) { point_sigma.IsEmpty = true; point_avg.IsEmpty = true; } else { point_sigma.IsEmpty = false; double alarm; double target; double yrange; bool ispercent; GetActualTargetAlarm(out target, out alarm, out yrange, out ispercent); double threshold1 = alarm; double threshold2 = alarm * 2; System.Drawing.Color c; if (((avg + sigma2) > (target + threshold2)) || ((avg + sigma2) < (target - threshold2))) { c = System.Drawing.Color.Red; } else if (((avg - sigma2) > (target + threshold2)) || ((avg - sigma2) < (target - threshold2))) { c = System.Drawing.Color.Red; } else if (((avg + sigma2) > (target + threshold1)) || ((avg + sigma2) < (target - threshold1))) { c = System.Drawing.Color.Orange; } else if (((avg - sigma2) > (target + threshold1)) || ((avg - sigma2) < (target - threshold1))) { c = System.Drawing.Color.Orange; } else { c = System.Drawing.Color.Green; } point_sigma.BorderColor = c; point_sigma.Color = c; point_avg.Color = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(65)))), ((int)(((byte)(140)))), ((int)(((byte)(240))))); point_avg.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(180)))), ((int)(((byte)(26)))), ((int)(((byte)(59)))), ((int)(((byte)(105))))); } } void DataBindAll_all() { chart1.Series["series_avg"].Points.Clear(); chart1.Series["series_sigma"].Points.Clear(); if (DataSource != null) { for (int i = 0; i < DataSource.Count(); i++) { UpdateOnePoint_all(i); } } CheckDataSourceEmpty(); UpdateCalState_all(); } /// <summary> /// 与 Multi,EmptyValue,DataFirst,DataLast 相关 /// </summary> void UpdateCalState_all() { int emptyvalue = EmptyValue; double multi = Multi; if (DataSource == null) { goto _err; } string title; var dats_max = from data in DataSource where data.Avg != emptyvalue select data.Avg+data.Sigma*2; var dats_min = from data in DataSource where data.Avg != emptyvalue select data.Avg - data.Sigma * 2; List<int> dats = new List<int>(); dats.AddRange(dats_max); dats.AddRange(dats_min); if (dats.Count() == 0) { goto _err; } else { //AutoTarget = (int)dats.Average(); double avg = dats.Average() / multi; double max = dats.Max() / multi; double min = dats.Min() / multi; double sum = 0; foreach (int dat in dats) { sum += Math.Pow(avg - (double)dat / multi, 2); } sum /= (dats.Count() - 1); double sigma = Math.Sqrt(sum); if (IsPercent) { title = "Max:+" + ((max / avg * 100) - 100).ToString("N1") + "% " + "Min:-" + (100 - (min / avg * 100)).ToString("N1") + "% " + "Avg:" + avg.ToString("N1") + " " + "2σ:" + (sigma / avg * 2 * 100).ToString("N1") + "%"; } else { title = "Max:" + max.ToString("N1") + " " + "Min:" + min.ToString("N1") + " " + "Avg:" + avg.ToString("N1") + " " + "2σ:" + (sigma * 2).ToString("N1"); } } goto _end; _err: //title = Properties.Resources.StrMax + ":" + "NULL " + // Properties.Resources.StrMin+":"+"NULL "+ // Properties.Resources.StrAvg+":"+"NULL "+ // "3Sigma:"+"NULL "; title = "Max:null Min:null Avg:null 2σ:null"; _end: CalState = title; UpdateDataCalRange_avg(); } #endregion #region avg protected void SetCustomLabel(Axis axisY, double p, GridTickTypes gt, System.Drawing.Color c, bool ispercent, string message) { double multi = Multi; double r = axisY.Maximum - axisY.Minimum; double f, t; f = p + r / 2; t = p - r / 2; double target = (axisY.Maximum + axisY.Minimum) / 2; CustomLabel customLabel; string str; if (!ispercent) { str = p.ToString("N1"); } else { if (target == p) { str = p.ToString("N1"); } else { double percent; percent = ((p - target) / target) * 100; str = percent.ToString("N1") + "%"; } } if (!string.IsNullOrEmpty(message)) str = message + " " + str; customLabel = axisY.CustomLabels.Add(f, t, str); customLabel.GridTicks = gt; customLabel.ForeColor = c; } /// <summary> /// 获取实际应用的目标值,公差 /// </summary> /// <param name="target"></param> /// <param name="alarm"></param> /// <param name="ispercent"></param> void GetActualTargetAlarm(out double target, out double alarm, out double yrange, out bool ispercent) { double multi = Multi; if (IsAutoTarget) target = AutoTarget / multi;//AutoTarget 计算得到的平均值 else target = Target / multi; ispercent = IsPercent; if ((ispercent) && (Target <= 0) || (Alarm < 0) || (target <= 0)) ispercent = false; if (ispercent) { alarm = target * Alarm / Target; yrange = alarm * YRangePercent; } else { alarm = Alarm / multi; yrange = alarm * YRangePercent; } if ((alarm < 0.1) || (yrange < 0.3)) { alarm = 0.1; yrange = 0.3; } } protected virtual void UpdateAxisY_avg() { double alarm; double target; double yrange; bool ispercent; GetActualTargetAlarm(out target, out alarm, out yrange, out ispercent); Axis axisY = chart1.ChartAreas["chartArea1"].AxisY; axisY.Crossing = target; // Set manual minimum and maximum values. axisY.Minimum = target - yrange;// 3 * alarm; axisY.Maximum = target + yrange;// 3 * alarm; axisY.CustomLabels.Clear(); //目标值 SetCustomLabel(axisY, target, GridTickTypes.All, System.Drawing.Color.Green, false, null); //上下限 SetCustomLabel(axisY, target + alarm, GridTickTypes.All, System.Drawing.Color.DarkGoldenrod, ispercent, null); SetCustomLabel(axisY, target - alarm, GridTickTypes.All, System.Drawing.Color.DarkGoldenrod, ispercent, null); //2倍上下限 SetCustomLabel(axisY, target + alarm * 2, GridTickTypes.All, System.Drawing.Color.Red, ispercent, null); SetCustomLabel(axisY, target - alarm * 2, GridTickTypes.All, System.Drawing.Color.Red, ispercent, null); //显示范围数值 if ((yrange != alarm) && (yrange != 2 * alarm)) { SetCustomLabel(axisY, axisY.Minimum, GridTickTypes.TickMark, System.Drawing.Color.Black, ispercent, null); SetCustomLabel(axisY, axisY.Maximum, GridTickTypes.TickMark, System.Drawing.Color.Black, ispercent, null); } //显示范围太小,多加纵坐标 if (yrange <= alarm) { SetCustomLabel(axisY, target - yrange / 2, GridTickTypes.All, System.Drawing.Color.Black, ispercent, null); SetCustomLabel(axisY, target + yrange / 2, GridTickTypes.All, System.Drawing.Color.Black, ispercent, null); } } protected virtual void UpdateOnePoint_avg(int index) { double multi = Multi; int emptyvalue = EmptyValue; DataPoint point_avg; double avg = DataSource[index].Avg / multi; if (index >= chart1.Series["series_avg"].Points.Count) { chart1.Series["series_avg"].Points.AddXY(index, avg); point_avg = chart1.Series["series_avg"].Points[index]; } else { point_avg = chart1.Series["series_avg"].Points[index]; point_avg.YValues[0] = avg; point_avg.XValue = index; } //设定空数据 if (DataSource[index].Avg == emptyvalue) { point_avg.IsEmpty = true; } else { point_avg.IsEmpty = false; double alarm; double target; double yrange; bool ispercent; GetActualTargetAlarm(out target, out alarm, out yrange, out ispercent); double threshold1 = alarm; double threshold2 = alarm * 2; System.Drawing.Color c; if ((avg > (target + threshold2)) || (avg < (target - threshold2))) { c = System.Drawing.Color.Red; } else if ((avg > (target + threshold1)) || (avg < (target - threshold1))) { c = System.Drawing.Color.Orange; } else { c = System.Drawing.Color.Green; } point_avg.BorderColor = c; point_avg.Color = c; } } void DataBindAll_avg() { chart1.Series["series_avg"].ChartType = SeriesChartType.Column; chart1.Series["series_avg"].Points.Clear(); chart1.Series["series_sigma"].Points.Clear(); if (DataSource != null) { for (int i = 0; i < DataSource.Count(); i++) { UpdateOnePoint_avg(i); } } CheckDataSourceEmpty(); UpdateCalState_avg(); } /// <summary> /// 与 Multi,EmptyValue,DataFirst,DataLast 相关 /// </summary> void UpdateCalState_avg() { int emptyvalue = EmptyValue; double multi = Multi; if (DataSource == null) { goto _err; } string title; var dats = from data in DataSource where data.Avg != emptyvalue select data.Avg; if (dats.Count() == 0) { goto _err; } else { //AutoTarget = (int)dats.Average(); double avg = dats.Average() / multi; double max = dats.Max() / multi; double min = dats.Min() / multi; double sum = 0; foreach (int dat in dats) { sum += Math.Pow(avg - (double)dat / multi, 2); } sum /= (dats.Count() - 1); double sigma = Math.Sqrt(sum); if (IsPercent) { title = "Max:+" + ((max / avg * 100) - 100).ToString("N1") + "% " + "Min:-" + (100 - (min / avg * 100)).ToString("N1") + "% " + "Avg:" + avg.ToString("N1") + " " + "2σ:" + (sigma / avg * 2 * 100).ToString("N1") + "%"; } else { title = "Max:" + max.ToString("N1") + " " + "Min:" + min.ToString("N1") + " " + "Avg:" + avg.ToString("N1") + " " + "2σ:" + (sigma * 2).ToString("N1"); } } goto _end; _err: //title = Properties.Resources.StrMax + ":" + "NULL " + // Properties.Resources.StrMin+":"+"NULL "+ // Properties.Resources.StrAvg+":"+"NULL "+ // "3Sigma:"+"NULL "; title = "Max:null Min:null Avg:null 2σ:null"; _end: CalState = title; UpdateDataCalRange_avg(); } /// <summary> /// 与 FirstBoltNo,DataFirst,DataLast 相关 /// </summary> void UpdateDataCalRange_avg() { System.Windows.Forms.DataVisualization.Charting.Axis axisX = chart1.ChartAreas["chartArea1"].AxisX; axisX.CustomLabels.Clear(); System.Windows.Forms.DataVisualization.Charting.CustomLabel customlabel = new System.Windows.Forms.DataVisualization.Charting.CustomLabel(0,DataCnt, CalState, 1, System.Windows.Forms.DataVisualization.Charting.LabelMarkStyle.LineSideMark); axisX.CustomLabels.Add(customlabel); } #endregion #region INotifyPropertyChanged 成员 public void NotifyPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public event PropertyChangedEventHandler PropertyChanged; #endregion } public interface IGraphRange { #region 附加属性 /// <summary> /// 标题1 /// </summary> string Title { get; set; } /// <summary> /// 标题2 /// </summary> string Title2 { get; set; } /// <summary> /// 计算结果 /// </summary> string CalState { get; set; } /// <summary> /// 空的数据,定义某个值为空,默认为 99999998 /// </summary> int EmptyValue { get; set; } /// <summary> /// 放大倍数, 目标值= Target/放大倍数 /// </summary> int Multi { get; set; } /// <summary> /// %显示 /// </summary> bool IsPercent { get; set; } /// <summary> /// 自动目标值 /// </summary> bool IsAutoTarget { get; set; } /// <summary> /// 图显示的内容, /// 当为Sigma2, 只有 IsPercent 有效 /// 当为Avg, /// 当为All, /// </summary> GraphRangeType Type { get; set; } /// <summary> /// 目标值 /// </summary> int Target { get; set; } /// <summary> /// 报警值 /// </summary> int Alarm { get; set; } /// <summary> /// 数据总量 /// </summary> int DataCnt { get; set; } /// <summary> /// 数据,当 数据>数据总量, 只显示最后的数据! /// </summary> ObservableCollection<GraphRangeData> DataSource { get; set; } /// <summary> /// 设置事件 /// </summary> event EventHandler ShowSettingDialogEvent; #endregion } public enum GraphRangeType { /// <summary> /// 只显示 Sigma2 趋势图 /// </summary> Sigma2, /// <summary> /// 只显示 平均值趋势图 /// </summary> Avg, /// <summary> /// 平均值 与 Sigma2 趋势图 /// </summary> All } /// <summary> /// X轴显示类型 /// </summary> public enum GraphRangeXAxisType { /// <summary> /// 时间 /// </summary> Time, /// <summary> /// 序号!! /// </summary> Index } public struct GraphRangeData { public DateTime Time; public int Avg; public int Sigma; public bool InCtrl;//控制中 } }