using Microsoft.EntityFrameworkCore.Migrations; #nullable disable namespace ZB.MOM.WW.OtOpcUa.Configuration.Migrations { /// /// Phase 7 follow-up (task #241) — extends dbo.sp_ComputeGenerationDiff to emit /// Script / VirtualTag / ScriptedAlarm rows alongside the existing Namespace / /// DriverInstance / Equipment / Tag / NodeAcl output. Admin DiffViewer now shows /// Phase 7 changes between generations. /// /// /// Logical ids: ScriptId, VirtualTagId, ScriptedAlarmId — stable across generations /// so a Script whose source changes surfaces as Modified (CHECKSUM picks up the /// SourceHash delta) while a renamed script surfaces as Modified on Name alone. /// ScriptedAlarmState is deliberately excluded — it's not generation-scoped, so /// diffing it between generations is meaningless. /// /// public partial class ExtendComputeGenerationDiffWithPhase7 : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.Sql(Procs.ComputeGenerationDiffV3); } /// protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.Sql(Procs.ComputeGenerationDiffV2); } private static class Procs { /// V3 — adds Script / VirtualTag / ScriptedAlarm sections. public const string ComputeGenerationDiffV3 = @" CREATE OR ALTER PROCEDURE dbo.sp_ComputeGenerationDiff @FromGenerationId bigint, @ToGenerationId bigint AS BEGIN SET NOCOUNT ON; CREATE TABLE #diff (TableName nvarchar(32), LogicalId nvarchar(128), ChangeKind nvarchar(16)); WITH f AS (SELECT NamespaceId AS LogicalId, CHECKSUM(NamespaceUri, Kind, Enabled, Notes) AS Sig FROM dbo.Namespace WHERE GenerationId = @FromGenerationId), t AS (SELECT NamespaceId AS LogicalId, CHECKSUM(NamespaceUri, Kind, Enabled, Notes) AS Sig FROM dbo.Namespace WHERE GenerationId = @ToGenerationId) INSERT #diff SELECT 'Namespace', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)), CASE WHEN f.LogicalId IS NULL THEN 'Added' WHEN t.LogicalId IS NULL THEN 'Removed' WHEN f.Sig <> t.Sig THEN 'Modified' ELSE 'Unchanged' END FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig; WITH f AS (SELECT DriverInstanceId AS LogicalId, CHECKSUM(ClusterId, NamespaceId, Name, DriverType, Enabled, CONVERT(varchar(max), DriverConfig)) AS Sig FROM dbo.DriverInstance WHERE GenerationId = @FromGenerationId), t AS (SELECT DriverInstanceId AS LogicalId, CHECKSUM(ClusterId, NamespaceId, Name, DriverType, Enabled, CONVERT(varchar(max), DriverConfig)) AS Sig FROM dbo.DriverInstance WHERE GenerationId = @ToGenerationId) INSERT #diff SELECT 'DriverInstance', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)), CASE WHEN f.LogicalId IS NULL THEN 'Added' WHEN t.LogicalId IS NULL THEN 'Removed' WHEN f.Sig <> t.Sig THEN 'Modified' ELSE 'Unchanged' END FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig; WITH f AS (SELECT EquipmentId AS LogicalId, CHECKSUM(EquipmentUuid, DriverInstanceId, UnsLineId, Name, MachineCode, ZTag, SAPID, EquipmentClassRef, Manufacturer, Model, SerialNumber) AS Sig FROM dbo.Equipment WHERE GenerationId = @FromGenerationId), t AS (SELECT EquipmentId AS LogicalId, CHECKSUM(EquipmentUuid, DriverInstanceId, UnsLineId, Name, MachineCode, ZTag, SAPID, EquipmentClassRef, Manufacturer, Model, SerialNumber) AS Sig FROM dbo.Equipment WHERE GenerationId = @ToGenerationId) INSERT #diff SELECT 'Equipment', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)), CASE WHEN f.LogicalId IS NULL THEN 'Added' WHEN t.LogicalId IS NULL THEN 'Removed' WHEN f.Sig <> t.Sig THEN 'Modified' ELSE 'Unchanged' END FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig; WITH f AS (SELECT TagId AS LogicalId, CHECKSUM(DriverInstanceId, DeviceId, EquipmentId, PollGroupId, FolderPath, Name, DataType, AccessLevel, WriteIdempotent, CONVERT(varchar(max), TagConfig)) AS Sig FROM dbo.Tag WHERE GenerationId = @FromGenerationId), t AS (SELECT TagId AS LogicalId, CHECKSUM(DriverInstanceId, DeviceId, EquipmentId, PollGroupId, FolderPath, Name, DataType, AccessLevel, WriteIdempotent, CONVERT(varchar(max), TagConfig)) AS Sig FROM dbo.Tag WHERE GenerationId = @ToGenerationId) INSERT #diff SELECT 'Tag', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)), CASE WHEN f.LogicalId IS NULL THEN 'Added' WHEN t.LogicalId IS NULL THEN 'Removed' WHEN f.Sig <> t.Sig THEN 'Modified' ELSE 'Unchanged' END FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig; WITH f AS ( SELECT CONVERT(nvarchar(128), LdapGroup + '|' + CONVERT(nvarchar(16), ScopeKind) + '|' + ISNULL(ScopeId, '(cluster)')) AS LogicalId, CHECKSUM(ClusterId, PermissionFlags, Notes) AS Sig FROM dbo.NodeAcl WHERE GenerationId = @FromGenerationId), t AS ( SELECT CONVERT(nvarchar(128), LdapGroup + '|' + CONVERT(nvarchar(16), ScopeKind) + '|' + ISNULL(ScopeId, '(cluster)')) AS LogicalId, CHECKSUM(ClusterId, PermissionFlags, Notes) AS Sig FROM dbo.NodeAcl WHERE GenerationId = @ToGenerationId) INSERT #diff SELECT 'NodeAcl', COALESCE(f.LogicalId, t.LogicalId), CASE WHEN f.LogicalId IS NULL THEN 'Added' WHEN t.LogicalId IS NULL THEN 'Removed' WHEN f.Sig <> t.Sig THEN 'Modified' ELSE 'Unchanged' END FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig; -- Phase 7 — Script section. CHECKSUM picks up source changes via SourceHash + rename -- via Name; Language future-proofs for non-C# engines. Same Name + same Source = -- Unchanged (identical hash). WITH f AS (SELECT ScriptId AS LogicalId, CHECKSUM(Name, SourceHash, Language) AS Sig FROM dbo.Script WHERE GenerationId = @FromGenerationId), t AS (SELECT ScriptId AS LogicalId, CHECKSUM(Name, SourceHash, Language) AS Sig FROM dbo.Script WHERE GenerationId = @ToGenerationId) INSERT #diff SELECT 'Script', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)), CASE WHEN f.LogicalId IS NULL THEN 'Added' WHEN t.LogicalId IS NULL THEN 'Removed' WHEN f.Sig <> t.Sig THEN 'Modified' ELSE 'Unchanged' END FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig; -- Phase 7 — VirtualTag section. WITH f AS (SELECT VirtualTagId AS LogicalId, CHECKSUM(EquipmentId, Name, DataType, ScriptId, ChangeTriggered, TimerIntervalMs, Historize, Enabled) AS Sig FROM dbo.VirtualTag WHERE GenerationId = @FromGenerationId), t AS (SELECT VirtualTagId AS LogicalId, CHECKSUM(EquipmentId, Name, DataType, ScriptId, ChangeTriggered, TimerIntervalMs, Historize, Enabled) AS Sig FROM dbo.VirtualTag WHERE GenerationId = @ToGenerationId) INSERT #diff SELECT 'VirtualTag', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)), CASE WHEN f.LogicalId IS NULL THEN 'Added' WHEN t.LogicalId IS NULL THEN 'Removed' WHEN f.Sig <> t.Sig THEN 'Modified' ELSE 'Unchanged' END FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig; -- Phase 7 — ScriptedAlarm section. ScriptedAlarmState (operator ack trail) is -- logical-id keyed outside the generation scope + intentionally excluded here — -- diffing ack state between generations is semantically meaningless. WITH f AS (SELECT ScriptedAlarmId AS LogicalId, CHECKSUM(EquipmentId, Name, AlarmType, Severity, MessageTemplate, PredicateScriptId, HistorizeToAveva, Retain, Enabled) AS Sig FROM dbo.ScriptedAlarm WHERE GenerationId = @FromGenerationId), t AS (SELECT ScriptedAlarmId AS LogicalId, CHECKSUM(EquipmentId, Name, AlarmType, Severity, MessageTemplate, PredicateScriptId, HistorizeToAveva, Retain, Enabled) AS Sig FROM dbo.ScriptedAlarm WHERE GenerationId = @ToGenerationId) INSERT #diff SELECT 'ScriptedAlarm', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)), CASE WHEN f.LogicalId IS NULL THEN 'Added' WHEN t.LogicalId IS NULL THEN 'Removed' WHEN f.Sig <> t.Sig THEN 'Modified' ELSE 'Unchanged' END FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig; SELECT TableName, LogicalId, ChangeKind FROM #diff; DROP TABLE #diff; END "; /// V2 — restores the pre-Phase-7 proc on Down(). public const string ComputeGenerationDiffV2 = @" CREATE OR ALTER PROCEDURE dbo.sp_ComputeGenerationDiff @FromGenerationId bigint, @ToGenerationId bigint AS BEGIN SET NOCOUNT ON; CREATE TABLE #diff (TableName nvarchar(32), LogicalId nvarchar(128), ChangeKind nvarchar(16)); WITH f AS (SELECT NamespaceId AS LogicalId, CHECKSUM(NamespaceUri, Kind, Enabled, Notes) AS Sig FROM dbo.Namespace WHERE GenerationId = @FromGenerationId), t AS (SELECT NamespaceId AS LogicalId, CHECKSUM(NamespaceUri, Kind, Enabled, Notes) AS Sig FROM dbo.Namespace WHERE GenerationId = @ToGenerationId) INSERT #diff SELECT 'Namespace', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)), CASE WHEN f.LogicalId IS NULL THEN 'Added' WHEN t.LogicalId IS NULL THEN 'Removed' WHEN f.Sig <> t.Sig THEN 'Modified' ELSE 'Unchanged' END FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig; WITH f AS (SELECT DriverInstanceId AS LogicalId, CHECKSUM(ClusterId, NamespaceId, Name, DriverType, Enabled, CONVERT(varchar(max), DriverConfig)) AS Sig FROM dbo.DriverInstance WHERE GenerationId = @FromGenerationId), t AS (SELECT DriverInstanceId AS LogicalId, CHECKSUM(ClusterId, NamespaceId, Name, DriverType, Enabled, CONVERT(varchar(max), DriverConfig)) AS Sig FROM dbo.DriverInstance WHERE GenerationId = @ToGenerationId) INSERT #diff SELECT 'DriverInstance', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)), CASE WHEN f.LogicalId IS NULL THEN 'Added' WHEN t.LogicalId IS NULL THEN 'Removed' WHEN f.Sig <> t.Sig THEN 'Modified' ELSE 'Unchanged' END FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig; WITH f AS (SELECT EquipmentId AS LogicalId, CHECKSUM(EquipmentUuid, DriverInstanceId, UnsLineId, Name, MachineCode, ZTag, SAPID, EquipmentClassRef, Manufacturer, Model, SerialNumber) AS Sig FROM dbo.Equipment WHERE GenerationId = @FromGenerationId), t AS (SELECT EquipmentId AS LogicalId, CHECKSUM(EquipmentUuid, DriverInstanceId, UnsLineId, Name, MachineCode, ZTag, SAPID, EquipmentClassRef, Manufacturer, Model, SerialNumber) AS Sig FROM dbo.Equipment WHERE GenerationId = @ToGenerationId) INSERT #diff SELECT 'Equipment', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)), CASE WHEN f.LogicalId IS NULL THEN 'Added' WHEN t.LogicalId IS NULL THEN 'Removed' WHEN f.Sig <> t.Sig THEN 'Modified' ELSE 'Unchanged' END FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig; WITH f AS (SELECT TagId AS LogicalId, CHECKSUM(DriverInstanceId, DeviceId, EquipmentId, PollGroupId, FolderPath, Name, DataType, AccessLevel, WriteIdempotent, CONVERT(varchar(max), TagConfig)) AS Sig FROM dbo.Tag WHERE GenerationId = @FromGenerationId), t AS (SELECT TagId AS LogicalId, CHECKSUM(DriverInstanceId, DeviceId, EquipmentId, PollGroupId, FolderPath, Name, DataType, AccessLevel, WriteIdempotent, CONVERT(varchar(max), TagConfig)) AS Sig FROM dbo.Tag WHERE GenerationId = @ToGenerationId) INSERT #diff SELECT 'Tag', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)), CASE WHEN f.LogicalId IS NULL THEN 'Added' WHEN t.LogicalId IS NULL THEN 'Removed' WHEN f.Sig <> t.Sig THEN 'Modified' ELSE 'Unchanged' END FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig; WITH f AS ( SELECT CONVERT(nvarchar(128), LdapGroup + '|' + CONVERT(nvarchar(16), ScopeKind) + '|' + ISNULL(ScopeId, '(cluster)')) AS LogicalId, CHECKSUM(ClusterId, PermissionFlags, Notes) AS Sig FROM dbo.NodeAcl WHERE GenerationId = @FromGenerationId), t AS ( SELECT CONVERT(nvarchar(128), LdapGroup + '|' + CONVERT(nvarchar(16), ScopeKind) + '|' + ISNULL(ScopeId, '(cluster)')) AS LogicalId, CHECKSUM(ClusterId, PermissionFlags, Notes) AS Sig FROM dbo.NodeAcl WHERE GenerationId = @ToGenerationId) INSERT #diff SELECT 'NodeAcl', COALESCE(f.LogicalId, t.LogicalId), CASE WHEN f.LogicalId IS NULL THEN 'Added' WHEN t.LogicalId IS NULL THEN 'Removed' WHEN f.Sig <> t.Sig THEN 'Modified' ELSE 'Unchanged' END FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig; SELECT TableName, LogicalId, ChangeKind FROM #diff; DROP TABLE #diff; END "; } } }