4f4cdd05ec
Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
72 lines
3.5 KiB
C#
72 lines
3.5 KiB
C#
using Microsoft.Extensions.Configuration;
|
|
using ZB.MOM.WW.LocalDb;
|
|
using ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian;
|
|
using ZB.MOM.WW.OtOpcUa.Runtime.DeploymentCache;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Host.Configuration;
|
|
|
|
/// <summary>
|
|
/// The <c>onReady</c> callback handed to <c>AddZbLocalDb</c>: creates the deployment-cache,
|
|
/// alarm store-and-forward, and scripted-alarm condition-state tables and opts them into
|
|
/// replication.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// Public rather than internal only so <c>LocalDbSetupTests</c> can drive the production
|
|
/// callback directly. Initialising a test database from a hand-written copy of this schema
|
|
/// would prove only that the test agrees with itself — the whole value of those tests is
|
|
/// that they exercise <b>this</b> method.
|
|
/// </para>
|
|
/// </remarks>
|
|
public static class LocalDbSetup
|
|
{
|
|
/// <summary>
|
|
/// Initialises the local database: DDL first, then replication registration.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// <b>THE ORDER IS LOAD-BEARING: DDL → RegisterReplicated → writes.</b>
|
|
/// <c>RegisterReplicated</c> is what installs the three AFTER triggers that capture
|
|
/// changes into the oplog. Any row written before that call is never captured, so it
|
|
/// never reaches the peer — silently, and permanently, because nothing ever revisits
|
|
/// history. <see cref="AlarmSfLegacyMigrator"/> therefore runs <b>last</b>, after every
|
|
/// registration — it writes rows, and they must be captured like any other write.
|
|
/// </para>
|
|
/// <para>
|
|
/// The alarm buffer's tables are created unconditionally, regardless of whether this
|
|
/// node has <c>AlarmHistorian:Enabled</c> set. An empty registered table costs three
|
|
/// triggers and nothing else, whereas creating it lazily when the sink first appears
|
|
/// would mean a node that enables the historian later writes rows before its triggers
|
|
/// exist — which is precisely the silent-loss shape above.
|
|
/// </para>
|
|
/// </remarks>
|
|
/// <param name="db">The freshly constructed local database.</param>
|
|
/// <param name="configuration">
|
|
/// Application configuration, read by the legacy migrator for the pre-consolidation queue's
|
|
/// path. Required rather than optional: an overload that silently skipped the migration
|
|
/// would be one wiring mistake away from discarding a node's undelivered alarm history.
|
|
/// </param>
|
|
public static void OnReady(ILocalDb db, IConfiguration configuration)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(db);
|
|
ArgumentNullException.ThrowIfNull(configuration);
|
|
|
|
// CreateConnection() hands back an already-open, pragma-configured connection carrying the
|
|
// zb_hlc_next() UDF the capture triggers need. Calling Open() on it would throw.
|
|
using (var connection = db.CreateConnection())
|
|
{
|
|
DeploymentCacheSchema.Apply(connection);
|
|
AlarmSfSchema.Apply(connection);
|
|
AlarmConditionStateSchema.Apply(connection);
|
|
}
|
|
|
|
db.RegisterReplicated(DeploymentCacheSchema.ArtifactsTable);
|
|
db.RegisterReplicated(DeploymentCacheSchema.PointerTable);
|
|
db.RegisterReplicated(AlarmSfSchema.EventsTable);
|
|
db.RegisterReplicated(AlarmConditionStateSchema.StateTable);
|
|
|
|
// LAST, and only here. This is the one call in OnReady that writes rows.
|
|
AlarmSfLegacyMigrator.Migrate(db, configuration);
|
|
}
|
|
}
|