fix(ablegacy): encode array writes at the data file's element width

Review r0 I3. The array encoder used the AUTHORED dataType's width while both
runtimes size their buffer from the FILE: an Int array on F8 wrote four shorts
into a 16-byte float buffer and sent it all (garbage neighbours, answered
Good); Long/Float on N overran it.

- The write path resolves the element width from the data file the address
  names (N/B/A/I/O/S 16-bit word, L 32-bit, F float) and encodes at THAT width.
- An authored type that does not match its file is refused BadTypeMismatch
  before any runtime exists (warned once, no health write).
- The same rule (AbLegacyTagDefinitionFactory.ArrayFileMismatch) backs an
  Inspect deploy warning and the AdminUI model's Validate error; FromTagConfig
  still accepts the tag so persisted configs keep loading.
- M6: the skipped F1 parity test gains the shapes the write path refuses
  (/bit, .sub, indirect, type/file mismatch, PLC-5 native).

Tests: one case per file type, a mismatch per pairing with nothing created,
and a positive control.

Refs #738
Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
This commit is contained in:
Joseph Doherty
2026-10-22 15:20:10 -04:00
parent 17ae2eea0b
commit d758dd1e16
6 changed files with 175 additions and 5 deletions
@@ -136,6 +136,24 @@ public sealed class AbLegacyTagConfigModelTests
AbLegacyTagConfigModel.FromJson($$"""{"address":"{{address}}","dataType":"{{dataType}}"}""").Validate().ShouldBeNull();
}
/// <summary>Review 07 r0 I3: an array must match its data file's element width.</summary>
[Theory]
[InlineData("F8:0", "Int", false)]
[InlineData("N7:0", "Long", false)]
[InlineData("N7:0", "Int", true)]
[InlineData("L9:0", "Long", true)]
[InlineData("F8:0", "Float", true)]
public void Validate_refuses_an_array_whose_type_does_not_match_its_file(string address, string dataType, bool valid)
{
var m = AbLegacyTagConfigModel.FromJson(
$$"""{"address":"{{address}}","dataType":"{{dataType}}","isArray":true,"arrayLength":4}""");
if (valid) m.Validate().ShouldBeNull();
else m.Validate().ShouldNotBeNull().ShouldContain("element width");
// A SCALAR of the same shape is not this rule's business.
AbLegacyTagConfigModel.FromJson($$"""{"address":"{{address}}","dataType":"{{dataType}}"}""").Validate().ShouldBeNull();
}
[Fact]
public void Validate_returns_null_for_scalar_tag_regardless_of_no_array_length()
{