fix(virtualtags): a disabled VirtualTag keeps its node and reads BadOutOfService (lmxopcua#787, owner decision)
v2-ci / unit-tests (push) Has been cancelled
v2-ci / unit-tests (push) Has been cancelled
Revises 963b0d66, which dropped a disabled VirtualTag from the runtime plan so a
bound client got BadNodeIdUnknown. Now the plan carries Enabled (part of value
equality): AddressSpaceComposer.Compose and DeploymentArtifact.BuildEquipmentVirtualTagPlans
keep the row with Enabled=false (byte-parity), the applier materialises the node as
normal, and VirtualTagHostActor spawns no VirtualTagActor for it but publishes
BadOutOfService (new OpcUaStatus constant) with a null value stamped at deploy, on
every apply. Enabled flips are node-irrelevant to the change classifier; enabled->disabled
stops the child, disabled->enabled resumes evaluation. Validator exemption and name
uniqueness unchanged. Scripted alarms keep their not-materialised convention.
Tests updated; new wire test DisabledVirtualTagWireTests. docs/VirtualTags.md updated.
Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
@@ -1405,9 +1405,10 @@ internal sealed class ParsedDeploymentArtifact : IDisposable
|
||||
if (string.IsNullOrWhiteSpace(virtualTagId) || string.IsNullOrWhiteSpace(equipmentId)
|
||||
|| string.IsNullOrWhiteSpace(name)) continue;
|
||||
|
||||
// Enabled (lmxopcua#787): a disabled VirtualTag is not deployed or evaluated — byte-parity with
|
||||
// AddressSpaceComposer.Compose's `.Where(v => v.Enabled)`. Absent/non-bool => the entity default (true).
|
||||
if (el.TryGetProperty("Enabled", out var enEl) && enEl.ValueKind == JsonValueKind.False) continue;
|
||||
// Enabled (lmxopcua#787): a disabled VirtualTag keeps its plan (node materialised, never evaluated,
|
||||
// BadOutOfService) — byte-parity with AddressSpaceComposer.Compose's `Enabled: v.Enabled`.
|
||||
// Absent/non-bool => the entity default (true).
|
||||
var enabled = !(el.TryGetProperty("Enabled", out var enEl) && enEl.ValueKind == JsonValueKind.False);
|
||||
|
||||
var source = scriptId is not null && scriptSourceById.TryGetValue(scriptId, out var src)
|
||||
? src : string.Empty;
|
||||
@@ -1442,7 +1443,8 @@ internal sealed class ParsedDeploymentArtifact : IDisposable
|
||||
DependencyRefs: EquipmentScriptPaths.ExtractDependencyRefs(expanded),
|
||||
Historize: historize,
|
||||
ChangeTriggered: changeTriggered,
|
||||
TimerIntervalMs: timerMs));
|
||||
TimerIntervalMs: timerMs,
|
||||
Enabled: enabled));
|
||||
}
|
||||
|
||||
result.Sort((a, b) =>
|
||||
|
||||
Reference in New Issue
Block a user