common/CordoPlot/PlotableTestResults.cs
2026-04-23 17:50:07 +02:00

536 lines
22 KiB
C#

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using ScottPlot;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.MeasurementRecords;
using Xylem.Common.Logic.ProductionOrderCore.TestResults;
using Xylem.Common.Metrology.Measurements;
namespace CordoPlot
{
public class PlotCalibrationRecord : CalibrationRecord
{
public PlotCalibrationRecord(CalibrationRecord c, int linenr)
{
base.AccuVolumeRaw = c.AccuVolumeRaw;
base.AmplitudeDownV = c.AmplitudeDownV;
base.AmplitudeUpV = c.AmplitudeUpV;
base.Channel = c.Channel;
base.Crc = c.Crc;
base.DecodedTime = c.DecodedTime;
base.DeltaTimeOfFlightS = c.DeltaTimeOfFlightS;
base.DeltaVolumeQm = c.DeltaVolumeQm;
base.DeltaVolumeRaw = c.DeltaVolumeRaw;
base.FlowRateCmPh = c.FlowRateCmPh;
base.ForwardVolumeCm = c.ForwardVolumeCm;
base.IsValid = c.IsValid;
base.NegativeReverseVolumeCm = c.NegativeReverseVolumeCm;
base.OverflowTimeS = c.OverflowTimeS;
base.OverflowVolumeCm = c.OverflowVolumeCm;
base.PulseWidthRatioDown = c.PulseWidthRatioDown;
base.PulseWidthRatioUp = c.PulseWidthRatioUp;
base.RawDeltaTimeOfFlight = c.RawDeltaTimeOfFlight;
base.RawTotalTimeOfFlight = c.RawTotalTimeOfFlight;
base.ReceivedTime = c.ReceivedTime;
base.SampleIntervalS = c.SampleIntervalS;
base.SyncMarkRecord = c.SyncMarkRecord;
base.TemperatureDegC = c.TemperatureDegC;
base.TemperaturePowFactor = c.TemperaturePowFactor;
base.TemperatureRaw = c.TemperatureRaw;
base.TimeS = c.TimeS;
base.TotalTimeOfFlightS = c.TotalTimeOfFlightS;
base.Validation = c.Validation;
base.VolumeCm = c.VolumeCm;
base.VolumeFactorRawToQm = c.VolumeFactorRawToQm;
base.VolumeScaleRawPerMl = c.VolumeScaleRawPerMl;
LineNummer = linenr;
}
public int LineNummer { get; set; }
}
public class PlotFlowTestRecord : FlowTestRecord
{
public PlotFlowTestRecord(FlowTestRecord c, int linenr)
{
base.Channel = 0;
base.Crc = c.Crc;
base.DecodedTime = c.DecodedTime;
base.FlowRateCmPh = c.FlowRateCmPh;
base.ForwardVolumeCm = c.ForwardVolumeCm;
base.IsValid = c.IsValid;
base.NegativeReverseVolumeCm = c.NegativeReverseVolumeCm;
base.OverflowTimeS = c.OverflowTimeS;
base.OverflowVolumeCm = c.OverflowVolumeCm;
base.ReceivedTime = c.ReceivedTime;
base.SyncMarkRecord = c.SyncMarkRecord;
base.TimeS = c.TimeS;
base.VolumeCm = c.VolumeCm;
LineNummer = linenr;
}
public int LineNummer { get; set; }
}
public class PlotResult
{
public List<double> VolumeLine = new List<double>();
public List<double> ScaleLine = new List<double>();
public List<double> GP30Erros = new List<double>();
public List<double> TestMarks = new List<double>();
public List<double> TofLine = new List<double>();
public List<double> AmplitudeUpVLine = new List<double>();
public List<double> AmplitudeDownVLine = new List<double>();
public Color Color { get; internal set; }
public void ScaleUp(List<double> MaxSacle)
{
if (VolumeLine.Count < MaxSacle.Count)
{
for (int i = VolumeLine.Count; i < MaxSacle.Count; i++)
{
VolumeLine.Add(double.NaN);
ScaleLine.Add(MaxSacle[i]);
}
}
}
}
public class TestPointResult
{
public int? Chnl { get; internal set; } = null;
public int TestNr { get; internal set; }
public List<IMeasurementRecord> measurementRecords = new List<IMeasurementRecord>();
}
public class PlotableTestResults : TestResults
{
private List<List<IMeasurementRecord>> ListOfRecords = new List<List<IMeasurementRecord>>();
private List<TestPointResult> testPointResults = new List<TestPointResult>();
public PlotableTestResults()
{
}
public System.String FileName { get; set; }
public System.Boolean HasResults { get; internal set; } = false;
public DataTable DisplayData { get; internal set; }
public DataTable ChlData1 { get; internal set; }
public DataTable ChlData2 { get; internal set; }
public DataTable ChlData3 { get; internal set; }
public bool PrepExprtDataPoints()
{
DisplayData = new DataTable("DisplayData");
DisplayData.Columns.Add("TestNr", typeof(int));
DisplayData.Columns.Add("FlowTargetQmh", typeof(double));
DisplayData.Columns.Add("FlowrateQmh", typeof(double));
DisplayData.Columns.Add("VolumeQm", typeof(double));
DisplayData.Columns.Add("Info", typeof(string));
DisplayData.Columns.Add("CALC!DurationS", typeof(double));
DisplayData.Columns.Add("ReceivedTimeUtc", typeof(DateTimeOffset));
DisplayData.Columns.Add("VolumeCm", typeof(double));
DisplayData.Columns.Add("OverflowVolumeCm", typeof(double));
DisplayData.Columns.Add("TimeS", typeof(double));
DisplayData.Columns.Add("OverflowTimeS", typeof(double));
DisplayData.Columns.Add("CRC", typeof(ushort));
DisplayData.Columns.Add("IsValid", typeof(bool));
DisplayData.Columns.Add("DecodedTimeUtc", typeof(DateTimeOffset));
DisplayData.Columns.Add("SyncMarkRecord", typeof(SyncMarkRecord));
DisplayData.Columns.Add("CALC!VolumenDiffFromStartQm", typeof(double));
DisplayData.Columns.Add("CALC!FlowrateFromStartQmH", typeof(double));
DisplayData.Columns.Add("CALC!ShortFlowrateQmH", typeof(double));
ChlData1 = new DataTable("ChlData1");
ChlData2 = new DataTable("ChlData2");
ChlData3 = new DataTable("ChlData3");
foreach (var item in testPointResults.Where(f => f.Chnl == 0))
{
FlowTestRecord firstRecord = null;
FlowTestRecord prevRecord = null;
foreach (var raw in item.measurementRecords)
{
if (raw is FlowTestRecord ftr)
{
if (firstRecord == null)
{
firstRecord = ftr;
prevRecord = ftr;
DisplayData.Rows.Add(
item.TestNr + 1,
this.Details[item.TestNr].FlowTargetQmh,
this.Details[item.TestNr].FlowrateQmh,
this.Details[item.TestNr].VolumeQm,
this.Details[item.TestNr].Info,
0,
ftr.ReceivedTime,
ftr.VolumeCm,
ftr.OverflowVolumeCm,
ftr.TimeS,
ftr.OverflowTimeS,
ftr.Crc,
ftr.IsValid,
ftr.DecodedTime,
ftr.SyncMarkRecord,
0,
0,
0
);
}
else
{
var volDiffStart = ftr.VolumeCm - firstRecord.VolumeCm;
var volDiffprev = ftr.VolumeCm - prevRecord.VolumeCm;
var timeDiffStart = ftr.TimeS - firstRecord.TimeS;
var timeDiffprev = ftr.TimeS - prevRecord.TimeS;
var ts = (ftr.ReceivedTime - firstRecord.ReceivedTime).TotalSeconds;
DisplayData.Rows.Add(
item.TestNr + 1,
this.Details[item.TestNr].FlowTargetQmh,
this.Details[item.TestNr].FlowrateQmh,
this.Details[item.TestNr].VolumeQm,
this.Details[item.TestNr].Info,
ts,
ftr.ReceivedTime,
ftr.VolumeCm,
ftr.OverflowVolumeCm,
ftr.TimeS,
ftr.OverflowTimeS,
ftr.Crc,
ftr.IsValid,
ftr.DecodedTime,
ftr.SyncMarkRecord,
volDiffStart,
(volDiffStart / timeDiffStart) * 3600,
(volDiffprev / timeDiffprev) * 3600
);
prevRecord = ftr;
}
}
}
}
for (int i = 1; i <= 3; i++)
{
var lCounter = 0;
DataTable dtChnl = ChlData3;
if (i == 1)
{
dtChnl = ChlData1;
}
else if (i == 2)
{
dtChnl = ChlData2;
}
else if (i == 2)
{
dtChnl = ChlData3;
}
dtChnl.Columns.Add("TestNr", typeof(int));
dtChnl.Columns.Add("FlowTargetQmh", typeof(double));
dtChnl.Columns.Add("FlowrateQmh", typeof(double));
dtChnl.Columns.Add("VolumeQm", typeof(double));
dtChnl.Columns.Add("Info", typeof(string));
dtChnl.Columns.Add("CALC!DurationS", typeof(double));
dtChnl.Columns.Add("ReceivedTimeUtc", typeof(DateTimeOffset));
dtChnl.Columns.Add("Validation", typeof(ushort));
dtChnl.Columns.Add("TotalTimeOfFlightS", typeof(double));
dtChnl.Columns.Add("DeltaTimeOfFlightS", typeof(double));
dtChnl.Columns.Add("RawTotalTimeOfFlight", typeof(int));
dtChnl.Columns.Add("RawDeltaTimeOfFlight", typeof(int));
dtChnl.Columns.Add("VolumeScaleRawPerMl", typeof(double));
dtChnl.Columns.Add("VolumeFactorRawToQm", typeof(double));
dtChnl.Columns.Add("DeltaVolumeRaw", typeof(double));
dtChnl.Columns.Add("DeltaVolumeQm", typeof(double));
dtChnl.Columns.Add("AccuVolumeRaw", typeof(double));
dtChnl.Columns.Add("SampleIntervalS", typeof(double));
dtChnl.Columns.Add("AmplitudeUpV", typeof(double));
dtChnl.Columns.Add("AmplitudeDownV", typeof(double));
dtChnl.Columns.Add("PulseWidthRatioUp", typeof(double));
dtChnl.Columns.Add("PulseWidthRatioDown", typeof(double));
dtChnl.Columns.Add("TemperatureRaw", typeof(double));
dtChnl.Columns.Add("TemperaturePowFactor", typeof(double));
dtChnl.Columns.Add("TemperatureDegC", typeof(double));
dtChnl.Columns.Add("CALC!VolumenDiffFromStart", typeof(double));
dtChnl.Columns.Add("CALC!FlowrateFromStart", typeof(double));
dtChnl.Columns.Add("CALC!ShortFlowrate", typeof(double));
foreach (var item in testPointResults.Where(f => f.Chnl == i))
{
CalibrationRecord firstRecord = null;
CalibrationRecord prevRecord = null;
foreach (var raw in item.measurementRecords)
{
if (raw is CalibrationRecord ftr)
{
if (firstRecord == null)
{
firstRecord = ftr;
prevRecord = ftr;
dtChnl.Rows.Add(item.TestNr + 1,
this.Details[item.TestNr].FlowTargetQmh,
this.Details[item.TestNr].FlowrateQmh,
this.Details[item.TestNr].VolumeQm,
this.Details[item.TestNr].Info,
0.0,
ftr.ReceivedTime,
ftr.Validation,
ftr.TotalTimeOfFlightS,
ftr.DeltaTimeOfFlightS,
ftr.RawTotalTimeOfFlight,
ftr.RawDeltaTimeOfFlight,
ftr.VolumeScaleRawPerMl,
ftr.VolumeFactorRawToQm,
ftr.DeltaVolumeRaw,
ftr.DeltaVolumeQm,
ftr.AccuVolumeRaw,
ftr.SampleIntervalS,
ftr.AmplitudeUpV,
ftr.AmplitudeDownV,
ftr.PulseWidthRatioUp,
ftr.PulseWidthRatioDown,
ftr.TemperatureRaw,
ftr.TemperaturePowFactor,
ftr.TemperatureDegC,
0,
0,
0
);
}
else
{
var volDiffStart = ftr.VolumeCm - firstRecord.VolumeCm;
var volDiffprev = ftr.VolumeCm - prevRecord.VolumeCm;
var timeDiffStart = ftr.TimeS - firstRecord.TimeS;
var timeDiffprev = ftr.TimeS - prevRecord.TimeS;
var ts = (ftr.ReceivedTime - firstRecord.ReceivedTime).TotalSeconds;
dtChnl.Rows.Add(item.TestNr + 1,
this.Details[item.TestNr].FlowTargetQmh,
this.Details[item.TestNr].FlowrateQmh,
this.Details[item.TestNr].VolumeQm,
this.Details[item.TestNr].Info,
ts,
ftr.ReceivedTime,
ftr.Validation,
ftr.TotalTimeOfFlightS,
ftr.DeltaTimeOfFlightS,
ftr.RawTotalTimeOfFlight,
ftr.RawDeltaTimeOfFlight,
ftr.VolumeScaleRawPerMl,
ftr.VolumeFactorRawToQm,
ftr.DeltaVolumeRaw,
ftr.DeltaVolumeQm,
ftr.AccuVolumeRaw,
ftr.SampleIntervalS,
ftr.AmplitudeUpV,
ftr.AmplitudeDownV,
ftr.PulseWidthRatioUp,
ftr.PulseWidthRatioDown,
ftr.TemperatureRaw,
ftr.TemperaturePowFactor,
ftr.TemperatureDegC,
volDiffStart,
(volDiffStart / timeDiffStart) * 3600,
(volDiffprev / timeDiffprev) * 3600
);
prevRecord = ftr;
}
}
}
}
}
return true;
}
public bool MapDataPoints(List<List<IMeasurementRecord>> listOfRecords)
{
ListOfRecords = listOfRecords;
var CurrentTestPoint = 0;
var CurrentChnl = -1;
var testPointLocked = false;
//14787
foreach (var allChnlItems in this.ListOfRecords)
{
var intiVol = 0.0;
CurrentChnl = CurrentChnl + 1;//29586
CurrentTestPoint = 0;
testPointLocked = false;
foreach (var item in allChnlItems)
{
if (!testPointLocked && item.GetDataSyncMark() == Xylem.Common.CommonCore.Consts.SyncMarkRecord.SyncStart)
{
testPointLocked = true;
testPointResults.Add(new TestPointResult());
testPointResults.Last().Chnl = CurrentChnl;
testPointResults.Last().TestNr = CurrentTestPoint;
CurrentTestPoint = CurrentTestPoint + 1;
}
if (testPointLocked)
{
testPointResults.Last().measurementRecords.Add(item);
if (item.GetDataSyncMark() == Xylem.Common.CommonCore.Consts.SyncMarkRecord.SyncEnd)
{
testPointLocked = false;
}
}
}
}
return true;
}
public PlotResult Plot(int Chnl, int? TestNr = null)
{
//
var results = testPointResults.Where(t => t.Chnl == Chnl && (!TestNr.HasValue || t.TestNr == TestNr.Value));
List<double> VolumeLine = new List<double>();
double LastVolOrig = 0;
List<double> ScaleLine = new List<double>();
List<double> GP30Erros = new List<double>();
List<double> TestMarks = new List<double>();
List<double> TofLines = new List<double>();
List<double> AmplitudeUpVLines = new List<double>();
List<double> AmplitudeDownVLines = new List<double>();
var linecounter = 0;
var counter = 0;
var intiVol = 0.0;
var currentTestNr = -1;
foreach (var lines in results)
{
counter = 0;
foreach (var line in lines.measurementRecords)
{
if (lines.TestNr != currentTestNr)
{
TestMarks.Add(line.GetTimeS());
currentTestNr = lines.TestNr;
intiVol = 0.0;
LastVolOrig = line.GetVolumeCm();
}
ScaleLine.Add(line.GetTimeS());
if (line is PlotCalibrationRecord cal)
{
GP30Erros.Add(cal.Validation);
TofLines.Add(cal.TotalTimeOfFlightS);
AmplitudeUpVLines.Add(cal.AmplitudeUpV);
AmplitudeDownVLines.Add(cal.AmplitudeDownV);
//VolumeLine.Add(cal.LineNummer);
}
else if (line is PlotFlowTestRecord disp)
{
//VolumeLine.Add(disp.LineNummer);
}
if (!VolumeLine.Any())
{
VolumeLine.Add(0);
LastVolOrig = line.GetVolumeCm();
intiVol = 0;
}
else
{
var addVol = line.GetVolumeCm() - LastVolOrig;
var dutOverflowVolumeCm = line.GetOverflowVolumeCm();
//an overflow is detected when absolute volume difference is higher than 1/2 of the OverflowVolumeCm
if (intiVol != 0 && Math.Abs(addVol) > (0.5 * dutOverflowVolumeCm))
{
//if the overflow has been detected the end volume is higher than the start volume
//indicates the negative flow direction
if (addVol < 0)
{
//negative overflow
intiVol += dutOverflowVolumeCm;
//LoggerMeasurement.Info($"Slot:{Slot}, Channel: {IntermediateRecord.GetChannel()} - has negative overflow. currentVolumeQm ({currentVolumeQm}) - lastVolumeQm ({lastVolumeQm}). current accuOverflowVolume is {_accuDutOverflowVolumeCm}");
}
else
{
//positive overflow
intiVol += dutOverflowVolumeCm;
//LoggerMeasurement.Info($"Slot:{Slot}, Channel: {IntermediateRecord.GetChannel()} - has positive overflow. currentVolumeQm ({currentVolumeQm}) - lastVolumeQm ({lastVolumeQm}). current accuOverflowVolume is {_accuDutOverflowVolumeCm}");
}
}
intiVol += addVol;
LastVolOrig = line.GetVolumeCm();
VolumeLine.Add(intiVol);
}
counter = counter + 1;
}
linecounter = 1 + linecounter;
}
return new PlotResult() { VolumeLine = VolumeLine, ScaleLine = ScaleLine, GP30Erros = GP30Erros, TestMarks = TestMarks, TofLine = TofLines, AmplitudeUpVLine = AmplitudeUpVLines, AmplitudeDownVLine = AmplitudeDownVLines };
}
}
}