Merge branch 'develop/SLM-PT50_genesisDirectDecode' of http://87.197.120.129:33300/michal/tbf into develop/SLM-PT50
This commit is contained in:
commit
3376ac41b4
@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
[assembly: AssemblyVersion("3.9.3013.1")]
|
||||
[assembly: AssemblyFileVersion("3.9.3013.1")]
|
||||
[assembly: AssemblyVersion("3.9.3030.1")]
|
||||
[assembly: AssemblyFileVersion("3.9.3030.1")]
|
||||
|
||||
@ -111,6 +111,7 @@ namespace TBF.Rig.BuiltIn.PumpTandem
|
||||
|
||||
public void TurnOff()
|
||||
{
|
||||
log.DebugFormat("{0}.TurnOff()", Name);
|
||||
if ((pumpFm1 != null) && (pumpFm2 != null))
|
||||
{
|
||||
pumpFm1.TurnOff();
|
||||
@ -120,6 +121,7 @@ namespace TBF.Rig.BuiltIn.PumpTandem
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
log.DebugFormat("DONE {0}.TurnOff()", Name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -332,6 +332,8 @@ namespace TBF.Rig.Danfoss.VLT2800
|
||||
Telegram.UpdateTelegramChecksum(msg);
|
||||
}
|
||||
SendData(msg);
|
||||
|
||||
log.DebugFormat("DONE {0}.TurnOff()", Name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -264,6 +264,8 @@ namespace TBF.Rig.Modbus.PumpFM.DanfossVLT
|
||||
|
||||
modbus.SendMessage(msg, Name);
|
||||
}
|
||||
|
||||
log.DebugFormat("DONE {0}.TurnOff()", Name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -416,6 +416,8 @@ namespace TBF.Rig.Modbus.PumpFM.Grundfoss
|
||||
// --- DIAGNOSTIKA ---
|
||||
LiveLogDiag.Log1("GF TurnOff msg = " + BitConverter.ToString(msg));
|
||||
}
|
||||
|
||||
log.DebugFormat("DONE {0}.TurnOff()", Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -6,7 +6,7 @@ using System;
|
||||
using System.Globalization;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.StreamingProtocol;
|
||||
|
||||
using Xylem.Common.Metrology.Measurements;
|
||||
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
|
||||
@ -25,6 +25,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
|
||||
public static readonly int Length = 42;
|
||||
private static CultureInfo culture;
|
||||
|
||||
public MeasurementRecord data;
|
||||
|
||||
///
|
||||
/// Strobed value
|
||||
@ -124,11 +125,7 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
|
||||
}
|
||||
|
||||
|
||||
// -------- TIMESTAMP (seconds) --------
|
||||
private const double TS_RANGE = StreamingDecoder.CpuTimeOverflowS;
|
||||
|
||||
// -------- VOLUME (liters) --------
|
||||
private const double VOL_RANGE = StreamingDecoder.DefaultAccuDutOverflowVolumeCm * 1000;
|
||||
|
||||
/// <summary>
|
||||
/// update data by CalibrationRecord
|
||||
@ -149,7 +146,8 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
|
||||
if (data == null)
|
||||
throw new ArgumentNullException(nameof(data));
|
||||
|
||||
UpdateData( data.Channel, data.VolumeCm, data.TimeS, counter, refFlow, ref volumeRawExtLast, ref timestampExtLast);
|
||||
this.data = data;
|
||||
UpdateData( data.Channel, data.VolumeCm, data.OverflowVolumeCm, data.TimeS, data.OverflowTimeS, counter, refFlow, ref volumeRawExtLast, ref timestampExtLast);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -170,8 +168,8 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
|
||||
{
|
||||
if (data == null)
|
||||
throw new ArgumentNullException(nameof(data));
|
||||
|
||||
UpdateData( data.Channel, data.VolumeCm, data.TimeS, counter, refFlow, ref volumeRawExtLast, ref timestampExtLast);
|
||||
this.data = data;
|
||||
UpdateData( data.Channel, data.VolumeCm, data.OverflowVolumeCm, data.TimeS, data.OverflowTimeS, counter, refFlow, ref volumeRawExtLast, ref timestampExtLast);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -187,7 +185,9 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
|
||||
private void UpdateData(
|
||||
int channel,
|
||||
double volumeCm,
|
||||
double OverflowVolumeCm,
|
||||
double timeS,
|
||||
double OverflowTimeS,
|
||||
int counter,
|
||||
float refFlow,
|
||||
ref double volumeRawExtLast,
|
||||
@ -200,29 +200,30 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
|
||||
Flags = OptoTelegramFlags.OK;
|
||||
|
||||
FlowRaw = 0;
|
||||
VolumeRaw = volumeCm * 1000.0; // liters
|
||||
double nV = NormalizeByOverflow(volumeCm, OverflowVolumeCm);
|
||||
VolumeRaw = nV * 1000.0; // liters
|
||||
CheckSum = 0;
|
||||
Impedance = 0;
|
||||
EmfRaw = 0;
|
||||
MagneticFieldRaw = 0;
|
||||
|
||||
double ts = NormalizeTimestamp(timeS);
|
||||
double ts = NormalizeByOverflow(timeS, OverflowTimeS);
|
||||
Timestamp = ts;
|
||||
|
||||
VolumeRawExt = UnwrapVolume(VolumeRaw, ref volumeRawExtLast);
|
||||
TimestampExt = UnwrapTimestamp(ts, ref timestampExtLast);
|
||||
VolumeRawExt = UnwrapVolume(VolumeRaw,OverflowVolumeCm * 1000, ref volumeRawExtLast);
|
||||
TimestampExt = UnwrapTimestamp(ts,OverflowTimeS, ref timestampExtLast);
|
||||
}
|
||||
|
||||
private static double NormalizeTimestamp(double timeS)
|
||||
private static double NormalizeByOverflow(double value, double overfValue = 0)
|
||||
{
|
||||
double ts = timeS % TS_RANGE;
|
||||
if (ts < 0)
|
||||
ts += TS_RANGE;
|
||||
double nValue = value % overfValue;
|
||||
if (nValue < 0)
|
||||
nValue += overfValue;
|
||||
|
||||
return ts;
|
||||
return nValue;
|
||||
}
|
||||
|
||||
private static double UnwrapVolume(double currentVolume, ref double volumeRawExtLast)
|
||||
private static double UnwrapVolume(double currentVolume, double overfValue, ref double volumeRawExtLast)
|
||||
{
|
||||
double result;
|
||||
|
||||
@ -232,16 +233,25 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
|
||||
}
|
||||
else
|
||||
{
|
||||
result = currentVolume < volumeRawExtLast
|
||||
? currentVolume + VOL_RANGE
|
||||
: currentVolume;
|
||||
double lastMod = volumeRawExtLast % overfValue;
|
||||
if (lastMod < 0)
|
||||
lastMod += overfValue;
|
||||
|
||||
double delta = currentVolume - lastMod;
|
||||
|
||||
if (delta < -overfValue / 2.0)
|
||||
delta += overfValue;
|
||||
else if (delta > overfValue / 2.0)
|
||||
delta -= overfValue;
|
||||
|
||||
result = volumeRawExtLast + delta;
|
||||
}
|
||||
|
||||
volumeRawExtLast = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
private static double UnwrapTimestamp(double currentTimestamp, ref double timestampExtLast)
|
||||
private static double UnwrapTimestamp(double currentTimestamp,double overfValue, ref double timestampExtLast)
|
||||
{
|
||||
double result;
|
||||
|
||||
@ -251,16 +261,16 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
|
||||
}
|
||||
else
|
||||
{
|
||||
double lastMod = timestampExtLast % TS_RANGE;
|
||||
double lastMod = timestampExtLast % overfValue;
|
||||
if (lastMod < 0)
|
||||
lastMod += TS_RANGE;
|
||||
lastMod += overfValue;
|
||||
|
||||
double delta = currentTimestamp - lastMod;
|
||||
|
||||
if (delta < -TS_RANGE / 2.0)
|
||||
delta += TS_RANGE;
|
||||
else if (delta > TS_RANGE / 2.0)
|
||||
delta -= TS_RANGE;
|
||||
if (delta < -overfValue / 2.0)
|
||||
delta += overfValue;
|
||||
else if (delta > overfValue / 2.0)
|
||||
delta -= overfValue;
|
||||
|
||||
result = timestampExtLast + delta;
|
||||
}
|
||||
@ -349,5 +359,33 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
|
||||
Label());
|
||||
}
|
||||
}
|
||||
|
||||
public void Copy(OptoTelegramRaw optoTelegramRaw)
|
||||
{
|
||||
this.Flags = optoTelegramRaw.Flags;
|
||||
this.DateTime = optoTelegramRaw.DateTime;
|
||||
this.RefFlow = optoTelegramRaw.RefFlow;
|
||||
this.Counter = optoTelegramRaw.Counter;
|
||||
this.EmfRaw = optoTelegramRaw.EmfRaw;
|
||||
this.MagneticFieldRaw = optoTelegramRaw.MagneticFieldRaw;
|
||||
this.FlowRaw = optoTelegramRaw.FlowRaw;
|
||||
this.VolumeRaw = optoTelegramRaw.VolumeRaw;
|
||||
this.VolumeRawExt = optoTelegramRaw.VolumeRawExt;
|
||||
this.Impedance = optoTelegramRaw.Impedance;
|
||||
this.Timestamp = optoTelegramRaw.Timestamp;
|
||||
this.TimestampExt = optoTelegramRaw.TimestampExt;
|
||||
this.CheckSum = optoTelegramRaw.CheckSum;
|
||||
this.iChannel = optoTelegramRaw.iChannel;
|
||||
}
|
||||
|
||||
public string rawDataToString()
|
||||
{
|
||||
if (data != null)
|
||||
{
|
||||
return string.Format("Channel: {0}, VolumeCm:{1}, OverflowVolumeCm:{2}, TimeS: {3}, OverflowTimeS:{4} ",
|
||||
data.Channel, data.VolumeCm, data.OverflowVolumeCm, data.TimeS, data.OverflowTimeS);
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -227,6 +227,20 @@ namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Utils
|
||||
return _serialPort.ReadExisting();
|
||||
}
|
||||
|
||||
public void ResetInputBuffer()
|
||||
{
|
||||
if (!IsOpen)
|
||||
throw new InvalidOperationException("Serial port not open");
|
||||
_serialPort.DiscardInBuffer();
|
||||
}
|
||||
|
||||
public void ResetOutputBuffer()
|
||||
{
|
||||
if (!IsOpen)
|
||||
throw new InvalidOperationException("Serial port not open");
|
||||
_serialPort.DiscardOutBuffer();
|
||||
}
|
||||
|
||||
public Encoding Encoding => _serialPort?.Encoding ?? _encoding;
|
||||
|
||||
public int BytesToRead => (_serialPort != null && _serialPort.IsOpen) ? _serialPort.BytesToRead : 0;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -678,7 +678,8 @@ namespace TBF.Rig.Sequences
|
||||
/// On error or when STOP pressed
|
||||
///
|
||||
foreach (var fmPump in PumpsWithFM) fmPump.TurnOff();
|
||||
|
||||
log.Debug("STOP or ERROR: Pumps with FM stopped!");
|
||||
|
||||
if (inPath != null)
|
||||
{
|
||||
///
|
||||
|
||||
@ -170,8 +170,8 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
|
||||
string testName = Results.Utils.GetTestName(test.Name, test.Repeats, repetitionNr);
|
||||
TestStartTime = DateTime.Now;
|
||||
|
||||
bool atleastOneGenesis = false;
|
||||
atleastOneGenesis = GenesisHeadBatch.Start(sensPath.RegisterReaders, Program.LocalSettings.LastSNTexts);
|
||||
// bool atleastOneGenesis = false;
|
||||
// atleastOneGenesis = GenesisHeadBatch.Start(sensPath.RegisterReaders, Program.LocalSettings.LastSNTexts);
|
||||
|
||||
//------------------------------------------------
|
||||
Bridge.OnActivity(this, Strings.Checking_tank_capacity);
|
||||
@ -452,23 +452,23 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
|
||||
}
|
||||
}
|
||||
|
||||
if (atleastOneGenesis)
|
||||
{
|
||||
log.Info($"Genesis - Starting... Test Name:{test.Name.ToLower()}.");
|
||||
if (test.Name.ToLower().Contains("calib"))
|
||||
{
|
||||
log.Info("Genesis - Calibration starting.");
|
||||
GenesisHeadBatch.BatchHolder.Value.MetersLogin();
|
||||
GenesisHeadBatch.BatchHolder.Value.MetersInitCalibration();
|
||||
|
||||
}
|
||||
if (test.Name.ToLower().Contains("init"))
|
||||
{
|
||||
log.Info("Genesis - init starting.");
|
||||
GenesisHeadBatch.BatchHolder.Value.MetersLogin();
|
||||
GenesisHeadBatch.BatchHolder.Value.MetersInitMeasurement();
|
||||
}
|
||||
}
|
||||
// if (atleastOneGenesis)
|
||||
// {
|
||||
// log.Info($"Genesis - Starting... Test Name:{test.Name.ToLower()}.");
|
||||
// if (test.Name.ToLower().Contains("calib"))
|
||||
// {
|
||||
// log.Info("Genesis - Calibration starting.");
|
||||
// GenesisHeadBatch.BatchHolder.Value.MetersLogin();
|
||||
// GenesisHeadBatch.BatchHolder.Value.MetersInitCalibration();
|
||||
//
|
||||
// }
|
||||
// if (test.Name.ToLower().Contains("init"))
|
||||
// {
|
||||
// log.Info("Genesis - init starting.");
|
||||
// GenesisHeadBatch.BatchHolder.Value.MetersLogin();
|
||||
// GenesisHeadBatch.BatchHolder.Value.MetersInitMeasurement();
|
||||
// }
|
||||
// }
|
||||
|
||||
if (drainTheTank)
|
||||
{
|
||||
@ -610,19 +610,32 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection
|
||||
/// Measurement loop end
|
||||
StopRecordingStatistics();
|
||||
|
||||
if (atleastOneGenesis)
|
||||
{
|
||||
if (test.Name.ToLower().Contains("calib"))
|
||||
{
|
||||
log.Info("Genesis - Calibration stopped.");
|
||||
GenesisHeadBatch.BatchHolder.Value.MetersStopCalibration();
|
||||
}
|
||||
else
|
||||
{
|
||||
log.Info("Genesis - Init measurement stopped.");
|
||||
GenesisHeadBatch.BatchHolder.Value.MetersStopMeasurement();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (sensPath != null && sensPath.RegisterReaders != null)
|
||||
{
|
||||
foreach (var rr in sensPath.RegisterReaders)
|
||||
{
|
||||
var datastreamRR = rr as ISmartReader;
|
||||
if (datastreamRR != null)
|
||||
{
|
||||
datastreamRR.StopDataStreamProcessing();
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (atleastOneGenesis)
|
||||
// {
|
||||
// if (test.Name.ToLower().Contains("calib"))
|
||||
// {
|
||||
// log.Info("Genesis - Calibration stopped.");
|
||||
// GenesisHeadBatch.BatchHolder.Value.MetersStopCalibration();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// log.Info("Genesis - Init measurement stopped.");
|
||||
// GenesisHeadBatch.BatchHolder.Value.MetersStopMeasurement();
|
||||
// }
|
||||
// }
|
||||
|
||||
///
|
||||
/// (Berlin:) Water is stopped immediately after the test and before the 2nd mass measurement in case:
|
||||
|
||||
@ -12,13 +12,17 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.common
|
||||
private static CalibrationRecord CreateCalibrationRecord(
|
||||
int channel = 1,
|
||||
double volumeCm = 1.234,
|
||||
double timeS = 12.5)
|
||||
double overflowVolumeCm = 8.38860799804687,
|
||||
double timeS = 12.5,
|
||||
double overflowTimeS = 65536.0)
|
||||
{
|
||||
return new CalibrationRecord
|
||||
{
|
||||
Channel = channel,
|
||||
VolumeCm = volumeCm,
|
||||
TimeS = timeS
|
||||
OverflowVolumeCm = overflowVolumeCm,
|
||||
TimeS = timeS,
|
||||
OverflowTimeS = overflowTimeS
|
||||
};
|
||||
}
|
||||
|
||||
@ -26,7 +30,12 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.common
|
||||
public void UpdateFromSmart_FirstSample_ShouldInitializeFields()
|
||||
{
|
||||
var telegram = new OptoTelegramRaw();
|
||||
var data = CreateCalibrationRecord(channel: 2, volumeCm: 2.5, timeS: 100.0);
|
||||
var data = CreateCalibrationRecord(
|
||||
channel: 2,
|
||||
volumeCm: 2.5,
|
||||
overflowVolumeCm: 8.38860799804687,
|
||||
timeS: 100.0,
|
||||
overflowTimeS: 65536.0);
|
||||
|
||||
double lastVolume = double.NaN;
|
||||
double lastTimestamp = double.NaN;
|
||||
@ -54,32 +63,479 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.common
|
||||
Assert.AreEqual(0, telegram.MagneticFieldRaw);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void UpdateFromSmart_RealGenesisDecodedSample_ShouldMapExpectedValues()
|
||||
{
|
||||
var telegram = new OptoTelegramRaw();
|
||||
|
||||
var data = CreateCalibrationRecord(
|
||||
channel: 2,
|
||||
volumeCm: 0.48025591796875,
|
||||
overflowVolumeCm: 8.38860799804687,
|
||||
timeS: 62727.8408813477,
|
||||
overflowTimeS: 65536.0);
|
||||
|
||||
double lastVolume = double.NaN;
|
||||
double lastTimestamp = double.NaN;
|
||||
|
||||
telegram.UpdateFromSmart(data, counter: 1, refFlow: 0.0f, ref lastVolume, ref lastTimestamp);
|
||||
|
||||
Assert.AreEqual(1, telegram.IChannel());
|
||||
Assert.AreEqual(1, telegram.Counter);
|
||||
Assert.AreEqual(0.0f, telegram.RefFlow);
|
||||
Assert.AreEqual(OptoTelegramFlags.OK, telegram.Flags);
|
||||
|
||||
Assert.AreEqual(480.25591796875, telegram.VolumeRaw, 1e-9);
|
||||
Assert.AreEqual(480.25591796875, telegram.VolumeRawExt, 1e-9);
|
||||
Assert.AreEqual(480.25591796875, lastVolume, 1e-9);
|
||||
|
||||
Assert.AreEqual(62727.8408813477, telegram.Timestamp, 1e-9);
|
||||
Assert.AreEqual(62727.8408813477, telegram.TimestampExt, 1e-9);
|
||||
Assert.AreEqual(62727.8408813477, lastTimestamp, 1e-9);
|
||||
|
||||
Assert.AreEqual(0, telegram.FlowRaw);
|
||||
Assert.AreEqual(0, telegram.CheckSum);
|
||||
Assert.AreEqual(0, telegram.Impedance);
|
||||
Assert.AreEqual(0, telegram.EmfRaw);
|
||||
Assert.AreEqual(0, telegram.MagneticFieldRaw);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void UpdateFromSmart_WhenVolumeDecreases_ShouldApplyVolumeRollover()
|
||||
{
|
||||
var telegram = new OptoTelegramRaw();
|
||||
var data = CreateCalibrationRecord(channel: 1, volumeCm: 1.0, timeS: 10.0);
|
||||
var overflowVolumeCm = 8.38860799804687;
|
||||
var overflowRaw = overflowVolumeCm * 1000.0;
|
||||
|
||||
double previousExtendedVolume = 5000.0;
|
||||
var data = CreateCalibrationRecord(
|
||||
channel: 1,
|
||||
volumeCm: 1.0,
|
||||
overflowVolumeCm: overflowVolumeCm,
|
||||
timeS: 10.0,
|
||||
overflowTimeS: 65536.0);
|
||||
|
||||
// Must be close enough to overflow so that current=1000 is interpreted as a wrap
|
||||
double previousExtendedVolume = 8000.0;
|
||||
double previousExtendedTimestamp = 10.0;
|
||||
|
||||
telegram.UpdateFromSmart(data, counter: 1, refFlow: 0.5f, ref previousExtendedVolume, ref previousExtendedTimestamp);
|
||||
telegram.UpdateFromSmart(
|
||||
data,
|
||||
counter: 1,
|
||||
refFlow: 0.5f,
|
||||
ref previousExtendedVolume,
|
||||
ref previousExtendedTimestamp);
|
||||
|
||||
double expectedVolumeRaw = 1000.0;
|
||||
double expectedExtended = expectedVolumeRaw + StreamingDecoder.DefaultAccuDutOverflowVolumeCm * 1000.0;
|
||||
double expectedExtended = expectedVolumeRaw + overflowRaw;
|
||||
|
||||
Assert.AreEqual(expectedVolumeRaw, telegram.VolumeRaw, 1e-9);
|
||||
Assert.AreEqual(expectedExtended, telegram.VolumeRawExt, 1e-6);
|
||||
Assert.AreEqual(expectedExtended, previousExtendedVolume, 1e-6);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void UpdateFromSmart_MultipleVolumeRollover_ShouldKeepIncreasingExtendedVolume()
|
||||
{
|
||||
var telegram = new OptoTelegramRaw();
|
||||
double overflowVolumeCm = 8.38860799804687;
|
||||
|
||||
double lastVolumeExt = double.NaN;
|
||||
double lastTimestampExt = double.NaN;
|
||||
|
||||
// Safe sequence: small forward steps, natural wrap, small forward steps
|
||||
double[] samples =
|
||||
{
|
||||
7.5,
|
||||
8.0,
|
||||
0.2,
|
||||
1.0,
|
||||
1.8,
|
||||
2.6,
|
||||
3.4,
|
||||
4.2,
|
||||
5.0,
|
||||
5.8,
|
||||
6.6,
|
||||
7.4,
|
||||
8.1,
|
||||
0.3,
|
||||
1.1
|
||||
};
|
||||
|
||||
double previousExt = double.NaN;
|
||||
|
||||
for (int i = 0; i < samples.Length; i++)
|
||||
{
|
||||
var data = CreateCalibrationRecord(
|
||||
channel: 1,
|
||||
volumeCm: samples[i],
|
||||
overflowVolumeCm: overflowVolumeCm,
|
||||
timeS: i,
|
||||
overflowTimeS: 65536.0);
|
||||
|
||||
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
|
||||
|
||||
double expectedRaw = samples[i] * 1000.0;
|
||||
Assert.AreEqual(expectedRaw, telegram.VolumeRaw, 1e-9, $"Raw mismatch at index {i}");
|
||||
|
||||
if (!double.IsNaN(previousExt))
|
||||
{
|
||||
Assert.IsTrue(
|
||||
telegram.VolumeRawExt >= previousExt,
|
||||
$"Volume should not decrease at index {i}. Prev={previousExt}, Current={telegram.VolumeRawExt}");
|
||||
}
|
||||
|
||||
previousExt = telegram.VolumeRawExt;
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void UpdateFromSmart_MultipleVolumeRollover_FirstToLastDeltaShouldBePositive()
|
||||
{
|
||||
var telegram = new OptoTelegramRaw();
|
||||
double overflowVolumeCm = 8.38860799804687;
|
||||
|
||||
double lastVolumeExt = double.NaN;
|
||||
double lastTimestampExt = double.NaN;
|
||||
|
||||
double[] samples =
|
||||
{
|
||||
7.5, 8.0, 0.2, 1.0, 1.8, 2.6, 3.4, 4.2, 5.0, 5.8, 6.6, 7.4, 8.1, 0.3, 1.1
|
||||
};
|
||||
|
||||
double firstExt = double.NaN;
|
||||
|
||||
for (int i = 0; i < samples.Length; i++)
|
||||
{
|
||||
var data = CreateCalibrationRecord(
|
||||
channel: 1,
|
||||
volumeCm: samples[i],
|
||||
overflowVolumeCm: overflowVolumeCm,
|
||||
timeS: i,
|
||||
overflowTimeS: 65536.0);
|
||||
|
||||
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
|
||||
|
||||
if (i == 0)
|
||||
firstExt = telegram.VolumeRawExt;
|
||||
}
|
||||
|
||||
Assert.IsTrue(lastVolumeExt > firstExt,
|
||||
$"Volume should increase from first to last. First={firstExt}, Last={lastVolumeExt}");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void UpdateFromSmart_MultipleVolumeRollover_ShouldMatchPhysicalDelta()
|
||||
{
|
||||
var telegram = new OptoTelegramRaw();
|
||||
double overflowVolumeCm = 8.38860799804687;
|
||||
|
||||
double lastVolumeExt = double.NaN;
|
||||
double lastTimestampExt = double.NaN;
|
||||
|
||||
double[] samples =
|
||||
{
|
||||
7.5,
|
||||
8.0,
|
||||
0.2,
|
||||
1.0,
|
||||
1.8,
|
||||
2.6,
|
||||
3.4,
|
||||
4.2,
|
||||
5.0,
|
||||
5.8,
|
||||
6.6,
|
||||
7.4,
|
||||
8.1,
|
||||
0.3,
|
||||
1.1
|
||||
};
|
||||
|
||||
double firstExt = double.NaN;
|
||||
double lastExt = double.NaN;
|
||||
|
||||
for (int i = 0; i < samples.Length; i++)
|
||||
{
|
||||
var data = CreateCalibrationRecord(
|
||||
channel: 1,
|
||||
volumeCm: samples[i],
|
||||
overflowVolumeCm: overflowVolumeCm,
|
||||
timeS: i,
|
||||
overflowTimeS: 65536.0);
|
||||
|
||||
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
|
||||
|
||||
if (i == 0)
|
||||
firstExt = telegram.VolumeRawExt;
|
||||
|
||||
if (i == samples.Length - 1)
|
||||
lastExt = telegram.VolumeRawExt;
|
||||
}
|
||||
|
||||
double start = samples[0];
|
||||
double end = samples[samples.Length - 1];
|
||||
|
||||
int wraps = 0;
|
||||
for (int i = 1; i < samples.Length; i++)
|
||||
{
|
||||
if (samples[i] < samples[i - 1])
|
||||
wraps++;
|
||||
}
|
||||
|
||||
double expectedDeltaCm = wraps * overflowVolumeCm + end - start;
|
||||
double expectedDeltaRaw = expectedDeltaCm * 1000.0;
|
||||
double actualDeltaRaw = lastExt - firstExt;
|
||||
|
||||
Assert.AreEqual(
|
||||
expectedDeltaRaw,
|
||||
actualDeltaRaw,
|
||||
1e-6,
|
||||
$"Delta mismatch. Wraps={wraps}, Expected={expectedDeltaRaw}, Actual={actualDeltaRaw}");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void UpdateFromSmart_MultipleTimestampWrap_ShouldMatchPhysicalDelta()
|
||||
{
|
||||
var telegram = new OptoTelegramRaw();
|
||||
|
||||
double overflowTimeS = 65536.0;
|
||||
|
||||
double lastVolumeExt = double.NaN;
|
||||
double lastTimestampExt = double.NaN;
|
||||
|
||||
// Safe sequence: small forward steps, natural wrap, small forward steps, second wrap
|
||||
double[] times =
|
||||
{
|
||||
65530.0,
|
||||
65535.0,
|
||||
2.0, // wrap 1
|
||||
10.0,
|
||||
100.0,
|
||||
1000.0,
|
||||
10000.0,
|
||||
30000.0,
|
||||
50000.0,
|
||||
65534.0,
|
||||
3.0, // wrap 2
|
||||
20.0
|
||||
};
|
||||
|
||||
double firstExt = double.NaN;
|
||||
double lastExt = double.NaN;
|
||||
|
||||
for (int i = 0; i < times.Length; i++)
|
||||
{
|
||||
var data = CreateCalibrationRecord(
|
||||
channel: 1,
|
||||
volumeCm: 1.0,
|
||||
overflowVolumeCm: 8.38860799804687,
|
||||
timeS: times[i],
|
||||
overflowTimeS: overflowTimeS);
|
||||
|
||||
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
|
||||
|
||||
if (i == 0)
|
||||
firstExt = telegram.TimestampExt;
|
||||
|
||||
if (i == times.Length - 1)
|
||||
lastExt = telegram.TimestampExt;
|
||||
}
|
||||
|
||||
double start = times[0];
|
||||
double end = times[times.Length - 1];
|
||||
|
||||
int wraps = 0;
|
||||
for (int i = 1; i < times.Length; i++)
|
||||
{
|
||||
if (times[i] < times[i - 1])
|
||||
wraps++;
|
||||
}
|
||||
|
||||
double expectedDelta = wraps * overflowTimeS + end - start;
|
||||
double actualDelta = lastExt - firstExt;
|
||||
|
||||
Assert.AreEqual(
|
||||
expectedDelta,
|
||||
actualDelta,
|
||||
1e-6,
|
||||
$"Timestamp delta mismatch. Wraps={wraps}, Expected={expectedDelta}, Actual={actualDelta}");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void UpdateFromSmart_MultipleTimestampWrap_ShouldKeepIncreasingExtendedTimestamp()
|
||||
{
|
||||
var telegram = new OptoTelegramRaw();
|
||||
|
||||
double overflowTimeS = 65536.0;
|
||||
|
||||
double lastVolumeExt = double.NaN;
|
||||
double lastTimestampExt = double.NaN;
|
||||
|
||||
double[] times =
|
||||
{
|
||||
65530.0,
|
||||
65535.0,
|
||||
2.0,
|
||||
10.0,
|
||||
100.0,
|
||||
1000.0,
|
||||
10000.0,
|
||||
30000.0,
|
||||
50000.0,
|
||||
65534.0,
|
||||
3.0,
|
||||
20.0
|
||||
};
|
||||
|
||||
double previousExt = double.NaN;
|
||||
|
||||
for (int i = 0; i < times.Length; i++)
|
||||
{
|
||||
var data = CreateCalibrationRecord(
|
||||
channel: 1,
|
||||
volumeCm: 1.0,
|
||||
overflowVolumeCm: 8.38860799804687,
|
||||
timeS: times[i],
|
||||
overflowTimeS: overflowTimeS);
|
||||
|
||||
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
|
||||
|
||||
if (!double.IsNaN(previousExt))
|
||||
{
|
||||
Assert.IsTrue(
|
||||
telegram.TimestampExt >= previousExt,
|
||||
$"Timestamp should not decrease at index {i}. Prev={previousExt}, Current={telegram.TimestampExt}");
|
||||
}
|
||||
|
||||
previousExt = telegram.TimestampExt;
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void UpdateFromSmart_MultipleVolumeRollover_ShouldProduceExpectedExtendedValues()
|
||||
{
|
||||
var telegram = new OptoTelegramRaw();
|
||||
double overflowVolumeCm = 8.38860799804687;
|
||||
double overflowRaw = overflowVolumeCm * 1000.0;
|
||||
|
||||
double lastVolumeExt = double.NaN;
|
||||
double lastTimestampExt = double.NaN;
|
||||
|
||||
double[] samples =
|
||||
{
|
||||
7.5,
|
||||
8.0,
|
||||
0.2,
|
||||
0.8,
|
||||
1.4
|
||||
};
|
||||
|
||||
double[] expectedExt =
|
||||
{
|
||||
7500.0,
|
||||
8000.0,
|
||||
200.0 + overflowRaw,
|
||||
800.0 + overflowRaw,
|
||||
1400.0 + overflowRaw
|
||||
};
|
||||
|
||||
for (int i = 0; i < samples.Length; i++)
|
||||
{
|
||||
var data = CreateCalibrationRecord(
|
||||
channel: 1,
|
||||
volumeCm: samples[i],
|
||||
overflowVolumeCm: overflowVolumeCm,
|
||||
timeS: i,
|
||||
overflowTimeS: 65536.0);
|
||||
|
||||
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
|
||||
|
||||
Assert.AreEqual(samples[i] * 1000.0, telegram.VolumeRaw, 1e-9, $"Raw mismatch at index {i}");
|
||||
Assert.AreEqual(expectedExt[i], telegram.VolumeRawExt, 1e-6, $"Mismatch at index {i}");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void UpdateFromSmart_TwoVolumeRollovers_ShouldKeepExtendedVolumeIncreasing()
|
||||
{
|
||||
var telegram = new OptoTelegramRaw();
|
||||
double overflowVolumeCm = 8.38860799804687;
|
||||
double overflowRaw = overflowVolumeCm * 1000.0;
|
||||
|
||||
double lastVolumeExt = double.NaN;
|
||||
double lastTimestampExt = double.NaN;
|
||||
|
||||
double[] samples =
|
||||
{
|
||||
6.8,
|
||||
8.1,
|
||||
0.3,
|
||||
1.2,
|
||||
2.1,
|
||||
3.0,
|
||||
4.0,
|
||||
7.5,
|
||||
8.1,
|
||||
0.4,
|
||||
1.1
|
||||
};
|
||||
|
||||
double[] expectedExt =
|
||||
{
|
||||
6800.0,
|
||||
8100.0,
|
||||
overflowRaw + 300.0,
|
||||
overflowRaw + 1200.0,
|
||||
overflowRaw + 2100.0,
|
||||
overflowRaw + 3000.0,
|
||||
overflowRaw + 4000.0,
|
||||
overflowRaw + 7500.0,
|
||||
overflowRaw + 8100.0,
|
||||
2.0 * overflowRaw + 400.0,
|
||||
2.0 * overflowRaw + 1100.0
|
||||
};
|
||||
|
||||
double previous = double.NaN;
|
||||
|
||||
for (int i = 0; i < samples.Length; i++)
|
||||
{
|
||||
var data = CreateCalibrationRecord(
|
||||
channel: 1,
|
||||
volumeCm: samples[i],
|
||||
overflowVolumeCm: overflowVolumeCm,
|
||||
timeS: i,
|
||||
overflowTimeS: 65536.0);
|
||||
|
||||
telegram.UpdateFromSmart(data, i, 0, ref lastVolumeExt, ref lastTimestampExt);
|
||||
|
||||
Assert.AreEqual(samples[i] * 1000.0, telegram.VolumeRaw, 1e-9, $"Raw mismatch at index {i}");
|
||||
Assert.AreEqual(expectedExt[i], telegram.VolumeRawExt, 1e-6, $"Mismatch at index {i}");
|
||||
|
||||
if (!double.IsNaN(previous))
|
||||
{
|
||||
Assert.IsTrue(
|
||||
telegram.VolumeRawExt > previous,
|
||||
$"Extended volume did not increase at index {i}. Prev={previous}, Current={telegram.VolumeRawExt}");
|
||||
}
|
||||
|
||||
previous = telegram.VolumeRawExt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void UpdateFromSmart_WhenTimestampWraps_ShouldUnwrapForward()
|
||||
{
|
||||
var telegram = new OptoTelegramRaw();
|
||||
double tsRange = StreamingDecoder.CpuTimeOverflowS;
|
||||
|
||||
var data = CreateCalibrationRecord(channel: 1, volumeCm: 1.0, timeS: 1.0);
|
||||
var data = CreateCalibrationRecord(
|
||||
channel: 1,
|
||||
volumeCm: 1.0,
|
||||
overflowVolumeCm: 8.38860799804687,
|
||||
timeS: 1.0,
|
||||
overflowTimeS: tsRange);
|
||||
|
||||
double previousExtendedVolume = 1000.0;
|
||||
double previousExtendedTimestamp = tsRange - 0.25;
|
||||
|
||||
@ -16,10 +16,11 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
public int CloseCalls { get; private set; }
|
||||
public int DiscardInCalls { get; private set; }
|
||||
public int DiscardOutCalls { get; private set; }
|
||||
|
||||
|
||||
public Encoding Encoding { get; set; } = Encoding.ASCII;
|
||||
|
||||
public int BytesToRead => _lines.Count > 0 ? 1 : 0;
|
||||
public int BytesToRead => _isOpen && _lines.Count > 0 ? 1 : 0;
|
||||
public int BytesToWrite => 0;
|
||||
|
||||
public void EnqueueLine(string line) => _lines.Enqueue(line);
|
||||
|
||||
@ -0,0 +1,220 @@
|
||||
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()
|
||||
{
|
||||
var reader = new GenesisSmartReader();
|
||||
InitializeReaderForTest(reader);
|
||||
|
||||
string[] realInputLines = LoadRealInputLines();
|
||||
Assert.IsTrue(realInputLines.Length > 0, "No test input lines were provided.");
|
||||
|
||||
int processedCount = 0;
|
||||
int firstValidIx = -1;
|
||||
int lastValidIx = -1;
|
||||
|
||||
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
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RealInput_ShouldSupport_RolloverNormalization()
|
||||
{
|
||||
var reader = new GenesisSmartReader();
|
||||
InitializeReaderForTest(reader);
|
||||
|
||||
string[] realInputLines = LoadRealInputLinesWithRollover();
|
||||
Assert.IsTrue(realInputLines.Length > 0, "No rollover input lines were provided.");
|
||||
|
||||
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.IsTrue(reader.TimestampSecEnd >= reader.TimestampSecStart,
|
||||
"Normalized end time should be >= start time");
|
||||
Assert.IsTrue(reader.VolumeLtrEnd >= reader.VolumeLtrStart,
|
||||
"Normalized end volume should be >= start volume");
|
||||
}
|
||||
|
||||
private static void InitializeReaderForTest(GenesisSmartReader reader)
|
||||
{
|
||||
const int channelCount = 3;
|
||||
|
||||
SetPrivateField(reader, "volumeRawExtLast", new double[channelCount]);
|
||||
SetPrivateField(reader, "timestampExtLast", new double[channelCount]);
|
||||
|
||||
SetPrivateField(reader, "lastTimestamp", new double[channelCount]);
|
||||
SetPrivateField(reader, "timestampSec", Enumerable.Repeat(double.NaN, channelCount).ToArray());
|
||||
SetPrivateField(reader, "timestampSec0", Enumerable.Repeat(double.NaN, channelCount).ToArray());
|
||||
|
||||
SetPrivateField(reader, "lastVolumeRaw", new double[channelCount]);
|
||||
SetPrivateField(reader, "volumeLtr", Enumerable.Repeat(double.NaN, channelCount).ToArray());
|
||||
SetPrivateField(reader, "volumeLtr0", Enumerable.Repeat(double.NaN, channelCount).ToArray());
|
||||
|
||||
var optoData = new OptoTelegramRaw[GenesisSmartReader.OptoDataBufferSize];
|
||||
for (int i = 0; i < optoData.Length; i++)
|
||||
optoData[i] = new OptoTelegramRaw();
|
||||
|
||||
SetPrivateField(reader, "optoData", optoData);
|
||||
SetPrivateField(reader, "optoDataCount", 0);
|
||||
SetPrivateField(reader, "toBeFlushed", new OptoTelegramRaw());
|
||||
|
||||
SetPrivateField(reader, "flowDirectionDetection", new FlowDirectionDetection());
|
||||
SetPrivateField(reader, "dataStreamState", DataStreamState.ProcessAndSave);
|
||||
SetPrivateField(reader, "synchronized", false);
|
||||
SetPrivateField(reader, "synchronized2", false);
|
||||
SetPrivateField(reader, "partOfTelegram", string.Empty);
|
||||
SetPrivateField(reader, "startDataProcessing", true);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Method '{methodName}' expects {parameters.Length} parameters, but {args.Length} were provided.");
|
||||
}
|
||||
|
||||
var invokeArgs = new object[args.Length];
|
||||
Array.Copy(args, invokeArgs, args.Length);
|
||||
|
||||
return method.Invoke(target, invokeArgs);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,7 +2,6 @@ using System;
|
||||
using System.Reflection;
|
||||
using Common;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TBF.Rig.Generic;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
|
||||
@ -14,6 +13,12 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
{
|
||||
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);
|
||||
@ -32,13 +37,25 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
return (T)field.GetValue(target);
|
||||
}
|
||||
|
||||
private static void InvokePrivateMethod(object target, string methodName)
|
||||
private static void SetPrivateStaticField(Type type, string fieldName, object value)
|
||||
{
|
||||
var method = target.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if (method == null)
|
||||
throw new MissingMethodException(target.GetType().FullName, methodName);
|
||||
var field = type.GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic);
|
||||
if (field == null)
|
||||
throw new MissingFieldException(type.FullName, fieldName);
|
||||
|
||||
method.Invoke(target, null);
|
||||
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)
|
||||
@ -53,8 +70,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
DateTime = DateTime.Now
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static GenesisCfg CreateCfg()
|
||||
{
|
||||
var cfg = new GenesisCfg(null);
|
||||
@ -75,21 +91,27 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SimulatedInterleaved20x3ChannelStream_ShouldComputeStartAndEndVolumeAndTimeCorrectly()
|
||||
{
|
||||
var fake = new FakeSerialDriver();
|
||||
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
||||
|
||||
SetPrivateField(reader, "volumeRawExtLast", new double[3]);
|
||||
SetPrivateField(reader, "timestampExtLast", new double[3]);
|
||||
SetPrivateField(reader, "flowDirectionDetection", new FlowDirectionDetection());
|
||||
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++)
|
||||
@ -108,66 +130,82 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
reader.TestStartTelegramIx = 2;
|
||||
reader.TestEndTelegramIx = index - 1;
|
||||
|
||||
InvokePrepareCalculatedChannelData(reader);
|
||||
|
||||
Assert.AreEqual(200.0, reader.VolumeLtrStart, 1e-9);
|
||||
Assert.AreEqual(217.0, reader.VolumeLtrEnd, 1e-9);
|
||||
Assert.AreEqual(219.0, reader.VolumeLtrEnd, 1e-9);
|
||||
|
||||
Assert.AreEqual(1.0, reader.TimestampSecStart, 1e-9);
|
||||
Assert.AreEqual(18.0, reader.TimestampSecEnd, 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_ShouldIgnoreInvalidTrailingRecords_AndUsePreviousValidPerChannelSamples()
|
||||
public void VolumeLtrEnd_ShouldRecalculateShorterChannelToLongestChannelTimeSpan()
|
||||
{
|
||||
var reader = new GenesisSmartReader(CreateCfg(), null);
|
||||
|
||||
SetPrivateField(reader, "optoData", new OptoTelegramRaw[GenesisSmartReader.OptoDataBufferSize]);
|
||||
|
||||
var reader = CreateReaderWithOptoBuffer();
|
||||
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
|
||||
for (int i = 0; i < optoData.Length; i++)
|
||||
optoData[i] = new OptoTelegramRaw();
|
||||
|
||||
int index = 0;
|
||||
|
||||
// Build 10 groups of valid 3-channel data
|
||||
for (int g = 0; g < 10; g++)
|
||||
{
|
||||
optoData[index++] = CreateOptoRecord(0, 100 + g, 1 + g, index);
|
||||
optoData[index++] = CreateOptoRecord(1, 200 + g, 1 + g, index);
|
||||
optoData[index++] = CreateOptoRecord(2, 300 + g, 1 + g, index);
|
||||
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++;
|
||||
}
|
||||
|
||||
// Corrupt the last two channel-1 records
|
||||
optoData[25].Flags = OptoTelegramFlags.InvalidTelegram; // group 8, channel 1
|
||||
optoData[28].Flags = OptoTelegramFlags.InvalidTelegram; // group 9, channel 1
|
||||
// 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;
|
||||
|
||||
// Start still comes from first full valid 3-channel snapshot:
|
||||
// (100 + 200 + 300) / 3 = 200
|
||||
Assert.AreEqual(200.0, reader.VolumeLtrStart, 1e-9);
|
||||
InvokePrepareCalculatedChannelData(reader);
|
||||
|
||||
// End with last 5 valid per channel:
|
||||
// ch0 valid last 5: 105,106,107,108,109 => avg 107
|
||||
// ch1 valid last 5: 203,204,205,206,207 => avg 205
|
||||
// because 208 and 209 were invalid
|
||||
// ch2 valid last 5: 305,306,307,308,309 => avg 307
|
||||
// total avg = (107 + 205 + 307) / 3 = 206.3333333333...
|
||||
Assert.AreEqual(206.33333333333334, reader.VolumeLtrEnd, 1e-9);
|
||||
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 = new GenesisSmartReader(CreateCfg(), null);
|
||||
|
||||
SetPrivateField(reader, "optoData", new OptoTelegramRaw[GenesisSmartReader.OptoDataBufferSize]);
|
||||
|
||||
var reader = CreateReaderWithOptoBuffer();
|
||||
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
|
||||
for (int i = 0; i < optoData.Length; i++)
|
||||
optoData[i] = new OptoTelegramRaw();
|
||||
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
@ -184,8 +222,97 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,507 @@
|
||||
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 GenesisSmartReaderChannelTests
|
||||
{
|
||||
private GenesisSmartReader _reader;
|
||||
|
||||
[TestInitialize]
|
||||
public void Setup()
|
||||
{
|
||||
_reader = new GenesisSmartReader();
|
||||
}
|
||||
|
||||
private static OptoTelegramRaw Rec(
|
||||
int channel,
|
||||
double time,
|
||||
double volume,
|
||||
OptoTelegramFlags flags = OptoTelegramFlags.OK)
|
||||
{
|
||||
return new OptoTelegramRaw
|
||||
{
|
||||
iChannel = channel,
|
||||
TimestampExt = time,
|
||||
VolumeRawExt = volume,
|
||||
Flags = flags
|
||||
};
|
||||
}
|
||||
|
||||
private object InvokePrivate(string methodName, params object[] args)
|
||||
{
|
||||
var mi = typeof(GenesisSmartReader).GetMethod(
|
||||
methodName,
|
||||
BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
Assert.IsNotNull(mi, $"Method '{methodName}' was not found.");
|
||||
|
||||
return mi.Invoke(_reader, args);
|
||||
}
|
||||
|
||||
private static T GetPrivateField<T>(object instance, string fieldName)
|
||||
{
|
||||
var fi = instance.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
Assert.IsNotNull(fi, $"Field '{fieldName}' was not found.");
|
||||
return (T)fi.GetValue(instance);
|
||||
}
|
||||
|
||||
private static void SetPrivateField(object instance, string fieldName, object value)
|
||||
{
|
||||
var fi = instance.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
Assert.IsNotNull(fi, $"Field '{fieldName}' was not found.");
|
||||
fi.SetValue(instance, value);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GroupRecordsPerChannel_Groups_Valid_Records_By_Channel()
|
||||
{
|
||||
var input = new[]
|
||||
{
|
||||
Rec(0, 10, 100),
|
||||
Rec(1, 11, 200),
|
||||
Rec(2, 12, 300),
|
||||
Rec(0, 13, 110),
|
||||
Rec(1, 14, 220),
|
||||
Rec(2, 15, 330),
|
||||
};
|
||||
|
||||
var grouped = (OptoTelegramRaw[][])InvokePrivate(
|
||||
"GroupRecordsPerChannel",
|
||||
(object)input, 0, input.Length - 1, input.Length);
|
||||
|
||||
Assert.AreEqual(3, grouped.Length);
|
||||
|
||||
Assert.AreEqual(2, grouped[0].Length);
|
||||
Assert.AreEqual(2, grouped[1].Length);
|
||||
Assert.AreEqual(2, grouped[2].Length);
|
||||
|
||||
Assert.AreEqual(100, grouped[0][0].VolumeRawExt);
|
||||
Assert.AreEqual(110, grouped[0][1].VolumeRawExt);
|
||||
|
||||
Assert.AreEqual(200, grouped[1][0].VolumeRawExt);
|
||||
Assert.AreEqual(220, grouped[1][1].VolumeRawExt);
|
||||
|
||||
Assert.AreEqual(300, grouped[2][0].VolumeRawExt);
|
||||
Assert.AreEqual(330, grouped[2][1].VolumeRawExt);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GroupRecordsPerChannel_Ignores_Invalid_Records()
|
||||
{
|
||||
var input = new[]
|
||||
{
|
||||
Rec(0, 10, 100, OptoTelegramFlags.OK),
|
||||
Rec(1, 11, 200, OptoTelegramFlags.InvalidTelegram),
|
||||
Rec(2, 12, 300, OptoTelegramFlags.OK),
|
||||
Rec(0, 13, 110, OptoTelegramFlags.SyncError),
|
||||
Rec(1, 14, 220, OptoTelegramFlags.OK),
|
||||
};
|
||||
|
||||
var grouped = (OptoTelegramRaw[][])InvokePrivate(
|
||||
"GroupRecordsPerChannel",
|
||||
(object)input, 0, input.Length - 1, input.Length);
|
||||
|
||||
Assert.AreEqual(1, grouped[0].Length);
|
||||
Assert.AreEqual(1, grouped[1].Length);
|
||||
Assert.AreEqual(1, grouped[2].Length);
|
||||
|
||||
Assert.AreEqual(100, grouped[0][0].VolumeRawExt);
|
||||
Assert.AreEqual(220, grouped[1][0].VolumeRawExt);
|
||||
Assert.AreEqual(300, grouped[2][0].VolumeRawExt);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GroupRecordsPerChannel_Swaps_Start_And_End_When_Reversed()
|
||||
{
|
||||
var input = new[]
|
||||
{
|
||||
Rec(0, 10, 100),
|
||||
Rec(1, 11, 200),
|
||||
Rec(0, 12, 120),
|
||||
};
|
||||
|
||||
var grouped = (OptoTelegramRaw[][])InvokePrivate(
|
||||
"GroupRecordsPerChannel",
|
||||
(object)input, 2, 0, input.Length);
|
||||
|
||||
Assert.AreEqual(2, grouped[0].Length);
|
||||
Assert.AreEqual(1, grouped[1].Length);
|
||||
Assert.AreEqual(0, grouped[2].Length);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GroupRecordsPerChannel_Returns_Empty_For_Invalid_Range()
|
||||
{
|
||||
var input = new[]
|
||||
{
|
||||
Rec(0, 10, 100),
|
||||
Rec(1, 11, 200),
|
||||
};
|
||||
|
||||
var grouped = (OptoTelegramRaw[][])InvokePrivate(
|
||||
"GroupRecordsPerChannel",
|
||||
(object)input, -1, 1, input.Length);
|
||||
|
||||
Assert.AreEqual(3, grouped.Length);
|
||||
Assert.IsTrue(grouped.All(x => x.Length == 0));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TimeDeltaPerChannel_Computes_First_Last_Deltas()
|
||||
{
|
||||
var grouped = new[]
|
||||
{
|
||||
new[]
|
||||
{
|
||||
Rec(0, 10, 100),
|
||||
Rec(0, 18, 170),
|
||||
Rec(0, 15, 120)
|
||||
},
|
||||
new[]
|
||||
{
|
||||
Rec(1, 20, 500),
|
||||
Rec(1, 23, 530)
|
||||
},
|
||||
new[]
|
||||
{
|
||||
Rec(2, 30, 900)
|
||||
}
|
||||
};
|
||||
|
||||
var result = (ValueTuple<int, double?, double?>[])InvokePrivate(
|
||||
"TimeDeltaPerChannel",
|
||||
(object)grouped);
|
||||
|
||||
Assert.AreEqual(0, result[0].Item1);
|
||||
Assert.AreEqual(5, result[0].Item2);
|
||||
Assert.AreEqual(20, result[0].Item3);
|
||||
|
||||
Assert.AreEqual(1, result[1].Item1);
|
||||
Assert.AreEqual(3, result[1].Item2);
|
||||
Assert.AreEqual(30, result[1].Item3);
|
||||
|
||||
Assert.AreEqual(2, result[2].Item1);
|
||||
Assert.IsNull(result[2].Item2);
|
||||
Assert.IsNull(result[2].Item3);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void VolumeStartPerChannel_Returns_Last_Seen_Value_Backward_From_Index()
|
||||
{
|
||||
var input = new[]
|
||||
{
|
||||
Rec(0, 10, 100),
|
||||
Rec(1, 11, 200),
|
||||
Rec(0, 12, 120),
|
||||
Rec(2, 13, 300),
|
||||
Rec(1, 14, 220),
|
||||
};
|
||||
|
||||
var result = (double?[])InvokePrivate(
|
||||
"VolumeStartPerChannel",
|
||||
(object)input, input.Length, 4);
|
||||
|
||||
Assert.AreEqual(120, result[0]);
|
||||
Assert.AreEqual(220, result[1]);
|
||||
Assert.AreEqual(300, result[2]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TimeStartPerChannel_Returns_Last_Seen_Time_Backward_From_Index()
|
||||
{
|
||||
var input = new[]
|
||||
{
|
||||
Rec(0, 10, 100),
|
||||
Rec(1, 11, 200),
|
||||
Rec(0, 12, 120),
|
||||
Rec(2, 13, 300),
|
||||
Rec(1, 14, 220),
|
||||
};
|
||||
|
||||
var result = (double?[])InvokePrivate(
|
||||
"TimeStartPerChannel",
|
||||
(object)input, input.Length, 4);
|
||||
|
||||
Assert.AreEqual(12, result[0]);
|
||||
Assert.AreEqual(14, result[1]);
|
||||
Assert.AreEqual(13, result[2]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void VolumeEndPerChannel_Averages_Last_N_Samples_Per_Channel()
|
||||
{
|
||||
var input = new[]
|
||||
{
|
||||
Rec(0, 10, 100),
|
||||
Rec(1, 11, 200),
|
||||
Rec(2, 12, 300),
|
||||
Rec(0, 13, 120),
|
||||
Rec(1, 14, 220),
|
||||
Rec(2, 15, 330),
|
||||
Rec(0, 16, 140),
|
||||
Rec(1, 17, 240),
|
||||
Rec(2, 18, 360),
|
||||
};
|
||||
|
||||
var result = (double?[])InvokePrivate(
|
||||
"VolumeEndPerChannel",
|
||||
(object)input, input.Length, 8, 2);
|
||||
|
||||
Assert.AreEqual((140 + 120) / 2.0, result[0]);
|
||||
Assert.AreEqual((240 + 220) / 2.0, result[1]);
|
||||
Assert.AreEqual((360 + 330) / 2.0, result[2]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TimeEndPerChannel_Averages_Last_N_Samples_Per_Channel()
|
||||
{
|
||||
var input = new[]
|
||||
{
|
||||
Rec(0, 10, 100),
|
||||
Rec(1, 11, 200),
|
||||
Rec(2, 12, 300),
|
||||
Rec(0, 13, 120),
|
||||
Rec(1, 14, 220),
|
||||
Rec(2, 15, 330),
|
||||
Rec(0, 16, 140),
|
||||
Rec(1, 17, 240),
|
||||
Rec(2, 18, 360),
|
||||
};
|
||||
|
||||
var result = (double?[])InvokePrivate(
|
||||
"TimeEndPerChannel",
|
||||
(object)input, input.Length, 8, 2);
|
||||
|
||||
Assert.AreEqual((16 + 13) / 2.0, result[0]);
|
||||
Assert.AreEqual((17 + 14) / 2.0, result[1]);
|
||||
Assert.AreEqual((18 + 15) / 2.0, result[2]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void VolumeStartPerChannel_Returns_All_Nulls_For_OutOfRange_Index()
|
||||
{
|
||||
var input = new[]
|
||||
{
|
||||
Rec(0, 10, 100),
|
||||
Rec(1, 11, 200),
|
||||
Rec(2, 12, 300),
|
||||
};
|
||||
|
||||
var result = (double?[])InvokePrivate(
|
||||
"VolumeStartPerChannel",
|
||||
(object)input, input.Length, -1);
|
||||
|
||||
Assert.IsNull(result[0]);
|
||||
Assert.IsNull(result[1]);
|
||||
Assert.IsNull(result[2]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TimeEndPerChannel_Uses_One_Sample_When_SamplesPerChannel_Is_Zero()
|
||||
{
|
||||
var input = new[]
|
||||
{
|
||||
Rec(0, 10, 100),
|
||||
Rec(1, 11, 200),
|
||||
Rec(2, 12, 300),
|
||||
Rec(0, 13, 120),
|
||||
Rec(1, 14, 220),
|
||||
Rec(2, 15, 330),
|
||||
};
|
||||
|
||||
var result = (double?[])InvokePrivate(
|
||||
"TimeEndPerChannel",
|
||||
(object)input, input.Length, 5, 0);
|
||||
|
||||
Assert.AreEqual(13, result[0]);
|
||||
Assert.AreEqual(14, result[1]);
|
||||
Assert.AreEqual(15, result[2]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Precreated_OptoData_Private_Methods_Read_Correct_Values()
|
||||
{
|
||||
var input = new[]
|
||||
{
|
||||
Rec(0, 10, 100),
|
||||
Rec(1, 11, 200),
|
||||
Rec(2, 12, 300),
|
||||
Rec(0, 13, 120),
|
||||
Rec(1, 14, 220),
|
||||
Rec(2, 15, 330),
|
||||
Rec(0, 16, 140),
|
||||
Rec(1, 17, 240),
|
||||
Rec(2, 18, 360),
|
||||
};
|
||||
|
||||
SetPrivateField(_reader, "optoData", input);
|
||||
SetPrivateField(_reader, "optoDataCount", input.Length);
|
||||
|
||||
_reader.TestStartTelegramIx = 5;
|
||||
_reader.TestEndTelegramIx = 8;
|
||||
|
||||
Assert.AreEqual(input.Length, GetPrivateField<int>(_reader, "optoDataCount"));
|
||||
|
||||
var startVolumes = (double?[])InvokePrivate(
|
||||
"VolumeStartPerChannel",
|
||||
(object)input, input.Length, 5);
|
||||
|
||||
var endVolumes = (double?[])InvokePrivate(
|
||||
"VolumeEndPerChannel",
|
||||
(object)input, input.Length, 8, 2 * GenesisSmartReader.StartEndFilterSamplesCount2 + 1);
|
||||
|
||||
var startTimes = (double?[])InvokePrivate(
|
||||
"TimeStartPerChannel",
|
||||
(object)input, input.Length, 5);
|
||||
|
||||
var endTimes = (double?[])InvokePrivate(
|
||||
"TimeEndPerChannel",
|
||||
(object)input, input.Length, 8, 2 * GenesisSmartReader.StartEndFilterSamplesCount2 + 1);
|
||||
|
||||
Assert.AreEqual(120, startVolumes[0]);
|
||||
Assert.AreEqual(220, startVolumes[1]);
|
||||
Assert.AreEqual(330, startVolumes[2]);
|
||||
|
||||
Assert.AreEqual(13, startTimes[0]);
|
||||
Assert.AreEqual(14, startTimes[1]);
|
||||
Assert.AreEqual(15, startTimes[2]);
|
||||
|
||||
Assert.AreEqual((140 + 120 + 100) / 3.0, endVolumes[0]);
|
||||
Assert.AreEqual((240 + 220 + 200) / 3.0, endVolumes[1]);
|
||||
Assert.AreEqual((360 + 330 + 300) / 3.0, endVolumes[2]);
|
||||
|
||||
Assert.AreEqual((16 + 13 + 10) / 3.0, endTimes[0]);
|
||||
Assert.AreEqual((17 + 14 + 11) / 3.0, endTimes[1]);
|
||||
Assert.AreEqual((18 + 15 + 12) / 3.0, endTimes[2]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Public_Channel_Properties_Default_To_Zero_When_NoSamples_Is_True()
|
||||
{
|
||||
var input = new[]
|
||||
{
|
||||
Rec(0, 10, 100),
|
||||
Rec(1, 11, 200),
|
||||
Rec(2, 12, 300),
|
||||
};
|
||||
|
||||
SetPrivateField(_reader, "optoData", input);
|
||||
SetPrivateField(_reader, "optoDataCount", input.Length);
|
||||
|
||||
_reader.TestStartTelegramIx = 5;
|
||||
_reader.TestEndTelegramIx = 8;
|
||||
|
||||
Assert.IsTrue(_reader.NoSamples);
|
||||
|
||||
Assert.AreEqual(0, _reader.VolumeLtrStartCh1);
|
||||
Assert.AreEqual(0, _reader.VolumeLtrStartCh2);
|
||||
Assert.AreEqual(0, _reader.VolumeLtrStartCh3);
|
||||
|
||||
Assert.AreEqual(0, _reader.VolumeLtrEndCh1);
|
||||
Assert.AreEqual(0, _reader.VolumeLtrEndCh2);
|
||||
Assert.AreEqual(0, _reader.VolumeLtrEndCh3);
|
||||
|
||||
Assert.AreEqual(0, _reader.TimestampSecStartCh1);
|
||||
Assert.AreEqual(0, _reader.TimestampSecStartCh2);
|
||||
Assert.AreEqual(0, _reader.TimestampSecStartCh3);
|
||||
|
||||
Assert.AreEqual(0, _reader.TimestampSecEndCh1);
|
||||
Assert.AreEqual(0, _reader.TimestampSecEndCh2);
|
||||
Assert.AreEqual(0, _reader.TimestampSecEndCh3);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Copy_Should_Copy_From_Source_Record()
|
||||
{
|
||||
var source = Rec(2, 123.5, 456.7, OptoTelegramFlags.OK_TestEnd);
|
||||
source.Counter = 77;
|
||||
source.RefFlow = 9.5f;
|
||||
source.FlowRaw = 111;
|
||||
source.VolumeRaw = 222;
|
||||
source.Impedance = 333;
|
||||
source.CheckSum = 44;
|
||||
|
||||
var target = new OptoTelegramRaw();
|
||||
target.Copy(source);
|
||||
|
||||
Assert.AreEqual(source.iChannel, target.iChannel);
|
||||
Assert.AreEqual(source.TimestampExt, target.TimestampExt);
|
||||
Assert.AreEqual(source.VolumeRawExt, target.VolumeRawExt);
|
||||
Assert.AreEqual(source.Flags, target.Flags);
|
||||
Assert.AreEqual(source.Counter, target.Counter);
|
||||
Assert.AreEqual(source.RefFlow, target.RefFlow);
|
||||
Assert.AreEqual(source.FlowRaw, target.FlowRaw);
|
||||
Assert.AreEqual(source.VolumeRaw, target.VolumeRaw);
|
||||
Assert.AreEqual(source.Impedance, target.Impedance);
|
||||
Assert.AreEqual(source.CheckSum, target.CheckSum);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RecalculateVolumeAndTimeDeltaPerChannel_Returns_Recalculated_Output()
|
||||
{
|
||||
var input = new[]
|
||||
{
|
||||
Rec(0, 10, 100),
|
||||
Rec(0, 20, 200),
|
||||
|
||||
Rec(1, 10, 500),
|
||||
Rec(1, 30, 560),
|
||||
|
||||
Rec(2, 10, 1000),
|
||||
Rec(2, 15, 1050),
|
||||
};
|
||||
|
||||
var result = (OptoTelegramRaw[][])InvokePrivate(
|
||||
"RecalculateVolumeAndTimeDeltaPerChannel",
|
||||
(object)input,
|
||||
input.Length);
|
||||
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual(3, result.Length);
|
||||
|
||||
for (int ch = 0; ch < 3; ch++)
|
||||
{
|
||||
Assert.IsNotNull(result[ch]);
|
||||
Assert.AreEqual(2, result[ch].Length);
|
||||
Assert.IsNotNull(result[ch][0]);
|
||||
Assert.IsNotNull(result[ch][1]);
|
||||
|
||||
Assert.AreEqual(10, result[ch][0].TimestampExt, 0.0001);
|
||||
Assert.AreEqual(30, result[ch][1].TimestampExt, 0.0001);
|
||||
}
|
||||
|
||||
Assert.AreEqual(100, result[0][0].VolumeRawExt, 0.0001);
|
||||
Assert.AreEqual(300, result[0][1].VolumeRawExt, 0.0001);
|
||||
|
||||
Assert.AreEqual(500, result[1][0].VolumeRawExt, 0.0001);
|
||||
Assert.AreEqual(560, result[1][1].VolumeRawExt, 0.0001);
|
||||
|
||||
Assert.AreEqual(1000, result[2][0].VolumeRawExt, 0.0001);
|
||||
Assert.AreEqual(1200, result[2][1].VolumeRawExt, 0.0001);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RecalculateVolumeAndTimeDeltaPerChannel_Returns_Null_When_No_Channel_Has_Two_Records()
|
||||
{
|
||||
var input = new[]
|
||||
{
|
||||
Rec(0, 10, 100),
|
||||
Rec(1, 20, 200),
|
||||
Rec(2, 30, 300),
|
||||
};
|
||||
|
||||
var result = (OptoTelegramRaw[][])InvokePrivate(
|
||||
"RecalculateVolumeAndTimeDeltaPerChannel",
|
||||
(object)input,
|
||||
input.Length);
|
||||
|
||||
Assert.IsNull(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -39,6 +39,18 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
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]);
|
||||
@ -65,7 +77,8 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
|
||||
Assert.IsTrue(fake.IsOpen);
|
||||
Assert.IsTrue(fake.OpenCalls >= 1);
|
||||
Assert.AreEqual(1, fake.DiscardInCalls);
|
||||
Assert.AreEqual(2, fake.DiscardInCalls);
|
||||
Assert.AreEqual(2, fake.DiscardOutCalls);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@ -105,36 +118,39 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ProcessOptoLine_ShouldIncreaseOptoDataCount()
|
||||
public void ProcessOptoLine_ShouldInsertMultipleTelegrams_WithCorrectChannels()
|
||||
{
|
||||
var fake = new FakeSerialDriver();
|
||||
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
||||
|
||||
reader.Initialize();
|
||||
InitializeThreeChannelState(reader);
|
||||
|
||||
var line =
|
||||
"@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331";
|
||||
|
||||
reader.optoSerialPort = fake;
|
||||
fake.Open();
|
||||
|
||||
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave);
|
||||
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
||||
reader.Initialize();
|
||||
reader.optoSerialPort = fake;
|
||||
|
||||
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
|
||||
Assert.AreEqual(1, optoDataCount);
|
||||
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)]
|
||||
[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();
|
||||
@ -146,7 +162,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
reader.optoSerialPort = fake;
|
||||
InitializeThreeChannelState(reader);
|
||||
|
||||
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave);
|
||||
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out bool resetDataBuffer);
|
||||
|
||||
var volumeRawExtLast = GetPrivateField<double[]>(reader, "volumeRawExtLast");
|
||||
var timestampExtLast = GetPrivateField<double[]>(reader, "timestampExtLast");
|
||||
@ -185,7 +201,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
var ev = reader.Run();
|
||||
|
||||
Assert.AreEqual(Event.ReadRegisterDone, ev);
|
||||
Assert.AreEqual(4.0, reader.WMVolume, 1E-6); // average of (3, 4, 5)
|
||||
Assert.AreEqual(4.0, reader.WMVolume, 1E-6);
|
||||
Assert.AreEqual(4000, reader.WMPulses);
|
||||
}
|
||||
|
||||
@ -204,16 +220,16 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
var line =
|
||||
"@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD";
|
||||
|
||||
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave);
|
||||
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)]
|
||||
[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();
|
||||
@ -223,7 +239,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
reader.Initialize();
|
||||
reader.optoSerialPort = fake;
|
||||
|
||||
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave);
|
||||
reader.ProcessOptoLine(line, DataStreamState.ProcessAndSave, out bool resetDataBuffer);
|
||||
|
||||
var volumeRawExtLast = GetPrivateField<double[]>(reader, "volumeRawExtLast");
|
||||
var timestampExtLast = GetPrivateField<double[]>(reader, "timestampExtLast");
|
||||
@ -267,30 +283,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ProcessOptoLine_ShouldInsertMultipleTelegrams_WithCorrectChannels()
|
||||
{
|
||||
var fake = new FakeSerialDriver();
|
||||
fake.Open();
|
||||
|
||||
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
||||
reader.Initialize();
|
||||
reader.optoSerialPort = fake;
|
||||
|
||||
reader.ProcessOptoLine( "@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331", DataStreamState.ProcessAndSave);
|
||||
reader.ProcessOptoLine( "@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD", DataStreamState.ProcessAndSave);
|
||||
reader.ProcessOptoLine( "@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E", DataStreamState.ProcessAndSave);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void VolumeLtrStart_And_VolumeLtrEnd_ShouldUsePerChannelAverages()
|
||||
public void VolumeLtrStart_And_VolumeLtrEnd_ShouldUsePreparedCachedChannelData()
|
||||
{
|
||||
var fake = new FakeSerialDriver();
|
||||
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
||||
@ -298,14 +291,6 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
|
||||
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
|
||||
|
||||
// Build 18 records: 6 groups of channels 0,1,2
|
||||
// Start group average = (10 + 20 + 30)/3 = 20
|
||||
// Last 5 per channel:
|
||||
// ch0: 11,12,13,14,15 => avg 13
|
||||
// ch1: 21,22,23,24,25 => avg 23
|
||||
// ch2: 31,32,33,34,35 => avg 33
|
||||
// final average = (13 + 23 + 33)/3 = 23
|
||||
|
||||
int idx = 0;
|
||||
for (int group = 0; group < 6; group++)
|
||||
{
|
||||
@ -315,11 +300,19 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
}
|
||||
|
||||
SetPrivateField(reader, "optoDataCount", 18);
|
||||
reader.TestStartTelegramIx = 2; // first full 3-channel group
|
||||
reader.TestEndTelegramIx = 17; // last record
|
||||
reader.TestStartTelegramIx = 1;
|
||||
reader.TestEndTelegramIx = 17;
|
||||
|
||||
Assert.AreEqual(20.0, reader.VolumeLtrStart, 1e-9);
|
||||
Assert.AreEqual(23.0, reader.VolumeLtrEnd, 1e-9);
|
||||
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)
|
||||
@ -332,7 +325,7 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
iChannel = channel
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void VolumeLtrEnd_ShouldAverageOnlyAvailableChannels()
|
||||
{
|
||||
@ -344,7 +337,6 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
|
||||
int idx = 0;
|
||||
|
||||
// only channels 0 and 1
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
optoData[idx++] = CreateOptoRecord(0, 10 + i, 100 + i);
|
||||
@ -355,8 +347,214 @@ namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
|
||||
reader.TestStartTelegramIx = 1;
|
||||
reader.TestEndTelegramIx = idx - 1;
|
||||
|
||||
// ch0 avg = 12, ch1 avg = 22 => total avg = 17
|
||||
Assert.AreEqual(17.0, reader.VolumeLtrEnd, 1e-9);
|
||||
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);
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,220 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
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 GenesisSmartReaderThreadedReadTests
|
||||
{
|
||||
private const int ChannelCount = 3;
|
||||
|
||||
private static GenesisCfg CreateCfg()
|
||||
{
|
||||
var cfg = new GenesisCfg(null);
|
||||
cfg.OptoComPortNr = 7;
|
||||
cfg.RfidComPortNr = 8;
|
||||
return cfg;
|
||||
}
|
||||
|
||||
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 bool WaitUntil(Func<bool> condition, int timeoutMs = 1500, int pollMs = 20)
|
||||
{
|
||||
var start = Environment.TickCount;
|
||||
|
||||
while (Environment.TickCount - start < timeoutMs)
|
||||
{
|
||||
if (condition())
|
||||
return true;
|
||||
|
||||
Thread.Sleep(pollMs);
|
||||
}
|
||||
|
||||
return condition();
|
||||
}
|
||||
|
||||
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 });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Start_ShouldOpenPort_AndStartBackgroundReadLoop()
|
||||
{
|
||||
var fake = new FakeSerialDriver();
|
||||
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
||||
|
||||
reader.Initialize();
|
||||
reader.Start();
|
||||
|
||||
Assert.IsTrue(fake.IsOpen);
|
||||
Assert.IsTrue(fake.OpenCalls >= 1);
|
||||
|
||||
var readLoopTask = GetPrivateField<System.Threading.Tasks.Task>(reader, "_readLoopTask");
|
||||
Assert.IsNotNull(readLoopTask);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BackgroundReadLoop_ShouldMoveIncomingLinesToQueue()
|
||||
{
|
||||
var fake = new FakeSerialDriver();
|
||||
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
||||
|
||||
reader.Initialize();
|
||||
InitializeThreeChannelState(reader);
|
||||
reader.Start();
|
||||
|
||||
fake.EnqueueLine("@h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331");
|
||||
|
||||
bool queued = WaitUntil(() =>
|
||||
{
|
||||
var queue = GetPrivateField<System.Collections.Concurrent.ConcurrentQueue<string>>(reader, "_receivedLines");
|
||||
return !queue.IsEmpty;
|
||||
});
|
||||
|
||||
Assert.IsTrue(queued, "Expected background task to enqueue received serial line.");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RunDeviceBefore_ShouldDrainQueue_AndInsertTelegram()
|
||||
{
|
||||
var fake = new FakeSerialDriver();
|
||||
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
||||
|
||||
reader.Initialize();
|
||||
InitializeThreeChannelState(reader);
|
||||
reader.Start();
|
||||
|
||||
fake.EnqueueLine("@h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD");
|
||||
|
||||
bool queued = WaitUntil(() =>
|
||||
{
|
||||
var queue = GetPrivateField<System.Collections.Concurrent.ConcurrentQueue<string>>(reader, "_receivedLines");
|
||||
return !queue.IsEmpty;
|
||||
});
|
||||
|
||||
Assert.IsTrue(queued, "Expected line to be queued before processing.");
|
||||
|
||||
reader.RunDeviceBefore();
|
||||
|
||||
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
|
||||
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
|
||||
|
||||
Assert.AreEqual(1, optoDataCount);
|
||||
Assert.AreEqual(1, optoData[0].IChannel());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RunDeviceBefore_ShouldDrainAllQueuedLines()
|
||||
{
|
||||
var fake = new FakeSerialDriver();
|
||||
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
||||
|
||||
reader.Initialize();
|
||||
InitializeThreeChannelState(reader);
|
||||
reader.Start();
|
||||
|
||||
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");
|
||||
|
||||
bool queued = WaitUntil(() =>
|
||||
{
|
||||
var queue = GetPrivateField<System.Collections.Concurrent.ConcurrentQueue<string>>(reader, "_receivedLines");
|
||||
return !queue.IsEmpty;
|
||||
});
|
||||
|
||||
Assert.IsTrue(queued);
|
||||
|
||||
reader.RunDeviceBefore();
|
||||
|
||||
int optoDataCount = GetPrivateField<int>(reader, "optoDataCount");
|
||||
var queueAfter = GetPrivateField<System.Collections.Concurrent.ConcurrentQueue<string>>(reader, "_receivedLines");
|
||||
|
||||
Assert.AreEqual(3, optoDataCount);
|
||||
Assert.IsTrue(queueAfter.IsEmpty, "Queue should be empty after RunDeviceBefore drains it.");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void StopDataStreamProcessing_ShouldStopReadLoop_AndClosePort()
|
||||
{
|
||||
var fake = new FakeSerialDriver();
|
||||
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
||||
|
||||
reader.Initialize();
|
||||
reader.Start();
|
||||
|
||||
Assert.IsTrue(fake.IsOpen);
|
||||
|
||||
reader.StopDataStreamProcessing();
|
||||
|
||||
Assert.IsFalse(fake.IsOpen);
|
||||
|
||||
var cts = GetPrivateField<System.Threading.CancellationTokenSource>(reader, "_readLoopCts");
|
||||
var task = GetPrivateField<System.Threading.Tasks.Task>(reader, "_readLoopTask");
|
||||
|
||||
Assert.IsNull(cts);
|
||||
Assert.IsNull(task);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IncomingLines_ShouldNotBeProcessedUntilRunDeviceBeforeIsCalled()
|
||||
{
|
||||
var fake = new FakeSerialDriver();
|
||||
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
|
||||
|
||||
reader.Initialize();
|
||||
InitializeThreeChannelState(reader);
|
||||
reader.Start();
|
||||
|
||||
fake.EnqueueLine("@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E");
|
||||
|
||||
bool queued = WaitUntil(() =>
|
||||
{
|
||||
var queue = GetPrivateField<System.Collections.Concurrent.ConcurrentQueue<string>>(reader, "_receivedLines");
|
||||
return !queue.IsEmpty;
|
||||
});
|
||||
|
||||
Assert.IsTrue(queued);
|
||||
|
||||
int optoDataCountBefore = GetPrivateField<int>(reader, "optoDataCount");
|
||||
Assert.AreEqual(0, optoDataCountBefore, "Background thread should only enqueue, not process.");
|
||||
|
||||
reader.RunDeviceBefore();
|
||||
|
||||
int optoDataCountAfter = GetPrivateField<int>(reader, "optoDataCount");
|
||||
Assert.AreEqual(1, optoDataCountAfter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -108,8 +108,11 @@
|
||||
<Compile Include="Rig\Output\FileWriters\Enhanced\WriterTest.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\common\OptoTelegramRawTest.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\FakeSerialDriver.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisReaderTests.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisSmartReaderChannelAveragingTests.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisSmartReaderChannelTests.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisSmartReaderTest.cs" />
|
||||
<Compile Include="Rig\RegisterReaders\GenesisRegReader\implementations\GenesisSmartReaderThreadedReadTests.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