using MMDAL.DTO.Inspection; using System; using System.Collections.Generic; using System.Linq; namespace MMBLL.Inspection { public class InspectionParameterComputationBLL : IInspectionParameterComputationBLL { // Sets FINALVALUE = average of InputValue samples per PARAMETERID+PARAMETERSLNO group. public void EnrichWithFinalValues(IList parameters) { if (parameters == null || parameters.Count == 0) return; var groups = parameters .GroupBy(p => new { p.ParameterId, p.InspectionParameterParameterSlNo }); foreach (var group in groups) { var vals = group .Select(p => double.TryParse(p.InspectionParameterInputValue, out var d) ? d : (double?)null) .Where(v => v.HasValue) .Select(v => v.Value) .ToList(); double avg = vals.Count > 0 ? vals.Average() : 0; double? minVal = group.First().MinVal; double? maxVal = group.First().MaxVal; byte result = EvaluateParameter(group.ToList(), minVal, maxVal); foreach (var p in group) { p.InspectionParameterAverage = avg; p.InspectionParameterFinalValue = avg; p.InspectionParameterResult = result; // 0=Pending, 1=Pass, 2=Fail } } } // Returns 1=Pass, 2=Fail. No limits → Pass (0=Pending when no value). public byte EvaluateSample(double value, double? min, double? max) { if (min.HasValue && value < min.Value) return 2; if (max.HasValue && value > max.Value) return 2; return 1; } // Returns 2=Fail if any sample fails, 1=Pass if all pass, 0=Pending if no samples. public byte EvaluateParameter(IList samples, double? min, double? max) { if (samples == null || samples.Count == 0) return 0; foreach (var s in samples) { if (!double.TryParse(s.InspectionParameterInputValue, out var val)) continue; if (EvaluateSample(val, min, max) == 2) return 2; } return 1; } // Cpk = Min((USL−avg)/(3σ), (avg−LSL)/(3σ)). Returns null if no limits, σ=0, or <3 samples. public double? ComputeCpk(IList values, double? min, double? max) { if (!min.HasValue && !max.HasValue) return null; if (values == null || values.Count < 3) return null; double avg = values.Average(); double variance = values.Sum(v => Math.Pow(v - avg, 2)) / values.Count; double sigma = Math.Sqrt(variance); if (sigma == 0) return null; double? cpkUpper = max.HasValue ? (max.Value - avg) / (3 * sigma) : (double?)null; double? cpkLower = min.HasValue ? (avg - min.Value) / (3 * sigma) : (double?)null; if (cpkUpper.HasValue && cpkLower.HasValue) return Math.Min(cpkUpper.Value, cpkLower.Value); return cpkUpper ?? cpkLower; } // Builds per-parameter summary with stats and Cpk. public IList BuildSummary(IList parameters) { if (parameters == null || parameters.Count == 0) return new List(); var result = new List(); var groups = parameters .GroupBy(p => new { p.ParameterId, p.InspectionParameterParameterSlNo }); foreach (var group in groups) { var first = group.First(); var vals = group .Select(p => double.TryParse(p.InspectionParameterInputValue, out var d) ? d : (double?)null) .Where(v => v.HasValue) .Select(v => v.Value) .ToList(); double? minVal = first.MinVal; double? maxVal = first.MaxVal; int passCount = 0, failCount = 0; foreach (var v in vals) { if (EvaluateSample(v, minVal, maxVal) == 1) passCount++; else failCount++; } double? avg = vals.Count > 0 ? vals.Average() : (double?)null; double? min = vals.Count > 0 ? vals.Min() : (double?)null; double? max = vals.Count > 0 ? vals.Max() : (double?)null; double? stdDev = null; if (vals.Count > 1 && avg.HasValue) { double variance = vals.Sum(v => Math.Pow(v - avg.Value, 2)) / vals.Count; stdDev = Math.Sqrt(variance); } result.Add(new ParameterSummaryDTO { ParameterId = first.ParameterId, ParameterName = first.ParameterName, UomName = first.UomName, MinVal = minVal, MaxVal = maxVal, SampleCount = vals.Count, Average = avg, Minimum = min, Maximum = max, StdDev = stdDev, Cpk = ComputeCpk(vals, minVal, maxVal), PassCount = passCount, FailCount = failCount, Samples = vals.Cast().ToList() }); } return result; } } }