laatzen/Common/CordonelPreadjustmentUi/Processes/Actions/AmplitudeTestProcess.cs
2021-10-01 11:09:20 +02:00

540 lines
26 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Diagnostics.Eventing.Reader;
using System.Linq;
using System.Text;
using CordonelPreadjustmentUi.Processes.Itinerary;
using System.Threading;
using static CordonelPreadjustmentUi.PreAdjustmentControl;
using System.Threading.Tasks;
using CordonelPreadjustmentUi.Helper;
using CordonelPreadjustmentUi.Const;
using CordonelPreadjustmentUi.Helper.Extensions;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
namespace CordonelPreadjustmentUi.Processes.Actions
{
public class AmplitudeTestProcess : BaseProcess
{
public AmplitudeTestProcess(string processName, StatusPanelItems panelState, string performMessage, string failedMessage, int? expectedTimeS) : base(processName, panelState, performMessage, failedMessage, expectedTimeS)
{
}
public override List<Task> StartWork()
{
return doWorkProcess(base.CurrentProcessProgress);
}
private List<Task> doWorkProcess(ProcessProgress pp)
{
var MetersToProcess = MeterStateCtls.Where(e => e.IsEnabled).ToList();
var listOfProcessTask = new List<Task>();
foreach (var meterctl in MetersToProcess)
{
listOfProcessTask.Add(new Task<bool>(() =>
{
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<Double>(pp.Setting.NumberOfPaths);
var OptimalPoint = new MultiPathDataLogContainer<Int32>(pp.Setting.NumberOfPaths);
var TofAvgMeanTotal = new MultiPathDataLogContainer<List<Double>>(pp.Setting.NumberOfPaths);
TofAvgMeanTotal.Update(null,new List<Double>());
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.FirstHitPercent1, FirstHitLevelPercentage);
if (CheckAbort(Ok, meterctl)) { return Ok; }
#endregion
#region Write to percentage register PATH1
if (pp.Setting.NumberOfPaths > 1)
{
Ok = WriteRegisterSafe(meterctl.Meter, "Percentage", Register.Genesisflow.FirstHitPercent2, FirstHitLevelPercentage);
if (CheckAbort(Ok, meterctl)) { return Ok; }
}
#endregion
#region Write to percentage register PATH2
if (pp.Setting.NumberOfPaths > 2)
{
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;
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
for (UInt16 Path = 0; Path < meterctl.Meter.listOfTotalTimeOfFlightSPerPath.GetPathCount(); Path++)
{
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.AddAditonlaLog($"" +
$"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.AddAditonlaLog($"Amp. Test Mean value PATH {Path + 1}", $"at {FirstHitLevelPercentage}={Math.Round(meanVal, 4).ToString("000.0000", pp.Setting.Culture)}");
var cTofAvgMeanTotal = TofAvgMeanTotal.Get(Path).ToList();
cTofAvgMeanTotal.Add(meanVal);
TofAvgMeanTotal.Update(Path, cTofAvgMeanTotal);
//meterctl.Meter.listOfTotalTimeOfFlightSPerPath.Update(Path,new List<Double>());
}
#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<double>(pp.Setting.NumberOfPaths);
var DistanceRight = new MultiPathDataLogContainer<double>(pp.Setting.NumberOfPaths);
var CalculationFailed = new MultiPathDataLogContainer<bool>(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.AddAditonlaLog($"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.AddAditonlaLog($"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);
for (UInt16 Path = 0; Path < OptimalPoint.GetPathCount(); Path++)
{
pp.StatusLabel = ($"{PredefinedMessages.WaitUntilRegisterWriteFinished(pp.Setting.Culture)}");
Ok = false;
String RegValue = String.Empty;
switch (Path)
{
case 0:
RegValue = Register.Genesisflow.FirstHitPercent1;
break;
case 1:
RegValue = Register.Genesisflow.FirstHitPercent2;
break;
case 2:
RegValue = Register.Genesisflow.FirstHitPercent3;
break;
default:
RegValue = Register.Genesisflow.FirstHitPercent1;
break;
};
Ok = WriteRegisterSafe(meterctl.Meter, "Percentage", RegValue, OptimalPoint.Get(Path));
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 <AFrost@sentec.co.uk> 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 havent 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<Double>());
meterctl.Meter.logAmpTest = true;
pp.DebugMessage($"Start collecting data", meterctl.Meter.Slot, "DC", meterctl.Meter.PcbId);
var LinecounterFollowing = new MultiPathDataLogContainer<Int32>(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<Double> TofAvg, out Double left, out Double right, out Int32 xAtOptimalPoint)
{
List<Double> ValuesLeft = new List<Double>();
List<Double> ValuesRight = new List<Double>();
List<Double> MinDistance = new List<Double>();
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<Double> CalculateNearestDistanceLeft(ProcessProgress pp, List<Double> TofAvg, out Double left)
{
List<Double> Distance = new List<Double>();
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<Double> CalculateNearestDistanceRight(ProcessProgress pp, List<Double> TofAvg, out Double right)
{
List<Double> Distance = new List<Double>();
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;
}
}
}