feat(siteruntime): site SQLite native_alarm_state store
This commit is contained in:
@@ -111,6 +111,15 @@ public class SiteStorageService
|
||||
oauth_config TEXT,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS native_alarm_state (
|
||||
instance_unique_name TEXT NOT NULL,
|
||||
source_canonical_name TEXT NOT NULL,
|
||||
source_reference TEXT NOT NULL,
|
||||
condition_json TEXT NOT NULL,
|
||||
last_transition_at TEXT NOT NULL,
|
||||
PRIMARY KEY (instance_unique_name, source_canonical_name, source_reference)
|
||||
);
|
||||
";
|
||||
await command.ExecuteNonQueryAsync();
|
||||
|
||||
@@ -346,6 +355,104 @@ public class SiteStorageService
|
||||
_logger.LogDebug("Cleared static overrides for {Instance}", instanceName);
|
||||
}
|
||||
|
||||
// ── Task 14: Native Alarm State store (read-only mirror of source A&C conditions) ──
|
||||
|
||||
/// <summary>
|
||||
/// Inserts or updates a single mirrored native alarm condition, keyed by
|
||||
/// (instance, source canonical name, source reference). Newer transitions overwrite older ones.
|
||||
/// </summary>
|
||||
public async Task UpsertNativeAlarmAsync(
|
||||
string instanceName, string sourceCanonicalName, string sourceReference,
|
||||
string conditionJson, DateTimeOffset lastTransitionAt)
|
||||
{
|
||||
await using var connection = new SqliteConnection(_connectionString);
|
||||
await connection.OpenAsync();
|
||||
|
||||
await using var command = connection.CreateCommand();
|
||||
command.CommandText = @"
|
||||
INSERT INTO native_alarm_state
|
||||
(instance_unique_name, source_canonical_name, source_reference, condition_json, last_transition_at)
|
||||
VALUES (@name, @source, @ref, @json, @at)
|
||||
ON CONFLICT(instance_unique_name, source_canonical_name, source_reference) DO UPDATE SET
|
||||
condition_json = excluded.condition_json,
|
||||
last_transition_at = excluded.last_transition_at";
|
||||
|
||||
command.Parameters.AddWithValue("@name", instanceName);
|
||||
command.Parameters.AddWithValue("@source", sourceCanonicalName);
|
||||
command.Parameters.AddWithValue("@ref", sourceReference);
|
||||
command.Parameters.AddWithValue("@json", conditionJson);
|
||||
command.Parameters.AddWithValue("@at", lastTransitionAt.ToString("O"));
|
||||
|
||||
await command.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a single mirrored native alarm condition (e.g. a return-to-normal that drops out of retention).
|
||||
/// </summary>
|
||||
public async Task DeleteNativeAlarmAsync(string instanceName, string sourceCanonicalName, string sourceReference)
|
||||
{
|
||||
await using var connection = new SqliteConnection(_connectionString);
|
||||
await connection.OpenAsync();
|
||||
|
||||
await using var command = connection.CreateCommand();
|
||||
command.CommandText = @"
|
||||
DELETE FROM native_alarm_state
|
||||
WHERE instance_unique_name = @name
|
||||
AND source_canonical_name = @source
|
||||
AND source_reference = @ref";
|
||||
command.Parameters.AddWithValue("@name", instanceName);
|
||||
command.Parameters.AddWithValue("@source", sourceCanonicalName);
|
||||
command.Parameters.AddWithValue("@ref", sourceReference);
|
||||
|
||||
await command.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all mirrored native alarm conditions for an instance's source binding,
|
||||
/// used to rehydrate a NativeAlarmActor on (re)start.
|
||||
/// </summary>
|
||||
public async Task<IReadOnlyList<NativeAlarmRow>> GetNativeAlarmsAsync(string instanceName, string sourceCanonicalName)
|
||||
{
|
||||
await using var connection = new SqliteConnection(_connectionString);
|
||||
await connection.OpenAsync();
|
||||
|
||||
await using var command = connection.CreateCommand();
|
||||
command.CommandText = @"
|
||||
SELECT source_reference, condition_json, last_transition_at
|
||||
FROM native_alarm_state
|
||||
WHERE instance_unique_name = @name AND source_canonical_name = @source";
|
||||
command.Parameters.AddWithValue("@name", instanceName);
|
||||
command.Parameters.AddWithValue("@source", sourceCanonicalName);
|
||||
|
||||
var results = new List<NativeAlarmRow>();
|
||||
await using var reader = await command.ExecuteReaderAsync();
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
results.Add(new NativeAlarmRow(
|
||||
reader.GetString(0),
|
||||
reader.GetString(1),
|
||||
DateTimeOffset.Parse(reader.GetString(2), null, System.Globalization.DateTimeStyles.RoundtripKind)));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all mirrored native alarm conditions for an instance. Called on redeployment / stop.
|
||||
/// </summary>
|
||||
public async Task ClearNativeAlarmsForInstanceAsync(string instanceName)
|
||||
{
|
||||
await using var connection = new SqliteConnection(_connectionString);
|
||||
await connection.OpenAsync();
|
||||
|
||||
await using var command = connection.CreateCommand();
|
||||
command.CommandText = "DELETE FROM native_alarm_state WHERE instance_unique_name = @name";
|
||||
command.Parameters.AddWithValue("@name", instanceName);
|
||||
|
||||
await command.ExecuteNonQueryAsync();
|
||||
_logger.LogDebug("Cleared native alarm state for {Instance}", instanceName);
|
||||
}
|
||||
|
||||
// ── WP-33: Shared Script CRUD ──
|
||||
|
||||
/// <summary>
|
||||
@@ -699,3 +806,8 @@ public class StoredDataConnectionDefinition
|
||||
/// </summary>
|
||||
public int FailoverRetryCount { get; init; } = 3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A single mirrored native alarm condition row from the site-local <c>native_alarm_state</c> table (Task 14).
|
||||
/// </summary>
|
||||
public record NativeAlarmRow(string SourceReference, string ConditionJson, DateTimeOffset LastTransitionAt);
|
||||
|
||||
Reference in New Issue
Block a user