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;
///
/// Default implementation.
///
///
/// Pure projection — no new health plumbing. The
/// already holds the latest
/// SiteHealthReport per site (fed by the existing site→central health
/// transport); this service reads those reports and resolves their connection
/// NAME keys back to DataConnection.Id using the same site repository the
/// design page loads from. Health reports key connections by Name, 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").
///
public sealed class ConnectionHealthQueryService : IConnectionHealthQueryService
{
private readonly ISiteRepository _siteRepository;
private readonly ICentralHealthAggregator _healthAggregator;
///
/// Initializes the service with the site repository (connection name→id source)
/// and the central health aggregator (latest-report source).
///
/// Source of sites and their connections.
/// Source of the latest per-site health report.
public ConnectionHealthQueryService(
ISiteRepository siteRepository,
ICentralHealthAggregator healthAggregator)
{
_siteRepository = siteRepository ?? throw new ArgumentNullException(nameof(siteRepository));
_healthAggregator = healthAggregator ?? throw new ArgumentNullException(nameof(healthAggregator));
}
///
public async Task> 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();
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;
}
}