using System.Collections.Generic; namespace ZB.MOM.WW.OtOpcUa.Runtime.Historian; /// /// Binds the ContinuousHistorization configuration section that gates the continuous /// historization of driver (non-Galaxy) tag values. When is true /// and the ServerHistorian gateway is configured, the Host builds a durable, /// crash-safe FasterLogHistorizationOutbox + a gateway-backed IHistorianValueWriter /// and WithOtOpcUaRuntimeActors spawns the ; /// otherwise no recorder is spawned and driver tag values are not historized. /// /// The recorder taps the per-node dependency-mux value fan-out, appends each numeric value to /// the outbox (the crash boundary), and drains the outbox to the historian's SQL live-value /// write path (WriteLiveValues) through the single ServerHistorian gateway. The /// gateway connection (endpoint / key / TLS) is sourced from ServerHistorianOptions — this /// section carries only the recorder + outbox knobs, never a gateway address or credential. /// /// public sealed class ContinuousHistorizationOptions { /// The configuration section name this options class binds. public const string SectionName = "ContinuousHistorization"; /// /// When true (and the ServerHistorian gateway is configured), the /// continuous-historization recorder + its durable outbox are wired and spawned; when /// false (the default) no recorder is spawned and driver tag values are not historized. /// public bool Enabled { get; init; } /// /// Directory holding the FasterLog outbox segment + commit files. Required when /// is true. In production set an absolute path on durable /// storage — a relative path resolves against the host's working directory, which may change /// across deployments. /// public string OutboxPath { get; init; } = ""; /// /// Outbox commit cadence: PerEntry (the default) fsyncs the log before each append /// returns (safest, no loss window); Periodic batches commits onto a background timer /// every ms (higher throughput, a bounded worst-case loss window). /// Parsed case-insensitively against HistorizationCommitMode; an unrecognized value falls /// back to PerEntry. /// public string CommitMode { get; init; } = "PerEntry"; /// Periodic-mode commit cadence in milliseconds; must be positive when /// is Periodic. Ignored under PerEntry. public int CommitIntervalMs { get; init; } = 100; /// Maximum outbox entries peeked + written per drain pass; clamped to a positive value by /// the recorder (a non-positive value falls back to 64). public int DrainBatchSize { get; init; } = 64; /// Steady drain cadence in seconds (also the post-success reschedule). Defaults to 2. public double DrainIntervalSeconds { get; init; } = 2; /// Maximum un-acked outbox entries before the drop-oldest capacity policy kicks in; /// 0 (the default) means unbounded. public int Capacity { get; init; } /// Initial retry backoff (seconds) after a failed drain pass. Defaults to 1. public double MinBackoffSeconds { get; init; } = 1; /// Cap (seconds) on the exponential retry backoff after repeated drain failures. Defaults to 30. public double MaxBackoffSeconds { get; init; } = 30; /// Returns operator-facing misconfiguration warnings for an Enabled recorder /// (empty when disabled or correctly configured). Pure — the registration logs each entry. /// Zero or more human-readable warning messages (never carrying secret values). public IReadOnlyList Validate() { var warnings = new List(); if (!Enabled) return warnings; if (string.IsNullOrWhiteSpace(OutboxPath)) warnings.Add("ContinuousHistorization:OutboxPath is empty while historization is enabled — the durable outbox has no directory to persist to; host startup will fail when the outbox is constructed. Set an absolute path on durable storage."); if (string.Equals(CommitMode, "Periodic", StringComparison.OrdinalIgnoreCase) && CommitIntervalMs <= 0) warnings.Add($"ContinuousHistorization:CommitIntervalMs is {CommitIntervalMs} — must be > 0 in Periodic commit mode; the periodic-commit loop cannot run."); return warnings; } }