diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Services/BindingGroupBuilder.cs b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Services/BindingGroupBuilder.cs
index 43b2cb77..e801b162 100644
--- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Services/BindingGroupBuilder.cs
+++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Services/BindingGroupBuilder.cs
@@ -114,4 +114,22 @@ public static class BindingGroupBuilder
}
return any ? (candidate, false) : (null, false);
}
+
+ /// Result of a bulk apply for one member.
+ public sealed record BindingChange(string CanonicalName, int? ConnectionId, string? Override);
+
+ ///
+ /// Rewrites every member of to
+ /// {objectPath}.{memberTail} on . A blank
+ /// clears the overrides (template default applies) and
+ /// leaves the connection as given. Surrounding whitespace and a trailing '.' are tolerated.
+ ///
+ public static IReadOnlyList ApplyObject(BindingGroup group, int? connectionId, string? objectPath)
+ {
+ var obj = (objectPath ?? string.Empty).Trim().TrimEnd('.');
+ return group.Members
+ .Select(m => new BindingChange(m.Row.CanonicalName, connectionId,
+ obj.Length == 0 ? null : obj + "." + m.MemberTail))
+ .ToList();
+ }
}
diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Services/BindingGroupBuilderTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Services/BindingGroupBuilderTests.cs
index 21b969a4..488c4901 100644
--- a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Services/BindingGroupBuilderTests.cs
+++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Services/BindingGroupBuilderTests.cs
@@ -111,4 +111,35 @@ public class BindingGroupBuilderTests
var rows = new[] { Row("M.A", "A", 1, "Obj.A"), Row("M.B", "B", 1, "Obj.Other") };
Assert.True(Assert.Single(BindingGroupBuilder.Build(rows)).IsObjectMixed);
}
+
+ [Fact]
+ public void ApplyObject_RewritesEveryMember_AndSetsConnection()
+ {
+ var g = Assert.Single(BindingGroupBuilder.Build(new[]
+ {
+ Row("RightMESReceiver.MoveInFlag", "MoveInFlag", 1, "MESReceiver_024.MoveInFlag"),
+ Row("RightMESReceiver.ScanStateCmd", "ScanStateCmd", null, null),
+ }));
+ var changes = BindingGroupBuilder.ApplyObject(g, connectionId: 1, objectPath: " MESReceiver_022. ");
+ Assert.Equal(2, changes.Count);
+ Assert.Equal(("RightMESReceiver.MoveInFlag", 1, "MESReceiver_022.MoveInFlag"), (changes[0].CanonicalName, changes[0].ConnectionId, changes[0].Override));
+ Assert.Equal(("RightMESReceiver.ScanStateCmd", 1, "MESReceiver_022.ScanStateCmd"), (changes[1].CanonicalName, changes[1].ConnectionId, changes[1].Override));
+ }
+
+ [Fact]
+ public void ApplyObject_NestedTail_KeepsNesting()
+ {
+ var g = Assert.Single(BindingGroupBuilder.Build(new[] { Row("LeftReactorSide.LeakTest.DeltaVac", "DeltaVac", 1, null) }));
+ var c = Assert.Single(BindingGroupBuilder.ApplyObject(g, 1, "Left_002"));
+ Assert.Equal("Left_002.LeakTest.DeltaVac", c.Override);
+ }
+
+ [Fact]
+ public void ApplyObject_EmptyObject_ClearsOverrides_KeepsConnection()
+ {
+ var g = Assert.Single(BindingGroupBuilder.Build(new[] { Row("M.A", "A", 1, "Obj.A") }));
+ var c = Assert.Single(BindingGroupBuilder.ApplyObject(g, 2, ""));
+ Assert.Null(c.Override);
+ Assert.Equal(2, c.ConnectionId);
+ }
}