Scope alarm tracking to selected templates and surface endpoint/security state on the dashboard so operators can deploy in large galaxies without drowning clients in irrelevant alarms or guessing what the server is advertising
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
@@ -112,6 +113,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
Historian = historianInfo,
|
||||
Alarms = alarmInfo,
|
||||
Redundancy = BuildRedundancyInfo(),
|
||||
Endpoints = BuildEndpointsInfo(),
|
||||
Footer = new FooterInfo
|
||||
{
|
||||
Timestamp = DateTime.UtcNow,
|
||||
@@ -143,10 +145,37 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
ActiveAlarmCount = _nodeManager?.ActiveAlarmCount ?? 0,
|
||||
TransitionCount = _nodeManager?.AlarmTransitionCount ?? 0,
|
||||
AckEventCount = _nodeManager?.AlarmAckEventCount ?? 0,
|
||||
AckWriteFailures = _nodeManager?.AlarmAckWriteFailures ?? 0
|
||||
AckWriteFailures = _nodeManager?.AlarmAckWriteFailures ?? 0,
|
||||
FilterEnabled = _nodeManager?.AlarmFilterEnabled ?? false,
|
||||
FilterPatternCount = _nodeManager?.AlarmFilterPatternCount ?? 0,
|
||||
FilterIncludedObjectCount = _nodeManager?.AlarmFilterIncludedObjectCount ?? 0
|
||||
};
|
||||
}
|
||||
|
||||
private EndpointsInfo BuildEndpointsInfo()
|
||||
{
|
||||
var info = new EndpointsInfo();
|
||||
if (_serverHost == null)
|
||||
return info;
|
||||
|
||||
info.BaseAddresses = _serverHost.BaseAddresses.ToList();
|
||||
info.UserTokenPolicies = _serverHost.UserTokenPolicies.Distinct().ToList();
|
||||
foreach (var policy in _serverHost.SecurityPolicies)
|
||||
{
|
||||
var uri = policy.SecurityPolicyUri ?? "";
|
||||
var hashIdx = uri.LastIndexOf('#');
|
||||
var name = hashIdx >= 0 && hashIdx < uri.Length - 1 ? uri.Substring(hashIdx + 1) : uri;
|
||||
info.SecurityProfiles.Add(new SecurityProfileInfo
|
||||
{
|
||||
PolicyUri = uri,
|
||||
PolicyName = name,
|
||||
SecurityMode = policy.SecurityMode.ToString()
|
||||
});
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
private RedundancyInfo? BuildRedundancyInfo()
|
||||
{
|
||||
if (_redundancyConfig == null || !_redundancyConfig.Enabled)
|
||||
@@ -208,6 +237,37 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
sb.AppendLine($"<p>Status: <b>{data.Health.Status}</b> — {data.Health.Message}</p>");
|
||||
sb.AppendLine("</div>");
|
||||
|
||||
// Endpoints panel (exposed URLs + security profiles)
|
||||
var endpointsColor = data.Endpoints.BaseAddresses.Count > 0 ? "green" : "gray";
|
||||
sb.AppendLine($"<div class='panel {endpointsColor}'><h2>Endpoints</h2>");
|
||||
if (data.Endpoints.BaseAddresses.Count == 0)
|
||||
{
|
||||
sb.AppendLine("<p>No endpoints — OPC UA server not started.</p>");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine("<p><b>Base Addresses:</b></p><ul>");
|
||||
foreach (var addr in data.Endpoints.BaseAddresses)
|
||||
sb.AppendLine($"<li>{WebUtility.HtmlEncode(addr)}</li>");
|
||||
sb.AppendLine("</ul>");
|
||||
|
||||
sb.AppendLine("<p><b>Security Profiles:</b></p>");
|
||||
sb.AppendLine("<table><tr><th>Mode</th><th>Policy</th><th>Policy URI</th></tr>");
|
||||
foreach (var profile in data.Endpoints.SecurityProfiles)
|
||||
{
|
||||
sb.AppendLine(
|
||||
$"<tr><td>{WebUtility.HtmlEncode(profile.SecurityMode)}</td>" +
|
||||
$"<td>{WebUtility.HtmlEncode(profile.PolicyName)}</td>" +
|
||||
$"<td>{WebUtility.HtmlEncode(profile.PolicyUri)}</td></tr>");
|
||||
}
|
||||
sb.AppendLine("</table>");
|
||||
|
||||
if (data.Endpoints.UserTokenPolicies.Count > 0)
|
||||
sb.AppendLine(
|
||||
$"<p><b>User Token Policies:</b> {WebUtility.HtmlEncode(string.Join(", ", data.Endpoints.UserTokenPolicies))}</p>");
|
||||
}
|
||||
sb.AppendLine("</div>");
|
||||
|
||||
// Redundancy panel (only when enabled)
|
||||
if (data.Redundancy != null)
|
||||
{
|
||||
@@ -258,6 +318,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Status
|
||||
$"<p>Tracking: <b>{data.Alarms.TrackingEnabled}</b> | Conditions: {data.Alarms.ConditionCount} | Active: <b>{data.Alarms.ActiveAlarmCount}</b></p>");
|
||||
sb.AppendLine(
|
||||
$"<p>Transitions: {data.Alarms.TransitionCount:N0} | Ack Events: {data.Alarms.AckEventCount:N0} | Ack Write Failures: {data.Alarms.AckWriteFailures}</p>");
|
||||
if (data.Alarms.FilterEnabled)
|
||||
sb.AppendLine(
|
||||
$"<p>Filter: <b>{data.Alarms.FilterPatternCount}</b> pattern(s), <b>{data.Alarms.FilterIncludedObjectCount}</b> object(s) included</p>");
|
||||
sb.AppendLine("</div>");
|
||||
|
||||
// Operations table
|
||||
|
||||
Reference in New Issue
Block a user