@page "/clusters/{ClusterId}" @using Microsoft.AspNetCore.Components.Web @using Microsoft.AspNetCore.SignalR.Client @using ZB.MOM.WW.OtOpcUa.Admin.Hubs @using ZB.MOM.WW.OtOpcUa.Admin.Services @using ZB.MOM.WW.OtOpcUa.Configuration.Entities @using ZB.MOM.WW.OtOpcUa.Configuration.Enums @implements IAsyncDisposable @rendermode RenderMode.InteractiveServer @inject ClusterService ClusterSvc @inject GenerationService GenerationSvc @inject NavigationManager Nav @if (_cluster is null) {

Loading…

} else { @if (_liveBanner is not null) {
Live update: @_liveBanner
}

@_cluster.Name

@_cluster.ClusterId @if (!_cluster.Enabled) { Disabled }
@if (_currentDraft is not null) { Edit current draft (gen @_currentDraft.GenerationId) } else { }
@if (_tab == "overview") {
Enterprise / Site
@_cluster.Enterprise / @_cluster.Site
Redundancy
@_cluster.RedundancyMode (@_cluster.NodeCount node@(_cluster.NodeCount == 1 ? "" : "s"))
Current published
@if (_currentPublished is not null) { @_currentPublished.GenerationId (@_currentPublished.PublishedAt?.ToString("u")) } else { none published yet }
Created
@_cluster.CreatedAt.ToString("u") by @_cluster.CreatedBy
} else if (_tab == "generations") { } else if (_tab == "equipment" && _currentDraft is not null) { } else if (_tab == "uns" && _currentDraft is not null) { } else if (_tab == "namespaces" && _currentDraft is not null) { } else if (_tab == "drivers" && _currentDraft is not null) { } else if (_tab == "acls" && _currentDraft is not null) { } else if (_tab == "audit") { } else {

Open a draft to edit this cluster's content.

} } @code { [Parameter] public string ClusterId { get; set; } = string.Empty; private ServerCluster? _cluster; private ConfigGeneration? _currentDraft; private ConfigGeneration? _currentPublished; private string _tab = "overview"; private bool _busy; private HubConnection? _hub; private string? _liveBanner; private string Tab(string key) => _tab == key ? "active" : string.Empty; protected override async Task OnInitializedAsync() { await LoadAsync(); await ConnectHubAsync(); } private async Task LoadAsync() { _cluster = await ClusterSvc.FindAsync(ClusterId, CancellationToken.None); var gens = await GenerationSvc.ListRecentAsync(ClusterId, 50, CancellationToken.None); _currentDraft = gens.FirstOrDefault(g => g.Status == GenerationStatus.Draft); _currentPublished = gens.FirstOrDefault(g => g.Status == GenerationStatus.Published); } private async Task ConnectHubAsync() { _hub = new HubConnectionBuilder() .WithUrl(Nav.ToAbsoluteUri("/hubs/fleet")) .WithAutomaticReconnect() .Build(); _hub.On("NodeStateChanged", async msg => { if (msg.ClusterId != ClusterId) return; _liveBanner = $"Node {msg.NodeId}: {msg.LastAppliedStatus ?? "seen"} at {msg.LastAppliedAt?.ToString("u") ?? msg.LastSeenAt?.ToString("u") ?? "-"}"; await LoadAsync(); await InvokeAsync(StateHasChanged); }); await _hub.StartAsync(); await _hub.SendAsync("SubscribeCluster", ClusterId); } private async Task CreateDraftAsync() { _busy = true; try { var draft = await GenerationSvc.CreateDraftAsync(ClusterId, createdBy: "admin-ui", CancellationToken.None); Nav.NavigateTo($"/clusters/{ClusterId}/draft/{draft.GenerationId}"); } finally { _busy = false; } } public async ValueTask DisposeAsync() { if (_hub is not null) await _hub.DisposeAsync(); } }