/// /// Copyright (c) 2019-2023 Sensus Metering Systems /// using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Windows.Forms.DataVisualization.Charting; using Common; using Results.Entities; using Results.Resources; namespace Results.Output { public class WMChart { public static Chart GetChart(WaterMeter wm, bool isPrinter) { return GetChart(wm, isPrinter, Common.Unit.lph); } public static Chart GetChart(WaterMeter wm, bool isPrinter, Common.Unit flowUnit) { double maxFlow = 0; IList unsortedPoints = new List(); foreach (var mtr in wm.RegularMeterTestRslts()) { double flow = mtr.TestRslt.Flow; var td = mtr.TestRslt.TestData; if (mtr.IsPilotRslt() && flow > 0 && td.Evaluate && (td.Publish == (sbyte)Publish.Always || (!isPrinter && td.Publish == (sbyte)Publish.OnScreen))) { if (flow > maxFlow) maxFlow = flow; unsortedPoints.Add(new PointF((float)Common.Units.ConvertTo(flowUnit, flow), (float)mtr.Error)); } } IEnumerable sortedPoints = unsortedPoints.OrderBy(x => x.X); bool isQ4 = (maxFlow > 1.1 * wm.WaterMeterData.Q3_Qn); Chart chart = new Chart { Name = "chart", Text = "chart", Dock = System.Windows.Forms.DockStyle.Fill, TabIndex = 0, Size = new System.Drawing.Size(5, 5), Location = new System.Drawing.Point(3, 3), }; ChartArea chArea = new ChartArea { Name = "ChartArea1" }; chArea.AxisX.Title = Strings.Flow; chArea.AxisX.IsLogarithmic = true; chArea.AxisX.LogarithmBase = 10; chArea.AxisX.IsLabelAutoFit = true; Series upperLimit = new Series { Name = Strings.Upper_limit, ChartArea = "ChartArea1", ChartType = SeriesChartType.Line, Color = Color.Red }; Series lowerLimit = new Series { Name = Strings.Lower_limit, ChartArea = "ChartArea1", ChartType = SeriesChartType.Line, Color = Color.Red }; List xs = new List() { 0.0001, 0.0002, 0.0005, 0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000 }; double spacer = 0.8; double from = Common.Units.ConvertTo(flowUnit, wm.WaterMeterData.Q1_Qmin); double to = Common.Units.ConvertTo(flowUnit, isQ4 ? wm.WaterMeterData.Q4_Qmax : wm.WaterMeterData.Q3_Qn); bool inRange = false; double qSharp = (wm.WaterMeterData.Qnames == (sbyte)FlowNames.QpQi) ? GetQsharp(wm, flowUnit) : 0; double lastXs = 0; for (int i = 0; i < xs.Count - 3; i += 3) { if (xs[i] <= from && from < xs[i + 3]) { chArea.AxisX.Minimum = xs[i]; inRange = true; } if (inRange) { for (int j = i; j < i + 3; j++) { CustomLabel cl = new CustomLabel(); cl.Text = string.Format("{0} {1}", xs[j], flowUnit.ToDescription()); cl.FromPosition = Math.Log10(xs[j] * spacer); cl.ToPosition = Math.Log10(xs[j] / spacer); chArea.AxisX.CustomLabels.Add(cl); if (wm.WaterMeterData.Qnames == (sbyte)FlowNames.QpQi) { if (lastXs != 0) { double insertedXs = Math.Sqrt(lastXs * xs[j]); if (lastXs < qSharp && qSharp < insertedXs) { double errLim3 = GetErrorLimit(wm, flowUnit, qSharp); lowerLimit.Points.AddXY(qSharp, -errLim3); upperLimit.Points.AddXY(qSharp, +errLim3); } double errLim2 = GetErrorLimit(wm, flowUnit, insertedXs); lowerLimit.Points.AddXY(insertedXs, -errLim2); upperLimit.Points.AddXY(insertedXs, +errLim2); if (insertedXs < qSharp && qSharp < xs[j]) { double errLim3 = GetErrorLimit(wm, flowUnit, qSharp); lowerLimit.Points.AddXY(qSharp, -errLim3); upperLimit.Points.AddXY(qSharp, +errLim3); } } lastXs = xs[j]; double errLim = GetErrorLimit(wm, flowUnit, xs[j]); lowerLimit.Points.AddXY(xs[j], -errLim); upperLimit.Points.AddXY(xs[j], +errLim); } } } if (xs[i] < to && to <= xs[i + 3]) { chArea.AxisX.Maximum = xs[i + 3]; CustomLabel cl = new CustomLabel(); cl.Text = string.Format("{0} {1}", xs[i + 3], flowUnit.ToDescription()); cl.FromPosition = Math.Log10(xs[i + 3] * spacer); cl.ToPosition = Math.Log10(xs[i + 3] / spacer); chArea.AxisX.CustomLabels.Add(cl); if (wm.WaterMeterData.Qnames == (sbyte)FlowNames.QpQi) { if (lastXs != 0) { double insertedXs = Math.Sqrt(lastXs * xs[i + 3]); if (lastXs < qSharp && qSharp < insertedXs) { double errLim3 = GetErrorLimit(wm, flowUnit, qSharp); lowerLimit.Points.AddXY(qSharp, -errLim3); upperLimit.Points.AddXY(qSharp, +errLim3); } double errLim2 = GetErrorLimit(wm, flowUnit, insertedXs); lowerLimit.Points.AddXY(insertedXs, -errLim2); upperLimit.Points.AddXY(insertedXs, +errLim2); if (insertedXs < qSharp && qSharp < xs[i + 3]) { double errLim3 = GetErrorLimit(wm, flowUnit, qSharp); lowerLimit.Points.AddXY(qSharp, -errLim3); upperLimit.Points.AddXY(qSharp, +errLim3); } } double errLim = GetErrorLimit(wm, flowUnit, xs[i + 3]); lowerLimit.Points.AddXY(xs[i + 3], -errLim); upperLimit.Points.AddXY(xs[i + 3], +errLim); } break; } } chArea.AxisY.Title = Strings.Relative_error; chArea.AxisY.Interval = 3; chArea.AxisY.LabelStyle.Format = "{#} %"; Legend legend1 = new Legend { Name = "Legend1" }; if (wm.WaterMeterData.Qnames == (sbyte)FlowNames.Q3Q2Q1 || wm.WaterMeterData.Qnames == (sbyte)FlowNames.QnQtQmin) { upperLimit.Points.AddXY(Units.ConvertTo(flowUnit, wm.WaterMeterData.Q1_Qmin), wm.WaterMeterData.ErrLimLoQ); upperLimit.Points.AddXY(Units.ConvertTo(flowUnit, wm.WaterMeterData.Q2_Qt), wm.WaterMeterData.ErrLimLoQ); upperLimit.Points.AddXY(Units.ConvertTo(flowUnit, wm.WaterMeterData.Q2_Qt), wm.WaterMeterData.ErrLimHiQ); upperLimit.Points.AddXY(Units.ConvertTo(flowUnit, wm.WaterMeterData.Q3_Qn), wm.WaterMeterData.ErrLimHiQ); if (isQ4) upperLimit.Points.AddXY(Units.ConvertTo(flowUnit, wm.WaterMeterData.Q4_Qmax), wm.WaterMeterData.ErrLimHiQ); lowerLimit.Points.AddXY(Units.ConvertTo(flowUnit, wm.WaterMeterData.Q1_Qmin), -wm.WaterMeterData.ErrLimLoQ); lowerLimit.Points.AddXY(Units.ConvertTo(flowUnit, wm.WaterMeterData.Q2_Qt), -wm.WaterMeterData.ErrLimLoQ); lowerLimit.Points.AddXY(Units.ConvertTo(flowUnit, wm.WaterMeterData.Q2_Qt), -wm.WaterMeterData.ErrLimHiQ); lowerLimit.Points.AddXY(Units.ConvertTo(flowUnit, wm.WaterMeterData.Q3_Qn), -wm.WaterMeterData.ErrLimHiQ); if (isQ4) lowerLimit.Points.AddXY(Units.ConvertTo(flowUnit, wm.WaterMeterData.Q4_Qmax), -wm.WaterMeterData.ErrLimHiQ); } Series line = new Series { Name = Strings.Relative_error, ChartArea = "ChartArea1", ChartType = SeriesChartType.Line, Color = Color.DeepSkyBlue }; Series dots = new Series { Name = Strings.Relative_error + " ", ChartArea = "ChartArea1", ChartType = SeriesChartType.Point, Color = Color.RoyalBlue }; dots.MarkerStyle = MarkerStyle.Circle; if (isPrinter) dots.MarkerSize = 1; foreach (var point in sortedPoints) { line.Points.AddXY(point.X, point.Y); dots.Points.AddXY(point.X, point.Y); } chart.ChartAreas.Add(chArea); chart.Legends.Add(legend1); if (upperLimit.Points.Count > 1) chart.Series.Add(upperLimit); if (lowerLimit.Points.Count > 1) chart.Series.Add(lowerLimit); chart.Series.Add(dots); chart.Series.Add(line); return chart; } static double GetErrorLimit(WaterMeter wm, Unit flowUnit, double x) { double flow_m3 = Units.ConvertFrom(flowUnit, x); switch (Convert.ToInt32(wm.WaterMeterData.Q2_Qt)) { default: case 1: return Math.Min(3.5, 1.0 + 0.01 * wm.WaterMeterData.Q3_Qn / flow_m3); case 2: return Math.Min(5.0, 2.0 + 0.02 * wm.WaterMeterData.Q3_Qn / flow_m3); case 3: return Math.Min(5.0, 3.0 + 0.05 * wm.WaterMeterData.Q3_Qn / flow_m3); } } static double GetQsharp(WaterMeter wm, Unit flowUnit) { double qSharp; switch (Convert.ToInt32(wm.WaterMeterData.Q2_Qt)) { default: case 1: qSharp = (0.01 / 2.5) * wm.WaterMeterData.Q3_Qn; break; case 2: qSharp = (0.02 / 3.0) * wm.WaterMeterData.Q3_Qn; break; case 3: qSharp = (0.05 / 2.0) * wm.WaterMeterData.Q3_Qn; break; } return Units.ConvertTo(flowUnit, qSharp); } } }