fix(code-review): resolve OpcUaServer-001 — UNS Area/Line rename refreshes folder DisplayName
A rename-only deploy produced an IsEmpty plan that short-circuited before MaterialiseHierarchy, leaving the OPC UA folder DisplayName stale. AddressSpacePlanner now diffs UnsAreas/UnsLines by stable id into a RenamedFolders set (counted in IsEmpty); the applier refreshes the folder in place via a new UpdateFolderDisplayName on ISurgicalAddressSpaceSink (forwarded through DeferredAddressSpaceSink so it is NOT inert on driver hosts; falls back to rebuild when the sink is non-surgical). DeploymentArtifact byte-parity untouched (rename rides the existing Name round-trip). No EF migration, no serialized wire/proto contract change. +13 OpcUaServer tests, Runtime rebuild test.
This commit is contained in:
@@ -75,7 +75,9 @@ public sealed class AddressSpaceApplier
|
||||
var changedCount =
|
||||
plan.ChangedEquipment.Count + plan.ChangedDrivers.Count + plan.ChangedAlarms.Count +
|
||||
plan.ChangedEquipmentTags.Count +
|
||||
plan.ChangedEquipmentVirtualTags.Count;
|
||||
plan.ChangedEquipmentVirtualTags.Count +
|
||||
// OpcUaServer-001: a UNS Area/Line rename is an in-place change to an existing folder node.
|
||||
plan.RenamedFolders.Count;
|
||||
var addedCount =
|
||||
plan.AddedEquipment.Count + plan.AddedDrivers.Count + plan.AddedAlarms.Count +
|
||||
plan.AddedEquipmentTags.Count +
|
||||
@@ -113,6 +115,11 @@ public sealed class AddressSpaceApplier
|
||||
plan.ChangedEquipmentVirtualTags.Any(d => !VtagDeltaIsNodeIrrelevant(d));
|
||||
|
||||
var surgicalTagDeltas = plan.ChangedEquipmentTags.Where(TagDeltaIsSurgicalEligible).ToList();
|
||||
// OpcUaServer-001 — UNS Area / Line renames are surgically applicable (in-place DisplayName swap)
|
||||
// when no structural rebuild fires. When a rebuild DOES fire (any add/remove/structural change),
|
||||
// MaterialiseHierarchy re-creates every folder with the new display names, so the renames are
|
||||
// covered for free and need no separate surgical pass.
|
||||
var renamedFolders = plan.RenamedFolders;
|
||||
var rebuilt = false;
|
||||
|
||||
if (structuralRebuild)
|
||||
@@ -120,12 +127,24 @@ public sealed class AddressSpaceApplier
|
||||
SafeRebuild();
|
||||
rebuilt = true;
|
||||
}
|
||||
else if (surgicalTagDeltas.Count > 0)
|
||||
else if (surgicalTagDeltas.Count > 0 || renamedFolders.Count > 0)
|
||||
{
|
||||
if (_sink is ISurgicalAddressSpaceSink surgical)
|
||||
{
|
||||
var allApplied = true;
|
||||
foreach (var d in surgicalTagDeltas)
|
||||
// Folder renames first — an in-place DisplayName swap on the existing Area/Line folder.
|
||||
foreach (var rename in renamedFolders)
|
||||
{
|
||||
bool ok;
|
||||
try { ok = surgical.UpdateFolderDisplayName(rename.FolderNodeId, rename.NewDisplayName); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "AddressSpaceApplier: surgical UpdateFolderDisplayName threw for {Node}", rename.FolderNodeId);
|
||||
ok = false;
|
||||
}
|
||||
if (!ok) { allApplied = false; break; }
|
||||
}
|
||||
foreach (var d in allApplied ? surgicalTagDeltas : Enumerable.Empty<AddressSpacePlan.EquipmentTagDelta>())
|
||||
{
|
||||
// Compute the node id + writable + historian + shape EXACTLY as MaterialiseEquipmentTags
|
||||
// would so the in-place update matches what a rebuild would have produced. Array tags are
|
||||
@@ -155,8 +174,8 @@ public sealed class AddressSpaceApplier
|
||||
}
|
||||
|
||||
_logger.LogInformation(
|
||||
"AddressSpaceApplier: applied plan (added={Added}, removed={Removed}, changed={Changed}, surgicalTags={Surgical}, rebuild={Rebuild})",
|
||||
addedCount, removedCount, changedCount, rebuilt ? 0 : surgicalTagDeltas.Count, rebuilt);
|
||||
"AddressSpaceApplier: applied plan (added={Added}, removed={Removed}, changed={Changed}, surgicalTags={Surgical}, renamedFolders={Renamed}, rebuild={Rebuild})",
|
||||
addedCount, removedCount, changedCount, rebuilt ? 0 : surgicalTagDeltas.Count, rebuilt ? 0 : renamedFolders.Count, rebuilt);
|
||||
|
||||
return new AddressSpaceApplyOutcome(removedCount, addedCount, changedCount, rebuilt);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user