- Extend `CalibrationRecord` constructor to include overflow fields (`OverflowVolumeCm`, `OverflowTimeS`). - Implement additional unit tests in `OptoTelegramRawTest` to validate multi-rollover scenarios and extended calculations. - Add `GenesisSmartReaderThreadedReadTests` to cover threaded read-loop functionality and serial processing. - Enhance logging in `Pump` classes (`TurnOff` methods). - Update project file to include new test files.
318 lines
12 KiB
C#
318 lines
12 KiB
C#
using System;
|
|
using System.Reflection;
|
|
using Common;
|
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
using TBF.Rig.RegisterReaders.GenesisRegReader;
|
|
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
|
|
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
|
|
|
|
namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|
{
|
|
[TestClass]
|
|
public class GenesisSmartReaderChannelAveragingTests
|
|
{
|
|
private const int ChannelCount = 3;
|
|
|
|
[TestInitialize]
|
|
public void TestInitialize()
|
|
{
|
|
SetPrivateStaticField(typeof(GenesisSmartReader), "chenelsSwichCount", ChannelCount);
|
|
}
|
|
|
|
private static void SetPrivateField(object target, string fieldName, object value)
|
|
{
|
|
var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
|
|
if (field == null)
|
|
throw new MissingFieldException(target.GetType().FullName, fieldName);
|
|
|
|
field.SetValue(target, value);
|
|
}
|
|
|
|
private static T GetPrivateField<T>(object target, string fieldName)
|
|
{
|
|
var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
|
|
if (field == null)
|
|
throw new MissingFieldException(target.GetType().FullName, fieldName);
|
|
|
|
return (T)field.GetValue(target);
|
|
}
|
|
|
|
private static void SetPrivateStaticField(Type type, string fieldName, object value)
|
|
{
|
|
var field = type.GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic);
|
|
if (field == null)
|
|
throw new MissingFieldException(type.FullName, fieldName);
|
|
|
|
field.SetValue(null, value);
|
|
}
|
|
|
|
private static void InvokePrepareCalculatedChannelData(GenesisSmartReader reader)
|
|
{
|
|
var method = typeof(GenesisSmartReader).GetMethod(
|
|
"PrepareCalculatedChannelData",
|
|
BindingFlags.Instance | BindingFlags.NonPublic);
|
|
|
|
if (method == null)
|
|
throw new MissingMethodException(typeof(GenesisSmartReader).FullName, "PrepareCalculatedChannelData");
|
|
|
|
method.Invoke(reader, null);
|
|
}
|
|
|
|
private static OptoTelegramRaw CreateOptoRecord(int channel, double volumeRawExt, double timestampExt, int counter)
|
|
{
|
|
return new OptoTelegramRaw
|
|
{
|
|
Flags = OptoTelegramFlags.OK,
|
|
iChannel = channel,
|
|
VolumeRawExt = volumeRawExt,
|
|
TimestampExt = timestampExt,
|
|
Counter = counter,
|
|
DateTime = DateTime.Now
|
|
};
|
|
}
|
|
|
|
private static GenesisCfg CreateCfg()
|
|
{
|
|
var cfg = new GenesisCfg(null);
|
|
|
|
cfg.OptoComPortNr = 7;
|
|
cfg.RfidComPortNr = 8;
|
|
cfg.MuxBoardNr = 1;
|
|
cfg.Group = 1;
|
|
cfg.CommunicationInterface = CommunicationInterface.RFID;
|
|
|
|
if (cfg.ProcParams != null)
|
|
{
|
|
cfg.ProcParams.Counting = Counting.Arbitrary;
|
|
cfg.ProcParams.FactorLimitLo = 0;
|
|
cfg.ProcParams.FactorLimitHi = 65535;
|
|
cfg.ProcParams.CalibTarget = 0;
|
|
}
|
|
|
|
return cfg;
|
|
}
|
|
|
|
private static GenesisSmartReader CreateReaderWithOptoBuffer()
|
|
{
|
|
var reader = new GenesisSmartReader(CreateCfg(), null);
|
|
|
|
SetPrivateField(reader, "optoData", new OptoTelegramRaw[GenesisSmartReader.OptoDataBufferSize]);
|
|
|
|
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
|
|
for (int i = 0; i < optoData.Length; i++)
|
|
{
|
|
optoData[i] = new OptoTelegramRaw();
|
|
}
|
|
|
|
return reader;
|
|
}
|
|
|
|
[TestMethod]
|
|
public void SimulatedInterleaved20x3ChannelStream_ShouldComputeRecalculatedStartAndEndVolumeAndTimeCorrectly()
|
|
{
|
|
var reader = CreateReaderWithOptoBuffer();
|
|
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
|
|
|
|
int index = 0;
|
|
for (int g = 0; g < 20; g++)
|
|
{
|
|
optoData[index] = CreateOptoRecord(0, 100 + g, 1 + g, index);
|
|
index++;
|
|
|
|
optoData[index] = CreateOptoRecord(1, 200 + g, 1 + g, index);
|
|
index++;
|
|
|
|
optoData[index] = CreateOptoRecord(2, 300 + g, 1 + g, index);
|
|
index++;
|
|
}
|
|
|
|
SetPrivateField(reader, "optoDataCount", index);
|
|
reader.TestStartTelegramIx = 2;
|
|
reader.TestEndTelegramIx = index - 1;
|
|
|
|
InvokePrepareCalculatedChannelData(reader);
|
|
|
|
Assert.AreEqual(200.0, reader.VolumeLtrStart, 1e-9);
|
|
Assert.AreEqual(219.0, reader.VolumeLtrEnd, 1e-9);
|
|
|
|
Assert.AreEqual(1.0, reader.TimestampSecStart, 1e-9);
|
|
Assert.AreEqual(20.0, reader.TimestampSecEnd, 1e-9);
|
|
|
|
Assert.AreEqual(200.0, reader.VolumeLtrStartRaw, 1e-9);
|
|
Assert.AreEqual(219.0, reader.VolumeLtrEndRaw, 1e-9);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void VolumeLtrEnd_ShouldRecalculateShorterChannelToLongestChannelTimeSpan()
|
|
{
|
|
var reader = CreateReaderWithOptoBuffer();
|
|
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
|
|
|
|
int index = 0;
|
|
|
|
for (int g = 0; g < 10; g++)
|
|
{
|
|
optoData[index] = CreateOptoRecord(0, 100 + g, 1 + g, index);
|
|
index++;
|
|
|
|
optoData[index] = CreateOptoRecord(1, 200 + g, 1 + g, index);
|
|
index++;
|
|
|
|
optoData[index] = CreateOptoRecord(2, 300 + g, 1 + g, index);
|
|
index++;
|
|
}
|
|
|
|
// channel 1 loses its last two valid records
|
|
// valid ch1 becomes: 200..207 with timestamps 1..8
|
|
// longest channel deltaTime is 9 (channels 0 and 2)
|
|
// ch1 deltaVolume = 7, deltaTime = 7 => recalculated deltaVolume = 7 * 9 / 7 = 9
|
|
// recalculated end ch1 = 200 + 9 = 209
|
|
optoData[25].Flags = OptoTelegramFlags.InvalidTelegram;
|
|
optoData[28].Flags = OptoTelegramFlags.InvalidTelegram;
|
|
|
|
SetPrivateField(reader, "optoDataCount", index);
|
|
reader.TestStartTelegramIx = 2;
|
|
reader.TestEndTelegramIx = index - 1;
|
|
|
|
InvokePrepareCalculatedChannelData(reader);
|
|
|
|
Assert.AreEqual(200.0, reader.VolumeLtrStart, 1e-9);
|
|
Assert.AreEqual(209.0, reader.VolumeLtrEnd, 1e-9);
|
|
|
|
Assert.AreEqual(208.33333333333334, reader.VolumeLtrEndRaw, 1e-9);
|
|
|
|
Assert.AreEqual(100.0, reader.VolumeLtrStartCh1, 1e-9);
|
|
Assert.AreEqual(200.0, reader.VolumeLtrStartCh2, 1e-9);
|
|
Assert.AreEqual(300.0, reader.VolumeLtrStartCh3, 1e-9);
|
|
|
|
Assert.AreEqual(109.0, reader.VolumeLtrEndCh1, 1e-9);
|
|
Assert.AreEqual(209.0, reader.VolumeLtrEndCh2, 1e-9);
|
|
Assert.AreEqual(309.0, reader.VolumeLtrEndCh3, 1e-9);
|
|
|
|
Assert.AreEqual(1.0, reader.TimestampSecStart, 1e-9);
|
|
Assert.AreEqual(10.0, reader.TimestampSecEnd, 1e-9);
|
|
|
|
Assert.AreEqual(1.0, reader.TimestampSecStartCh1, 1e-9);
|
|
Assert.AreEqual(1.0, reader.TimestampSecStartCh2, 1e-9);
|
|
Assert.AreEqual(1.0, reader.TimestampSecStartCh3, 1e-9);
|
|
|
|
Assert.AreEqual(10.0, reader.TimestampSecEndCh1, 1e-9);
|
|
Assert.AreEqual(10.0, reader.TimestampSecEndCh2, 1e-9);
|
|
Assert.AreEqual(10.0, reader.TimestampSecEndCh3, 1e-9);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void VolumeLtrStart_And_End_ShouldBeZero_WhenAllSamplesAreInvalid()
|
|
{
|
|
var reader = CreateReaderWithOptoBuffer();
|
|
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
|
|
|
|
for (int i = 0; i < 9; i++)
|
|
{
|
|
optoData[i] = new OptoTelegramRaw
|
|
{
|
|
Flags = OptoTelegramFlags.InvalidTelegram,
|
|
iChannel = i % 3,
|
|
VolumeRawExt = 100 + i,
|
|
TimestampExt = 1 + i
|
|
};
|
|
}
|
|
|
|
SetPrivateField(reader, "optoDataCount", 9);
|
|
reader.TestStartTelegramIx = 2;
|
|
reader.TestEndTelegramIx = 8;
|
|
|
|
InvokePrepareCalculatedChannelData(reader);
|
|
|
|
Assert.AreEqual(0.0, reader.VolumeLtrStart, 1e-9);
|
|
Assert.AreEqual(0.0, reader.VolumeLtrEnd, 1e-9);
|
|
Assert.AreEqual(0.0, reader.TimestampSecStart, 1e-9);
|
|
Assert.AreEqual(0.0, reader.TimestampSecEnd, 1e-9);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void SimulatedInterleaved20x3ChannelStream_ShouldComputePerChannelStartAndEndValues()
|
|
{
|
|
var reader = CreateReaderWithOptoBuffer();
|
|
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
|
|
|
|
int index = 0;
|
|
for (int g = 0; g < 20; g++)
|
|
{
|
|
optoData[index] = CreateOptoRecord(0, 100 + g, 1 + g, index);
|
|
index++;
|
|
|
|
optoData[index] = CreateOptoRecord(1, 200 + g, 1 + g, index);
|
|
index++;
|
|
|
|
optoData[index] = CreateOptoRecord(2, 300 + g, 1 + g, index);
|
|
index++;
|
|
}
|
|
|
|
SetPrivateField(reader, "optoDataCount", index);
|
|
reader.TestStartTelegramIx = 2;
|
|
reader.TestEndTelegramIx = index - 1;
|
|
|
|
InvokePrepareCalculatedChannelData(reader);
|
|
|
|
Assert.AreEqual(100.0, reader.VolumeLtrStartCh1, 1e-9);
|
|
Assert.AreEqual(200.0, reader.VolumeLtrStartCh2, 1e-9);
|
|
Assert.AreEqual(300.0, reader.VolumeLtrStartCh3, 1e-9);
|
|
|
|
Assert.AreEqual(119.0, reader.VolumeLtrEndCh1, 1e-9);
|
|
Assert.AreEqual(219.0, reader.VolumeLtrEndCh2, 1e-9);
|
|
Assert.AreEqual(319.0, reader.VolumeLtrEndCh3, 1e-9);
|
|
|
|
Assert.AreEqual(1.0, reader.TimestampSecStartCh1, 1e-9);
|
|
Assert.AreEqual(1.0, reader.TimestampSecStartCh2, 1e-9);
|
|
Assert.AreEqual(1.0, reader.TimestampSecStartCh3, 1e-9);
|
|
|
|
Assert.AreEqual(20.0, reader.TimestampSecEndCh1, 1e-9);
|
|
Assert.AreEqual(20.0, reader.TimestampSecEndCh2, 1e-9);
|
|
Assert.AreEqual(20.0, reader.TimestampSecEndCh3, 1e-9);
|
|
|
|
Assert.AreEqual(200.0, reader.VolumeLtrStart, 1e-9);
|
|
Assert.AreEqual(219.0, reader.VolumeLtrEnd, 1e-9);
|
|
Assert.AreEqual(1.0, reader.TimestampSecStart, 1e-9);
|
|
Assert.AreEqual(20.0, reader.TimestampSecEnd, 1e-9);
|
|
|
|
Assert.AreEqual(200.0, reader.VolumeLtrStartRaw, 1e-9);
|
|
Assert.AreEqual(219.0, reader.VolumeLtrEndRaw, 1e-9);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void VolumeLtrEnd_ShouldAverageOnlyAvailableChannels()
|
|
{
|
|
var reader = CreateReaderWithOptoBuffer();
|
|
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
|
|
|
|
int idx = 0;
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
optoData[idx++] = CreateOptoRecord(0, 10 + i, 100 + i, idx);
|
|
optoData[idx++] = CreateOptoRecord(1, 20 + i, 100 + i, idx);
|
|
}
|
|
|
|
SetPrivateField(reader, "optoDataCount", idx);
|
|
reader.TestStartTelegramIx = 1;
|
|
reader.TestEndTelegramIx = idx - 1;
|
|
|
|
InvokePrepareCalculatedChannelData(reader);
|
|
|
|
Assert.AreEqual(15.0, reader.VolumeLtrStart, 1e-9);
|
|
Assert.AreEqual(19.0, reader.VolumeLtrEnd, 1e-9);
|
|
|
|
Assert.AreEqual(10.0, reader.VolumeLtrStartCh1, 1e-9);
|
|
Assert.AreEqual(20.0, reader.VolumeLtrStartCh2, 1e-9);
|
|
Assert.AreEqual(0.0, reader.VolumeLtrStartCh3, 1e-9);
|
|
|
|
Assert.AreEqual(14.0, reader.VolumeLtrEndCh1, 1e-9);
|
|
Assert.AreEqual(24.0, reader.VolumeLtrEndCh2, 1e-9);
|
|
Assert.AreEqual(0.0, reader.VolumeLtrEndCh3, 1e-9);
|
|
|
|
Assert.AreEqual(100.0, reader.TimestampSecStart, 1e-9);
|
|
Assert.AreEqual(104.0, reader.TimestampSecEnd, 1e-9);
|
|
}
|
|
}
|
|
} |