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 @@ + + +