- 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.
759 lines
31 KiB
C#
759 lines
31 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Common;
|
|
using JetBrains.Annotations;
|
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
using TBF.Rig;
|
|
using TBF.Rig.RegisterReaders.GenesisRegReader;
|
|
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
|
|
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
|
|
|
|
namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
|
{
|
|
[TestClass]
|
|
[TestSubject(typeof(GenesisSmartReader))]
|
|
public class GenesisSmartReaderTest
|
|
{
|
|
private const int ChannelCount = 3;
|
|
|
|
private static GenesisCfg CreateCfg()
|
|
{
|
|
Factory factory = new Factory();
|
|
return new GenesisCfg(factory);
|
|
}
|
|
|
|
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 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 void InitializeThreeChannelState(GenesisSmartReader reader)
|
|
{
|
|
SetPrivateField(reader, "volumeRawExtLast", new double[ChannelCount]);
|
|
SetPrivateField(reader, "timestampExtLast", new double[ChannelCount]);
|
|
|
|
SetPrivateField(reader, "lastVolumeRaw", new double[ChannelCount]);
|
|
SetPrivateField(reader, "lastTimestamp", new double[ChannelCount]);
|
|
|
|
SetPrivateField(reader, "volumeLtr", new[] { 0.0, 0.0, 0.0 });
|
|
SetPrivateField(reader, "volumeLtr0", new[] { 0.0, 0.0, 0.0 });
|
|
|
|
SetPrivateField(reader, "timestampSec", new[] { 0.0, 0.0, 0.0 });
|
|
SetPrivateField(reader, "timestampSec0", new[] { 0.0, 0.0, 0.0 });
|
|
}
|
|
|
|
private static void EnableProcessingLoopForTests(GenesisSmartReader reader)
|
|
{
|
|
SetPrivateField(reader, "dataStreamState", DataStreamState.ProcessAndSave);
|
|
SetPrivateField(reader, "startDataProcessing", true);
|
|
reader.StopQueueData = false;
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Start_ShouldOpenPort_AndEnableProcessing()
|
|
{
|
|
var fake = new FakeSerialDriver();
|
|
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
|
|
|
reader.Initialize();
|
|
reader.Start();
|
|
|
|
Assert.IsTrue(fake.IsOpen);
|
|
Assert.IsTrue(fake.OpenCalls >= 1);
|
|
Assert.AreEqual(2, fake.DiscardInCalls);
|
|
Assert.AreEqual(2, fake.DiscardOutCalls);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Stop_ShouldClosePort()
|
|
{
|
|
var fake = new FakeSerialDriver();
|
|
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
|
|
|
reader.Initialize();
|
|
reader.Start();
|
|
|
|
Assert.IsTrue(fake.IsOpen);
|
|
|
|
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
|
|
Assert.IsNotNull(optoData);
|
|
|
|
if (optoData[0] == null)
|
|
optoData[0] = new OptoTelegramRaw();
|
|
|
|
if (optoData[1] == null)
|
|
optoData[1] = new OptoTelegramRaw();
|
|
|
|
optoData[0].Flags = OptoTelegramFlags.OK;
|
|
optoData[0].TimestampExt = 100.0;
|
|
optoData[0].VolumeRawExt = 10.0;
|
|
|
|
optoData[1].Flags = OptoTelegramFlags.OK;
|
|
optoData[1].TimestampExt = 101.0;
|
|
optoData[1].VolumeRawExt = 11.0;
|
|
|
|
SetPrivateField(reader, "optoDataCount", 2);
|
|
SetPrivateField(reader, "dataStreamState", DataStreamState.ProcessAndSave);
|
|
|
|
reader.TestStartTelegramIx = 0;
|
|
reader.TestEndTelegramIx = 1;
|
|
|
|
reader.Stop();
|
|
|
|
Assert.IsFalse(fake.IsOpen);
|
|
Assert.AreEqual(2, fake.CloseCalls);
|
|
}
|
|
|
|
|
|
|
|
[TestMethod]
|
|
public void Run_ShouldCaptureStartTelegramIndex_WhenTimeReached()
|
|
{
|
|
var fake = new FakeSerialDriver();
|
|
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
|
|
|
reader.Initialize();
|
|
|
|
SetPrivateField(reader, "currentTelegramIx", 12);
|
|
SetPrivateField(reader, "timeFromStart", 7);
|
|
|
|
InitializeThreeChannelState(reader);
|
|
|
|
var ev = reader.Run();
|
|
|
|
Assert.AreEqual(Event.ReadRegisterDone, ev);
|
|
Assert.AreEqual(12, reader.TestStartTelegramIx);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ProcessOptoLine_ShouldInsertMultipleTelegrams_WithCorrectChannels()
|
|
{
|
|
var fake = new FakeSerialDriver();
|
|
fake.Open();
|
|
|
|
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
|
reader.Initialize();
|
|
reader.optoSerialPort = fake;
|
|
|
|
bool reset;
|
|
|
|
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset);
|
|
|
|
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset);
|
|
|
|
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset);
|
|
|
|
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
|
|
var optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
|
|
|
|
Assert.AreEqual(3, optoDataCount);
|
|
Assert.AreEqual(0, optoData[0].IChannel());
|
|
Assert.AreEqual(1, optoData[1].IChannel());
|
|
Assert.AreEqual(2, optoData[2].IChannel());
|
|
}
|
|
|
|
[DataTestMethod]
|
|
[DataRow("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", 0)]
|
|
[DataRow("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", 1)]
|
|
[DataRow("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", 2)]
|
|
public void ProcessOptoLine_ShouldUpdateExpectedChannel(string line, int expectedChannelIndex)
|
|
{
|
|
var fake = new FakeSerialDriver();
|
|
fake.Open();
|
|
|
|
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
|
reader.Initialize();
|
|
|
|
reader.optoSerialPort = fake;
|
|
InitializeThreeChannelState(reader);
|
|
|
|
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out bool resetDataBuffer);
|
|
|
|
var volumeRawExtLast = GetPrivateField<double[]>(reader, "volumeRawExtLast");
|
|
var timestampExtLast = GetPrivateField<double[]>(reader, "timestampExtLast");
|
|
|
|
for (int i = 0; i < ChannelCount; i++)
|
|
{
|
|
if (i == expectedChannelIndex)
|
|
{
|
|
Assert.IsTrue(
|
|
volumeRawExtLast[i] != 0 || timestampExtLast[i] != 0,
|
|
$"Channel {i} was expected to be updated, but it was not.");
|
|
}
|
|
else
|
|
{
|
|
Assert.AreEqual(0.0, volumeRawExtLast[i], $"Unexpected volume update in channel {i}");
|
|
Assert.AreEqual(0.0, timestampExtLast[i], $"Unexpected timestamp update in channel {i}");
|
|
}
|
|
}
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Run_WhenChannel0IsAverageModeMinus2_ShouldReadUsingAllThreeChannels()
|
|
{
|
|
var fake = new FakeSerialDriver();
|
|
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
|
|
|
reader.Initialize();
|
|
reader.Start();
|
|
|
|
SetPrivateField(reader, "channel0", -2);
|
|
SetPrivateField(reader, "volumeLtr0", new[] { 10.0, 20.0, 30.0 });
|
|
SetPrivateField(reader, "volumeLtr", new[] { 13.0, 24.0, 35.0 });
|
|
SetPrivateField(reader, "timestampSec0", new[] { 100.0, 100.0, 100.0 });
|
|
SetPrivateField(reader, "timestampSec", new[] { 110.0, 112.0, 114.0 });
|
|
|
|
var ev = reader.Run();
|
|
|
|
Assert.AreEqual(Event.ReadRegisterDone, ev);
|
|
Assert.AreEqual(4.0, reader.WMVolume, 1E-6);
|
|
Assert.AreEqual(4000, reader.WMPulses);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ProcessOptoLine_FirstValidTelegram_ShouldSetCurrentTelegramIndex()
|
|
{
|
|
var fake = new FakeSerialDriver();
|
|
fake.Open();
|
|
|
|
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
|
reader.Initialize();
|
|
|
|
reader.optoSerialPort = fake;
|
|
InitializeThreeChannelState(reader);
|
|
|
|
var line =
|
|
"@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD";
|
|
|
|
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out bool resetDataBuffer);
|
|
|
|
int currentTelegramIx = GetPrivateField<int>(reader, "currentTelegramIx");
|
|
Assert.AreEqual(0, currentTelegramIx);
|
|
}
|
|
|
|
[DataTestMethod]
|
|
[DataRow("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", 0)]
|
|
[DataRow("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", 1)]
|
|
[DataRow("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", 2)]
|
|
public void ProcessOptoLine_ShouldInsertTelegramAndUpdateExpectedChannel(string line, int expectedChannelIndex)
|
|
{
|
|
var fake = new FakeSerialDriver();
|
|
fake.Open();
|
|
|
|
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
|
reader.Initialize();
|
|
reader.optoSerialPort = fake;
|
|
|
|
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out bool resetDataBuffer);
|
|
|
|
var volumeRawExtLast = GetPrivateField<double[]>(reader, "volumeRawExtLast");
|
|
var timestampExtLast = GetPrivateField<double[]>(reader, "timestampExtLast");
|
|
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
|
|
var optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
|
|
|
|
Assert.AreEqual(1, optoDataCount, "Exactly one telegram should be inserted.");
|
|
|
|
var inserted = optoData[0];
|
|
Assert.IsNotNull(inserted, "Inserted telegram should not be null.");
|
|
|
|
Assert.AreEqual(expectedChannelIndex, inserted.IChannel(), "Inserted telegram channel does not match expected channel.");
|
|
Assert.AreEqual(0, inserted.Counter, "First inserted telegram should have counter 0.");
|
|
Assert.AreEqual(OptoTelegramFlags.OK, inserted.Flags, "Inserted telegram should be marked OK.");
|
|
Assert.AreNotEqual(default(DateTime), inserted.DateTime, "Inserted telegram DateTime should be initialized.");
|
|
|
|
Assert.AreNotEqual(0.0, inserted.VolumeRaw, "Inserted telegram VolumeRaw should be set.");
|
|
Assert.AreNotEqual(0.0, inserted.VolumeRawExt, "Inserted telegram VolumeRawExt should be set.");
|
|
Assert.AreNotEqual(0.0, inserted.Timestamp, "Inserted telegram Timestamp should be set.");
|
|
Assert.AreNotEqual(0.0, inserted.TimestampExt, "Inserted telegram TimestampExt should be set.");
|
|
|
|
for (int i = 0; i < ChannelCount; i++)
|
|
{
|
|
if (i == expectedChannelIndex)
|
|
{
|
|
Assert.AreNotEqual(0.0, volumeRawExtLast[i], $"Expected channel {i} volume cache was not updated.");
|
|
Assert.AreNotEqual(0.0, timestampExtLast[i], $"Expected channel {i} timestamp cache was not updated.");
|
|
}
|
|
else
|
|
{
|
|
Assert.AreEqual(0.0, volumeRawExtLast[i], $"Unexpected volume cache update in channel {i}.");
|
|
Assert.AreEqual(0.0, timestampExtLast[i], $"Unexpected timestamp cache update in channel {i}.");
|
|
}
|
|
}
|
|
|
|
Assert.AreEqual(volumeRawExtLast[expectedChannelIndex], inserted.VolumeRawExt, 1e-9,
|
|
"Inserted telegram VolumeRawExt should match updated channel cache.");
|
|
|
|
Assert.AreEqual(timestampExtLast[expectedChannelIndex], inserted.TimestampExt, 1e-9,
|
|
"Inserted telegram TimestampExt should match updated channel cache.");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void VolumeLtrStart_And_VolumeLtrEnd_ShouldUsePreparedCachedChannelData()
|
|
{
|
|
var fake = new FakeSerialDriver();
|
|
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
|
reader.Initialize();
|
|
|
|
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
|
|
|
|
int idx = 0;
|
|
for (int group = 0; group < 6; group++)
|
|
{
|
|
optoData[idx++] = CreateOptoRecord(0, 10 + group, 100 + group);
|
|
optoData[idx++] = CreateOptoRecord(1, 20 + group, 100 + group);
|
|
optoData[idx++] = CreateOptoRecord(2, 30 + group, 100 + group);
|
|
}
|
|
|
|
SetPrivateField(reader, "optoDataCount", 18);
|
|
reader.TestStartTelegramIx = 1;
|
|
reader.TestEndTelegramIx = 17;
|
|
|
|
InvokePrepareCalculatedChannelData(reader);
|
|
|
|
Assert.AreEqual((11.0 + 20.0 + 30.0) / 3.0, reader.VolumeLtrStart, 1e-9);
|
|
Assert.AreEqual((15.0 + 25.0 + 35.0) / 3.0, reader.VolumeLtrEnd, 1e-9);
|
|
|
|
Assert.AreEqual(100.0, reader.TimestampSecStart, 1e-9);
|
|
Assert.AreEqual(105.0, reader.TimestampSecEnd, 1e-9);
|
|
|
|
Assert.AreEqual((11.0 + 20.0 + 30.0) / 3.0, reader.VolumeLtrStartRaw, 1e-9);
|
|
Assert.AreEqual((15.0 + 25.0 + 35.0) / 3.0, reader.VolumeLtrEndRaw, 1e-9);
|
|
}
|
|
|
|
private static OptoTelegramRaw CreateOptoRecord(int channel, double volumeRawExt, double timestampExt)
|
|
{
|
|
return new OptoTelegramRaw
|
|
{
|
|
Flags = OptoTelegramFlags.OK,
|
|
VolumeRawExt = volumeRawExt,
|
|
TimestampExt = timestampExt,
|
|
iChannel = channel
|
|
};
|
|
}
|
|
|
|
[TestMethod]
|
|
public void VolumeLtrEnd_ShouldAverageOnlyAvailableChannels()
|
|
{
|
|
var fake = new FakeSerialDriver();
|
|
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
|
reader.Initialize();
|
|
|
|
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
|
|
|
|
int idx = 0;
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
optoData[idx++] = CreateOptoRecord(0, 10 + i, 100 + i);
|
|
optoData[idx++] = CreateOptoRecord(1, 20 + i, 100 + i);
|
|
}
|
|
|
|
SetPrivateField(reader, "optoDataCount", idx);
|
|
reader.TestStartTelegramIx = 1;
|
|
reader.TestEndTelegramIx = idx - 1;
|
|
|
|
InvokePrepareCalculatedChannelData(reader);
|
|
|
|
Assert.AreEqual(15.5, reader.VolumeLtrStart, 1e-9);
|
|
Assert.AreEqual(19.0, reader.VolumeLtrEnd, 1e-9);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ProcessOptoLine_Block_f_h1_h2_h3_f_ShouldResetBuffersOnlyAfterLastF()
|
|
{
|
|
var fake = new FakeSerialDriver();
|
|
fake.Open();
|
|
|
|
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
|
reader.Initialize();
|
|
reader.optoSerialPort = fake;
|
|
reader.ResetAfterBlockRepetitions = 1;
|
|
|
|
bool reset;
|
|
|
|
reader.ProcessOptoLine("@f AA754B 4D0CEE78 5D89", DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset, "Reset must not happen on the first @f.");
|
|
|
|
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
|
|
DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset, "Reset must not happen after @h 1.");
|
|
|
|
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD",
|
|
DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset, "Reset must not happen after @h 2.");
|
|
|
|
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E",
|
|
DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset, "Reset must not happen immediately after @h 3.");
|
|
|
|
reader.ProcessOptoLine("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
|
|
//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]
|
|
public void ReadOptoData_FullBlock_ShouldDiscardBuffersAfterClosingF()
|
|
{
|
|
var fake = new FakeSerialDriver();
|
|
|
|
fake.EnqueueLine("@f AA754B 4D0CEE78 5D89");
|
|
fake.EnqueueLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331");
|
|
fake.EnqueueLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD");
|
|
fake.EnqueueLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E");
|
|
fake.EnqueueLine("@f AA7C01 4D0CFE76 B08F");
|
|
|
|
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
|
reader.Initialize();
|
|
|
|
fake.Open();
|
|
reader.optoSerialPort = fake;
|
|
reader.ResetAfterBlockRepetitions = 1;
|
|
|
|
var readMethod = typeof(GenesisSmartReader).GetMethod(
|
|
"ReadOptoData",
|
|
BindingFlags.Instance | BindingFlags.NonPublic,
|
|
null,
|
|
new[] { typeof(DataStreamState) },
|
|
null);
|
|
|
|
Assert.IsNotNull(readMethod);
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
readMethod.Invoke(reader, new object[] { DataStreamState.ProcessAndSave });
|
|
}
|
|
|
|
Assert.AreEqual(1, fake.DiscardInCalls, "Input buffer should be discarded once after completed block.");
|
|
Assert.AreEqual(1, fake.DiscardOutCalls, "Output buffer should be discarded once after completed block.");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ProcessOptoLine_LastF_AfterH3_ShouldRequestBufferReset()
|
|
{
|
|
var fake = new FakeSerialDriver();
|
|
fake.Open();
|
|
|
|
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
|
reader.Initialize();
|
|
reader.optoSerialPort = fake;
|
|
reader.ResetAfterBlockRepetitions = 1;
|
|
|
|
bool reset;
|
|
|
|
reader.ProcessOptoLine("@f AA754B 4D0CEE78 5D89", DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset, "Opening @f must not reset buffers.");
|
|
|
|
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset);
|
|
|
|
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset);
|
|
|
|
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset);
|
|
|
|
reader.ProcessOptoLine("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsTrue(reset, "Closing @f after @h 3 must request reset.");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ProcessOptoLine_LastF_AfterH3_ShouldNotRequestReset_WhenRepetitionCountIsGreaterThanOne()
|
|
{
|
|
var fake = new FakeSerialDriver();
|
|
fake.Open();
|
|
|
|
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
|
reader.Initialize();
|
|
reader.optoSerialPort = fake;
|
|
reader.ResetAfterBlockRepetitions = 3;
|
|
|
|
bool reset;
|
|
|
|
reader.ProcessOptoLine("@f AA754B 4D0CEE78 5D89", DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset);
|
|
|
|
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset);
|
|
|
|
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset);
|
|
|
|
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset);
|
|
|
|
reader.ProcessOptoLine("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset, "Reset must not happen after the first completed block when repetition count is 3.");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ProcessOptoLine_ShouldResetOnlyAfterThirdCompletedBlock()
|
|
{
|
|
var fake = new FakeSerialDriver();
|
|
fake.Open();
|
|
|
|
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
|
reader.Initialize();
|
|
reader.optoSerialPort = fake;
|
|
reader.ResetAfterBlockRepetitions = 3;
|
|
|
|
bool reset;
|
|
|
|
for (int repetition = 1; repetition <= 3; repetition++)
|
|
{
|
|
reader.ProcessOptoLine("@f AA754B 4D0CEE78 5D89", DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset, "Reset must not happen on opening @f, repetition " + repetition);
|
|
|
|
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
|
|
DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset, "Reset must not happen after @h1, repetition " + repetition);
|
|
|
|
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD",
|
|
DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset, "Reset must not happen after @h2, repetition " + repetition);
|
|
|
|
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E",
|
|
DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset, "Reset must not happen after @h3, repetition " + repetition);
|
|
|
|
reader.ProcessOptoLine("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
|
|
|
|
if (repetition < 3)
|
|
Assert.IsFalse(reset, "Reset must not happen before third full block.");
|
|
else
|
|
Assert.IsTrue(reset, "Reset must happen on third full block.");
|
|
}
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ProcessOptoLine_ShouldResetOnlyAfterFifthCompletedBlock()
|
|
{
|
|
var fake = new FakeSerialDriver();
|
|
fake.Open();
|
|
|
|
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
|
reader.Initialize();
|
|
reader.optoSerialPort = fake;
|
|
reader.ResetAfterBlockRepetitions = 5;
|
|
|
|
bool reset;
|
|
|
|
for (int repetition = 1; repetition <= 5; repetition++)
|
|
{
|
|
reader.ProcessOptoLine("@f AA754B 4D0CEE78 5D89", DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset, "Reset must not happen on opening @f, repetition " + repetition);
|
|
|
|
reader.ProcessOptoLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331",
|
|
DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset, "Reset must not happen after @h1, repetition " + repetition);
|
|
|
|
reader.ProcessOptoLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD",
|
|
DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset, "Reset must not happen after @h2, repetition " + repetition);
|
|
|
|
reader.ProcessOptoLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E",
|
|
DataStreamState.ProcessAndSave, out reset);
|
|
Assert.IsFalse(reset, "Reset must not happen after @h3, repetition " + repetition);
|
|
|
|
reader.ProcessOptoLine("@f AA7C01 4D0CFE76 B08F", DataStreamState.ProcessAndSave, out reset);
|
|
|
|
if (repetition < 5)
|
|
Assert.IsFalse(reset, "Reset must not happen before fifth full block.");
|
|
else
|
|
Assert.IsTrue(reset, "Reset must happen on fifth full block.");
|
|
}
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ProcessingLoop_Should_Keep_FIFO_Order_For_Enqueued_Items()
|
|
{
|
|
var reader = new GenesisSmartReader();
|
|
|
|
var expected = Enumerable.Range(1, 200)
|
|
.Select(i => $"LINE_{i:D4}")
|
|
.ToList();
|
|
|
|
var actual = new List<string>();
|
|
var sync = new object();
|
|
var allProcessed = new CountdownEvent(expected.Count);
|
|
|
|
reader.TestProcessLineOverride = line => true;
|
|
EnableProcessingLoopForTests(reader);
|
|
|
|
reader.TestLineProcessed = line =>
|
|
{
|
|
lock (sync)
|
|
{
|
|
actual.Add(line);
|
|
}
|
|
|
|
allProcessed.Signal();
|
|
};
|
|
|
|
reader.StartProcessingLoop();
|
|
|
|
try
|
|
{
|
|
foreach (var line in expected)
|
|
{
|
|
reader.TestEnqueueLine(line);
|
|
}
|
|
|
|
bool completed = allProcessed.Wait(TimeSpan.FromSeconds(10));
|
|
Assert.IsTrue(completed, "Timed out waiting for all queued items to be processed.");
|
|
|
|
CollectionAssert.AreEqual(expected, actual,
|
|
"Dequeued/processed order does not match enqueued order.");
|
|
}
|
|
finally
|
|
{
|
|
reader.StopProcessingLoop();
|
|
allProcessed.Dispose();
|
|
}
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ProcessingLoop_Should_Keep_FIFO_Order_With_Concurrent_Producers()
|
|
{
|
|
var reader = new GenesisSmartReader();
|
|
|
|
const int producerCount = 4;
|
|
const int itemsPerProducer = 100;
|
|
int totalItems = producerCount * itemsPerProducer;
|
|
|
|
var expected = new List<string>();
|
|
var processed = new List<string>();
|
|
|
|
var expectedLock = new object();
|
|
var processedLock = new object();
|
|
int sequence = 0;
|
|
|
|
var allProcessed = new CountdownEvent(totalItems);
|
|
|
|
try
|
|
{
|
|
reader.TestProcessLineOverride = line => true;
|
|
EnableProcessingLoopForTests(reader);
|
|
|
|
reader.TestLineProcessed = line =>
|
|
{
|
|
lock (processedLock)
|
|
{
|
|
processed.Add(line);
|
|
}
|
|
|
|
allProcessed.Signal();
|
|
};
|
|
|
|
reader.StartProcessingLoop();
|
|
|
|
var tasks = new List<Task>();
|
|
|
|
for (int p = 0; p < producerCount; p++)
|
|
{
|
|
int producerId = p;
|
|
|
|
tasks.Add(Task.Run(() =>
|
|
{
|
|
for (int i = 0; i < itemsPerProducer; i++)
|
|
{
|
|
int seq = Interlocked.Increment(ref sequence);
|
|
string line = string.Format("{0:D6}|P{1}|I{2:D4}", seq, producerId, i);
|
|
|
|
lock (expectedLock)
|
|
{
|
|
expected.Add(line);
|
|
reader.TestEnqueueLine(line);
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
|
|
Task.WaitAll(tasks.ToArray());
|
|
|
|
bool completed = allProcessed.Wait(TimeSpan.FromSeconds(15));
|
|
Assert.IsTrue(completed, "Timed out waiting for all concurrent items to be processed.");
|
|
|
|
CollectionAssert.AreEqual(expected, processed,
|
|
"Processed order does not match actual enqueue order under concurrent producers.");
|
|
}
|
|
finally
|
|
{
|
|
reader.StopProcessingLoop();
|
|
allProcessed.Dispose();
|
|
}
|
|
}
|
|
|
|
|
|
[TestMethod]
|
|
public void PrepareCalculatedChannelData_SimulateMode_ReturnsExpectedFakeValues()
|
|
{
|
|
var sut = new GenesisSmartReader();
|
|
|
|
sut.PrepareCalculatedChannelDataSimulationForTest();
|
|
|
|
Assert.IsFalse(sut.NoSamples);
|
|
|
|
Assert.AreEqual(100.0, sut.VolumeLtrStartCh1, 0.0001);
|
|
Assert.AreEqual(105.0, sut.VolumeLtrStartCh2, 0.0001);
|
|
Assert.AreEqual(110.0, sut.VolumeLtrStartCh3, 0.0001);
|
|
|
|
Assert.AreEqual(150.0, sut.VolumeLtrEndCh1, 0.0001);
|
|
Assert.AreEqual(157.0, sut.VolumeLtrEndCh2, 0.0001);
|
|
Assert.AreEqual(164.0, sut.VolumeLtrEndCh3, 0.0001);
|
|
|
|
Assert.AreEqual(10.0, sut.TimestampSecStartCh1, 0.0001);
|
|
Assert.AreEqual(10.0, sut.TimestampSecStartCh2, 0.0001);
|
|
Assert.AreEqual(10.0, sut.TimestampSecStartCh3, 0.0001);
|
|
|
|
Assert.AreEqual(20.0, sut.TimestampSecEndCh1, 0.0001);
|
|
Assert.AreEqual(20.0, sut.TimestampSecEndCh2, 0.0001);
|
|
Assert.AreEqual(20.0, sut.TimestampSecEndCh3, 0.0001);
|
|
|
|
Assert.AreEqual(105.0, sut.VolumeLtrStartRaw, 0.0001);
|
|
Assert.AreEqual(157.0, sut.VolumeLtrEndRaw, 0.0001);
|
|
|
|
Assert.AreEqual(10.0, sut.TimestampSecStart, 0.0001);
|
|
Assert.AreEqual(20.0, sut.TimestampSecEnd, 0.0001);
|
|
}
|
|
|
|
|
|
[TestMethod]
|
|
public void METHOD()
|
|
{
|
|
|
|
}
|
|
}
|
|
} |