feat(lmxproxy): phase 1 — v2 protocol types and domain model

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-03-21 23:41:56 -04:00
parent 08d2a07d8b
commit 0d63fb1105
87 changed files with 3389 additions and 956 deletions

View File

@@ -0,0 +1,29 @@
using FluentAssertions;
using Xunit;
using ZB.MOM.WW.LmxProxy.Client.Domain;
namespace ZB.MOM.WW.LmxProxy.Client.Tests.Domain;
public class QualityExtensionsTests
{
[Theory]
[InlineData(Quality.Good, true)]
[InlineData(Quality.Good_LocalOverride, true)]
[InlineData(Quality.Uncertain, false)]
[InlineData(Quality.Bad, false)]
public void IsGood(Quality q, bool expected) => q.IsGood().Should().Be(expected);
[Theory]
[InlineData(Quality.Uncertain, true)]
[InlineData(Quality.Uncertain_LastUsable, true)]
[InlineData(Quality.Good, false)]
[InlineData(Quality.Bad, false)]
public void IsUncertain(Quality q, bool expected) => q.IsUncertain().Should().Be(expected);
[Theory]
[InlineData(Quality.Bad, true)]
[InlineData(Quality.Bad_CommFailure, true)]
[InlineData(Quality.Good, false)]
[InlineData(Quality.Uncertain, false)]
public void IsBad(Quality q, bool expected) => q.IsBad().Should().Be(expected);
}

View File

@@ -0,0 +1,134 @@
using FluentAssertions;
using Xunit;
using ZB.MOM.WW.LmxProxy.Client.Domain;
namespace ZB.MOM.WW.LmxProxy.Client.Tests.Domain;
public class ScadaContractsTests
{
[Fact]
public void TypedValue_GetValueCase_Bool()
{
var tv = new TypedValue { BoolValue = true };
tv.GetValueCase().Should().Be(TypedValueCase.BoolValue);
}
[Fact]
public void TypedValue_GetValueCase_Int32()
{
var tv = new TypedValue { Int32Value = 42 };
tv.GetValueCase().Should().Be(TypedValueCase.Int32Value);
}
[Fact]
public void TypedValue_GetValueCase_Double()
{
var tv = new TypedValue { DoubleValue = 3.14 };
tv.GetValueCase().Should().Be(TypedValueCase.DoubleValue);
}
[Fact]
public void TypedValue_GetValueCase_String()
{
var tv = new TypedValue { StringValue = "hello" };
tv.GetValueCase().Should().Be(TypedValueCase.StringValue);
}
[Fact]
public void TypedValue_GetValueCase_None_WhenDefault()
{
var tv = new TypedValue();
tv.GetValueCase().Should().Be(TypedValueCase.None);
}
[Fact]
public void TypedValue_GetValueCase_Datetime()
{
var tv = new TypedValue { DatetimeValue = DateTime.UtcNow.Ticks };
tv.GetValueCase().Should().Be(TypedValueCase.DatetimeValue);
}
[Fact]
public void TypedValue_GetValueCase_BytesValue()
{
var tv = new TypedValue { BytesValue = new byte[] { 1, 2, 3 } };
tv.GetValueCase().Should().Be(TypedValueCase.BytesValue);
}
[Fact]
public void TypedValue_GetValueCase_ArrayValue()
{
var tv = new TypedValue
{
ArrayValue = new ArrayValue
{
FloatValues = new FloatArray { Values = { 1.0f, 2.0f } }
}
};
tv.GetValueCase().Should().Be(TypedValueCase.ArrayValue);
}
[Fact]
public void QualityCode_IsGood()
{
var qc = new QualityCode { StatusCode = 0x00000000, SymbolicName = "Good" };
qc.IsGood.Should().BeTrue();
qc.IsBad.Should().BeFalse();
qc.IsUncertain.Should().BeFalse();
}
[Fact]
public void QualityCode_IsBad()
{
var qc = new QualityCode { StatusCode = 0x80000000, SymbolicName = "Bad" };
qc.IsGood.Should().BeFalse();
qc.IsBad.Should().BeTrue();
qc.IsUncertain.Should().BeFalse();
}
[Fact]
public void QualityCode_IsUncertain()
{
var qc = new QualityCode { StatusCode = 0x40900000, SymbolicName = "UncertainLastUsableValue" };
qc.IsGood.Should().BeFalse();
qc.IsBad.Should().BeFalse();
qc.IsUncertain.Should().BeTrue();
}
[Fact]
public void VtqMessage_DefaultProperties()
{
var vtq = new VtqMessage();
vtq.Tag.Should().BeEmpty();
vtq.Value.Should().BeNull();
vtq.TimestampUtcTicks.Should().Be(0);
vtq.Quality.Should().BeNull();
}
[Fact]
public void WriteBatchAndWaitRequest_FlagValue_IsTypedValue()
{
var req = new WriteBatchAndWaitRequest
{
SessionId = "abc",
FlagTag = "Motor.Done",
FlagValue = new TypedValue { BoolValue = true },
TimeoutMs = 5000,
PollIntervalMs = 100
};
req.FlagValue.Should().NotBeNull();
req.FlagValue!.GetValueCase().Should().Be(TypedValueCase.BoolValue);
}
[Fact]
public void WriteItem_Value_IsTypedValue()
{
var item = new WriteItem
{
Tag = "Motor.Speed",
Value = new TypedValue { DoubleValue = 42.5 }
};
item.Value.Should().NotBeNull();
item.Value!.GetValueCase().Should().Be(TypedValueCase.DoubleValue);
}
}

View File

@@ -0,0 +1,33 @@
using FluentAssertions;
using Xunit;
using ZB.MOM.WW.LmxProxy.Client.Domain;
namespace ZB.MOM.WW.LmxProxy.Client.Tests.Domain;
public class VtqTests
{
[Fact]
public void Good_FactoryMethod()
{
var vtq = Vtq.Good(42.0);
vtq.Value.Should().Be(42.0);
vtq.Quality.Should().Be(Quality.Good);
vtq.Timestamp.Kind.Should().Be(DateTimeKind.Utc);
}
[Fact]
public void Bad_FactoryMethod()
{
var vtq = Vtq.Bad();
vtq.Value.Should().BeNull();
vtq.Quality.Should().Be(Quality.Bad);
}
[Fact]
public void Uncertain_FactoryMethod()
{
var vtq = Vtq.Uncertain("stale");
vtq.Value.Should().Be("stale");
vtq.Quality.Should().Be(Quality.Uncertain);
}
}