fix(opcua): carry native alarm condition state across a full rebuild (#641)
A full RebuildAddressSpace recreated every native Part 9 condition inactive + acknowledged, and when the alarm-tag set was unchanged (e.g. one UnsTagReference added to an alarm-bearing raw tag) nothing re-told the new node its state: the host does not re-subscribe for an unchanged set, and Galaxy / OpcUaClient have no snapshot to replay anyway. Active alarms read normal until their next device transition. OtOpcUaNodeManager now captures each NATIVE condition's live state (from the node, so a client-applied ack is kept) before the rebuild clears, restores it when the same id is re-materialised native in the same apply, and reports ONE condition event per restored off-normal condition at the new IOpcUaAddressSpaceSink.CompleteAddressSpaceApply (after notifier wiring). Unclaimed captures are discarded there, so a later re-add is fresh. Quality is carried silently for normal conditions too (it is only re-annotated on a connectivity edge). Scripted conditions are untouched (#487 re-asserts them). OpcUaPublishActor calls CompleteAddressSpaceApply at the end of every apply. Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
+5
-3
@@ -80,11 +80,13 @@ public class DeferredSinkForwardingReflectionTests
|
||||
// v3 B4-WP2 dual-namespace invariant: a node id alone is no longer globally unique across the two
|
||||
// namespaces (Raw vs UNS), so every sink method that NAMES a node must carry an AddressSpaceRealm
|
||||
// discriminator — the sink resolves the namespace from the realm rather than parsing the id string.
|
||||
// RebuildAddressSpace is the sole node-naming-free method (it clears BOTH realms). This guard locks in
|
||||
// the realm surface so a future method can't be added that names a node without a realm.
|
||||
// RebuildAddressSpace (it clears BOTH realms) and CompleteAddressSpaceApply (it closes an apply pass,
|
||||
// Gitea #641) are the only node-naming-free methods. This guard locks in the realm surface so a future
|
||||
// method can't be added that names a node without a realm.
|
||||
foreach (var method in ForwardingInterfaces.SelectMany(i => i.GetMethods()))
|
||||
{
|
||||
if (method.Name == nameof(IOpcUaAddressSpaceSink.RebuildAddressSpace)) continue;
|
||||
if (method.Name is nameof(IOpcUaAddressSpaceSink.RebuildAddressSpace)
|
||||
or nameof(IOpcUaAddressSpaceSink.CompleteAddressSpaceApply)) continue;
|
||||
|
||||
method.GetParameters().Any(p => CarriesRealm(p.ParameterType)).ShouldBeTrue(
|
||||
$"{method.DeclaringType!.Name}.{method.Name} names a node but has no AddressSpaceRealm parameter — " +
|
||||
|
||||
Reference in New Issue
Block a user