A heartbeat-registered site that has never sent a full report now has
LastReportReceivedAt = null instead of the year-0001 sentinel. TimestampDisplay
accepts DateTimeOffset? and renders null as a placeholder ('awaiting first
report') rather than a ~2000-year-stale date. Cross-module: HealthMonitoring +
CentralUI.
45 lines
1.9 KiB
C#
45 lines
1.9 KiB
C#
using ScadaLink.Commons.Messages.Health;
|
|
|
|
namespace ScadaLink.HealthMonitoring;
|
|
|
|
/// <summary>
|
|
/// In-memory state for a single site's health, stored by the central aggregator.
|
|
/// Immutable: every state transition produces a new instance which the aggregator
|
|
/// installs into its <c>ConcurrentDictionary</c> via an atomic compare-and-swap.
|
|
/// This makes handing the reference straight to UI callers safe — a consumer can
|
|
/// never observe a torn or half-applied update.
|
|
/// </summary>
|
|
public sealed record SiteHealthState
|
|
{
|
|
public required string SiteId { get; init; }
|
|
|
|
/// <summary>
|
|
/// The latest full <see cref="SiteHealthReport"/> received for the site, or
|
|
/// <c>null</c> if the site is known only via heartbeats and has not yet sent
|
|
/// a report.
|
|
/// </summary>
|
|
public SiteHealthReport? LatestReport { get; init; }
|
|
|
|
/// <summary>
|
|
/// Time the latest full <see cref="SiteHealthReport"/> was processed, or
|
|
/// <c>null</c> if the site is known only via heartbeats and has not yet sent
|
|
/// a report. Used by the UI to surface report staleness during failover;
|
|
/// the <c>null</c> case must be rendered as "no report yet" rather than as a
|
|
/// timestamp (a <c>default</c> sentinel would display as year-0001).
|
|
/// </summary>
|
|
public DateTimeOffset? LastReportReceivedAt { get; init; }
|
|
|
|
/// <summary>
|
|
/// Time the most recent signal of any kind (full report OR heartbeat) was
|
|
/// received. Drives offline detection — heartbeats from the standby keep the
|
|
/// site marked online even when the active node is unable to produce a report
|
|
/// (mid-failover, brief stalls). Heartbeat cadence is owned by the Cluster
|
|
/// Infrastructure / SiteCommunicationActor (every
|
|
/// CommunicationOptions.TransportHeartbeatInterval — 5s by default).
|
|
/// </summary>
|
|
public DateTimeOffset LastHeartbeatAt { get; init; }
|
|
|
|
public long LastSequenceNumber { get; init; }
|
|
public bool IsOnline { get; init; }
|
|
}
|