using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using Tango.DispenserAnalyzer.UI.Analysis; using Tango.DispenserAnalyzer.UI.Models; using MathNet.Numerics.LinearAlgebra; using System.Linq.Expressions; using System.Diagnostics; using OxyPlot; using System.Collections.ObjectModel; using Tango.Documents; using System.IO; using Tango.Core.Helpers; namespace Tango.DispenserAnalyzer.UI.Analyzers { [Analyzer("flow")] public class FlowAnalyser : IDispenserDispenserAnalyser { private IReader _reader; public IReader Reader { get { return _reader; } set { _reader = value; } } public FlowAnalyser() { Reader = new DispenserReader(); } public Task> Process(List csvRows, bool backgroundMode) { return Task.Factory.StartNew>(() => { List results = new List(); List commands = csvRows.Where(x => x.Command != null && x.Command.ToLower().Contains("label")).ToList(); var pairs = commands.Select((x, i) => new { Index = i, Value = x }).GroupBy(x => x.Index/2 ).Select(x => x.Select(v => v.Value).ToList()).ToList(); MovingAverageFilter filter = new MovingAverageFilter(); int flowtestNumber = 0; for (int index = 0; index < pairs.Count(); index++) { var pair = pairs[index]; if (pair.Count != 2) continue; List rangeTestValues = csvRows.Where(x => x.Index > pair[0].Index && x.Index < pair[1].Index).ToList(); if (index % 2 == 1)//testing Flow-error { bool isCancelAverageProc = (bool)Settings.GetValueByName(AnalyzerSettingsEnum.CancelMovingAVG); List filteredValues = rangeTestValues.Skip((int)((double)Settings.GetValueByName(AnalyzerSettingsEnum.ExcludeAnalysis))).ToList(); if (false == isCancelAverageProc) { //Move Average data List tasks = new List(); int calc_count = (int)filteredValues.Count() / 4; int start_index = 0; while (start_index < filteredValues.Count()) { int calc_amount = (start_index + calc_count) >= (filteredValues.Count() - 4) ? filteredValues.Count() - start_index : calc_count; var source_filter = filteredValues.Skip(start_index).Take(calc_amount).ToList(); tasks.Add(Task.Run(() => { filter.Filtering(source_filter); })); start_index += calc_amount; } Task.WaitAll(tasks.ToArray()); } //calculate difference Max Min values for each 300 values int periodCalcMaxMin = (int)((double)Settings.GetValueByName(AnalyzerSettingsEnum.MaxMinRange)); int intervalCalcMaxMin = (int)((double)Settings.GetValueByName(AnalyzerSettingsEnum.MaxMinIntervals)); List differenceMaxMin = new List(); List differenceMaxMinToLocationArr = new List(); int location_index = 0; for (int i = 0; i < (filteredValues.Count - periodCalcMaxMin); i+= intervalCalcMaxMin) { var rangeItems =(filteredValues.Skip(i).Take(periodCalcMaxMin).ToList()); int range = (int)(rangeItems.Max(t => t.Pressure) - rangeItems.Min(t => t.Pressure)); differenceMaxMin.Add(range); differenceMaxMinToLocationArr.Add(++location_index); } FlowAverageAnalyzerResult averageResult = new FlowAverageAnalyzerResult(); averageResult.BackgroundMode = backgroundMode; averageResult.AverageValue = filteredValues.Average(t => t.Pressure); averageResult.Result = (averageResult.AverageValue <= (double)Settings.GetValueByName(AnalyzerSettingsEnum.AvgMaxValue) && averageResult.AverageValue >= (double)Settings.GetValueByName(AnalyzerSettingsEnum.AvgMinValue)) ? AnalyzerResultValue.Passed : AnalyzerResultValue.Failed; results.Add(averageResult); FlowAnalyzerResult result = new FlowAnalyzerResult(++flowtestNumber); result.BackgroundMode = backgroundMode; result.AverageValue = averageResult.AverageValue; result.SetLocalErrors(differenceMaxMin, differenceMaxMinToLocationArr); result.CalculteTrendOfChart(differenceMaxMin); results.Add(result); } else//testing PBU { PrimingAnalyzerResult result = new PrimingAnalyzerResult(); result.BackgroundMode = backgroundMode; int avgMinIndex = rangeTestValues.Select(x => x.Index).Min(); int avgMaxIndex = rangeTestValues.Select(x => x.Index).Max(); double totalsec = TimeSpan.FromMilliseconds((avgMaxIndex - avgMinIndex) * 100).TotalSeconds; result.Time = totalsec.ToString() + $" sec (succeed for period < {AnalyzerSettingsEnum.FlowPBUPassFail})"; result.OnlyTime = totalsec.ToString(); result.Result = (totalsec < (double)Settings.GetValueByName(AnalyzerSettingsEnum.FlowPBUPassFail)) ? AnalyzerResultValue.Passed : AnalyzerResultValue.Failed; results.Add(result); } } return results; }); } public void GetPoints(List samples, IList points) { samples.ForEach(x => { if (x.Pressure != 0.0) { points.Add(new OxyPlot.DataPoint(x.Index, x.Pressure)); } }); } public bool ShowChartAfterProcess { get { return true; } } public bool AvailableCompareResults { get { return false; } } public class FlowAverageAnalyzerResult : AnalyzerResultBase { [Description("Average Value")] public double AverageValue { get; set; } public FlowAverageAnalyzerResult() : base() { AverageValue = 0.0; Result = AnalyzerResultValue.Undetermined; } } public class FlowAnalyzerResult : AnalyzerResultBase { #region Properties [Description("Max Error")] public string LocalErrors { get; set; } public double AverageValue { get; set; } public int TestNumber { get; set; } public double PersentageOfError { get; set; } public int MaxLocalError { get; set; } public int Occurrence { get; set; } public string Trend { get; set; } #endregion Properties public FlowAnalyzerResult(int testNumber) :base() { AverageValue = 0.0; Result = AnalyzerResultValue.Undetermined; TestNumber = testNumber; } /// /// Calculate result, max error. Set oxy plot column chart. /// /// The difference maximum minimum. public void SetLocalErrors(List differenceMaxMin, List differenceMaxMinToLocationArr) { List< int > deviationsRange = new List(); for (int n = 0; n < differenceMaxMinToLocationArr.Count && n < differenceMaxMin.Count; n++) { if(differenceMaxMinToLocationArr.ElementAt(n) >= (double)Settings.GetValueByName(AnalyzerSettingsEnum.DeviationAtRevolutionsMin) && differenceMaxMinToLocationArr.ElementAt(n) <= (double)Settings.GetValueByName(AnalyzerSettingsEnum.DeviationAtRevolutionsMax)) deviationsRange.Add( differenceMaxMin.ElementAt(n)); } // int count = differenceMaxMin.Where(x => x > 25 ).Count(); int max_key = FindMaxErrorObject(differenceMaxMin, deviationsRange); if (!BackgroundMode) { var points = RangeToCountChart.Points; points.Clear(); for (int i = 0; i <= max_key; i++) { int val = differenceMaxMin.Count(x => x == i); if(val > 0 || points.Count > 0) { points.Add(new DataPoint(i, val)); } } this.IsShowPlotResult = true; RangeToCountChart.Title = $"Local error histogram {TestNumber}"; RangeToCountChart.UpdateData(); } var rangeToTimePoints = RangeToTimeChart.Points; rangeToTimePoints.Clear(); for(int y = 0; y < differenceMaxMinToLocationArr.Count && y < differenceMaxMin.Count; y++) { rangeToTimePoints.Add(new DataPoint(differenceMaxMinToLocationArr.ElementAt(y), differenceMaxMin.ElementAt(y))); } if (!BackgroundMode) { RangeToTimeChart.Title = $"Local error vs position {TestNumber}"; RangeToTimeChart.UpdateData(); } string filename = FileHelper.GetFileToSaveFlowRangeToTimeData(TestNumber); if(filename.IsNotNullOrEmpty() && rangeToTimePoints.Count > 0) { ExportnDataToExcel(rangeToTimePoints.ToList(), filename); } } public void CalculteTrendOfChart(List points) { int interval = (int)points.Count/6; List average_results = new List(); for (int i = 0; i < points.Count; i += interval) { var b = points.Skip(i).Take(interval).Average(); average_results.Add((int)(points.Skip(i).Take(interval).Average())); } if(average_results.Count <=1) { Trend = "Unsteady"; return; } // strictly increasing if (average_results[0] < average_results[average_results.Count-1]) { int i = 1; // Check for strictly // increasing condition // & find the break point while (i < average_results.Count && average_results[i - 1] <= average_results[i]) { i++; } if(i >= average_results.Count-1 ) { Trend = "Increase"; return; } } else { int i = 1; while (i < average_results.Count && average_results[i - 1] >= average_results[i]) { i++; } if (i >= average_results.Count-1 ) { Trend = "Decrease"; return; } } Trend = "Wave"; } private double BuildMeasurementError(List range_values) { int count = range_values.Count(); return (count - (int)((double)Settings.GetValueByName(AnalyzerSettingsEnum.TakeOffMaxMin))); } /// /// Finds the maximum error object. Init LocalErrors message. Return max range value. /// private int FindMaxErrorObject(List range_values, List deviationsRange) { var countValArr = range_values.GroupBy(x => x).Select(t => new { Key = t.Key, Value = t.Count() }).OrderBy(x=>x.Key).ToArray(); var deviationsValArr = deviationsRange.GroupBy(x => x).Select(t => new { Key = t.Key, Value = t.Count() }).OrderBy(x => x.Key).ToArray(); double merror = (double)Settings.GetValueByName(AnalyzerSettingsEnum.TakeOffMaxMin);// BuildMeasurementError(range_values); double dividerMaxError = (double)Settings.GetValueByName(AnalyzerSettingsEnum.DividerMaxError);// BuildMeasurementError(range_values); double sum = 0; int max_key = 0; for (int i = countValArr.Count() - 1; i >= 0; i--) { sum += countValArr[i].Value; if (max_key == 0) max_key = (int)countValArr[i].Key; if (sum > merror) { PersentageOfError = countValArr[i].Key / dividerMaxError * 100; MaxLocalError = countValArr[i].Key; Occurrence = countValArr[i].Value; LocalErrors = $" {PersentageOfError.ToString("F2")}% where max local error = {MaxLocalError.ToString()} and occurrence = {Occurrence.ToString()}"; break; } } var res = MaxLocalError / dividerMaxError * 100; if(res <= (double)Settings.GetValueByName(AnalyzerSettingsEnum.MaxError)) Result = AnalyzerResultValue.Passed; else if(MaxLocalError <= 26) { Result = AnalyzerResultValue.Failed; var entryindeviations = deviationsValArr.FirstOrDefault(x => x.Key == MaxLocalError); if (entryindeviations != null) { int val = entryindeviations.Value; if((Occurrence - val) == 0) Result = AnalyzerResultValue.Passed; } } else Result = AnalyzerResultValue.Failed; return max_key; } /// /// Exports the calibration data to excel. /// /// The calibration points. /// Name of the file. public static void ExportnDataToExcel(List dataPoints, String fileName) { try { CreateDataExcelTemplate(fileName); using (ExcelWriter writer = new ExcelWriter(fileName)) { writer.UpdateTableSize("RangeToTimeData", "A1:B" + (dataPoints.Count + 2).ToString()); writer.WriteData(dataPoints, "RangeToTimeData"); } } catch (Exception ex) { Debug.WriteLine("Error: ", ex.Message); } } /// /// Creates the calibration data excel template. /// /// Name of the file. public static void CreateDataExcelTemplate(String fileName) { var stream = EmbeddedResourceHelper.GetEmbeddedResourceStream("Tango.DispenserAnalyzer.UI.Models.FlowRangeToTimeResults.xlsx"); using (FileStream fs = new FileStream(fileName, FileMode.Create)) { stream.Seek(0, SeekOrigin.Begin); stream.CopyTo(fs); } } } public class MovingAverageFilter { public MovingAverageFilter() { } public void Filtering(List source) { int periodAverage = 5; for (int i = 0; i < (source.Count - 5); i++) { source[i].Pressure = source.Skip(i).Take(periodAverage).Average(x => x.Pressure); } } } } }