fix(ablegacy): coalesceReads absent means default; the form never pins it
Review r0 M9. The AdminUI form wrote coalesceReads:true on every save, pinning today's default into every edited driver. AbLegacyDriverOptions.CoalesceReads is now bool? (null = default ON, never serialised, same encoding as Transport); the form omits the key when the switch is on and writes only an explicit false. Persisted coalesceReads:true keeps loading as on. Refs #738 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
This commit is contained in:
@@ -51,9 +51,12 @@ public sealed class AbLegacyDriverOptions
|
||||
/// <summary>
|
||||
/// Whether scalar tags on adjacent elements of one N / B / F / L file are read as ONE multi-element PCCC read
|
||||
/// (driver-config key <c>coalesceReads</c>, default <see langword="true"/>). A block the device refuses falls
|
||||
/// back to per-tag reads on its own; set this false to read every tag singly.
|
||||
/// back to per-tag reads on its own; set this false to read every tag singly. <see langword="null"/> (the key
|
||||
/// absent) means the default, ON - and null is never written, so a save through any serializer (the AdminUI
|
||||
/// form included) keeps a defaulted driver defaulted instead of pinning today's default (review 07 r0 M9).
|
||||
/// </summary>
|
||||
public bool CoalesceReads { get; init; } = true;
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public bool? CoalesceReads { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Timeout for the AdminUI Test Connect probe, in seconds. The AdminUI clamps to a
|
||||
|
||||
@@ -72,7 +72,7 @@ public sealed partial class AbLegacyDriver
|
||||
/// </summary>
|
||||
private List<ReadUnit> PlanReadUnits(DeviceState device, List<ReadItem> items)
|
||||
{
|
||||
if (!_options.CoalesceReads || items.Count < 2) return [.. items.Select(i => new ReadUnit(i, null))];
|
||||
if (_options.CoalesceReads == false || items.Count < 2) return [.. items.Select(i => new ReadUnit(i, null))];
|
||||
|
||||
var units = new List<ReadUnit>(items.Count);
|
||||
var groups = new Dictionary<(string Letter, int File, AbLegacyDataType Type), List<ReadItem>>();
|
||||
|
||||
@@ -97,7 +97,7 @@ public static class AbLegacyDriverFactoryExtensions
|
||||
ProbeAddress = dto.Probe?.ProbeAddress ?? "S:0",
|
||||
},
|
||||
Timeout = PositiveTimeoutOrDefault(dto.TimeoutMs, 2_000),
|
||||
CoalesceReads = dto.CoalesceReads ?? true,
|
||||
CoalesceReads = dto.CoalesceReads,
|
||||
// Absent ⇒ null ("use the default" = Libplctag, resolved by AbLegacyTransportResolver);
|
||||
// a bad value still fails exactly as any other bad enum field does.
|
||||
Transport = string.IsNullOrWhiteSpace(dto.Transport)
|
||||
|
||||
+3
-2
@@ -208,7 +208,7 @@
|
||||
_loadedProbeTimeout = o.Probe.Timeout,
|
||||
TimeoutSeconds = (int)o.Timeout.TotalSeconds,
|
||||
Transport = o.Transport,
|
||||
CoalesceReads = o.CoalesceReads,
|
||||
CoalesceReads = o.CoalesceReads ?? true,
|
||||
ProbeEnabled = o.Probe.Enabled,
|
||||
ProbeIntervalSeconds = (int)o.Probe.Interval.TotalSeconds,
|
||||
ProbeTimeoutSeconds = (int)o.Probe.Timeout.TotalSeconds,
|
||||
@@ -229,7 +229,8 @@
|
||||
},
|
||||
Timeout = DriverConfigMs.Save(TimeoutSeconds, _loadedTimeout),
|
||||
Transport = Transport,
|
||||
CoalesceReads = CoalesceReads,
|
||||
// On = the default: OMIT the key (never pin a concrete default); off is the only value written.
|
||||
CoalesceReads = CoalesceReads ? null : false,
|
||||
ProbeTimeoutSeconds = AdminProbeTimeoutSeconds,
|
||||
};
|
||||
}
|
||||
|
||||
+1
-1
@@ -180,5 +180,5 @@ public sealed class AbLegacyReadCoalescingTests
|
||||
[InlineData("""{"coalesceReads":false,"devices":[{"hostAddress":"ab://10.0.0.5/1,0"}]}""", false)]
|
||||
[InlineData("""{"coalesceReads":true,"someFutureKey":1,"devices":[{"hostAddress":"ab://10.0.0.5/1,0"}]}""", true)]
|
||||
public void ParseOptions_reads_coalesceReads_defaulting_true(string json, bool expected) =>
|
||||
AbLegacyDriverFactoryExtensions.ParseOptions("drv", json).CoalesceReads.ShouldBe(expected);
|
||||
(AbLegacyDriverFactoryExtensions.ParseOptions("drv", json).CoalesceReads != false).ShouldBe(expected);
|
||||
}
|
||||
|
||||
+6
-2
@@ -269,8 +269,12 @@ public sealed class AbLegacyDriverPageFormSerializationTests
|
||||
|
||||
var saved = AbLegacyDriverForm.Serialize(form.ToOptions(loaded.Devices), original);
|
||||
|
||||
ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.AbLegacyDriverFactoryExtensions.ParseOptions("t", saved)
|
||||
.CoalesceReads.ShouldBe(edited);
|
||||
(ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.AbLegacyDriverFactoryExtensions.ParseOptions("t", saved)
|
||||
.CoalesceReads != false).ShouldBe(edited);
|
||||
// Review 07 r0 M9: ON is the default and is OMITTED, never pinned; only an explicit off is written.
|
||||
var root = System.Text.Json.Nodes.JsonNode.Parse(saved)!.AsObject();
|
||||
if (edited) root.ContainsKey("coalesceReads").ShouldBeFalse();
|
||||
else root["coalesceReads"]!.GetValue<bool>().ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user