82 lines
4.5 KiB
C#
82 lines
4.5 KiB
C#
using System.Collections.Generic;
|
|
using System.IO;
|
|
using ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Runtime.Historian;
|
|
|
|
/// <summary>
|
|
/// Binds the <c>AlarmHistorian</c> configuration section that gates the durable
|
|
/// store-and-forward alarm sink. When <see cref="Enabled"/> is <c>true</c>,
|
|
/// <c>AddAlarmHistorian</c> registers a <c>SqliteStoreAndForwardSink</c> (draining to the
|
|
/// Wonderware TCP writer supplied by the Host) in place of the
|
|
/// <c>NullAlarmHistorianSink</c> default; otherwise the Null default survives.
|
|
/// </summary>
|
|
public sealed class AlarmHistorianOptions
|
|
{
|
|
/// <summary>The configuration section name this options class binds.</summary>
|
|
public const string SectionName = "AlarmHistorian";
|
|
|
|
/// <summary>
|
|
/// When <c>true</c>, the durable SQLite store-and-forward sink is registered; when
|
|
/// <c>false</c> (the default) the no-op <c>NullAlarmHistorianSink</c> stays in place.
|
|
/// </summary>
|
|
public bool Enabled { get; init; }
|
|
|
|
/// <summary>Filesystem path to the local SQLite store-and-forward queue database.</summary>
|
|
public string DatabasePath { get; init; } = "alarm-historian.db";
|
|
|
|
/// <summary>TCP hostname or IP address the Wonderware historian sidecar listens on.</summary>
|
|
public string Host { get; init; } = "localhost";
|
|
|
|
/// <summary>TCP port the Wonderware historian sidecar listens on.</summary>
|
|
public int Port { get; init; } = 32569;
|
|
|
|
/// <summary>When <c>true</c>, the client connects over TLS.</summary>
|
|
public bool UseTls { get; init; }
|
|
|
|
/// <summary>Expected TLS server certificate thumbprint (hex, no spaces). Null or empty disables pinning.</summary>
|
|
public string? ServerCertThumbprint { get; init; }
|
|
|
|
/// <summary>Per-process shared secret the sidecar verifies in the Hello frame.</summary>
|
|
public string SharedSecret { get; init; } = "";
|
|
|
|
/// <summary>Maximum number of queued rows the drain worker forwards in a single batch.</summary>
|
|
public int BatchSize { get; init; } = 100;
|
|
|
|
/// <summary>Seconds between drain-worker ticks. Defaults to 5.</summary>
|
|
public int DrainIntervalSeconds { get; init; } = 5;
|
|
|
|
/// <summary>Maximum queued rows before the sink evicts the oldest. Defaults to 1,000,000
|
|
/// (matches <c>SqliteStoreAndForwardSink</c>'s <c>DefaultCapacity</c>).</summary>
|
|
public long Capacity { get; init; } = SqliteStoreAndForwardSink.DefaultCapacity;
|
|
|
|
/// <summary>Days to retain dead-lettered rows before purge. Defaults to 30.</summary>
|
|
public int DeadLetterRetentionDays { get; init; } = 30;
|
|
|
|
/// <summary>Maximum delivery attempts before a perpetually-retrying (poison) row is dead-lettered.
|
|
/// Defaults to 10 (matches <c>SqliteStoreAndForwardSink</c>'s <c>DefaultMaxAttempts</c>).</summary>
|
|
public int MaxAttempts { get; init; } = SqliteStoreAndForwardSink.DefaultMaxAttempts;
|
|
|
|
/// <summary>Returns operator-facing misconfiguration warnings for an <c>Enabled</c> historian
|
|
/// (empty when disabled or correctly configured). Pure — the registration logs each entry.</summary>
|
|
/// <returns>Zero or more human-readable warning messages.</returns>
|
|
public IReadOnlyList<string> Validate()
|
|
{
|
|
var warnings = new List<string>();
|
|
if (!Enabled) return warnings;
|
|
if (string.IsNullOrWhiteSpace(SharedSecret))
|
|
warnings.Add("AlarmHistorian:SharedSecret is empty while the historian is enabled — the Wonderware sidecar Hello frame will carry an empty secret.");
|
|
if (!Path.IsPathRooted(DatabasePath))
|
|
warnings.Add($"AlarmHistorian:DatabasePath '{DatabasePath}' is relative — it resolves against the process working directory (e.g. System32 for a Windows service). Set an absolute path.");
|
|
if (DrainIntervalSeconds <= 0)
|
|
warnings.Add($"AlarmHistorian:DrainIntervalSeconds is {DrainIntervalSeconds} — must be > 0; the drain timer will throw or spin at startup.");
|
|
if (Capacity <= 0)
|
|
warnings.Add($"AlarmHistorian:Capacity is {Capacity} — must be > 0; the sink constructor will throw at startup.");
|
|
if (DeadLetterRetentionDays <= 0)
|
|
warnings.Add($"AlarmHistorian:DeadLetterRetentionDays is {DeadLetterRetentionDays} — must be > 0; dead-lettered rows would be purged on every drain tick.");
|
|
if (MaxAttempts <= 0)
|
|
warnings.Add($"AlarmHistorian:MaxAttempts is {MaxAttempts} — must be > 0; the sink constructor will throw at startup.");
|
|
return warnings;
|
|
}
|
|
}
|