Complete OPC UA data flow: binding UI, flattening connections, real OPC UA client

- Add connection binding UI to Instances page (per-attribute and bulk assign)
- FlatteningService populates Connections dict from bound data connections
- Real OPC UA client using OPC Foundation SDK for live tag subscriptions
- DataConnectionFactory uses RealOpcUaClientFactory by default
- OpcUaDataConnection supports both "endpoint" and "EndpointUrl" config keys
This commit is contained in:
Joseph Doherty
2026-03-17 11:40:39 -04:00
parent dfb809a909
commit 8e1d0816b3
6 changed files with 366 additions and 2 deletions

View File

@@ -77,6 +77,25 @@ public class FlatteningService
// Step 7: Resolve alarm on-trigger script references to canonical names
ResolveAlarmScriptReferences(alarms, scripts);
// Step 8: Collect connection configurations for deployment packaging
var connections = new Dictionary<string, ConnectionConfig>();
foreach (var attr in attributes.Values)
{
if (attr.BoundDataConnectionId.HasValue &&
!string.IsNullOrEmpty(attr.BoundDataConnectionName) &&
!connections.ContainsKey(attr.BoundDataConnectionName))
{
if (dataConnections.TryGetValue(attr.BoundDataConnectionId.Value, out var conn))
{
connections[attr.BoundDataConnectionName] = new ConnectionConfig
{
Protocol = conn.Protocol,
ConfigurationJson = conn.Configuration
};
}
}
}
var config = new FlattenedConfiguration
{
InstanceUniqueName = instance.UniqueName,
@@ -86,6 +105,7 @@ public class FlatteningService
Attributes = attributes.Values.OrderBy(a => a.CanonicalName, StringComparer.Ordinal).ToList(),
Alarms = alarms.Values.OrderBy(a => a.CanonicalName, StringComparer.Ordinal).ToList(),
Scripts = scripts.Values.OrderBy(s => s.CanonicalName, StringComparer.Ordinal).ToList(),
Connections = connections.Count > 0 ? connections : null,
GeneratedAtUtc = DateTimeOffset.UtcNow
};