0ffd4bdbea
Comment/XML-doc changes only, no code logic: - strip project-tracking references (WP*, P*-*, row-NN, #NN, Task N) from comments, keeping the explanatory rationale - add missing summaries, <param>, <returns>, <typeparam> and <inheritdoc/> - replace misused <inheritdoc/> with real docs; where <inheritdoc/> was combined with useful remarks, move the rationale into // comments - drop stale replicationActor <param> on DeploymentManagerActor ctor Remaining 5 are false positives (CWE-1236 x2, failures-1, year-0001, M365). Build 0 errors / 0 warnings.
87 lines
3.6 KiB
C#
87 lines
3.6 KiB
C#
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
|
using ZB.MOM.WW.ScadaBridge.HealthMonitoring;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
|
|
|
/// <summary>
|
|
/// Default <see cref="IConnectionHealthQueryService"/> implementation.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Pure projection — no new health plumbing. The
|
|
/// <see cref="ICentralHealthAggregator"/> already holds the latest
|
|
/// <c>SiteHealthReport</c> per site (fed by the existing site→central health
|
|
/// transport); this service reads those reports and resolves their connection
|
|
/// NAME keys back to <c>DataConnection.Id</c> using the same site repository the
|
|
/// design page loads from. Health reports key connections by <c>Name</c>, the
|
|
/// page renders nodes by id, so the join is per-site Name → Id; doing it per-site
|
|
/// avoids cross-site name collisions (two sites can each own a "MainPlc").
|
|
/// </remarks>
|
|
public sealed class ConnectionHealthQueryService : IConnectionHealthQueryService
|
|
{
|
|
private readonly ISiteRepository _siteRepository;
|
|
private readonly ICentralHealthAggregator _healthAggregator;
|
|
|
|
/// <summary>
|
|
/// Initializes the service with the site repository (connection name→id source)
|
|
/// and the central health aggregator (latest-report source).
|
|
/// </summary>
|
|
/// <param name="siteRepository">Source of sites and their connections.</param>
|
|
/// <param name="healthAggregator">Source of the latest per-site health report.</param>
|
|
public ConnectionHealthQueryService(
|
|
ISiteRepository siteRepository,
|
|
ICentralHealthAggregator healthAggregator)
|
|
{
|
|
_siteRepository = siteRepository ?? throw new ArgumentNullException(nameof(siteRepository));
|
|
_healthAggregator = healthAggregator ?? throw new ArgumentNullException(nameof(healthAggregator));
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<IReadOnlyDictionary<int, ConnectionHealth>> GetConnectionHealthAsync(
|
|
CancellationToken ct = default)
|
|
{
|
|
var sites = await _siteRepository.GetAllSitesAsync(ct);
|
|
var connections = await _siteRepository.GetAllDataConnectionsAsync(ct);
|
|
|
|
// Per-site connection name → id. Ordinal: connection names are case-sensitive
|
|
// identifiers and the health report keys are produced from the same names.
|
|
var idByName = connections
|
|
.GroupBy(c => c.SiteId)
|
|
.ToDictionary(
|
|
g => g.Key,
|
|
g => g
|
|
.GroupBy(c => c.Name, StringComparer.Ordinal)
|
|
.ToDictionary(n => n.Key, n => n.First().Id, StringComparer.Ordinal));
|
|
|
|
var result = new Dictionary<int, ConnectionHealth>();
|
|
foreach (var site in sites)
|
|
{
|
|
// Health reports are keyed by the site's machine identifier.
|
|
var report = _healthAggregator.GetSiteState(site.SiteIdentifier)?.LatestReport;
|
|
if (report is null)
|
|
{
|
|
// No report yet (just-started central / heartbeat-only): contribute
|
|
// nothing — the page renders these connections as "unknown".
|
|
continue;
|
|
}
|
|
|
|
if (!idByName.TryGetValue(site.Id, out var siteIds))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
foreach (var (connName, health) in report.DataConnectionStatuses)
|
|
{
|
|
// Unknown name (e.g. deleted centrally but still live at the site):
|
|
// skip — there is no node to badge.
|
|
if (siteIds.TryGetValue(connName, out var connId))
|
|
{
|
|
result[connId] = health;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|