Add redundancy panel to status dashboard
Shows mode, role, ServiceLevel, ApplicationUri, and redundant server set when redundancy is enabled. Primary renders with a green border, secondary with yellow. Also included in the JSON API response. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -215,7 +215,8 @@ namespace ZB.MOM.WW.LmxOpcUa.Host
|
|||||||
// Step 13: Dashboard
|
// Step 13: Dashboard
|
||||||
_healthCheck = new HealthCheckService();
|
_healthCheck = new HealthCheckService();
|
||||||
_statusReport = new StatusReportService(_healthCheck, _config.Dashboard.RefreshIntervalSeconds);
|
_statusReport = new StatusReportService(_healthCheck, _config.Dashboard.RefreshIntervalSeconds);
|
||||||
_statusReport.SetComponents(effectiveMxClient, _metrics, _galaxyStats, _serverHost, _nodeManager);
|
_statusReport.SetComponents(effectiveMxClient, _metrics, _galaxyStats, _serverHost, _nodeManager,
|
||||||
|
_config.Redundancy, _config.OpcUa.ApplicationUri);
|
||||||
|
|
||||||
if (_config.Dashboard.Enabled)
|
if (_config.Dashboard.Enabled)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Dictionary<string, MetricsStatistics> Operations { get; set; } = new();
|
public Dictionary<string, MetricsStatistics> Operations { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the redundancy state when redundancy is enabled.
|
||||||
|
/// </summary>
|
||||||
|
public RedundancyInfo? Redundancy { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets footer details such as the snapshot timestamp and service version.
|
/// Gets or sets footer details such as the snapshot timestamp and service version.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -160,6 +165,42 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
|||||||
public long TotalEvents { get; set; }
|
public long TotalEvents { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dashboard model for redundancy state. Only populated when redundancy is enabled.
|
||||||
|
/// </summary>
|
||||||
|
public class RedundancyInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether redundancy is enabled.
|
||||||
|
/// </summary>
|
||||||
|
public bool Enabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the redundancy mode (e.g., "Warm", "Hot").
|
||||||
|
/// </summary>
|
||||||
|
public string Mode { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets this instance's role ("Primary" or "Secondary").
|
||||||
|
/// </summary>
|
||||||
|
public string Role { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the current ServiceLevel byte.
|
||||||
|
/// </summary>
|
||||||
|
public byte ServiceLevel { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets this instance's ApplicationUri.
|
||||||
|
/// </summary>
|
||||||
|
public string ApplicationUri { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of all server URIs in the redundant set.
|
||||||
|
/// </summary>
|
||||||
|
public List<string> ServerUris { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Dashboard model for the status page footer.
|
/// Dashboard model for the status page footer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using ZB.MOM.WW.LmxOpcUa.Host.Configuration;
|
||||||
using ZB.MOM.WW.LmxOpcUa.Host.Domain;
|
using ZB.MOM.WW.LmxOpcUa.Host.Domain;
|
||||||
using ZB.MOM.WW.LmxOpcUa.Host.GalaxyRepository;
|
using ZB.MOM.WW.LmxOpcUa.Host.GalaxyRepository;
|
||||||
using ZB.MOM.WW.LmxOpcUa.Host.Metrics;
|
using ZB.MOM.WW.LmxOpcUa.Host.Metrics;
|
||||||
@@ -21,6 +22,8 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
|||||||
private GalaxyRepositoryStats? _galaxyStats;
|
private GalaxyRepositoryStats? _galaxyStats;
|
||||||
private OpcUaServerHost? _serverHost;
|
private OpcUaServerHost? _serverHost;
|
||||||
private LmxNodeManager? _nodeManager;
|
private LmxNodeManager? _nodeManager;
|
||||||
|
private RedundancyConfiguration? _redundancyConfig;
|
||||||
|
private string? _applicationUri;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new status report service for the dashboard using the supplied health-check policy and refresh interval.
|
/// Initializes a new status report service for the dashboard using the supplied health-check policy and refresh interval.
|
||||||
@@ -43,13 +46,16 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
|||||||
/// <param name="nodeManager">The node manager whose queue depth and MXAccess event throughput should be surfaced on the dashboard.</param>
|
/// <param name="nodeManager">The node manager whose queue depth and MXAccess event throughput should be surfaced on the dashboard.</param>
|
||||||
public void SetComponents(IMxAccessClient? mxAccessClient, PerformanceMetrics? metrics,
|
public void SetComponents(IMxAccessClient? mxAccessClient, PerformanceMetrics? metrics,
|
||||||
GalaxyRepositoryStats? galaxyStats, OpcUaServerHost? serverHost,
|
GalaxyRepositoryStats? galaxyStats, OpcUaServerHost? serverHost,
|
||||||
LmxNodeManager? nodeManager = null)
|
LmxNodeManager? nodeManager = null,
|
||||||
|
RedundancyConfiguration? redundancyConfig = null, string? applicationUri = null)
|
||||||
{
|
{
|
||||||
_mxAccessClient = mxAccessClient;
|
_mxAccessClient = mxAccessClient;
|
||||||
_metrics = metrics;
|
_metrics = metrics;
|
||||||
_galaxyStats = galaxyStats;
|
_galaxyStats = galaxyStats;
|
||||||
_serverHost = serverHost;
|
_serverHost = serverHost;
|
||||||
_nodeManager = nodeManager;
|
_nodeManager = nodeManager;
|
||||||
|
_redundancyConfig = redundancyConfig;
|
||||||
|
_applicationUri = applicationUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -90,6 +96,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
|||||||
TotalEvents = _nodeManager?.TotalMxChangeEvents ?? 0
|
TotalEvents = _nodeManager?.TotalMxChangeEvents ?? 0
|
||||||
},
|
},
|
||||||
Operations = _metrics?.GetStatistics() ?? new(),
|
Operations = _metrics?.GetStatistics() ?? new(),
|
||||||
|
Redundancy = BuildRedundancyInfo(),
|
||||||
Footer = new FooterInfo
|
Footer = new FooterInfo
|
||||||
{
|
{
|
||||||
Timestamp = DateTime.UtcNow,
|
Timestamp = DateTime.UtcNow,
|
||||||
@@ -98,6 +105,30 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RedundancyInfo? BuildRedundancyInfo()
|
||||||
|
{
|
||||||
|
if (_redundancyConfig == null || !_redundancyConfig.Enabled)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var mxConnected = (_mxAccessClient?.State ?? ConnectionState.Disconnected) == ConnectionState.Connected;
|
||||||
|
var dbConnected = _galaxyStats?.DbConnected ?? false;
|
||||||
|
var isPrimary = string.Equals(_redundancyConfig.Role, "Primary", StringComparison.OrdinalIgnoreCase);
|
||||||
|
var baseLevel = isPrimary
|
||||||
|
? _redundancyConfig.ServiceLevelBase
|
||||||
|
: Math.Max(0, _redundancyConfig.ServiceLevelBase - 50);
|
||||||
|
var calculator = new ServiceLevelCalculator();
|
||||||
|
|
||||||
|
return new RedundancyInfo
|
||||||
|
{
|
||||||
|
Enabled = true,
|
||||||
|
Mode = _redundancyConfig.Mode,
|
||||||
|
Role = _redundancyConfig.Role,
|
||||||
|
ServiceLevel = calculator.Calculate(baseLevel, mxConnected, dbConnected),
|
||||||
|
ApplicationUri = _applicationUri ?? "",
|
||||||
|
ServerUris = new System.Collections.Generic.List<string>(_redundancyConfig.ServerUris)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates the operator-facing HTML dashboard for the current bridge status.
|
/// Generates the operator-facing HTML dashboard for the current bridge status.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -131,6 +162,17 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
|||||||
sb.AppendLine($"<p>Status: <b>{data.Health.Status}</b> — {data.Health.Message}</p>");
|
sb.AppendLine($"<p>Status: <b>{data.Health.Status}</b> — {data.Health.Message}</p>");
|
||||||
sb.AppendLine("</div>");
|
sb.AppendLine("</div>");
|
||||||
|
|
||||||
|
// Redundancy panel (only when enabled)
|
||||||
|
if (data.Redundancy != null)
|
||||||
|
{
|
||||||
|
var roleColor = data.Redundancy.Role == "Primary" ? "green" : "yellow";
|
||||||
|
sb.AppendLine($"<div class='panel {roleColor}'><h2>Redundancy</h2>");
|
||||||
|
sb.AppendLine($"<p>Mode: <b>{data.Redundancy.Mode}</b> | Role: <b>{data.Redundancy.Role}</b> | Service Level: <b>{data.Redundancy.ServiceLevel}</b></p>");
|
||||||
|
sb.AppendLine($"<p>Application URI: {data.Redundancy.ApplicationUri}</p>");
|
||||||
|
sb.AppendLine($"<p>Redundant Set: {string.Join(", ", data.Redundancy.ServerUris)}</p>");
|
||||||
|
sb.AppendLine("</div>");
|
||||||
|
}
|
||||||
|
|
||||||
// Subscriptions panel
|
// Subscriptions panel
|
||||||
sb.AppendLine("<div class='panel gray'><h2>Subscriptions</h2>");
|
sb.AppendLine("<div class='panel gray'><h2>Subscriptions</h2>");
|
||||||
sb.AppendLine($"<p>Active: {data.Subscriptions.ActiveCount}</p>");
|
sb.AppendLine($"<p>Active: {data.Subscriptions.ActiveCount}</p>");
|
||||||
|
|||||||
Reference in New Issue
Block a user