diff --git a/src/ZB.MOM.WW.ScadaBridge.Commons/Messages/Management/InstanceCommands.cs b/src/ZB.MOM.WW.ScadaBridge.Commons/Messages/Management/InstanceCommands.cs
index 9e13c71f..e7fcb0e9 100644
--- a/src/ZB.MOM.WW.ScadaBridge.Commons/Messages/Management/InstanceCommands.cs
+++ b/src/ZB.MOM.WW.ScadaBridge.Commons/Messages/Management/InstanceCommands.cs
@@ -12,8 +12,17 @@ public record MgmtDeleteInstanceCommand(int InstanceId);
/// . This is a named record (not a
/// ValueTuple) so it serializes with stable, named JSON properties and can
/// evolve additively per REQ-COM-5a.
+///
+/// DataSourceReferenceOverride is an optional per-instance override of
+/// the OPC UA node id (or other protocol address) for the bound attribute.
+/// When non-null it replaces the template's DataSourceReference at
+/// flattening time; when null the template default is used.
+///
///
-public record ConnectionBinding(string AttributeName, int DataConnectionId);
+public record ConnectionBinding(
+ string AttributeName,
+ int DataConnectionId,
+ string? DataSourceReferenceOverride = null);
public record SetConnectionBindingsCommand(int InstanceId, IReadOnlyList Bindings);
public record SetInstanceOverridesCommand(int InstanceId, IReadOnlyDictionary Overrides);
diff --git a/src/ZB.MOM.WW.ScadaBridge.TemplateEngine/Services/InstanceService.cs b/src/ZB.MOM.WW.ScadaBridge.TemplateEngine/Services/InstanceService.cs
index 0dd483a5..1c69a847 100644
--- a/src/ZB.MOM.WW.ScadaBridge.TemplateEngine/Services/InstanceService.cs
+++ b/src/ZB.MOM.WW.ScadaBridge.TemplateEngine/Services/InstanceService.cs
@@ -330,20 +330,22 @@ public class InstanceService
var results = new List();
- foreach (var (attrName, connId) in bindings)
+ foreach (var b in bindings)
{
- if (existingMap.TryGetValue(attrName, out var existing))
+ if (existingMap.TryGetValue(b.AttributeName, out var existing))
{
- existing.DataConnectionId = connId;
+ existing.DataConnectionId = b.DataConnectionId;
+ existing.DataSourceReferenceOverride = b.DataSourceReferenceOverride;
await _repository.UpdateInstanceConnectionBindingAsync(existing, cancellationToken);
results.Add(existing);
}
else
{
- var binding = new InstanceConnectionBinding(attrName)
+ var binding = new InstanceConnectionBinding(b.AttributeName)
{
InstanceId = instanceId,
- DataConnectionId = connId
+ DataConnectionId = b.DataConnectionId,
+ DataSourceReferenceOverride = b.DataSourceReferenceOverride
};
await _repository.AddInstanceConnectionBindingAsync(binding, cancellationToken);
results.Add(binding);
diff --git a/tests/ZB.MOM.WW.ScadaBridge.Commons.Tests/Messages/ConnectionBindingSerializationTests.cs b/tests/ZB.MOM.WW.ScadaBridge.Commons.Tests/Messages/ConnectionBindingSerializationTests.cs
index 9a7e504c..856aaf5a 100644
--- a/tests/ZB.MOM.WW.ScadaBridge.Commons.Tests/Messages/ConnectionBindingSerializationTests.cs
+++ b/tests/ZB.MOM.WW.ScadaBridge.Commons.Tests/Messages/ConnectionBindingSerializationTests.cs
@@ -51,4 +51,32 @@ public class ConnectionBindingSerializationTests
// ConnectionBinding is a record: each element compares by value.
Assert.Equal(original.Bindings, deserialized.Bindings);
}
+
+ [Fact]
+ public void Roundtrip_preserves_override_when_set()
+ {
+ var original = new ConnectionBinding("Speed", 7, "ns=2;s=Pump1.Speed");
+
+ var json = JsonSerializer.Serialize(original);
+ var roundtripped = JsonSerializer.Deserialize(json);
+
+ Assert.NotNull(roundtripped);
+ Assert.Equal(original, roundtripped);
+ Assert.Equal("ns=2;s=Pump1.Speed", roundtripped!.DataSourceReferenceOverride);
+ }
+
+ [Fact]
+ public void Roundtrip_defaults_override_to_null_when_absent()
+ {
+ // Older site builds will not emit the new field — deserialization
+ // must produce a null override and equal an explicit-null instance.
+ const string legacyJson = """{"AttributeName":"Speed","DataConnectionId":7}""";
+
+ var deserialized = JsonSerializer.Deserialize(legacyJson);
+
+ Assert.NotNull(deserialized);
+ Assert.Equal("Speed", deserialized!.AttributeName);
+ Assert.Equal(7, deserialized.DataConnectionId);
+ Assert.Null(deserialized.DataSourceReferenceOverride);
+ }
}