diff --git a/TBF/Properties/AssemblyInfo.cs b/TBF/Properties/AssemblyInfo.cs
index f61241027..09230b6c9 100644
--- a/TBF/Properties/AssemblyInfo.cs
+++ b/TBF/Properties/AssemblyInfo.cs
@@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
// Build Number
// Revision
//
-[assembly: AssemblyVersion("3.9.2200.1")]
-[assembly: AssemblyFileVersion("3.9.2200.1")]
+[assembly: AssemblyVersion("3.9.2201.1")]
+[assembly: AssemblyFileVersion("3.9.2201.1")]
diff --git a/TBF/TBF.csproj b/TBF/TBF.csproj
index 17680c08a..d462c63d9 100644
--- a/TBF/TBF.csproj
+++ b/TBF/TBF.csproj
@@ -110,8 +110,8 @@
..\packages\Iesi.Collections.4.0.0.4000\lib\net40\Iesi.Collections.dll
-
- ..\packages\log4net.2.0.15\lib\net45\log4net.dll
+
+ ..\packages\log4net.3.2.0\lib\net462\log4net.dll
@@ -1460,6 +1460,7 @@
+
diff --git a/TBFTests/Rig/RegisterReaders/iPerlASICReader/communication/C4/IperlHatProtocol/IperlHatIntegrationTests.cs b/TBFTests/Rig/RegisterReaders/iPerlASICReader/communication/C4/IperlHatProtocol/IperlHatIntegrationTests.cs
index 1e0a73c06..690ef914a 100644
--- a/TBFTests/Rig/RegisterReaders/iPerlASICReader/communication/C4/IperlHatProtocol/IperlHatIntegrationTests.cs
+++ b/TBFTests/Rig/RegisterReaders/iPerlASICReader/communication/C4/IperlHatProtocol/IperlHatIntegrationTests.cs
@@ -3,6 +3,10 @@ using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Text;
+using log4net;
+using log4net.Appender;
+using log4net.Layout;
+using log4net.Repository.Hierarchy;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed;
using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed.parserer;
@@ -16,6 +20,33 @@ namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHat
[TestClass]
public class IperlHatIntegrationTests
{
+
+ public static class LogConfig
+ {
+ public static void Configure()
+ {
+ var hierarchy = (Hierarchy)LogManager.GetRepository();
+ hierarchy.Root.RemoveAllAppenders();
+
+ var patternLayout = new PatternLayout
+ {
+ ConversionPattern = "%date{yyyy-MM-dd HH:mm:ss.fff} [%thread] %-5level %logger - %message%newline"
+ };
+ patternLayout.ActivateOptions();
+
+ var consoleAppender = new ConsoleAppender
+ {
+ Layout = patternLayout
+ };
+ consoleAppender.ActivateOptions();
+
+ hierarchy.Root.AddAppender(consoleAppender);
+ hierarchy.Root.Level = log4net.Core.Level.Debug;
+ hierarchy.Configured = true;
+ }
+ }
+ ILog log = LogManager.GetLogger(typeof(IperlHatIntegrationTests));
+
private const string ComPort = "COM3"; // CHANGE THIS
private const int BaudRate = 2400;
private const int BaudRateOpto = 38400;
@@ -461,7 +492,7 @@ namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHat
//method test connection to optho head
private void Serial_OptoRawSniff()
{
- using (var port = new SerialPort("COM4", BaudRateOpto, Parity.None, 8, StopBits.One))
+ using (var port = new SerialPort("COM15", BaudRateOpto, Parity.None, 8, StopBits.One))
{
port.Handshake = Handshake.None;
port.ReadTimeout = 10000;
@@ -489,7 +520,8 @@ namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication.C4.IperlHat
try
{
var data = (DiagnosticLedState4Data)parser.ParseLine(line,false);
- Console.WriteLine("Parsed: " + data);
+ Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] Time: Parsed: {data}");
+ log.Info("Parsed: " + data);
}catch(Exception e)
{
Console.WriteLine("Failed to parse: " + e.Message);
diff --git a/TBFTests/Rig/RegisterReaders/iPerlASICReader/communication/OptoHeadTestTest.cs b/TBFTests/Rig/RegisterReaders/iPerlASICReader/communication/OptoHeadTestTest.cs
index 04274dd42..ef7c1d8c6 100644
--- a/TBFTests/Rig/RegisterReaders/iPerlASICReader/communication/OptoHeadTestTest.cs
+++ b/TBFTests/Rig/RegisterReaders/iPerlASICReader/communication/OptoHeadTestTest.cs
@@ -15,8 +15,9 @@ namespace TBFTests.Rig.RegisterReaders.iPerlASICReader.communication
public void ReadRequest_PCB_Test()
{
IperlHead iHead = new IperlHead();
+ OptoHeadTest optoHeadTest = new OptoHeadTest(iHead);
- OptoHeadTest.ReadRequest_PCB(ref iHead);
+ optoHeadTest.ReadRequest_PCB();
}
}
}
\ No newline at end of file
diff --git a/TBFTests/Rig/TestMethods/iPerlCommunication/common/OptoTelegramRawTest.cs b/TBFTests/Rig/TestMethods/iPerlCommunication/common/OptoTelegramRawTest.cs
new file mode 100644
index 000000000..0f48bee1b
--- /dev/null
+++ b/TBFTests/Rig/TestMethods/iPerlCommunication/common/OptoTelegramRawTest.cs
@@ -0,0 +1,143 @@
+using System;
+using JetBrains.Annotations;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using TBF.Rig.TestMethods.iPerlCommunication.common;
+using TBF.Rig.TestMethods.iPerlCommunication.communication.C4.diagnosticLed.parserer;
+
+namespace TBFTests.Rig.TestMethods.iPerlCommunication.common
+{
+ [TestClass]
+ [TestSubject(typeof(OptoTelegramRaw))]
+ public class OptoTelegramRawTest
+ {
+ private const double EPS = 1e-6;
+
+ private static OptoTelegramRaw Create() => new OptoTelegramRaw();
+
+ private static DiagnosticLedState4Data CreateTelegram(uint rawVolume24, uint timestamp32)
+ {
+ string HexU(uint value, int bytes) => value.ToString($"X{bytes * 2}");
+ string[] fields =
+ {
+ "000000","0000","0000",
+ HexU(rawVolume24,3),"0000","0000",
+ HexU(timestamp32,4),"00","00000000",
+ "0000","0000","0000","0000","00"
+ };
+
+ return new DiagnosticLedState4Data("TEST", fields);
+ }
+
+ // helper: compute liters per tick exactly as production
+ private static double LitersPerTick =>
+ (4.0 / 1000.0) / 3.785411784 / 2.0;
+
+ private static double TimestampPerTick => 1.0 / 4096.0;
+
+ [TestMethod]
+ public void NormalProgression_NoWrap()
+ {
+ var sample = Create();
+
+ var d1 = CreateTelegram(1000, 2000);
+ double volLast = d1.RawVolume;
+ double tsLast = d1.AsicTimestamp;
+
+ var d2 = CreateTelegram(1010, 2010);
+
+ sample.UpdateFromSmart(d2, 0, 0, ref volLast, ref tsLast);
+
+ Assert.AreEqual(d2.RawVolume, sample.VolumeRawExt, EPS);
+ Assert.AreEqual(d2.AsicTimestamp, sample.TimestampExt, EPS);
+ }
+
+ [TestMethod]
+ public void VolumeForwardWrap_AddsOneFullVolumeRange()
+ {
+ var sample = Create();
+
+ var dLast = CreateTelegram((uint)((1 << 24) - 5), 0);
+ double volLast = dLast.RawVolume;
+ double tsLast = dLast.AsicTimestamp;
+
+ var dNew = CreateTelegram(3u, 0u);
+
+ sample.UpdateFromSmart(dNew, 0, 0, ref volLast, ref tsLast);
+
+ double expectedIncrease = (1 << 24) * LitersPerTick;
+
+ Assert.AreEqual(dNew.RawVolume + expectedIncrease, sample.VolumeRawExt, 1e-3);
+ }
+
+ [TestMethod]
+ public void TimestampForwardWrap_AddsOneFullTimeRange()
+ {
+ var sample = Create();
+
+ var dLast = CreateTelegram(0, (uint)((1UL << 32) - 10));
+ double volLast = dLast.RawVolume;
+ double tsLast = dLast.AsicTimestamp;
+
+ var dNew = CreateTelegram(0, 5);
+
+ sample.UpdateFromSmart(dNew, 0, 0, ref volLast, ref tsLast);
+
+ double expectedIncrease = (1UL << 32) * TimestampPerTick;
+
+ Assert.AreEqual(dNew.AsicTimestamp + expectedIncrease, sample.TimestampExt, 1e-3);
+ }
+
+
+ [TestMethod]
+ public void HugeJump_UsesFallback()
+ {
+ var sample = Create();
+
+ var dLast = CreateTelegram(1000, 1000);
+ double volLast = dLast.RawVolume;
+ double tsLast = dLast.AsicTimestamp;
+
+ var dNew = CreateTelegram((uint)(1 << 23), (uint)(1UL << 31));
+
+ sample.UpdateFromSmart(dNew, 0, 0, ref volLast, ref tsLast);
+
+ Assert.AreEqual(dNew.RawVolume, sample.VolumeRawExt, EPS);
+ Assert.AreEqual(dNew.AsicTimestamp, sample.TimestampExt, EPS);
+ }
+
+ [TestMethod]
+ public void LongRun_MultipleWraps_RemainsMonotonic()
+ {
+ var sample = new OptoTelegramRaw();
+
+ double volLast = double.NaN;
+ double tsLast = double.NaN;
+
+ uint volCounter = (uint)((1 << 24) - 100);
+ uint tsCounter = (uint)((1UL << 32) - 1000);
+
+ double prevVol = double.NegativeInfinity;
+ double prevTs = double.NegativeInfinity;
+
+ for (int i = 0; i < 500; i++)
+ {
+ unchecked
+ {
+ volCounter += 200_000u; // < 2^23
+ tsCounter += 50_000_000u; // < 2^31
+ }
+
+ var data = CreateTelegram(volCounter, tsCounter);
+ sample.UpdateFromSmart(data, 0, 0, ref volLast, ref tsLast);
+
+ Assert.IsTrue(sample.VolumeRawExt >= prevVol, $"Volume went backwards at i={i}");
+ Assert.IsTrue(sample.TimestampExt >= prevTs, $"Timestamp went backwards at i={i}");
+
+ prevVol = sample.VolumeRawExt;
+ prevTs = sample.TimestampExt;
+ }
+ }
+
+
+ }
+}
diff --git a/TBFTests/Rig/TestMethods/iPerlCommunication/iPerlHead/FlowDirectionDetectionTest.cs b/TBFTests/Rig/TestMethods/iPerlCommunication/iPerlHead/FlowDirectionDetectionTest.cs
new file mode 100644
index 000000000..b3abe6962
--- /dev/null
+++ b/TBFTests/Rig/TestMethods/iPerlCommunication/iPerlHead/FlowDirectionDetectionTest.cs
@@ -0,0 +1,159 @@
+using JetBrains.Annotations;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using TBF.Rig.TestMethods.iPerlCommunication.iPerlHead;
+
+namespace TBFTests.Rig.TestMethods.iPerlCommunication.iPerlHead
+{
+ using System;
+using System.Threading;
+using JetBrains.Annotations;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using TBF.Rig.TestMethods.iPerlCommunication.iPerlHead;
+using Common;
+
+ namespace TBFTests.Rig.TestMethods.iPerlCommunication.iPerlHead
+ {
+ [TestClass]
+ [TestSubject(typeof(FlowDirectionDetection))]
+ public class FlowDirectionDetectionTests
+ {
+ private FlowDirectionDetection Create() => new FlowDirectionDetection();
+
+ // helper: feed linear data
+ private void FeedLinearData(FlowDirectionDetection d, double slope, int samples = 64)
+ {
+ double volume = 0;
+ double time = 0;
+
+ for (int i = 0; i < samples; i++)
+ {
+ volume += slope; // linear growth
+ time += 0.125; // 8 Hz sampling
+
+ d.WriteToFifo(volume, time);
+ }
+ }
+
+ // --------------------------------------------------------
+
+ [TestMethod]
+ public void Fifo_NotFull_DataInvalid()
+ {
+ var d = Create();
+
+ d.WriteToFifo(1, 1);
+
+ Assert.IsFalse(d.AreFifoDataValid());
+ }
+
+ // --------------------------------------------------------
+
+ [TestMethod]
+ public void Fifo_Full_DataValid()
+ {
+ var d = Create();
+ FeedLinearData(d, 1.0);
+
+ Assert.IsTrue(d.AreFifoDataValid());
+ }
+
+ // --------------------------------------------------------
+
+ [TestMethod]
+ public void PositiveFlow_Detected()
+ {
+ var d = Create();
+ FeedLinearData(d, slope: 10); // increasing volume
+
+ var result = d.CheckFlowDirection(Counting.Positive, "HEAD");
+
+ Assert.AreEqual(OptoHeadState.OptoAndDirOK, result);
+ }
+
+ // --------------------------------------------------------
+
+ [TestMethod]
+ public void NegativeFlow_Detected()
+ {
+ var d = Create();
+ FeedLinearData(d, slope: -10); // decreasing volume
+
+ var result = d.CheckFlowDirection(Counting.Negative, "HEAD");
+
+ Assert.AreEqual(OptoHeadState.OptoAndDirOK, result);
+ }
+
+ // --------------------------------------------------------
+
+ [TestMethod]
+ public void WrongDirection_Fails()
+ {
+ var d = Create();
+ FeedLinearData(d, slope: -10); // negative flow
+
+ var result = d.CheckFlowDirection(Counting.Positive, "HEAD");
+
+ Assert.AreEqual(OptoHeadState.DirNok, result);
+ }
+
+ // --------------------------------------------------------
+
+ [TestMethod]
+ public void ArbitraryCounting_AlwaysPassesWhenSignalGood()
+ {
+ var d = Create();
+ FeedLinearData(d, slope: -5);
+
+ var result = d.CheckFlowDirection(Counting.Arbitrary, "HEAD");
+
+ Assert.AreEqual(OptoHeadState.OptoAndDirOK, result);
+ }
+
+ // --------------------------------------------------------
+
+ [TestMethod]
+ public void NoFlow_ReturnsDirNok()
+ {
+ var d = Create();
+
+ // flat signal → slope ≈ 0
+ for (int i = 0; i < 64; i++)
+ d.WriteToFifo(100, i * 0.125);
+
+ var result = d.CheckFlowDirection(Counting.Positive, "HEAD");
+
+ Assert.AreEqual(OptoHeadState.DirNok, result);
+ }
+
+ // --------------------------------------------------------
+
+ [TestMethod]
+ public void FifoOverwrite_StillDetectsCorrectly()
+ {
+ var d = Create();
+
+ // write more than FIFO size → overwrite happens
+ FeedLinearData(d, slope: 5, samples: 200);
+
+ var result = d.CheckFlowDirection(Counting.Positive, "HEAD");
+
+ Assert.AreEqual(OptoHeadState.OptoAndDirOK, result);
+ }
+
+ // --------------------------------------------------------
+
+ [TestMethod]
+ public void Timeout_InvalidatesFifo()
+ {
+ var d = Create();
+ FeedLinearData(d, slope: 5);
+
+ // simulate dropout > 4.5 sec
+ Thread.Sleep(5000);
+
+ Assert.IsFalse(d.AreFifoDataValid());
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/TBFTests/Rig/TestMethods/iPerlCommunication/iPerlHead/IperlHeadTest.cs b/TBFTests/Rig/TestMethods/iPerlCommunication/iPerlHead/IperlHeadTest.cs
new file mode 100644
index 000000000..a7d44cd40
--- /dev/null
+++ b/TBFTests/Rig/TestMethods/iPerlCommunication/iPerlHead/IperlHeadTest.cs
@@ -0,0 +1,30 @@
+using JetBrains.Annotations;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using TBF.Rig.TestMethods.iPerlCommunication.iPerlHead;
+
+namespace TBFTests.Rig.TestMethods.iPerlCommunication.iPerlHead
+{
+ [TestClass]
+ [TestSubject(typeof(IperlHead))]
+ public class IperlHeadTest
+ {
+
+ [TestMethod]
+ public void VolumeLtrStart()
+ {
+
+ }
+
+ [TestMethod]
+ public void VolumeLtrEnd()
+ {
+
+ }
+
+ [TestMethod]
+ public void NoSamples()
+ {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/TBFTests/TBFTests.csproj b/TBFTests/TBFTests.csproj
index 72dd47fef..2a79a2757 100644
--- a/TBFTests/TBFTests.csproj
+++ b/TBFTests/TBFTests.csproj
@@ -44,8 +44,8 @@
..\packages\JetBrains.Annotations.2023.3.0\lib\net20\JetBrains.Annotations.dll
-
- ..\packages\log4net.2.0.15\lib\net45\log4net.dll
+
+ ..\packages\log4net.3.2.0\lib\net462\log4net.dll
..\packages\Microsoft.Extensions.Configuration.Abstractions.5.0.0\lib\net461\Microsoft.Extensions.Configuration.Abstractions.dll
@@ -114,6 +114,9 @@
+
+
+