Files
lmxopcua/tests/ZB.MOM.WW.LmxOpcUa.Tests/OpcUa/DataValueConverterTests.cs
Joseph Doherty a7576ffb38 Implement LmxOpcUa server — all 6 phases complete
Full OPC UA server on .NET Framework 4.8 (x86) exposing AVEVA System
Platform Galaxy tags via MXAccess. Mirrors Galaxy object hierarchy as
OPC UA address space, translating contained-name browse paths to
tag-name runtime references.

Components implemented:
- Configuration: AppConfiguration with 4 sections, validator
- Domain: ConnectionState, Quality, Vtq, MxDataTypeMapper, error codes
- MxAccess: StaComThread, MxAccessClient (partial classes), MxProxyAdapter
  using strongly-typed ArchestrA.MxAccess COM interop
- Galaxy Repository: SQL queries (hierarchy, attributes, change detection),
  ChangeDetectionService with auto-rebuild on deploy
- OPC UA Server: LmxNodeManager (CustomNodeManager2), LmxOpcUaServer,
  OpcUaServerHost with programmatic config, SecurityPolicy None
- Status Dashboard: HTTP server with HTML/JSON/health endpoints
- Integration: Full 14-step startup, graceful shutdown, component wiring

175 tests (174 unit + 1 integration), all passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 05:55:27 -04:00

123 lines
3.4 KiB
C#

using System;
using Shouldly;
using Xunit;
using ZB.MOM.WW.LmxOpcUa.Host.Domain;
using ZB.MOM.WW.LmxOpcUa.Host.OpcUa;
namespace ZB.MOM.WW.LmxOpcUa.Tests.OpcUa
{
public class DataValueConverterTests
{
[Fact]
public void FromVtq_Boolean()
{
var vtq = Vtq.Good(true);
var dv = DataValueConverter.FromVtq(vtq);
dv.Value.ShouldBe(true);
Opc.Ua.StatusCode.IsGood(dv.StatusCode).ShouldBe(true);
}
[Fact]
public void FromVtq_Int32()
{
var vtq = Vtq.Good(42);
var dv = DataValueConverter.FromVtq(vtq);
dv.Value.ShouldBe(42);
}
[Fact]
public void FromVtq_Float()
{
var vtq = Vtq.Good(3.14f);
var dv = DataValueConverter.FromVtq(vtq);
dv.Value.ShouldBe(3.14f);
}
[Fact]
public void FromVtq_Double()
{
var vtq = Vtq.Good(3.14159);
var dv = DataValueConverter.FromVtq(vtq);
dv.Value.ShouldBe(3.14159);
}
[Fact]
public void FromVtq_String()
{
var vtq = Vtq.Good("hello");
var dv = DataValueConverter.FromVtq(vtq);
dv.Value.ShouldBe("hello");
}
[Fact]
public void FromVtq_DateTime_IsUtc()
{
var utcTime = new DateTime(2024, 6, 15, 10, 30, 0, DateTimeKind.Utc);
var vtq = new Vtq(utcTime, utcTime, Quality.Good);
var dv = DataValueConverter.FromVtq(vtq);
((DateTime)dv.Value).Kind.ShouldBe(DateTimeKind.Utc);
}
[Fact]
public void FromVtq_TimeSpan_ConvertedToSeconds()
{
var vtq = Vtq.Good(TimeSpan.FromMinutes(2.5));
var dv = DataValueConverter.FromVtq(vtq);
dv.Value.ShouldBe(150.0);
}
[Fact]
public void FromVtq_StringArray()
{
var arr = new[] { "a", "b", "c" };
var vtq = Vtq.Good(arr);
var dv = DataValueConverter.FromVtq(vtq);
dv.Value.ShouldBe(arr);
}
[Fact]
public void FromVtq_IntArray()
{
var arr = new[] { 1, 2, 3 };
var vtq = Vtq.Good(arr);
var dv = DataValueConverter.FromVtq(vtq);
dv.Value.ShouldBe(arr);
}
[Fact]
public void FromVtq_BadQuality_MapsToStatusCode()
{
var vtq = Vtq.Bad(Quality.BadCommFailure);
var dv = DataValueConverter.FromVtq(vtq);
Opc.Ua.StatusCode.IsBad(dv.StatusCode).ShouldBe(true);
}
[Fact]
public void FromVtq_UncertainQuality()
{
var vtq = Vtq.Uncertain(42);
var dv = DataValueConverter.FromVtq(vtq);
Opc.Ua.StatusCode.IsUncertain(dv.StatusCode).ShouldBe(true);
}
[Fact]
public void FromVtq_NullValue()
{
var vtq = Vtq.Good(null);
var dv = DataValueConverter.FromVtq(vtq);
dv.Value.ShouldBeNull();
}
[Fact]
public void ToVtq_RoundTrip()
{
var original = new Vtq(42, DateTime.UtcNow, Quality.Good);
var dv = DataValueConverter.FromVtq(original);
var roundTrip = DataValueConverter.ToVtq(dv);
roundTrip.Value.ShouldBe(42);
roundTrip.Quality.ShouldBe(Quality.Good);
}
}
}