Merge #602(S7)/#610.2: S7 connect backoff, write-path worst-state-wins, Volatile health, factory logging, driver-side array writes

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-08 07:01:32 -04:00
12 changed files with 845 additions and 75 deletions
@@ -103,4 +103,44 @@ public sealed class S7TagConfigModelTests
[InlineData("""{"address":"DB1.DBW0","dataType":7}""")]
public void Validate_accepts_every_dataType_the_driver_accepts(string json)
=> S7TagConfigModel.FromJson(json).Validate().ShouldBeNull();
// ---- S7-016: array shape vs the driver's init guards (which fail the WHOLE driver init) ----
[Theory]
[InlineData("Int64")]
[InlineData("UInt64")]
[InlineData("Float64")]
[InlineData("String")]
[InlineData("DateTime")]
public void Validate_rejects_wide_type_arrays(string dataType)
{
var m = S7TagConfigModel.FromJson(
$$"""{"address":"DB1.DBB0","dataType":"{{dataType}}","isArray":true,"arrayLength":4,"writable":false}""");
m.Validate().ShouldNotBeNull().ShouldContain("arrays");
}
[Fact]
public void Validate_rejects_a_writable_bool_array_but_accepts_it_read_only()
{
var writable = S7TagConfigModel.FromJson(
"""{"address":"DB1.DBX0.0","dataType":"Bool","isArray":true,"arrayLength":4}""");
writable.Validate().ShouldNotBeNull().ShouldContain("Bool array");
var readOnly = S7TagConfigModel.FromJson(
"""{"address":"DB1.DBX0.0","dataType":"Bool","isArray":true,"arrayLength":4,"writable":false}""");
readOnly.Validate().ShouldBeNull();
}
[Theory]
[InlineData("Byte", "DB1.DBB0")]
[InlineData("Int16", "DB1.DBW0")]
[InlineData("Float32", "DB1.DBD0")]
public void Validate_accepts_writable_arrays_of_supported_element_types(string dataType, string address)
{
var m = S7TagConfigModel.FromJson(
$$"""{"address":"{{address}}","dataType":"{{dataType}}","isArray":true,"arrayLength":4}""");
m.Validate().ShouldBeNull();
}
}