using Microsoft.Data.Sqlite;
namespace ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian;
///
/// DDL for the Part 9 scripted-alarm condition state: one row per alarm identity holding the
/// operator-supplied state (Enabled / Acked / Confirmed / Shelving) plus the ack/confirm audit
/// trail and comment history.
///
///
///
/// Replaces the central config DB's ScriptedAlarmState table as the node-local home
/// for this state (per-cluster mesh Phase 4, which cuts the ConfigDb from driver-only
/// nodes). Living in the consolidated LocalDb file is what lets the state replicate to the
/// redundant pair peer, exactly like the alarm store-and-forward buffer — see
/// , which this mirrors.
///
///
/// Deliberately depends on nothing but so it can be applied
/// to any connection — the host's LocalDbSetup.OnReady in production, and a bare
/// connection in a test — without dragging the DI graph along.
///
///
/// The primary key is the alarm identity, and Save is an unconditional upsert onto it.
/// Convergence across the pair is last-writer-wins over the primary key, handled by the
/// LocalDb replication HLC — so the store stacks no application-level last-write-wins on top,
/// the same discipline the alarm-sf sink and the deployment pointer follow.
///
///
/// ActiveState has no column — it is re-derived from the live predicate on startup,
/// so persisting it would only risk operators seeing a stale Active on restart. Likewise
/// LastActiveUtc / LastClearedUtc re-derive alongside it and get no columns.
/// LastTransitionUtc is carried by updated_at_utc — the row-write timestamp is
/// the last transition. Timestamps are round-trip ("O") TEXT so lexicographic order is
/// chronological; nullable timestamps and audit fields are stored as NULL.
///
///
public static class AlarmConditionStateSchema
{
/// Table holding one condition-state row per scripted-alarm identity.
public const string StateTable = "alarm_condition_state";
///
/// Creates the state table if it does not already exist. Idempotent.
///
///
/// An already-open connection. ILocalDb.CreateConnection() hands out open,
/// pragma-configured connections — do not call Open() on one.
///
public static void Apply(SqliteConnection connection)
{
ArgumentNullException.ThrowIfNull(connection);
using var cmd = connection.CreateCommand();
// The four *_state columns are always written to a mapped, non-null string (Enabled/Disabled,
// Acknowledged/Unacknowledged, Confirmed/Unconfirmed, Unshelved/OneShotShelved/TimedShelved),
// so they are NOT NULL. comments_json is never null either — an empty trail is the literal
// "[]". The audit user/comment/utc columns and shelving_expires_utc are NULL when the alarm
// has no such history yet. updated_at_utc carries LastTransitionUtc (there is no dedicated
// transition column).
cmd.CommandText = """
CREATE TABLE IF NOT EXISTS alarm_condition_state (
scripted_alarm_id TEXT NOT NULL PRIMARY KEY,
enabled_state TEXT NOT NULL,
acked_state TEXT NOT NULL,
confirmed_state TEXT NOT NULL,
shelving_state TEXT NOT NULL,
shelving_expires_utc TEXT NULL,
last_ack_user TEXT NULL,
last_ack_comment TEXT NULL,
last_ack_utc TEXT NULL,
last_confirm_user TEXT NULL,
last_confirm_comment TEXT NULL,
last_confirm_utc TEXT NULL,
comments_json TEXT NOT NULL,
updated_at_utc TEXT NOT NULL
);
""";
cmd.ExecuteNonQuery();
}
}