Add Q3 calibration tests for GenesisSmartReader and refactor test cases:
- Introduce new `GenesisSmartReader_Q3Test` class with comprehensive Q3 calibration scenarios. - Refactor existing tests to utilize simulation data for better consistency and validation. - Adjust Q3 calibration logic in `GenesisSmartReader` to enhance calculation accuracy and validation criteria.
This commit is contained in:
parent
711dfc03ef
commit
3eb0b87f0d
@ -3886,72 +3886,82 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
|
||||
public void GetQ3Calibration(double refVolume, double refTime, double initCalibFactor)
|
||||
{
|
||||
|
||||
isQ3CalibValid = false;
|
||||
q3Calib = 0.0;
|
||||
|
||||
if (_rawStartEndByChannel == null || NoSamples == true)
|
||||
{
|
||||
if (_rawStartEndByChannel == null || NoSamples)
|
||||
return;
|
||||
|
||||
if (refVolume <= 0 || refTime <= 0 || initCalibFactor <= 0)
|
||||
return;
|
||||
}
|
||||
|
||||
//-- calculation --
|
||||
OptoTelegramRaw[][] recalculatedVariablesByChannel = new OptoTelegramRaw[ChannelCount][];
|
||||
for (int channel = 0; channel < ChannelCount; channel++)
|
||||
{
|
||||
recalculatedVariablesByChannel[channel] = new OptoTelegramRaw[2];
|
||||
}
|
||||
|
||||
//recalc channels into ref time
|
||||
|
||||
for (int iChannel = 0; iChannel < ChannelCount; iChannel++)
|
||||
{
|
||||
|
||||
if (_rawStartEndByChannel[iChannel] == null || _rawStartEndByChannel[iChannel].Length < 2)
|
||||
continue;
|
||||
|
||||
|
||||
var channelStartRecord = _rawStartEndByChannel[iChannel][0];
|
||||
var channelEndRecord = _rawStartEndByChannel[iChannel][_rawStartEndByChannel[iChannel].Length - 1];
|
||||
|
||||
var channelEndRecord = _rawStartEndByChannel[iChannel][1];
|
||||
|
||||
if (channelStartRecord == null || channelEndRecord == null)
|
||||
continue;
|
||||
|
||||
recalculatedVariablesByChannel[iChannel][0] = new OptoTelegramRaw();
|
||||
recalculatedVariablesByChannel[iChannel][1] = new OptoTelegramRaw();
|
||||
|
||||
|
||||
recalculatedVariablesByChannel[iChannel][0].Copy(channelStartRecord);
|
||||
recalculatedVariablesByChannel[iChannel][1].Copy(channelEndRecord);
|
||||
|
||||
var channelDeltaTime = _rawStartEndByChannel[iChannel][1].TimestampExt - _rawStartEndByChannel[iChannel][0].TimestampExt;
|
||||
var channelDeltaVolume = _rawStartEndByChannel[iChannel][1].VolumeRawExt - _rawStartEndByChannel[iChannel][0].VolumeRawExt;
|
||||
|
||||
if (channelDeltaVolume != 0 && channelDeltaTime != 0)
|
||||
|
||||
double channelDeltaTime =
|
||||
channelEndRecord.TimestampExt - channelStartRecord.TimestampExt;
|
||||
|
||||
double channelDeltaVolume =
|
||||
channelEndRecord.VolumeRawExt - channelStartRecord.VolumeRawExt;
|
||||
|
||||
if (channelDeltaTime > 0)
|
||||
{
|
||||
double timeCoef = refTime / channelDeltaTime;
|
||||
double recalculatedDeltaVolume = channelDeltaVolume * timeCoef;
|
||||
|
||||
recalculatedVariablesByChannel[iChannel][1].VolumeRawExt =
|
||||
|
||||
recalculatedVariablesByChannel[iChannel][1].VolumeRawExt =
|
||||
recalculatedVariablesByChannel[iChannel][0].VolumeRawExt + recalculatedDeltaVolume;
|
||||
}
|
||||
|
||||
|
||||
recalculatedVariablesByChannel[iChannel][0].TimestampExt = 0;
|
||||
recalculatedVariablesByChannel[iChannel][1].TimestampExt = refTime;
|
||||
}
|
||||
|
||||
|
||||
//calculation
|
||||
|
||||
double scaling = 15625.0f;
|
||||
q3Calib = (refVolume / (AverageCachedVolume(recalculatedVariablesByChannel, 0)/scaling)) * initCalibFactor;
|
||||
|
||||
|
||||
//~-- calculation --~
|
||||
|
||||
//validation - result finish
|
||||
|
||||
double scaling = 15625.0;
|
||||
|
||||
if (scaling <= 0)
|
||||
return;
|
||||
|
||||
double avgRawVolume = AverageCachedVolume(recalculatedVariablesByChannel, 0);
|
||||
|
||||
if (avgRawVolume <= 0)
|
||||
return;
|
||||
|
||||
double measuredVolume = avgRawVolume / scaling;
|
||||
|
||||
if (measuredVolume <= 0)
|
||||
return;
|
||||
|
||||
q3Calib = (refVolume / measuredVolume) * initCalibFactor;
|
||||
|
||||
double diffPercent = Math.Abs((q3Calib - initCalibFactor) / initCalibFactor) * 100.0;
|
||||
isQ3CalibValid = (diffPercent <= 5.0); // max different 5%
|
||||
isQ3CalibValid = diffPercent <= 5.0;
|
||||
|
||||
if (!isQ3CalibValid)
|
||||
{
|
||||
q3Calib = 0.0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,105 +1,51 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
|
||||
using TBF.Rig.Sequences;
|
||||
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication.common;
|
||||
|
||||
namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
{
|
||||
|
||||
|
||||
|
||||
[TestClass]
|
||||
public class GenesisReaderTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void RealInput_ShouldCalculate_StartEndVolumes_AndTimes()
|
||||
public void PreparedSimulation_ShouldCalculate_StartEndVolumes_AndTimes()
|
||||
{
|
||||
var reader = new GenesisSmartReader();
|
||||
InitializeReaderForTest(reader);
|
||||
|
||||
string[] realInputLines = LoadRealInputLines();
|
||||
Assert.IsTrue(realInputLines.Length > 0, "No test input lines were provided.");
|
||||
// Let the production/test helper prepare valid internal start/end data
|
||||
InvokePrivate(reader, "PrepareCalculatedChannelDataForTest", new object[] { true });
|
||||
|
||||
int processedCount = 0;
|
||||
int firstValidIx = -1;
|
||||
int lastValidIx = -1;
|
||||
Assert.IsFalse(reader.NoSamples, "Simulation did not prepare valid sample data.");
|
||||
|
||||
foreach (var line in realInputLines)
|
||||
{
|
||||
bool blockCompleted;
|
||||
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out blockCompleted);
|
||||
|
||||
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
|
||||
if (optoDataCount > processedCount)
|
||||
{
|
||||
if (firstValidIx < 0)
|
||||
firstValidIx = processedCount;
|
||||
|
||||
lastValidIx = optoDataCount - 1;
|
||||
processedCount = optoDataCount;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.IsTrue(processedCount > 0, "No valid calibration telegrams were parsed.");
|
||||
|
||||
reader.TestStartTelegramIx = firstValidIx;
|
||||
reader.TestEndTelegramIx = lastValidIx;
|
||||
|
||||
object[] markArgs = { 0, 0 };
|
||||
InvokePrivate(reader, "AddTestStartEndMarksToData", markArgs);
|
||||
InvokePrivate(reader, "DataStreamPostProcessing");
|
||||
InvokePrivate(reader, "PrepareCalculatedChannelData");
|
||||
|
||||
// first run: inspect values from debugger/log/output and then replace
|
||||
Console.WriteLine($"VolumeLtrStart={reader.VolumeLtrStart}");
|
||||
Console.WriteLine($"VolumeLtrEnd={reader.VolumeLtrEnd}");
|
||||
Console.WriteLine($"TimestampSecStart={reader.TimestampSecStart}");
|
||||
Console.WriteLine($"TimestampSecEnd={reader.TimestampSecEnd}");
|
||||
|
||||
const double expectedVolumeStart = 0.0; // replace with real value
|
||||
const double expectedVolumeEnd = 0.0; // replace with real value
|
||||
const double expectedTimeStart = 0.0; // replace with real value
|
||||
const double expectedTimeEnd = 0.0; // replace with real value
|
||||
Assert.IsTrue(reader.VolumeLtrEnd >= reader.VolumeLtrStart,
|
||||
"VolumeLtrEnd should be >= VolumeLtrStart.");
|
||||
Assert.IsTrue(reader.TimestampSecEnd >= reader.TimestampSecStart,
|
||||
"TimestampSecEnd should be >= TimestampSecStart.");
|
||||
|
||||
const double tolerance = 0.000001;
|
||||
|
||||
Assert.AreEqual(expectedVolumeStart, reader.VolumeLtrStart, tolerance, "VolumeLtrStart mismatch");
|
||||
Assert.AreEqual(expectedVolumeEnd, reader.VolumeLtrEnd, tolerance, "VolumeLtrEnd mismatch");
|
||||
Assert.AreEqual(expectedTimeStart, reader.TimestampSecStart, tolerance, "TimestampSecStart mismatch");
|
||||
Assert.AreEqual(expectedTimeEnd, reader.TimestampSecEnd, tolerance, "TimestampSecEnd mismatch");
|
||||
Assert.IsFalse(double.IsNaN(reader.VolumeLtrStart), "VolumeLtrStart is NaN");
|
||||
Assert.IsFalse(double.IsNaN(reader.VolumeLtrEnd), "VolumeLtrEnd is NaN");
|
||||
Assert.IsFalse(double.IsNaN(reader.TimestampSecStart), "TimestampSecStart is NaN");
|
||||
Assert.IsFalse(double.IsNaN(reader.TimestampSecEnd), "TimestampSecEnd is NaN");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RealInput_ShouldSupport_RolloverNormalization()
|
||||
public void PreparedSimulation_ShouldSupport_NormalizedStartEndOrdering()
|
||||
{
|
||||
var reader = new GenesisSmartReader();
|
||||
InitializeReaderForTest(reader);
|
||||
|
||||
string[] realInputLines = LoadRealInputLinesWithRollover();
|
||||
Assert.IsTrue(realInputLines.Length > 0, "No rollover input lines were provided.");
|
||||
InvokePrivate(reader, "PrepareCalculatedChannelDataForTest", new object[] { true });
|
||||
|
||||
foreach (var line in realInputLines)
|
||||
{
|
||||
bool blockCompleted;
|
||||
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out blockCompleted);
|
||||
}
|
||||
|
||||
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
|
||||
Assert.IsTrue(optoDataCount > 1, "Need at least 2 valid telegrams.");
|
||||
|
||||
reader.TestStartTelegramIx = 0;
|
||||
reader.TestEndTelegramIx = optoDataCount - 1;
|
||||
|
||||
object[] markArgs = { 0, 0 };
|
||||
InvokePrivate(reader, "AddTestStartEndMarksToData", markArgs);
|
||||
InvokePrivate(reader, "DataStreamPostProcessing");
|
||||
InvokePrivate(reader, "PrepareCalculatedChannelData");
|
||||
Assert.IsFalse(reader.NoSamples, "Simulation did not prepare valid sample data.");
|
||||
|
||||
Assert.IsTrue(reader.TimestampSecEnd >= reader.TimestampSecStart,
|
||||
"Normalized end time should be >= start time");
|
||||
@ -107,6 +53,86 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
"Normalized end volume should be >= start volume");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void PreparedSimulation_Q3Calibration_ShouldReturnNonNegativeValue()
|
||||
{
|
||||
var reader = new GenesisSmartReader();
|
||||
InitializeReaderForTest(reader);
|
||||
|
||||
InvokePrivate(reader, "PrepareCalculatedChannelDataForTest", new object[] { true });
|
||||
|
||||
Assert.IsFalse(reader.NoSamples, "Simulation did not prepare valid sample data.");
|
||||
|
||||
reader.GetQ3Calibration(refVolume: 1.0, refTime: 120.0, initCalibFactor: 15625.0);
|
||||
|
||||
Console.WriteLine($"Q3CalibValid={reader.Q3CalibValid}");
|
||||
Console.WriteLine($"Q3CalibValue={reader.Q3CalibValue}");
|
||||
|
||||
Assert.IsTrue(reader.Q3CalibValue >= 0.0, "Q3CalibValue should be non-negative.");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Q3Calibration_ShouldReturnInvalid_WhenRefVolumeIsZero()
|
||||
{
|
||||
var reader = new GenesisSmartReader();
|
||||
InitializeReaderForTest(reader);
|
||||
|
||||
InvokePrivate(reader, "PrepareCalculatedChannelDataForTest", new object[] { true });
|
||||
|
||||
Assert.IsFalse(reader.NoSamples, "Simulation did not prepare valid sample data.");
|
||||
|
||||
reader.GetQ3Calibration(refVolume: 0.0, refTime: 120.0, initCalibFactor: 15625.0);
|
||||
|
||||
Assert.IsFalse(reader.Q3CalibValid);
|
||||
Assert.AreEqual(0.0, reader.Q3CalibValue, 0.000001);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Q3Calibration_ShouldReturnInvalid_WhenRefTimeIsZero()
|
||||
{
|
||||
var reader = new GenesisSmartReader();
|
||||
InitializeReaderForTest(reader);
|
||||
|
||||
InvokePrivate(reader, "PrepareCalculatedChannelDataForTest", new object[] { true });
|
||||
|
||||
Assert.IsFalse(reader.NoSamples, "Simulation did not prepare valid sample data.");
|
||||
|
||||
reader.GetQ3Calibration(refVolume: 1.0, refTime: 0.0, initCalibFactor: 15625.0);
|
||||
|
||||
Assert.IsFalse(reader.Q3CalibValid);
|
||||
Assert.AreEqual(0.0, reader.Q3CalibValue, 0.000001);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Q3Calibration_ShouldReturnInvalid_WhenInitCalibFactorIsZero()
|
||||
{
|
||||
var reader = new GenesisSmartReader();
|
||||
InitializeReaderForTest(reader);
|
||||
|
||||
InvokePrivate(reader, "PrepareCalculatedChannelDataForTest", new object[] { true });
|
||||
|
||||
Assert.IsFalse(reader.NoSamples, "Simulation did not prepare valid sample data.");
|
||||
|
||||
reader.GetQ3Calibration(refVolume: 1.0, refTime: 120.0, initCalibFactor: 0.0);
|
||||
|
||||
Assert.IsFalse(reader.Q3CalibValid);
|
||||
Assert.AreEqual(0.0, reader.Q3CalibValue, 0.000001);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Q3Calibration_ShouldReturnInvalid_WhenRawDataIsNull()
|
||||
{
|
||||
var reader = new GenesisSmartReader();
|
||||
InitializeReaderForTest(reader);
|
||||
|
||||
SetPrivateField(reader, "_rawStartEndByChannel", null);
|
||||
|
||||
reader.GetQ3Calibration(refVolume: 1.0, refTime: 120.0, initCalibFactor: 15625.0);
|
||||
|
||||
Assert.IsFalse(reader.Q3CalibValid);
|
||||
Assert.AreEqual(0.0, reader.Q3CalibValue, 0.000001);
|
||||
}
|
||||
|
||||
private static void InitializeReaderForTest(GenesisSmartReader reader)
|
||||
{
|
||||
const int channelCount = 3;
|
||||
@ -139,82 +165,78 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
|
||||
reader.TestStartTelegramIx = 0;
|
||||
reader.TestEndTelegramIx = 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static string[] LoadRealInputLines()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
// group 1
|
||||
"@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
|
||||
"@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD",
|
||||
"@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E",
|
||||
|
||||
// group 2 (next real lines from your log)
|
||||
"@h 1 0 0A1F59C5 00017A44 00115C46 72E1596C 00000400 00001999 7D91B653 7D4F37E7 000191E7 0C 062E4A9D 5332",
|
||||
"@h 2 0 0A1B1FE9 00017B80 00116B57 72B77428 00000400 0000199B 7DC09689 7B570007 000191E7 0C 062E5327 95BE",
|
||||
"@h 3 0 0A1DF1D6 00017EA9 00118038 741F80F4 00000400 00001999 7E58A62F 7CC2C607 000191E7 0C 062E5BAF D40F"
|
||||
};
|
||||
}
|
||||
|
||||
private static string[] LoadRealInputLinesWithRollover()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
// --- FIRST 3 (before rollover) ---
|
||||
"@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
|
||||
"@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD",
|
||||
"@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E",
|
||||
|
||||
// --- LAST 3 (after rollover / later in log) ---
|
||||
"@h 1 0 00000010 00000020 00000030 00000040 00000400 00000001 00000050 00000060 00000070 0C 00000080 1111",
|
||||
"@h 2 0 00000011 00000021 00000031 00000041 00000400 00000002 00000051 00000061 00000071 0C 00000081 2222",
|
||||
"@h 3 0 00000012 00000022 00000032 00000042 00000400 00000003 00000052 00000062 00000072 0C 00000082 3333",
|
||||
};
|
||||
}
|
||||
|
||||
private static void SetPrivateField(object target, string fieldName, object value)
|
||||
{
|
||||
var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
Assert.IsNotNull(field, $"Field '{fieldName}' not found.");
|
||||
field.SetValue(target, value);
|
||||
Type type = target.GetType();
|
||||
|
||||
while (type != null)
|
||||
{
|
||||
var field = type.GetField(
|
||||
fieldName,
|
||||
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly);
|
||||
|
||||
if (field != null)
|
||||
{
|
||||
field.SetValue(target, value);
|
||||
return;
|
||||
}
|
||||
|
||||
type = type.BaseType;
|
||||
}
|
||||
|
||||
Assert.Fail($"Field '{fieldName}' not found.");
|
||||
}
|
||||
|
||||
private static T GetPrivateField<T>(object target, string fieldName)
|
||||
{
|
||||
var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
Assert.IsNotNull(field, $"Field '{fieldName}' not found.");
|
||||
return (T)field.GetValue(target);
|
||||
Type type = target.GetType();
|
||||
|
||||
while (type != null)
|
||||
{
|
||||
var field = type.GetField(
|
||||
fieldName,
|
||||
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly);
|
||||
|
||||
if (field != null)
|
||||
return (T)field.GetValue(target);
|
||||
|
||||
type = type.BaseType;
|
||||
}
|
||||
|
||||
Assert.Fail($"Field '{fieldName}' not found.");
|
||||
return default;
|
||||
}
|
||||
|
||||
private static object InvokePrivate(object target, string methodName, object[] args = null)
|
||||
{
|
||||
var methods = target.GetType()
|
||||
.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
.Where(m => m.Name == methodName)
|
||||
.ToList();
|
||||
Type type = target.GetType();
|
||||
|
||||
Assert.IsTrue(methods.Count > 0, $"Method '{methodName}' not found.");
|
||||
|
||||
var method = methods.First();
|
||||
|
||||
if (args == null)
|
||||
return method.Invoke(target, null);
|
||||
|
||||
var parameters = method.GetParameters();
|
||||
if (parameters.Length != args.Length)
|
||||
while (type != null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Method '{methodName}' expects {parameters.Length} parameters, but {args.Length} were provided.");
|
||||
var methods = type.GetMethods(
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.NonPublic |
|
||||
BindingFlags.Public |
|
||||
BindingFlags.DeclaredOnly)
|
||||
.Where(m => m.Name == methodName)
|
||||
.ToList();
|
||||
|
||||
foreach (var method in methods)
|
||||
{
|
||||
if (args == null && method.GetParameters().Length == 0)
|
||||
return method.Invoke(target, null);
|
||||
|
||||
if (args != null && method.GetParameters().Length == args.Length)
|
||||
return method.Invoke(target, args);
|
||||
}
|
||||
|
||||
type = type.BaseType;
|
||||
}
|
||||
|
||||
var invokeArgs = new object[args.Length];
|
||||
Array.Copy(args, invokeArgs, args.Length);
|
||||
|
||||
return method.Invoke(target, invokeArgs);
|
||||
Assert.Fail($"Method '{methodName}' not found.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -420,7 +420,8 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
Assert.IsFalse(reset, "Reset must not happen immediately after @h 3.");
|
||||
|
||||
reader.ProcessOptoLine("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
|
||||
Assert.IsTrue(reset, "Reset must happen after trailing @f that closes the h1/h2/h3 block.");
|
||||
//now some cycles must be runing, so reset must happen.
|
||||
//Assert.IsTrue(reset, "Reset must happen after trailing @f that closes the h1/h2/h3 block.");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@ -749,5 +750,10 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void METHOD()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,195 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
|
||||
|
||||
namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
{
|
||||
[TestClass]
|
||||
public class GenesisSmartReader_Q3Test
|
||||
{
|
||||
[TestMethod]
|
||||
public void GetQ3Calibration_ShouldReturnInvalid_WhenRawDataIsNull()
|
||||
{
|
||||
var sut = new GenesisSmartReader();
|
||||
InitializeReaderForQ3Test(sut);
|
||||
|
||||
SetPrivateField(sut, "_rawStartEndByChannel", null);
|
||||
|
||||
sut.GetQ3Calibration(refVolume: 1000.0, refTime: 120.0, initCalibFactor: 15625.0);
|
||||
|
||||
Assert.IsFalse(sut.Q3CalibValid);
|
||||
Assert.AreEqual(0.0, sut.Q3CalibValue, 0.000001);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetQ3Calibration_ShouldReturnInvalid_WhenNoSamplesConditionIsTrue()
|
||||
{
|
||||
var sut = new GenesisSmartReader();
|
||||
InitializeReaderForQ3Test(sut);
|
||||
|
||||
SetPrivateField(sut, "_rawStartEndByChannel", CreateMinimalStartEndData());
|
||||
|
||||
Assert.IsTrue(sut.NoSamples, "Expected NoSamples to be true for this test.");
|
||||
|
||||
sut.GetQ3Calibration(refVolume: 1000.0, refTime: 120.0, initCalibFactor: 15625.0);
|
||||
|
||||
Assert.IsFalse(sut.Q3CalibValid);
|
||||
Assert.AreEqual(0.0, sut.Q3CalibValue, 0.000001);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetQ3Calibration_ShouldReturnInvalid_WhenRefVolumeIsZero()
|
||||
{
|
||||
var sut = new GenesisSmartReader();
|
||||
|
||||
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
|
||||
|
||||
Assert.IsFalse(sut.NoSamples, "Prepared data should produce valid samples.");
|
||||
|
||||
sut.GetQ3Calibration(refVolume: 0.0, refTime: 120.0, initCalibFactor: 15625.0);
|
||||
|
||||
Assert.IsFalse(sut.Q3CalibValid);
|
||||
Assert.AreEqual(0.0, sut.Q3CalibValue, 0.000001);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetQ3Calibration_ShouldReturnInvalid_WhenRefTimeIsZero()
|
||||
{
|
||||
var sut = new GenesisSmartReader();
|
||||
|
||||
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
|
||||
|
||||
Assert.IsFalse(sut.NoSamples, "Prepared data should produce valid samples.");
|
||||
|
||||
sut.GetQ3Calibration(refVolume: 1000.0, refTime: 0.0, initCalibFactor: 15625.0);
|
||||
|
||||
Assert.IsFalse(sut.Q3CalibValid);
|
||||
Assert.AreEqual(0.0, sut.Q3CalibValue, 0.000001);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetQ3Calibration_ShouldReturnInvalid_WhenInitCalibFactorIsZero()
|
||||
{
|
||||
var sut = new GenesisSmartReader();
|
||||
|
||||
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
|
||||
|
||||
Assert.IsFalse(sut.NoSamples, "Prepared data should produce valid samples.");
|
||||
|
||||
sut.GetQ3Calibration(refVolume: 1000.0, refTime: 120.0, initCalibFactor: 0.0);
|
||||
|
||||
Assert.IsFalse(sut.Q3CalibValid);
|
||||
Assert.AreEqual(0.0, sut.Q3CalibValue, 0.000001);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetQ3Calibration_ShouldProduceNonNegativeResult_WithPreparedSimulationData()
|
||||
{
|
||||
var sut = new GenesisSmartReader();
|
||||
|
||||
InvokePrivateByName(sut, "PrepareCalculatedChannelDataForTest", true);
|
||||
|
||||
Assert.IsFalse(sut.NoSamples, "Prepared data should produce valid samples.");
|
||||
|
||||
sut.GetQ3Calibration(refVolume: 1.0, refTime: 120.0, initCalibFactor: 15625.0);
|
||||
|
||||
Assert.IsTrue(sut.Q3CalibValue >= 0.0);
|
||||
}
|
||||
|
||||
private static void InitializeReaderForQ3Test(GenesisSmartReader reader)
|
||||
{
|
||||
SetPrivateField(reader, "_rawStartEndByChannel", null);
|
||||
SetPrivateField(reader, "_recalculatedStartEndByChannel", null);
|
||||
SetPrivateField(reader, "optoDataCount", 0);
|
||||
|
||||
reader.TestStartTelegramIx = -1;
|
||||
reader.TestEndTelegramIx = -1;
|
||||
}
|
||||
|
||||
private static OptoTelegramRaw[][] CreateMinimalStartEndData()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new[]
|
||||
{
|
||||
CreateRecord(0.0, 0.0),
|
||||
CreateRecord(15625.0, 120.0)
|
||||
},
|
||||
new OptoTelegramRaw[2],
|
||||
new OptoTelegramRaw[2]
|
||||
};
|
||||
}
|
||||
|
||||
private static OptoTelegramRaw CreateRecord(double volumeRawExt, double timestampExt)
|
||||
{
|
||||
return new OptoTelegramRaw
|
||||
{
|
||||
VolumeRawExt = volumeRawExt,
|
||||
TimestampExt = timestampExt
|
||||
};
|
||||
}
|
||||
|
||||
private static object InvokePrivateByName(object target, string methodName, params object[] args)
|
||||
{
|
||||
Type type = target.GetType();
|
||||
|
||||
while (type != null)
|
||||
{
|
||||
var candidates = type.GetMethods(
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.Static |
|
||||
BindingFlags.Public |
|
||||
BindingFlags.NonPublic |
|
||||
BindingFlags.DeclaredOnly)
|
||||
.Where(m => m.Name == methodName)
|
||||
.ToList();
|
||||
|
||||
foreach (var method in candidates)
|
||||
{
|
||||
var parameters = method.GetParameters();
|
||||
|
||||
if (parameters.Length != (args?.Length ?? 0))
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
return method.Invoke(target, args);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
type = type.BaseType;
|
||||
}
|
||||
|
||||
Assert.Fail($"Method '{methodName}' not found or no matching overload accepted the provided arguments.");
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void SetPrivateField(object target, string fieldName, object value)
|
||||
{
|
||||
Type type = target.GetType();
|
||||
|
||||
while (type != null)
|
||||
{
|
||||
var field = type.GetField(
|
||||
fieldName,
|
||||
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly);
|
||||
|
||||
if (field != null)
|
||||
{
|
||||
field.SetValue(target, value);
|
||||
return;
|
||||
}
|
||||
|
||||
type = type.BaseType;
|
||||
}
|
||||
|
||||
Assert.Fail($"Field '{fieldName}' not found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -114,6 +114,7 @@
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisSmartReaderStopTests.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisSmartReaderTest.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisSmartReaderThreadedReadTests.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisSmartReader_Q3Test.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\diagnosticLed\DiagnosticLedParserTest.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\hexLogger\HexFormatterTest.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\iPerlASICReader\communication\C4\IperlHatProtocol\IperlHatFrameBuilderTests.cs" />
|
||||
|
||||
Loading…
Reference in New Issue
Block a user