0f1f8ad8fa
I1: /hosts eviction keyed off the AUTHORED DriverInstances, so a driver deleted in /raw but not yet deployed - still running, still publishing - vanished every tick. The stores' sweep is now EvictUnconfiguredStale(set, olderThanUtc): an entry goes only when unconfigured AND its PublishedUtc is older than 60 s. Tests cover unconfigured+fresh kept (positive control), unconfigured+stale evicted, configured+stale kept, and the deploy hand-off. M1: Test Connect falls back to the driver's documented ProbeTimeoutSeconds default (Galaxy 30, OpcUaClient 15, ...) when the key is absent. M2: the budget never undercuts the config's root timeoutMs (+1 s). M5: RawBrowseModal compares the open generation captured before the await, so a close-then-reopen mid-open closes the stale session. M6: /alarms-historian keeps a 30 s re-poll on 'not registered' (a driver node may still be starting) instead of stopping for good. M7: /scripts source fetch failure shows in the dialog, not the error boundary. M8: the export modal maps only its preview rows; the endpoint writes via Results.Text (no second byte[] copy). M9: /hosts config queries are ordered, so SequenceEqual change detection holds. N1 tooltip, N2 CRLF-safe regex, N3 throttled Warning for subscriber faults, N5 equipment-import help text now describes RFC 4180 quoting. H3: MonacoEditor documents that themes are page-global. Refs #745 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
54 lines
3.4 KiB
C#
54 lines
3.4 KiB
C#
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Drivers;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Hubs;
|
|
|
|
/// <summary>
|
|
/// Singleton last-snapshot-per-instance cache. Populated by
|
|
/// <c>DriverStatusSignalRBridge</c> as it forwards DPS messages; read by
|
|
/// <see cref="DriverStatusHub.JoinDriver"/> so newly-joined clients see current state
|
|
/// without waiting for the next change event, and subscribed to directly by the Blazor
|
|
/// Server <c>DriverStatusPanel</c> via <see cref="SnapshotChanged"/>.
|
|
/// </summary>
|
|
public interface IDriverStatusSnapshotStore
|
|
{
|
|
/// <summary>Stores or replaces the last-known health snapshot for a driver instance.</summary>
|
|
/// <param name="snapshot">The driver health snapshot to store.</param>
|
|
void Upsert(DriverHealthChanged snapshot);
|
|
|
|
/// <summary>Attempts to retrieve the last-known health snapshot for a driver instance.</summary>
|
|
/// <param name="driverInstanceId">The driver instance's stable logical ID.</param>
|
|
/// <param name="snapshot">The stored snapshot, if found.</param>
|
|
/// <returns><see langword="true"/> if a snapshot was found; otherwise <see langword="false"/>.</returns>
|
|
bool TryGet(string driverInstanceId, out DriverHealthChanged snapshot);
|
|
|
|
/// <summary>Returns a point-in-time snapshot of every driver instance's last-known health.</summary>
|
|
/// <returns>A read-only collection of the last-known health snapshot for each driver instance.</returns>
|
|
IReadOnlyCollection<DriverHealthChanged> GetAll();
|
|
|
|
/// <summary>
|
|
/// Evicts every snapshot whose driver instance is not in <paramref name="configuredInstanceIds"/>
|
|
/// <b>and</b> whose last publish is older than <paramref name="olderThanUtc"/>. The stores have no other
|
|
/// removal path, so a deleted (or re-created under a new id) driver would otherwise keep its last
|
|
/// snapshot until the process restarts. Both conditions are required: the configured set comes from
|
|
/// the AUTHORED (draft) table, so a driver deleted in <c>/raw</c> but not yet deployed is still
|
|
/// running and still publishing — it must stay visible until a deploy actually stops it and its
|
|
/// snapshot goes stale. The <c>/hosts</c> page calls this after each successful config load; a failed
|
|
/// load must never call it.
|
|
/// </summary>
|
|
/// <param name="configuredInstanceIds">The configured driver-instance ids to keep. The set's own
|
|
/// comparer decides matching.</param>
|
|
/// <param name="olderThanUtc">Only an unconfigured entry published before this instant is evicted.</param>
|
|
/// <returns>The number of entries removed.</returns>
|
|
int EvictUnconfiguredStale(IReadOnlySet<string> configuredInstanceIds, DateTime olderThanUtc);
|
|
|
|
/// <summary>
|
|
/// Raised after every <see cref="Upsert"/> with the just-stored snapshot. Lets in-process
|
|
/// consumers (the Blazor Server <c>DriverStatusPanel</c>) receive live updates by reading
|
|
/// this singleton directly instead of opening a self-targeted SignalR connection — which a
|
|
/// server-side Blazor component cannot reach when the public URL (e.g. a reverse-proxy port)
|
|
/// differs from the local Kestrel bind. Handlers run on the caller's thread (the bridge
|
|
/// actor), so subscribers must marshal to their own sync context.
|
|
/// </summary>
|
|
event Action<DriverHealthChanged>? SnapshotChanged;
|
|
}
|