fix(address-space): exact element-type check for array writes + array-capability shape edges (#610 review R4)
MAJOR-1: the SDK skips TypeInfo.IsInstanceOfDataType on any node with an OnWriteValue handler (every writable equipment node), and the node manager's fail-fast treated numeric<->numeric as compatible, so a UInt32[] to a Galaxy Int32 array matched MxValueEncoder's int[] arm by CLR covariance and was written bit-reinterpreted with a Good result. EvaluateEquipmentWriteStructure now matches an ARRAY write's element type exactly (Byte[]/ByteString the only equivalence; Variant[]/object[] refused) -> BadTypeMismatch, nothing dispatched. MxValueEncoder's int[]/long[] arms are exact-type guarded too, and the Galaxy parity test's false "the SDK refuses" premise is corrected (the UInt32/UInt64 exclusion is now the encoder's own answer). Scalar writes keep numeric widening by decision: consumers write CLR-typed values (Double to a Float node) and every driver's scalar encoder range-checks. MINOR: Modbus row honours maxRegistersPerWrite (smallest of DriverConfig / the tag's DeviceConfig, default 123) and FC15's 1968-coil ceiling, and requires region + numeric address; Modbus/S7/Galaxy require isArray + integer arrayLength >= 1 (Galaxy: an unbounded node would let a short write resize the attribute); an array value to a scalar node is refused inline. Docs: Uns.md array-write rules + upgrade note (ReadWrite array tags become client-writable on first boot), Modbus.md, Galaxy.md. Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -121,6 +121,21 @@ public sealed class MxValueEncoderTests
|
||||
v.ArrayValue.TimestampValues.Values[0].ToDateTime().ShouldBe(inputs[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gitea #610 review — CLR array covariance makes <c>uint[] is int[]</c> and <c>ulong[] is long[]</c> true,
|
||||
/// so the int[]/long[] arms used to encode an unsigned array by reinterpreting its bits (4,000,000,000 →
|
||||
/// -294,967,296). An unsigned array is refused instead, like an out-of-range unsigned scalar.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Encode_UnsignedArrays_AreRefused_NotReinterpreted()
|
||||
{
|
||||
Should.Throw<ArgumentException>(() => MxValueEncoder.Encode(new uint[] { 4_000_000_000 }));
|
||||
Should.Throw<ArgumentException>(() => MxValueEncoder.Encode(new ulong[] { ulong.MaxValue }));
|
||||
// Positive control: the signed arrays still encode.
|
||||
MxValueEncoder.Encode(new[] { -1 }).ArrayValue.Int32Values.Values.ShouldBe(new[] { -1 });
|
||||
MxValueEncoder.Encode(new[] { -1L }).ArrayValue.Int64Values.Values.ShouldBe(new[] { -1L });
|
||||
}
|
||||
|
||||
/// <summary>Verifies that encoding an unsupported type throws an exception.</summary>
|
||||
[Fact]
|
||||
public void Encode_UnsupportedType_Throws()
|
||||
|
||||
Reference in New Issue
Block a user