using CordonelPreadjustmentUi.Const; using CordonelPreadjustmentUi.Helper; using CordonelPreadjustmentUi.Helper.Extensions; 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.Registers; using Xylem.Common.Ui.CordonelPreadjustmentUi; using static CordonelPreadjustmentUi.PreAdjustmentControl; namespace CordonelPreadjustmentUi.Processes.Actions { public class SPAmplitudeTestProcess : BaseProcess { public SPAmplitudeTestProcess(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(); foreach (var meterctl in MetersToProcess) { listOfProcessTask.Add(new Task(() => { pp.DebugMessage($"Performing {ProcessName} procedure", null, "CC"); #region Definitions and initializations const Int32 FirstHitShiftValue = 4; const Int32 FirstHitShiftValueEnd = 4; const Int32 FirstHitUpdatePeriodeValue = 1; const Int32 FirstHitUpdatePeriodeValueEnd = 10; const Int32 WaitBeforeLogging = 5; String DebugMessage = String.Empty; var TofAvgMean = new MultiPathDataLogContainer(pp.Setting.NumberOfPaths); var OptimalPoint = new MultiPathDataLogContainer(pp.Setting.NumberOfPaths); var TofAvgMeanTotal = new MultiPathDataLogContainer>(pp.Setting.NumberOfPaths); TofAvgMeanTotal.Update(null, new List()); Boolean Ok = false; #endregion #region StartLogging if (pp.Setting.AmpTestGenerateLogfiles) { String time = System.DateTime.Now.ToString(Formats.LogFileDateTimeString); String Header = $"Genesis ZeroFlowOffsetCalibration {meterctl.Meter.Slot}; PCB ID = {meterctl.Meter.PcbId}; Date = {time}; Operator = {Environment.UserName}\n"; meterctl.Meter.DataLog(Header); //GenesisZeroFlow._GenesisZeroFlow.SetLoggingPathAmplitudeTest(this.MeterNumber, $"{DataLogging.DefaultPath}AmplitudeTest_RawData_{TempPcbID}_{time}.txt"); meterctl.Meter.logAmpTest = true; pp.DebugMessage($" Start logging", meterctl.Meter.Slot, "DC", meterctl.Meter.PcbId); } #endregion Ok = false; Ok = WriteRegisterSafe(meterctl.Meter, "First hit update period", Register.Genesisflow.FirstHitUpdatePeriod, FirstHitUpdatePeriodeValue); if (CheckAbort(Ok, meterctl)) { return Ok; } pp.StatusLabel = (PredefinedMessages.WaitUntilRegisterWriteFinished(pp.Setting.Culture)); Ok = false; Ok = WriteRegisterSafe(meterctl.Meter, "WorkAround TriggerIdle", Register.Genesisflow.TriggerIdle, 1, false); Thread.Sleep(150); pp.StatusLabel = (PredefinedMessages.WaitUntilRegisterWriteFinished(pp.Setting.Culture)); Ok = false; Ok = WriteRegisterSafe(meterctl.Meter, "first hit shift", Register.Genesisflow.FirstHitShift, FirstHitShiftValueEnd); Thread.Sleep(150); pp.StatusLabel = (PredefinedMessages.WaitUntilRegisterWriteFinished(pp.Setting.Culture)); Ok = false; Ok = WriteRegisterSafe(meterctl.Meter, "WorkAround TriggerActive", Register.Genesisflow.TriggerActive, 1, false); Thread.Sleep(150); #region Set first hit level percentage from 5 to 35 pp.DebugMessage($"Start first hit level percentage sweep ", meterctl.Meter.Slot, "DC", meterctl.Meter.PcbId); for (Int32 FirstHitLevelPercentage = pp.Setting.AmpTestPercentageStart; FirstHitLevelPercentage <= pp.Setting.AmpTestPercentageStop; FirstHitLevelPercentage++) { if (CheckAbort(Ok, meterctl)) { return Ok; } pp.StatusLabel = $"{PredefinedMessages.WaitUntilPercentageSweepFinished(pp.Setting.Culture)} {FirstHitLevelPercentage.ToString(pp.Setting.Culture)}/{pp.Setting.AmpTestPercentageStop.ToString(pp.Setting.Culture)}"; #region Write to percentage register PATH0 Ok = false; Ok = WriteRegisterSafe(meterctl.Meter, "Percentage", Register.Genesisflow.FirstHitPercent3, FirstHitLevelPercentage); if (CheckAbort(Ok, meterctl)) { return Ok; } #endregion #region Wait for 1 second for (Int32 i = 0; i < WaitBeforeLogging; i++) { Thread.Sleep(200); } #endregion #region Record TOF Ok = RecordData(pp, meterctl, Ok, FirstHitLevelPercentage); bool retryRecord = false; if (true) { if (!meterctl.Meter.listOfTotalTimeOfFlightSPerPath.Get(2).Any()) { pp.StatusLabel = $"No Data for Path {2}. Start one recollect"; retryRecord = true; } } else { //for (UInt16 Path = 0; Path < meterctl.Meter.listOfTotalTimeOfFlightSPerPath.GetPathCount(); Path++) //{ // if (!meterctl.Meter.listOfTotalTimeOfFlightSPerPath.Get(Path).Any()) // { // pp.StatusLabel = $"No Data for Path {Path}. Start one recollect"; // retryRecord = true; // } //} } if (retryRecord) { Ok = RecordData(pp, meterctl, Ok, FirstHitLevelPercentage); } pp.StatusLabel = $"{PredefinedMessages.WaitUntilPercentageSweepFinished(pp.Setting.Culture)} {FirstHitLevelPercentage.ToString(pp.Setting.Culture)}/{ pp.Setting.AmpTestPercentageStop.ToString(pp.Setting.Culture)}; 0s"; Ok = true; #endregion #region Calculate mean value UInt16 Path = 2; var meanVal = meterctl.Meter.listOfTotalTimeOfFlightSPerPath.Get(Path).ToList().Mean(); pp.DebugMessage($"Calculate mean value {meanVal} at {meterctl.Meter.Slot}; PATH{(Path + 1).ToString(pp.Setting.Culture)}", meterctl.Meter.Slot, "DC", meterctl.Meter.PcbId); //earlier TofAvgMeanTotal //TofAvgMean.Update(Path, meanVal); if (meanVal == 0) { pp.DebugMessage($"Mean value is zero at {meterctl.Meter.Slot}; PATH{(Path + 1).ToString(pp.Setting.Culture)}; Value = {Math.Round(meanVal, 4).ToString("000.0000", pp.Setting.Culture)}", meterctl.Meter.Slot, "DC", meterctl.Meter.PcbId); meterctl.Meter.calibrationResult.AddAdditionalLog($"" + $"PATH {Path + 1}", Math.Round(meanVal, 4).ToString("000.0000", pp.Setting.Culture)); if (CheckAbort(false, meterctl)) { return false; } } pp.DebugMessage($"Mean value at {meterctl.Meter.Slot}; PATH{(Path + 1).ToString(pp.Setting.Culture)}; Value = {Math.Round(meanVal, 4).ToString("000.0000", pp.Setting.Culture)}", meterctl.Meter.Slot, "DC", meterctl.Meter.PcbId); meterctl.Meter.calibrationResult.AddAdditionalLog($"Amp. Test Mean value PATH {Path + 1}", $"at {FirstHitLevelPercentage}={Math.Round(meanVal, 4).ToString("000.0000", pp.Setting.Culture)}"); var cTofAvgMeanTotal = TofAvgMeanTotal.Get(0).ToList(); cTofAvgMeanTotal.Add(meanVal); TofAvgMeanTotal.Update(0, cTofAvgMeanTotal); //meterctl.Meter.listOfTotalTimeOfFlightSPerPath.Update(Path,new List()); #endregion } #endregion #region Write raw data to files //if (this.GenerateLogFiles) //{ // GenesisZeroFlow._GenesisZeroFlow.SetLoggingIndicatorAmpTest(this.MeterNumber, false); // String temp = GenesisZeroFlow._GenesisZeroFlow.GetDataToLog()[this.MeterNumber]; // pp.DebugMessage =$"{meterctl.Meter.PcbId}\tAPPLICATION:\t\t Write data to files at {meterctl.Meter.Slot}\n"; // Logging.WriteToFile(temp, GenesisZeroFlow._GenesisZeroFlow.GetLoggingPathAmplitudeTest()[this.MeterNumber]); //} #endregion #region Write mean values to file //String DateTime = System.DateTime.Now.ToString(Formats.LogFileDateTimeString; //if (this.GenerateMeanFiles) //{ // for (UInt16 Path = Constants.PATH0; Path < Constants.NumberOfPaths; Path++) // Logging.WriteToFile(GenesisZeroFlow._GenesisZeroFlow.GetAmpValuesToLog()[this.MeterNumber][Constants.PATH0], $@"..\ZeroflowCalibrationTool_MeanAmplitude_{meterctl.Meter.Slot}_PATH{Path + 1}_{TempPcbID}_{DateTime}.csv"); //} //this.ClearData(); //#endregion #region Calculate optimal point var DistanceLeft = new MultiPathDataLogContainer(pp.Setting.NumberOfPaths); var DistanceRight = new MultiPathDataLogContainer(pp.Setting.NumberOfPaths); var CalculationFailed = new MultiPathDataLogContainer(pp.Setting.NumberOfPaths); pp.DebugMessage($"Start calculating optimal points", meterctl.Meter.Slot, "DC", meterctl.Meter.PcbId); for (UInt16 Path = 0; Path < TofAvgMeanTotal.GetPathCount(); Path++) { var tmpOptimalPoint = 0; var tmpDistanceLeft = 0.0; var tmpDistanceRight = 0.0; var r = CalculateOptimalPoint(pp, meterctl.Meter.Slot, TofAvgMeanTotal.Get(Path).ToList(), out tmpDistanceLeft, out tmpDistanceRight, out tmpOptimalPoint); OptimalPoint.Update(Path, tmpOptimalPoint); DistanceLeft.Update(Path, tmpDistanceLeft); DistanceRight.Update(Path, tmpDistanceRight); CalculationFailed.Update(Path, r); if (!r) { meterctl.Meter.calibrationResult.AddAdditionalLog($"Amp. Test OptimalPoint PATH {Path + 1}", OptimalPoint.Get(Path).ToString(pp.Setting.Culture)); pp.DebugMessage($"Optimal point calculation successful PATH{Path + 1}; Value = {OptimalPoint.Get(Path).ToString(pp.Setting.Culture)}", meterctl.Meter.Slot, "DC", meterctl.Meter.PcbId); } else { meterctl.Meter.calibrationResult.AddAdditionalLog($"Amp. Test OptimalPoint PATH {Path + 1}", "NaN"); pp.DebugMessage($"Optimal point calculation failed, PATH{Path + 1}", meterctl.Meter.Slot, "DC", meterctl.Meter.PcbId); if (CheckAbort(!r, meterctl)) { return !r; } } } #endregion #region Check value pp.DebugMessage($"Start optimal point values check", meterctl.Meter.Slot, "DC", meterctl.Meter.PcbId); Boolean OptimalPointOK = true; for (UInt16 Path = 0; Path < OptimalPoint.GetPathCount(); Path++) { if (!this.CheckOptimalPoint(pp, OptimalPoint.Get(Path), out DebugMessage, meterctl.Meter.Slot)) { OptimalPointOK = false; } pp.DebugMessage($"{DebugMessage}, PATH{Path + 1}", meterctl.Meter.Slot, "DC", meterctl.Meter.PcbId); } if (CheckAbort(OptimalPointOK, meterctl)) { return OptimalPointOK; } #endregion #region Check distance Boolean CheckDistOK = true; for (UInt16 Path = 0; Path < DistanceLeft.GetPathCount(); Path++) { if (!this.CheckDistance(pp, DistanceLeft.Get(Path), DistanceRight.Get(Path), out DebugMessage, meterctl.Meter.Slot)) { CheckDistOK = false; } pp.DebugMessage($"{DebugMessage}, PATH{Path + 1}", meterctl.Meter.Slot, "DC", meterctl.Meter.PcbId); } if (CheckAbort(CheckDistOK, meterctl)) { return CheckDistOK; } #endregion #region Set first hit level percentage registers pp.DebugMessage($"Start writing to percentage registers", meterctl.Meter.Slot, "CC", meterctl.Meter.PcbId); pp.StatusLabel = ($"{PredefinedMessages.WaitUntilRegisterWriteFinished(pp.Setting.Culture)}"); Ok = false; String RegValue = Register.Genesisflow.FirstHitPercent2; Ok = WriteRegisterSafe(meterctl.Meter, "Percentage", RegValue, OptimalPoint.Get(0)); if (CheckAbort(Ok, meterctl)) { return Ok; } #endregion //here #region Set first hit update period to 10 pp.StatusLabel = (PredefinedMessages.WaitUntilRegisterWriteFinished(pp.Setting.Culture)); Ok = false; Ok = WriteRegisterSafe(meterctl.Meter, "first hit update period", Register.Genesisflow.FirstHitUpdatePeriod, FirstHitUpdatePeriodeValueEnd); if (CheckAbort(Ok, meterctl)) { return Ok; } #endregion #region Wait for 1 second for (Int32 i = 0; i < WaitBeforeLogging; i++) { Thread.Sleep(300); } #endregion #region Set first hit shift to 4 //Frost, Alex Do 02.07.2020 17:12 //RE: Writing 4 to 'First hit shift'-register failed //I think a workaround would be to go into idle mode before changing this register then back to active mode after.I haven’t tried this out yet pp.StatusLabel = (PredefinedMessages.WaitUntilRegisterWriteFinished(pp.Setting.Culture)); Ok = false; Ok = WriteRegisterSafe(meterctl.Meter, "WorkAround TriggerIdle", Register.Genesisflow.TriggerIdle, 1, false); Thread.Sleep(150); pp.StatusLabel = (PredefinedMessages.WaitUntilRegisterWriteFinished(pp.Setting.Culture)); Ok = false; Ok = WriteRegisterSafe(meterctl.Meter, "first hit shift", Register.Genesisflow.FirstHitShift, FirstHitShiftValueEnd); Thread.Sleep(150); pp.StatusLabel = (PredefinedMessages.WaitUntilRegisterWriteFinished(pp.Setting.Culture)); Ok = false; Ok = WriteRegisterSafe(meterctl.Meter, "WorkAround TriggerActive", Register.Genesisflow.TriggerActive, 1, false); Thread.Sleep(150); if (CheckAbort(Ok, meterctl)) { return Ok; } #endregion #endregion return Ok; })); } return listOfProcessTask; } private bool RecordData(ProcessProgress pp, MeterStateControl meterctl, bool Ok, int FirstHitLevelPercentage) { meterctl.Meter.listOfTotalTimeOfFlightSPerPath.Update(null, new List()); meterctl.Meter.logAmpTest = true; pp.DebugMessage($"Start collecting data", meterctl.Meter.Slot, "DC", meterctl.Meter.PcbId); var LinecounterFollowing = new MultiPathDataLogContainer(pp.Setting.NumberOfPaths); for (Int32 Seconds = 0; Seconds < pp.Setting.AmpTestTofRecordingTime; Seconds++) // eight seconds remove /2 { if (Int32.Equals((Seconds % pp.Setting.AmpTestActivityCheckInverval), 0)) { pp.StatusLabel = $"{PredefinedMessages.WaitUntilPercentageSweepFinished(pp.Setting.Culture)} {FirstHitLevelPercentage.ToString(pp.Setting.Culture)}/{ pp.Setting.AmpTestPercentageStop.ToString(pp.Setting.Culture)}; {(pp.Setting.AmpTestTofRecordingTime - Seconds).ToString(pp.Setting.Culture)}s"; Thread.Sleep(1000); Ok = ActivityCheck(pp, meterctl.Meter.listOfTotalTimeOfFlightSPerPath, pp.Setting.AmpTestTofRecordingTime); if (!Ok) { pp.DebugMessage($"Activity check failed", meterctl.Meter.Slot, "DC", meterctl.Meter.PcbId); // if (CheckAbort(Ok, meterctl)) { return Ok; } } } } return Ok; } private Boolean CalculateOptimalPoint(ProcessProgress pp, int Slot, List TofAvg, out Double left, out Double right, out Int32 xAtOptimalPoint) { List ValuesLeft = new List(); List ValuesRight = new List(); List MinDistance = new List(); xAtOptimalPoint = 0; Boolean SubError = true; left = 0; right = 0; ValuesLeft = this.CalculateNearestDistanceLeft(pp, TofAvg, out left); ValuesRight = this.CalculateNearestDistanceRight(pp, TofAvg, out right); for (UInt16 i = 0; i < ValuesLeft.Count(); i++) { Double temp1 = ValuesLeft.ElementAt(i); Double temp2 = ValuesRight.ElementAt(i); Thread.Sleep(10); MinDistance.Add(Math.Min(temp1, temp2)); } //String mindistance = $"mindistance: PATH{path.ToString(GlobalCulture)}\n"; //for (Int16 i = 0; i < MinDistance.Count(); i++) //{ // Double temp = MinDistance.ElementAt(i); // Misc.WaitNMilliseconds(10); // mindistance = $"{mindistance}{temp.ToString(GlobalCulture)}\n"; //} //Logging.WriteToFile(mindistance, @"..\mindistance_PATH"+ path.ToString() + ".txt"); Double Maximum = 0; for (Int16 i = 0; i < MinDistance.Count(); i++) { if ((!Int16.Equals(i, 0)) && (!Int16.Equals(i, (Int16)(TofAvg.Count() - 1)))) { if (MinDistance.ElementAt(i) > Maximum) { Maximum = MinDistance.ElementAt(i); xAtOptimalPoint = (Int32)i + pp.Setting.AmpTestPercentageStart; ; SubError = false; } } } return SubError; } private List CalculateNearestDistanceLeft(ProcessProgress pp, List TofAvg, out Double left) { List Distance = new List(); Double NearestDistance = 0; Boolean Detected = false; left = 0; for (Int32 Percentage = pp.Setting.AmpTestFirstHitLevelPropMin; Percentage <= pp.Setting.AmpTestFirstHitLevelPropMax; Percentage++) { Detected = false; for (Int32 PercentageFollowing = Percentage; PercentageFollowing <= pp.Setting.AmpTestFirstHitLevelPropMax; PercentageFollowing++) { if ((TofAvg.ElementAt(PercentageFollowing - pp.Setting.AmpTestFirstHitLevelPropMin) - TofAvg.ElementAt(Percentage - pp.Setting.AmpTestFirstHitLevelPropMin)) > pp.Setting.AmpTestTriggerValue) { NearestDistance = PercentageFollowing - Percentage; Detected = true; break; } } if (!Detected) { NearestDistance = pp.Setting.AmpTestFirstHitLevelPropMax - Percentage; } Distance.Add(NearestDistance); } for (Int16 i = 0; i < (Int16)Distance.Count(); i++) { if (Distance.ElementAt(i) >= left) { left = Distance.ElementAt(i); } } return Distance; } private List CalculateNearestDistanceRight(ProcessProgress pp, List TofAvg, out Double right) { List Distance = new List(); Double NearestDistance = 0; Boolean Detected = false; right = 0; for (Int32 Percentage = pp.Setting.AmpTestFirstHitLevelPropMax; Percentage >= (pp.Setting.AmpTestFirstHitLevelPropMin); Percentage--) { Detected = false; for (Int32 ProportionFollowing = Percentage; ProportionFollowing >= pp.Setting.AmpTestFirstHitLevelPropMin; ProportionFollowing--) { if ((TofAvg.ElementAt(ProportionFollowing - pp.Setting.AmpTestFirstHitLevelPropMin)) - (TofAvg.ElementAt(Percentage - pp.Setting.AmpTestFirstHitLevelPropMin)) < -pp.Setting.AmpTestTriggerValue) { NearestDistance = Percentage - ProportionFollowing; Detected = true; break; } } if (!Detected) { NearestDistance = Percentage - pp.Setting.AmpTestFirstHitLevelPropMin; } Distance.Add(NearestDistance); } Distance.Reverse(); for (Int16 i = 0; i < Distance.Count(); i++) { if (Distance.ElementAt(i) >= right) { right = Distance.ElementAt(i); } } return Distance; } private Boolean CheckOptimalPoint(ProcessProgress pp, Int32 optimalPoint, out String debugMessage, int Slot) { Boolean Ok = true; debugMessage = $"Optimal point check successful at {Slot}"; // ampTestLowerLimit, ampTestUpperLimit, ampTestDistance if (optimalPoint > pp.Setting.AmpTestFirstHitLevelPropMax) { debugMessage = $"Optimal point maximum check failed at {Slot}"; Ok = false; return Ok; } if (optimalPoint < pp.Setting.AmpTestFirstHitLevelPropMin) { debugMessage = $"Optimal point minimum check failed at {Slot}"; Ok = false; return Ok; } return Ok; } private Boolean CheckDistance(ProcessProgress pp, Double left, Double right, out String debugMessage, int Slot) { Boolean Ok = true; debugMessage = $"Distance check successful at {Slot}"; if (left < pp.Setting.AmpTestFirstHitLevelPropMin) { Ok = false; debugMessage = $"Distance check failed at {Slot}"; return Ok; } if (right < pp.Setting.AmpTestFirstHitLevelPropMin) { Ok = false; debugMessage = $"Distance check failed at {Slot}"; return Ok; } return Ok; } } }