chore: organize solution into module folders (Core/Server/Drivers/Client/Tooling)
Group all 69 projects into category subfolders under src/ and tests/ so the Rider Solution Explorer mirrors the module structure. Folders: Core, Server, Drivers (with a nested Driver CLIs subfolder), Client, Tooling. - Move every project folder on disk with git mv (history preserved as renames). - Recompute relative paths in 57 .csproj files: cross-category ProjectReferences, the lib/ HintPath+None refs in Driver.Historian.Wonderware, and the external mxaccessgw refs in Driver.Galaxy and its test project. - Rebuild ZB.MOM.WW.OtOpcUa.slnx with nested solution folders. - Re-prefix project paths in functional scripts (e2e, compliance, smoke SQL, integration, install). Build green (0 errors); unit tests pass. Docs left for a separate pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
using MxGateway.Contracts.Proto;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests.Runtime;
|
||||
|
||||
/// <summary>
|
||||
/// Exhaustive table-driven tests for <see cref="StatusCodeMap"/>. Pinning the byte→uint
|
||||
/// mapping here protects against accidental drift — every Galaxy deployment that
|
||||
/// reaches the parity matrix in PR 5.2 depends on these specific OPC UA StatusCode
|
||||
/// values matching the legacy <c>HistorianQualityMapper</c> output.
|
||||
/// </summary>
|
||||
public sealed class StatusCodeMapTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData((byte)192, 0x00000000u)] // Good
|
||||
[InlineData((byte)216, 0x00D80000u)] // Good_LocalOverride
|
||||
[InlineData((byte)64, 0x40000000u)] // Uncertain
|
||||
[InlineData((byte)68, 0x40A40000u)] // Uncertain_LastUsableValue
|
||||
[InlineData((byte)80, 0x408D0000u)] // Uncertain_SensorNotAccurate
|
||||
[InlineData((byte)84, 0x408E0000u)] // Uncertain_EngineeringUnitsExceeded
|
||||
[InlineData((byte)88, 0x408F0000u)] // Uncertain_SubNormal
|
||||
[InlineData((byte)0, 0x80000000u)] // Bad
|
||||
[InlineData((byte)4, 0x80890000u)] // Bad_ConfigurationError
|
||||
[InlineData((byte)8, 0x808A0000u)] // Bad_NotConnected
|
||||
[InlineData((byte)12, 0x808B0000u)] // Bad_DeviceFailure
|
||||
[InlineData((byte)16, 0x808C0000u)] // Bad_SensorFailure
|
||||
[InlineData((byte)20, 0x80050000u)] // Bad_CommunicationError
|
||||
[InlineData((byte)24, 0x808D0000u)] // Bad_OutOfService
|
||||
[InlineData((byte)32, 0x80320000u)] // Bad_WaitingForInitialData
|
||||
public void FromQualityByte_KnownValues_MapToOpcUaStatusCode(byte input, uint expected)
|
||||
{
|
||||
StatusCodeMap.FromQualityByte(input).ShouldBe(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData((byte)200)] // Unknown Good — falls back to category bucket
|
||||
[InlineData((byte)100)] // Unknown Uncertain
|
||||
[InlineData((byte)40)] // Unknown Bad
|
||||
public void FromQualityByte_UnknownValues_FallBackToCategoryBucket(byte input)
|
||||
{
|
||||
var mapped = StatusCodeMap.FromQualityByte(input);
|
||||
if (input >= 192) mapped.ShouldBe(StatusCodeMap.Good);
|
||||
else if (input >= 64) mapped.ShouldBe(StatusCodeMap.Uncertain);
|
||||
else mapped.ShouldBe(StatusCodeMap.Bad);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromMxStatus_NullStatus_IsGood()
|
||||
{
|
||||
StatusCodeMap.FromMxStatus(null).ShouldBe(StatusCodeMap.Good);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromMxStatus_SuccessNonZero_IsGood()
|
||||
{
|
||||
var s = new MxStatusProxy { Success = 1 };
|
||||
StatusCodeMap.FromMxStatus(s).ShouldBe(StatusCodeMap.Good);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromMxStatus_SuccessZero_DetailKnown_MapsToSpecificCode()
|
||||
{
|
||||
var s = new MxStatusProxy { Success = 0, Detail = 8 /* Bad_NotConnected */ };
|
||||
StatusCodeMap.FromMxStatus(s).ShouldBe(StatusCodeMap.BadNotConnected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromMxStatus_SuccessZero_DetailZero_DetectedByNonZero_IsCommunicationError()
|
||||
{
|
||||
var s = new MxStatusProxy { Success = 0, Detail = 0, RawDetectedBy = 3 };
|
||||
StatusCodeMap.FromMxStatus(s).ShouldBe(StatusCodeMap.BadCommunicationError);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromMxStatus_SuccessZero_AllZero_IsBad()
|
||||
{
|
||||
var s = new MxStatusProxy();
|
||||
StatusCodeMap.FromMxStatus(s).ShouldBe(StatusCodeMap.Bad);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TopByteCategoryBits_StayWithinOpcUaConvention()
|
||||
{
|
||||
// Sanity check that every Bad code we mint actually has the Bad top byte (0x80…),
|
||||
// every Uncertain has 0x40…, every Good has 0x00…. Pins the OPC UA Part 4 invariant.
|
||||
StatusCodeMap.Good.ShouldBeLessThan(0x40000000u);
|
||||
StatusCodeMap.GoodLocalOverride.ShouldBeLessThan(0x40000000u);
|
||||
|
||||
((StatusCodeMap.Uncertain >> 30) & 0x3u).ShouldBe(1u);
|
||||
((StatusCodeMap.UncertainLastUsableValue >> 30) & 0x3u).ShouldBe(1u);
|
||||
|
||||
((StatusCodeMap.Bad >> 30) & 0x3u).ShouldBe(2u);
|
||||
((StatusCodeMap.BadNotConnected >> 30) & 0x3u).ShouldBe(2u);
|
||||
((StatusCodeMap.BadOutOfService >> 30) & 0x3u).ShouldBe(2u);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user