diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/InstanceConfigure.razor b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/InstanceConfigure.razor index 109cb0ca..ddd95f87 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/InstanceConfigure.razor +++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/InstanceConfigure.razor @@ -9,6 +9,7 @@ @using ZB.MOM.WW.ScadaBridge.TemplateEngine.Flattening @using ZB.MOM.WW.ScadaBridge.TemplateEngine.Services @using ZB.MOM.WW.ScadaBridge.DeploymentManager +@using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Dialogs @attribute [Authorize(Policy = AuthorizationPolicies.RequireDeployment)] @inject ITemplateEngineRepository TemplateEngineRepository @inject ISiteRepository SiteRepository @@ -108,17 +109,22 @@ Attribute Tag Path Connection + Override + @foreach (var attr in _bindingDataSourceAttrs) { + var connId = GetBindingConnectionId(attr.Name); + var canBrowse = connId > 0; + var isOpcUa = IsOpcUa(connId); @attr.Name @attr.DataSourceReference + + + + + @if (isOpcUa) + { + + } + } @@ -331,6 +354,14 @@ + + @* OPC UA Tag Browser dialog (Task 18) — rendered once; OpenBrowser + tracks which binding row's override input receives the picked node id. *@ + } @@ -350,8 +381,24 @@ private List _bindingDataSourceAttrs = new(); private List _siteConnections = new(); private Dictionary _bindingSelections = new(); + /// + /// Per-attribute DataSourceReferenceOverride values (Task 18). Mirrors + /// by attribute name. Loaded from the + /// existing rows on init; round-tripped + /// through on SaveBindings. + /// + private Dictionary _bindingOverrides = new(); private int _bulkConnectionId; + // OPC UA tag browser (Task 18) — single dialog rendered at page bottom; + // _browserAttrInEdit tracks which row gets the picked node id on Select. + private OpcUaBrowserDialog? _browserRef; + private string? _browserAttrInEdit; + private string _browserSiteIdentifier = ""; + private string _browserConnectionName = ""; + private string? _browserInitial; + private string _siteIdentifier = ""; + // Overrides private List _overrideAttrs = new(); private Dictionary _overrideValues = new(); @@ -407,7 +454,11 @@ _templateName = template?.Name ?? $"#{_instance.TemplateId}"; var sites = await SiteRepository.GetAllSitesAsync(); - _siteName = sites.FirstOrDefault(s => s.Id == _instance.SiteId)?.Name ?? $"#{_instance.SiteId}"; + var site = sites.FirstOrDefault(s => s.Id == _instance.SiteId); + _siteName = site?.Name ?? $"#{_instance.SiteId}"; + // Task 18: cache the site's machine identifier — the OPC UA browse + // dialog routes by SiteIdentifier (string), not the numeric site id. + _siteIdentifier = site?.SiteIdentifier ?? ""; // Areas _siteAreas = (await TemplateEngineRepository.GetAreasBySiteIdAsync(_instance.SiteId)).ToList(); @@ -420,7 +471,11 @@ _siteConnections = (await SiteRepository.GetDataConnectionsBySiteIdAsync(_instance.SiteId)).ToList(); var existingBindings = await TemplateEngineRepository.GetBindingsByInstanceIdAsync(Id); foreach (var b in existingBindings) + { _bindingSelections[b.AttributeName] = b.DataConnectionId; + if (!string.IsNullOrEmpty(b.DataSourceReferenceOverride)) + _bindingOverrides[b.AttributeName] = b.DataSourceReferenceOverride; + } // Overrides _overrideAttrs = attrs.Where(a => !a.IsLocked).ToList(); @@ -474,12 +529,74 @@ _bindingSelections[attr.Name] = _bulkConnectionId; } + // ── Task 18: per-attribute override input + OPC UA tag browser ────────── + + private string? GetOverrideForAttr(string attrName) + => _bindingOverrides.GetValueOrDefault(attrName); + + private void OnOverrideForAttrChanged(string attrName, ChangeEventArgs e) + { + var val = e.Value?.ToString(); + if (string.IsNullOrWhiteSpace(val)) + _bindingOverrides.Remove(attrName); + else + _bindingOverrides[attrName] = val; + } + + /// Looks up the template default DataSourceReference for an attribute. + private string? GetTemplateDefault(string attrName) + => _bindingDataSourceAttrs.FirstOrDefault(a => a.Name == attrName)?.DataSourceReference; + + /// True when the row's selected data connection is OPC UA. + private bool IsOpcUa(int connectionId) + => connectionId > 0 + && string.Equals( + _siteConnections.FirstOrDefault(c => c.Id == connectionId)?.Protocol, + "OpcUa", + StringComparison.OrdinalIgnoreCase); + + /// + /// Opens the OPC UA tag browser dialog for the given attribute row. Remembers + /// which attribute is being edited so can + /// write the picked node id back to the right override input. + /// + private async Task OpenBrowser(string attrName) + { + var connId = GetBindingConnectionId(attrName); + var conn = _siteConnections.FirstOrDefault(c => c.Id == connId); + if (conn is null) return; + + _browserAttrInEdit = attrName; + _browserConnectionName = conn.Name; + _browserSiteIdentifier = _siteIdentifier; + _browserInitial = _bindingOverrides.GetValueOrDefault(attrName) + ?? GetTemplateDefault(attrName); + + if (_browserRef is not null) + await _browserRef.ShowAsync(); + } + + private void OnBrowserSelected(string nodeId) + { + if (_browserAttrInEdit is null) return; + _bindingOverrides[_browserAttrInEdit] = nodeId; + _browserAttrInEdit = null; + } + private async Task SaveBindings() { _saving = true; try { - var bindings = _bindingSelections.Select(kv => new ConnectionBinding(kv.Key, kv.Value)).ToList(); + // Task 18: include the per-attribute DataSourceReferenceOverride on + // the wire record so it round-trips through SetConnectionBindingsAsync + // into the InstanceConnectionBinding entity. + var bindings = _bindingSelections + .Select(kv => new ConnectionBinding( + kv.Key, + kv.Value, + _bindingOverrides.GetValueOrDefault(kv.Key))) + .ToList(); var user = await GetCurrentUserAsync(); var result = await InstanceService.SetConnectionBindingsAsync(Id, bindings, user); if (result.IsSuccess)