using CordonelPreadjustmentUi.Helper; using CordonelPreadjustmentUi.Processes.Itinerary; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.MeasurementRecords; using Xylem.Common.Hardware.WaterMeter.Genesis.Registers; using Xylem.Common.Ui.CordonelPreadjustmentUi.Helper.Extensions; using static CordonelPreadjustmentUi.PreAdjustmentControl; namespace CordonelPreadjustmentUi.Processes.Actions { public class OffsetTestProcess : BaseProcess { public OffsetTestProcess(string processName, StatusPanelItems panelState, string performMessage, string failedMessage, int? expectedTimeS) : base(processName, panelState, performMessage, failedMessage, expectedTimeS) { } public override List StartWork() { return doWorkProcess(base.CurrentProcessProgress); } private List doWorkProcess(ProcessProgress pp) { var MetersToProcess = MeterStateCtls.Where(e => e.IsEnabled).ToList(); var listOfProcessTask = new List(); listOfProcessTask.Add(new Task(() => { #region Wait for settling time (60s) for (Int32 Time = pp.Setting.OffsetTestSettlingTime; Time >= 0; Time--) { if (CheckAbort(true)) { return false; } pp.StatusLabel = $"{PredefinedMessages.ZeroflowOffsetWaitForSettling(pp.Setting.Culture)}{Time.ToString(pp.Setting.Culture)}"; Thread.Sleep(1000); } #endregion var listOfSubTask = new List(); foreach (var meterctl in MetersToProcess) { meterctl.Meter.CurrentRecords.Clear(); listOfSubTask.Add(new Task(() => { var Ok = true; Boolean PerformCheck = true; //Timeout calculation for offset test //(pp.Setting.OffsetTestNumberOfLines * number of path) / 1000 //((9000 * 3) / 1000 ) = 27 min var pataDataActivitCheck = new MultiPathDataLogContainer(pp.Setting.NumberOfPaths); var MaxTimeoutWarnings = 300; pataDataActivitCheck.Update(null, -1); Thread.Sleep(1800); while (PerformCheck) { var offsetRecords = new List(); try { offsetRecords = meterctl.Meter.CurrentRecords.ToList(); } catch (Exception ex) { try { offsetRecords = meterctl.Meter.CurrentRecords.ToList(); } catch (Exception) { Ok = false; PerformCheck = false; } } pp.StatusLabel = ($"{PredefinedMessages.ZeroflowOffsetAqqFinished(pp.Setting.Culture)} {offsetRecords.Count}/{(pp.Setting.OffsetTestNumberOfLines * 3)}"); // "Wait until data aquisition has finished:" Thread.Sleep(1800); for (UInt16 PathSum = 0; PathSum < pp.Setting.NumberOfPaths; PathSum++) { var currentCount = offsetRecords.Count(c => c.Channel == PathSum + 1); var oldCound = pataDataActivitCheck.Get(PathSum); if (currentCount <= oldCound + 1) { pp.DebugMessage($"Warn! Receive no new lines on path {PathSum}", meterctl.Slot, "DC", meterctl.Meter.PcbId); MaxTimeoutWarnings = MaxTimeoutWarnings - 1; } pataDataActivitCheck.Update(PathSum, currentCount); } //todo:enable timeout and canneclation token if (MaxTimeoutWarnings <= 0) { pp.DebugMessage($"Fatal timeout while collecting data. Please check LED singnal. Has {offsetRecords.Count} from {pp.Setting.OffsetTestNumberOfLines * 3}", meterctl.Slot, "DC", meterctl.Meter.PcbId); // Ok = false; } if (CheckAbort(Ok, meterctl)) { return Ok; } bool testDone = true; for (UInt16 Path = 0; Path < pp.Setting.NumberOfPaths; Path++) { var lstPathRecords = offsetRecords.Where(c => c.Channel == Path + 1).ToList(); if (lstPathRecords.Count(v => !v.IsValid) > pp.Setting.OffsetTestNumberOfLines * 0.1) { pp.DebugMessage($" To many bad data Packages {meterctl.Meter.Slot}; PATH {Path + 1}; Linecounter = {lstPathRecords.Count}", null, "DC"); Ok = false; break; } if (lstPathRecords.Count < pp.Setting.OffsetTestNumberOfLines) { testDone = false; } } if (testDone) { Ok = true; PerformCheck = false; } if (CheckAbort(Ok, meterctl)) { return Ok; } } var PathDataContainerBaddata = new MultiPathDataLogContainer>(pp.Setting.NumberOfPaths); var PathDataContainer = new MultiPathDataLogContainer>(pp.Setting.NumberOfPaths); for (UInt16 Path = 0; Path < PathDataContainer.GetPathCount(); Path++) { var offsetRecords = meterctl.Meter.CurrentRecords.ToList().Where(c => (c.Channel == (Path + 1)) && c.IsValid).ToList(); PathDataContainer.Update(Path, offsetRecords); offsetRecords = meterctl.Meter.CurrentRecords.ToList().Where(c => (c.Channel == (Path + 1)) && !c.IsValid).ToList(); PathDataContainerBaddata.Update(Path, offsetRecords); } #region Calculating mean values pp.DebugMessage($"Calculating mean values", meterctl.Meter.Slot, "APP", meterctl.Meter.PcbId); var amplitudeValuesMeanUp = new MultiPathDataLogContainer(pp.Setting.NumberOfPaths); var amplitudeValuesMeanDown = new MultiPathDataLogContainer(pp.Setting.NumberOfPaths); var DeltaTimeOfFlightAverage = new MultiPathDataLogContainer(pp.Setting.NumberOfPaths); for (UInt16 Path = 0; Path < PathDataContainer.GetPathCount(); Path++) { var meanAmplitudeUpV = PathDataContainer.Get(Path).Sum(ad => ad.AmplitudeUpV) / PathDataContainer.Get(Path).Count; var meanAmplitudeDownV = PathDataContainer.Get(Path).Sum(ad => ad.AmplitudeDownV) / PathDataContainer.Get(Path).Count; var avgDeltaTimeOfFlight = PathDataContainer.Get(Path).Sum(ad => ad.DeltaTimeOfFlightS) / PathDataContainer.Get(Path).Count; if (meanAmplitudeUpV == 0 || meanAmplitudeDownV == 0 || avgDeltaTimeOfFlight == 0) { pp.DebugMessage($"Mean value is zero at {meterctl.Meter.Slot}; PATH{Path + 1}", meterctl.Meter.Slot, "DC", meterctl.Meter.PcbId); if (pp.Setting.NumberOfPaths != 1) { Ok = false; } } if (CheckAbort(Ok, meterctl)) { return Ok; } amplitudeValuesMeanUp.Update(Path, meanAmplitudeUpV); amplitudeValuesMeanDown.Update(Path, meanAmplitudeDownV); DeltaTimeOfFlightAverage.Update(Path, avgDeltaTimeOfFlight); } #endregion var BadDataCheckOK = true; var AmplitudeCheckOK = true; var OffsetCheckOK = true; for (UInt16 Path = 0; Path < PathDataContainer.GetPathCount(); Path++) { pp.DebugMessage($"Bad data at PATH{Path + 1}:{PathDataContainerBaddata.Get(Path).Count()}", meterctl.Meter.Slot, "DC", meterctl.Meter.PcbId); if (PathDataContainerBaddata.Get(Path).Count() > pp.Setting.OffsetTestBadDataLimit) { BadDataCheckOK = false; pp.DebugMessage($"Bad datacheck failed!", meterctl.Meter.Slot, "DC", meterctl.Meter.PcbId); } pp.DebugMessage($"Average positive amplitude, PATH{(Path + 1)}: {Math.Round(amplitudeValuesMeanUp.Get(Path), 2).ToString("000.00")}mV", meterctl.Meter.Slot, "DC", meterctl.Meter.PcbId); pp.DebugMessage($"Average negative amplitude, PATH{(Path + 1)}: {Math.Round(amplitudeValuesMeanDown.Get(Path), 2).ToString("000.00")}mV", meterctl.Meter.Slot, "DC", meterctl.Meter.PcbId); meterctl.Meter.calibrationResult.AddAdditionalLog($"Offset Average positive amplitude PATH {(Path + 1)}", $"{Math.Round(amplitudeValuesMeanUp.Get(Path), 2).ToString("000.00")}mV"); meterctl.Meter.calibrationResult.AddAdditionalLog($"Offset Average negative amplitude PATH {(Path + 1)}", $"{Math.Round(amplitudeValuesMeanDown.Get(Path), 2).ToString("000.00")}mV"); if (pp.Setting.MeterSize == Xylem.Common.Logic.ProductionOrderCore.OrderData.MeterSize.DN200 || pp.Setting.MeterSize == Xylem.Common.Logic.ProductionOrderCore.OrderData.MeterSize.US8) { pp.Setting.OffsetTestLowerVoltageLimitMV = 180; } if ((amplitudeValuesMeanUp.Get(Path) > pp.Setting.OffsetTestUpperVoltageLimitMV) || (amplitudeValuesMeanUp.Get(Path) < pp.Setting.OffsetTestLowerVoltageLimitMV) || (amplitudeValuesMeanDown.Get(Path) < pp.Setting.OffsetTestLowerVoltageLimitMV) || (amplitudeValuesMeanDown.Get(Path) > pp.Setting.OffsetTestUpperVoltageLimitMV)) { AmplitudeCheckOK = false; pp.DebugMessage($"Amplitude check failed!", meterctl.Meter.Slot, "APP", meterctl.Meter.PcbId); } pp.DebugMessage($"Average delta time of flight valuePATH{(Path + 1)}: Raw{DeltaTimeOfFlightAverage.Get(Path)} -> in picoseconds= {Math.Round((DeltaTimeOfFlightAverage.Get(Path) / (Math.Pow(2, 38) * Math.Pow(10, 12))), 2).ToString("000.00")}", meterctl.Meter.Slot, "APP", meterctl.Meter.PcbId); meterctl.Meter.calibrationResult.AddAdditionalLog($"Offset Average delta time of flight value PATH {(Path + 1)} RAW ", $"{DeltaTimeOfFlightAverage.Get(Path)}"); meterctl.Meter.calibrationResult.AddAdditionalLog($"Offset Average delta time of flight value PATH {(Path + 1)} picoseconds ", $"{Math.Round((DeltaTimeOfFlightAverage.Get(Path) / (Math.Pow(2, 38) * Math.Pow(10, 12))), 2).ToString("000.00")}"); if ((DeltaTimeOfFlightAverage.Get(Path) > pp.Setting.OffsetTestOffsetLimitPS) || (DeltaTimeOfFlightAverage.Get(Path) < -pp.Setting.OffsetTestOffsetLimitPS)) { pp.DebugMessage($"Delta time of flight check failed", meterctl.Meter.Slot, "APP", meterctl.Meter.PcbId); OffsetCheckOK = false; } } if (!BadDataCheckOK || !AmplitudeCheckOK || !OffsetCheckOK) { pp.DebugMessage($"Skip offset test at least one test is failed", meterctl.Meter.Slot, "APP", meterctl.Meter.PcbId); if (pp.Setting.NumberOfPaths != 1) { if (CheckAbort(false, meterctl)) { return false; } } } Ok = meterctl.Meter.ReLogin(); if (CheckAbort(Ok, meterctl)) { return Ok; } for (UInt16 Path = 0; Path < DeltaTimeOfFlightAverage.GetPathCount(); Path++) { var OffsetValue = (Int32)((DeltaTimeOfFlightAverage.Get(Path) * Math.Pow(2, 40))); pp.StatusLabel = ($"{PredefinedMessages.WaitUntilRegisterWriteFinished(pp.Setting.Culture)}"); String RegValue = String.Empty; switch (Path) { case 0: RegValue = Register.Genesisflow.ZeroOffset1; break; case 1: RegValue = Register.Genesisflow.ZeroOffset2; break; case 2: RegValue = Register.Genesisflow.ZeroOffset3; break; default: System.Windows.Forms.MessageBox.Show($"Path number incorrect {Path}"); break; }; if (pp.Setting.NumberOfPaths != 1) { Ok = WriteRegisterSafe(meterctl.Meter, $"Zero flow offset Path {Path + 1}", RegValue, OffsetValue); if (CheckAbort(Ok, meterctl)) { return Ok; } } else { Ok = WriteRegisterSafe(meterctl.Meter, $"Zero flow offset Path {Path + 1}", RegValue, OffsetValue); Ok = true; } } return Ok; })); } listOfSubTask.ForEach(t => t.Start()); while (listOfSubTask.Any(t => !t.IsCompleted)) { Thread.Sleep(1000); } return true; })); return listOfProcessTask; } } }