tbf/TBFTests/Rig/RegisterReaders/GenesisRegReader/implementations/GenesisSmartReaderTest.cs
Michal Buzik 3eb0b87f0d 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.
2026-04-15 09:22:09 +02:00

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()
{
}
}
}