using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Driver.FOCAS; namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests; /// /// Tests for the managed helpers inside FwlibNative + FwlibFocasClient that don't require the /// licensed Fwlib32.dll — letter→ADR_* mapping, FocasDataType→data-type mapping, byte encoding. /// The actual P/Invoke calls can only run where the DLL is present; field testing covers those. /// [Trait("Category", "Unit")] public sealed class FwlibNativeHelperTests { [Theory] [InlineData("G", 0)] [InlineData("F", 1)] [InlineData("Y", 2)] [InlineData("X", 3)] [InlineData("A", 4)] [InlineData("R", 5)] [InlineData("T", 6)] [InlineData("K", 7)] [InlineData("C", 8)] [InlineData("D", 9)] [InlineData("E", 10)] [InlineData("g", 0)] // case-insensitive public void PmcAddrType_maps_every_valid_letter(string letter, short expected) { FocasPmcAddrType.FromLetter(letter).ShouldBe(expected); } [Theory] [InlineData("Z")] [InlineData("")] [InlineData("XX")] public void PmcAddrType_rejects_unknown_letters(string letter) { FocasPmcAddrType.FromLetter(letter).ShouldBeNull(); } [Theory] [InlineData(FocasDataType.Bit, 0)] // byte [InlineData(FocasDataType.Byte, 0)] [InlineData(FocasDataType.Int16, 1)] // word [InlineData(FocasDataType.Int32, 2)] // long [InlineData(FocasDataType.Float32, 4)] [InlineData(FocasDataType.Float64, 5)] public void PmcDataType_maps_FocasDataType_to_FOCAS_code(FocasDataType input, short expected) { FocasPmcDataType.FromFocasDataType(input).ShouldBe(expected); } [Fact] public void EncodePmcValue_Byte_writes_signed_byte_at_offset_0() { var buf = new byte[40]; FwlibFocasClient.EncodePmcValue(buf, FocasDataType.Byte, (sbyte)-5, bitIndex: null); ((sbyte)buf[0]).ShouldBe((sbyte)-5); } [Fact] public void EncodePmcValue_Int16_writes_little_endian() { var buf = new byte[40]; FwlibFocasClient.EncodePmcValue(buf, FocasDataType.Int16, (short)0x1234, bitIndex: null); buf[0].ShouldBe((byte)0x34); buf[1].ShouldBe((byte)0x12); } [Fact] public void EncodePmcValue_Int32_writes_little_endian() { var buf = new byte[40]; FwlibFocasClient.EncodePmcValue(buf, FocasDataType.Int32, 0x12345678, bitIndex: null); buf[0].ShouldBe((byte)0x78); buf[1].ShouldBe((byte)0x56); buf[2].ShouldBe((byte)0x34); buf[3].ShouldBe((byte)0x12); } [Fact] public void EncodePmcValue_Bit_without_bit_index_writes_byte_boolean() { // Task #181 closed the Bit-write gap — PMC Bit with a bitIndex now routes through // WritePmcBitAsync's RMW path upstream, and raw EncodePmcValue only gets the // no-bit-index case (treated as a whole-byte boolean). var buf = new byte[40]; FwlibFocasClient.EncodePmcValue(buf, FocasDataType.Bit, true, bitIndex: null); buf[0].ShouldBe((byte)1); FwlibFocasClient.EncodePmcValue(buf, FocasDataType.Bit, false, bitIndex: null); buf[0].ShouldBe((byte)0); } [Fact] public void EncodeParamValue_Int32_writes_little_endian() { var buf = new byte[32]; FwlibFocasClient.EncodeParamValue(buf, FocasDataType.Int32, 0x0A0B0C0D); buf[0].ShouldBe((byte)0x0D); buf[1].ShouldBe((byte)0x0C); buf[2].ShouldBe((byte)0x0B); buf[3].ShouldBe((byte)0x0A); } }