Apply code style formatting and restore partial modifiers on Avalonia views
Linter/formatter pass across the full codebase. Restores required partial keyword on AXAML code-behind classes that the formatter incorrectly removed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using ZB.MOM.WW.LmxOpcUa.Host.Configuration;
|
||||
@@ -10,24 +11,25 @@ using ZB.MOM.WW.LmxOpcUa.Host.OpcUa;
|
||||
namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
{
|
||||
/// <summary>
|
||||
/// Aggregates status from all components and generates HTML/JSON reports. (DASH-001 through DASH-009)
|
||||
/// Aggregates status from all components and generates HTML/JSON reports. (DASH-001 through DASH-009)
|
||||
/// </summary>
|
||||
public class StatusReportService
|
||||
{
|
||||
private readonly HealthCheckService _healthCheck;
|
||||
private readonly int _refreshIntervalSeconds;
|
||||
private readonly DateTime _startTime = DateTime.UtcNow;
|
||||
private string? _applicationUri;
|
||||
private GalaxyRepositoryStats? _galaxyStats;
|
||||
private PerformanceMetrics? _metrics;
|
||||
|
||||
private IMxAccessClient? _mxAccessClient;
|
||||
private PerformanceMetrics? _metrics;
|
||||
private GalaxyRepositoryStats? _galaxyStats;
|
||||
private OpcUaServerHost? _serverHost;
|
||||
private LmxNodeManager? _nodeManager;
|
||||
private RedundancyConfiguration? _redundancyConfig;
|
||||
private string? _applicationUri;
|
||||
private OpcUaServerHost? _serverHost;
|
||||
|
||||
/// <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.
|
||||
/// </summary>
|
||||
/// <param name="healthCheck">The health-check component used to derive the overall dashboard health status.</param>
|
||||
/// <param name="refreshIntervalSeconds">The HTML auto-refresh interval, in seconds, for the dashboard page.</param>
|
||||
@@ -38,13 +40,16 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Supplies the live bridge components whose status should be reflected in generated dashboard snapshots.
|
||||
/// Supplies the live bridge components whose status should be reflected in generated dashboard snapshots.
|
||||
/// </summary>
|
||||
/// <param name="mxAccessClient">The runtime client whose connection and subscription state should be reported.</param>
|
||||
/// <param name="metrics">The performance metrics collector whose operation statistics should be reported.</param>
|
||||
/// <param name="galaxyStats">The Galaxy repository statistics to surface on the dashboard.</param>
|
||||
/// <param name="serverHost">The OPC UA server host whose active session count should be reported.</param>
|
||||
/// <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,
|
||||
GalaxyRepositoryStats? galaxyStats, OpcUaServerHost? serverHost,
|
||||
LmxNodeManager? nodeManager = null,
|
||||
@@ -60,7 +65,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the structured dashboard snapshot consumed by the HTML and JSON renderers.
|
||||
/// Builds the structured dashboard snapshot consumed by the HTML and JSON renderers.
|
||||
/// </summary>
|
||||
/// <returns>The current dashboard status data for the bridge.</returns>
|
||||
public StatusData GetStatusData()
|
||||
@@ -96,7 +101,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
PendingItems = _nodeManager?.PendingDataChangeCount ?? 0,
|
||||
TotalEvents = _nodeManager?.TotalMxChangeEvents ?? 0
|
||||
},
|
||||
Operations = _metrics?.GetStatistics() ?? new(),
|
||||
Operations = _metrics?.GetStatistics() ?? new Dictionary<string, MetricsStatistics>(),
|
||||
Redundancy = BuildRedundancyInfo(),
|
||||
Footer = new FooterInfo
|
||||
{
|
||||
@@ -126,12 +131,12 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
Role = _redundancyConfig.Role,
|
||||
ServiceLevel = calculator.Calculate(baseLevel, mxConnected, dbConnected),
|
||||
ApplicationUri = _applicationUri ?? "",
|
||||
ServerUris = new System.Collections.Generic.List<string>(_redundancyConfig.ServerUris)
|
||||
ServerUris = new List<string>(_redundancyConfig.ServerUris)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the operator-facing HTML dashboard for the current bridge status.
|
||||
/// Generates the operator-facing HTML dashboard for the current bridge status.
|
||||
/// </summary>
|
||||
/// <returns>An HTML document containing the latest dashboard snapshot.</returns>
|
||||
public string GenerateHtml()
|
||||
@@ -146,16 +151,20 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
sb.AppendLine("<style>");
|
||||
sb.AppendLine("body { font-family: monospace; background: #1a1a2e; color: #eee; padding: 20px; }");
|
||||
sb.AppendLine(".panel { border: 2px solid #444; border-radius: 8px; padding: 15px; margin: 10px 0; }");
|
||||
sb.AppendLine(".green { border-color: #00cc66; } .red { border-color: #cc3333; } .yellow { border-color: #cccc33; } .gray { border-color: #666; }");
|
||||
sb.AppendLine("table { width: 100%; border-collapse: collapse; } th, td { text-align: left; padding: 4px 8px; border-bottom: 1px solid #333; }");
|
||||
sb.AppendLine(
|
||||
".green { border-color: #00cc66; } .red { border-color: #cc3333; } .yellow { border-color: #cccc33; } .gray { border-color: #666; }");
|
||||
sb.AppendLine(
|
||||
"table { width: 100%; border-collapse: collapse; } th, td { text-align: left; padding: 4px 8px; border-bottom: 1px solid #333; }");
|
||||
sb.AppendLine("h2 { margin: 0 0 10px 0; } h1 { color: #66ccff; }");
|
||||
sb.AppendLine("</style></head><body>");
|
||||
sb.AppendLine("<h1>LmxOpcUa Status Dashboard</h1>");
|
||||
|
||||
// Connection panel
|
||||
var connColor = data.Connection.State == "Connected" ? "green" : data.Connection.State == "Connecting" ? "yellow" : "red";
|
||||
var connColor = data.Connection.State == "Connected" ? "green" :
|
||||
data.Connection.State == "Connecting" ? "yellow" : "red";
|
||||
sb.AppendLine($"<div class='panel {connColor}'><h2>Connection</h2>");
|
||||
sb.AppendLine($"<p>State: <b>{data.Connection.State}</b> | Reconnects: {data.Connection.ReconnectCount} | Sessions: {data.Connection.ActiveSessions}</p>");
|
||||
sb.AppendLine(
|
||||
$"<p>State: <b>{data.Connection.State}</b> | Reconnects: {data.Connection.ReconnectCount} | Sessions: {data.Connection.ActiveSessions}</p>");
|
||||
sb.AppendLine("</div>");
|
||||
|
||||
// Health panel
|
||||
@@ -168,7 +177,8 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
{
|
||||
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>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>");
|
||||
@@ -181,25 +191,30 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
|
||||
// Data Change Dispatch panel
|
||||
sb.AppendLine("<div class='panel gray'><h2>Data Change Dispatch</h2>");
|
||||
sb.AppendLine($"<p>Events/sec: <b>{data.DataChange.EventsPerSecond:F1}</b> | Avg Batch Size: <b>{data.DataChange.AvgBatchSize:F1}</b> | Pending: {data.DataChange.PendingItems} | Total Events: {data.DataChange.TotalEvents:N0}</p>");
|
||||
sb.AppendLine(
|
||||
$"<p>Events/sec: <b>{data.DataChange.EventsPerSecond:F1}</b> | Avg Batch Size: <b>{data.DataChange.AvgBatchSize:F1}</b> | Pending: {data.DataChange.PendingItems} | Total Events: {data.DataChange.TotalEvents:N0}</p>");
|
||||
sb.AppendLine("</div>");
|
||||
|
||||
// Galaxy Info panel
|
||||
sb.AppendLine("<div class='panel gray'><h2>Galaxy Info</h2>");
|
||||
sb.AppendLine($"<p>Galaxy: <b>{data.Galaxy.GalaxyName}</b> | DB: {(data.Galaxy.DbConnected ? "Connected" : "Disconnected")}</p>");
|
||||
sb.AppendLine($"<p>Last Deploy: {data.Galaxy.LastDeployTime:O} | Objects: {data.Galaxy.ObjectCount} | Attributes: {data.Galaxy.AttributeCount}</p>");
|
||||
sb.AppendLine(
|
||||
$"<p>Galaxy: <b>{data.Galaxy.GalaxyName}</b> | DB: {(data.Galaxy.DbConnected ? "Connected" : "Disconnected")}</p>");
|
||||
sb.AppendLine(
|
||||
$"<p>Last Deploy: {data.Galaxy.LastDeployTime:O} | Objects: {data.Galaxy.ObjectCount} | Attributes: {data.Galaxy.AttributeCount}</p>");
|
||||
sb.AppendLine($"<p>Last Rebuild: {data.Galaxy.LastRebuildTime:O}</p>");
|
||||
sb.AppendLine("</div>");
|
||||
|
||||
// Operations table
|
||||
sb.AppendLine("<div class='panel gray'><h2>Operations</h2>");
|
||||
sb.AppendLine("<table><tr><th>Operation</th><th>Count</th><th>Success Rate</th><th>Avg (ms)</th><th>Min (ms)</th><th>Max (ms)</th><th>P95 (ms)</th></tr>");
|
||||
sb.AppendLine(
|
||||
"<table><tr><th>Operation</th><th>Count</th><th>Success Rate</th><th>Avg (ms)</th><th>Min (ms)</th><th>Max (ms)</th><th>P95 (ms)</th></tr>");
|
||||
foreach (var kvp in data.Operations)
|
||||
{
|
||||
var s = kvp.Value;
|
||||
sb.AppendLine($"<tr><td>{kvp.Key}</td><td>{s.TotalCount}</td><td>{s.SuccessRate:P1}</td>" +
|
||||
$"<td>{s.AverageMilliseconds:F1}</td><td>{s.MinMilliseconds:F1}</td><td>{s.MaxMilliseconds:F1}</td><td>{s.Percentile95Milliseconds:F1}</td></tr>");
|
||||
$"<td>{s.AverageMilliseconds:F1}</td><td>{s.MinMilliseconds:F1}</td><td>{s.MaxMilliseconds:F1}</td><td>{s.Percentile95Milliseconds:F1}</td></tr>");
|
||||
}
|
||||
|
||||
sb.AppendLine("</table></div>");
|
||||
|
||||
// Footer
|
||||
@@ -212,7 +227,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates an indented JSON status payload for API consumers.
|
||||
/// Generates an indented JSON status payload for API consumers.
|
||||
/// </summary>
|
||||
/// <returns>A JSON representation of the current dashboard snapshot.</returns>
|
||||
public string GenerateJson()
|
||||
@@ -222,9 +237,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the bridge should currently be considered healthy for the dashboard health endpoint.
|
||||
/// Determines whether the bridge should currently be considered healthy for the dashboard health endpoint.
|
||||
/// </summary>
|
||||
/// <returns><see langword="true"/> when the bridge meets the health policy; otherwise, <see langword="false"/>.</returns>
|
||||
/// <returns><see langword="true" /> when the bridge meets the health policy; otherwise, <see langword="false" />.</returns>
|
||||
public bool IsHealthy()
|
||||
{
|
||||
var state = _mxAccessClient?.State ?? ConnectionState.Disconnected;
|
||||
@@ -232,7 +247,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the rich health endpoint data including component health, ServiceLevel, and redundancy state.
|
||||
/// Builds the rich health endpoint data including component health, ServiceLevel, and redundancy state.
|
||||
/// </summary>
|
||||
public HealthEndpointData GetHealthData()
|
||||
{
|
||||
@@ -250,7 +265,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
{
|
||||
MxAccess = connectionState.ToString(),
|
||||
Database = dbConnected ? "Connected" : "Disconnected",
|
||||
OpcUaServer = (_serverHost?.IsRunning ?? false) ? "Running" : "Stopped"
|
||||
OpcUaServer = _serverHost?.IsRunning ?? false ? "Running" : "Stopped"
|
||||
},
|
||||
Uptime = FormatUptime(uptime),
|
||||
Timestamp = DateTime.UtcNow
|
||||
@@ -278,7 +293,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the JSON payload for the /api/health endpoint.
|
||||
/// Generates the JSON payload for the /api/health endpoint.
|
||||
/// </summary>
|
||||
public string GenerateHealthJson()
|
||||
{
|
||||
@@ -287,7 +302,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a focused health status HTML page for operators and monitoring dashboards.
|
||||
/// Generates a focused health status HTML page for operators and monitoring dashboards.
|
||||
/// </summary>
|
||||
public string GenerateHealthHtml()
|
||||
{
|
||||
@@ -304,13 +319,17 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
sb.AppendLine($"<meta http-equiv='refresh' content='{_refreshIntervalSeconds}'>");
|
||||
sb.AppendLine("<title>LmxOpcUa Health</title>");
|
||||
sb.AppendLine("<style>");
|
||||
sb.AppendLine("body { font-family: monospace; background: #1a1a2e; color: #eee; padding: 20px; margin: 0; }");
|
||||
sb.AppendLine(
|
||||
"body { font-family: monospace; background: #1a1a2e; color: #eee; padding: 20px; margin: 0; }");
|
||||
sb.AppendLine(".header { text-align: center; padding: 30px 0; }");
|
||||
sb.AppendLine(".status-badge { display: inline-block; font-size: 2em; font-weight: bold; padding: 15px 40px; border-radius: 12px; letter-spacing: 2px; }");
|
||||
sb.AppendLine(
|
||||
".status-badge { display: inline-block; font-size: 2em; font-weight: bold; padding: 15px 40px; border-radius: 12px; letter-spacing: 2px; }");
|
||||
sb.AppendLine(".service-level { text-align: center; font-size: 4em; font-weight: bold; margin: 20px 0; }");
|
||||
sb.AppendLine(".service-level .label { font-size: 0.3em; color: #999; display: block; }");
|
||||
sb.AppendLine(".components { display: flex; justify-content: center; gap: 20px; flex-wrap: wrap; margin: 30px auto; max-width: 800px; }");
|
||||
sb.AppendLine(".component { border: 2px solid #444; border-radius: 8px; padding: 20px; min-width: 200px; text-align: center; }");
|
||||
sb.AppendLine(
|
||||
".components { display: flex; justify-content: center; gap: 20px; flex-wrap: wrap; margin: 30px auto; max-width: 800px; }");
|
||||
sb.AppendLine(
|
||||
".component { border: 2px solid #444; border-radius: 8px; padding: 20px; min-width: 200px; text-align: center; }");
|
||||
sb.AppendLine(".component .name { font-size: 0.9em; color: #999; margin-bottom: 8px; }");
|
||||
sb.AppendLine(".component .value { font-size: 1.3em; font-weight: bold; }");
|
||||
sb.AppendLine(".meta { text-align: center; margin-top: 30px; color: #666; font-size: 0.85em; }");
|
||||
@@ -320,7 +339,8 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
|
||||
// Status badge
|
||||
sb.AppendLine("<div class='header'>");
|
||||
sb.AppendLine($"<div class='status-badge' style='background: {statusColor}; color: #000;'>{data.Status.ToUpperInvariant()}</div>");
|
||||
sb.AppendLine(
|
||||
$"<div class='status-badge' style='background: {statusColor}; color: #000;'>{data.Status.ToUpperInvariant()}</div>");
|
||||
sb.AppendLine("</div>");
|
||||
|
||||
// Service Level
|
||||
@@ -331,15 +351,17 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
|
||||
// Redundancy info
|
||||
if (data.RedundancyEnabled)
|
||||
{
|
||||
sb.AppendLine($"<div class='redundancy'>Role: <b>{data.RedundancyRole}</b> | Mode: <b>{data.RedundancyMode}</b></div>");
|
||||
}
|
||||
sb.AppendLine(
|
||||
$"<div class='redundancy'>Role: <b>{data.RedundancyRole}</b> | Mode: <b>{data.RedundancyMode}</b></div>");
|
||||
|
||||
// Component health cards
|
||||
sb.AppendLine("<div class='components'>");
|
||||
sb.AppendLine($"<div class='component' style='border-color: {mxColor};'><div class='name'>MXAccess</div><div class='value' style='color: {mxColor};'>{data.Components.MxAccess}</div></div>");
|
||||
sb.AppendLine($"<div class='component' style='border-color: {dbColor};'><div class='name'>Galaxy Database</div><div class='value' style='color: {dbColor};'>{data.Components.Database}</div></div>");
|
||||
sb.AppendLine($"<div class='component' style='border-color: {uaColor};'><div class='name'>OPC UA Server</div><div class='value' style='color: {uaColor};'>{data.Components.OpcUaServer}</div></div>");
|
||||
sb.AppendLine(
|
||||
$"<div class='component' style='border-color: {mxColor};'><div class='name'>MXAccess</div><div class='value' style='color: {mxColor};'>{data.Components.MxAccess}</div></div>");
|
||||
sb.AppendLine(
|
||||
$"<div class='component' style='border-color: {dbColor};'><div class='name'>Galaxy Database</div><div class='value' style='color: {dbColor};'>{data.Components.Database}</div></div>");
|
||||
sb.AppendLine(
|
||||
$"<div class='component' style='border-color: {uaColor};'><div class='name'>OPC UA Server</div><div class='value' style='color: {uaColor};'>{data.Components.OpcUaServer}</div></div>");
|
||||
sb.AppendLine("</div>");
|
||||
|
||||
// Footer
|
||||
@@ -358,4 +380,4 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
return $"{(int)ts.TotalMinutes}m {ts.Seconds}s";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user