-- Phase 7 live OPC UA E2E smoke seed (task #240). -- -- Idempotent — DROP-and-recreate of one cluster's worth of test config: -- * 1 ServerCluster ('p7-smoke') -- * 1 ClusterNode ('p7-smoke-node') -- * 1 ConfigGeneration (created Draft, then flipped to Published at the end) -- * 1 Namespace (Equipment kind) -- * 1 UnsArea / UnsLine / Equipment / Tag — Tag bound to a real Galaxy attribute -- * 1 DriverInstance (Galaxy) -- * 1 Script + 1 VirtualTag using it -- * 1 Script + 1 ScriptedAlarm using it -- -- Drop & re-create deletes ALL rows scoped to the cluster (in dependency order) -- so re-running this script after a code change starts from a clean state. -- Table-level CHECK constraints are validated on insert; if a constraint is -- violated this script aborts with the offending row's column. -- -- Usage: -- sqlcmd -S "localhost,14330" -d OtOpcUaConfig -U sa -P "OtOpcUaDev_2026!" \ -- -i scripts/smoke/seed-phase-7-smoke.sql SET NOCOUNT ON; SET XACT_ABORT ON; SET QUOTED_IDENTIFIER ON; SET ANSI_NULLS ON; SET ANSI_PADDING ON; SET ANSI_WARNINGS ON; SET ARITHABORT ON; SET CONCAT_NULL_YIELDS_NULL ON; DECLARE @ClusterId nvarchar(64) = 'p7-smoke'; DECLARE @NodeId nvarchar(64) = 'p7-smoke-node'; DECLARE @DrvId nvarchar(64) = 'p7-smoke-galaxy'; DECLARE @NsId nvarchar(64) = 'p7-smoke-ns'; DECLARE @AreaId nvarchar(64) = 'p7-smoke-area'; DECLARE @LineId nvarchar(64) = 'p7-smoke-line'; DECLARE @EqId nvarchar(64) = 'p7-smoke-eq'; DECLARE @EqUuid uniqueidentifier = '5B2CF10D-5B2C-4F10-B5B2-CF10D5B2CF10'; DECLARE @TagId nvarchar(64) = 'p7-smoke-tag-source'; DECLARE @VtScript nvarchar(64) = 'p7-smoke-script-vt'; DECLARE @AlScript nvarchar(64) = 'p7-smoke-script-al'; DECLARE @VtId nvarchar(64) = 'p7-smoke-vt-derived'; DECLARE @AlId nvarchar(64) = 'p7-smoke-al-overtemp'; BEGIN TRAN; -- Wipe any prior smoke state. Order matters: child rows first. DELETE s FROM dbo.ScriptedAlarmState s WHERE s.ScriptedAlarmId = @AlId; DELETE FROM dbo.ScriptedAlarm WHERE ScriptedAlarmId = @AlId; DELETE FROM dbo.VirtualTag WHERE VirtualTagId = @VtId; DELETE FROM dbo.Script WHERE ScriptId IN (@VtScript, @AlScript); DELETE FROM dbo.Tag WHERE TagId = @TagId; DELETE FROM dbo.Equipment WHERE EquipmentId = @EqId; DELETE FROM dbo.UnsLine WHERE UnsLineId = @LineId; DELETE FROM dbo.UnsArea WHERE UnsAreaId = @AreaId; DELETE FROM dbo.DriverInstance WHERE DriverInstanceId = @DrvId; DELETE FROM dbo.Namespace WHERE NamespaceId = @NsId; DELETE FROM dbo.ConfigGeneration WHERE ClusterId = @ClusterId; DELETE FROM dbo.ClusterNodeCredential WHERE NodeId = @NodeId; DELETE FROM dbo.ClusterNodeGenerationState WHERE NodeId = @NodeId; DELETE FROM dbo.ClusterNode WHERE NodeId = @NodeId; DELETE FROM dbo.ServerCluster WHERE ClusterId = @ClusterId; -- 1. Cluster + Node INSERT dbo.ServerCluster(ClusterId, Name, Enterprise, Site, NodeCount, RedundancyMode, Enabled, CreatedBy) VALUES (@ClusterId, 'P7 Smoke', 'zb', 'lab', 1, 'None', 1, 'p7-smoke'); INSERT dbo.ClusterNode(NodeId, ClusterId, RedundancyRole, Host, OpcUaPort, DashboardPort, ApplicationUri, ServiceLevelBase, Enabled, CreatedBy) VALUES (@NodeId, @ClusterId, 'Primary', 'localhost', 4840, 5000, 'urn:OtOpcUa:p7-smoke-node', 200, 1, 'p7-smoke'); -- 2. Generation (created Draft, flipped to Published at the end so insert order -- constraints (one Draft per cluster, etc.) don't fight us). DECLARE @Gen bigint; INSERT dbo.ConfigGeneration(ClusterId, Status, CreatedBy) VALUES (@ClusterId, 'Draft', 'p7-smoke'); SET @Gen = SCOPE_IDENTITY(); -- 3. Namespace INSERT dbo.Namespace(GenerationId, NamespaceId, ClusterId, Kind, NamespaceUri, Enabled) VALUES (@Gen, @NsId, @ClusterId, 'Equipment', 'urn:p7-smoke:eq', 1); -- 4. UNS hierarchy INSERT dbo.UnsArea(GenerationId, UnsAreaId, ClusterId, Name) VALUES (@Gen, @AreaId, @ClusterId, 'lab-floor'); INSERT dbo.UnsLine(GenerationId, UnsLineId, UnsAreaId, Name) VALUES (@Gen, @LineId, @AreaId, 'galaxy-line'); INSERT dbo.Equipment(GenerationId, EquipmentId, EquipmentUuid, DriverInstanceId, UnsLineId, Name, MachineCode, Enabled) VALUES (@Gen, @EqId, @EqUuid, @DrvId, @LineId, 'reactor-1', 'p7-rx-001', 1); -- 5. Driver — Galaxy proxy. DriverConfig JSON tells the proxy how to reach the -- already-running OtOpcUaGalaxyHost. Secret + pipe name match -- .local/galaxy-host-secret.txt + the OtOpcUaGalaxyHost service env. INSERT dbo.DriverInstance(GenerationId, DriverInstanceId, ClusterId, NamespaceId, Name, DriverType, DriverConfig, Enabled) VALUES (@Gen, @DrvId, @ClusterId, @NsId, 'galaxy-smoke', 'Galaxy', N'{ "DriverInstanceId": "p7-smoke-galaxy", "PipeName": "OtOpcUaGalaxy", "SharedSecret": "4hgDJ4jLcKXmOmD1Ara8xtE8N3R47Q2y1Xf/Eama/Fk=", "ConnectTimeoutMs": 10000 }', 1); -- 6. One driver-sourced Tag bound to the Equipment. TagConfig is the Galaxy -- fullRef ("DelmiaReceiver_001.DownloadPath" style); replace with a real -- attribute on this Galaxy. The script paths below use -- /lab-floor/galaxy-line/reactor-1/Source which the EquipmentNodeWalker -- emits + the DriverSubscriptionBridge maps to this driver fullRef. INSERT dbo.Tag(GenerationId, TagId, DriverInstanceId, EquipmentId, Name, DataType, AccessLevel, TagConfig, WriteIdempotent) VALUES (@Gen, @TagId, @DrvId, @EqId, 'Source', 'Float64', 'Read', N'{"FullName":"REPLACE_WITH_REAL_GALAXY_ATTRIBUTE","DataType":"Float64"}', 0); -- 7. Scripts (SourceHash is SHA-256 of SourceCode, computed externally — using -- a placeholder here; the engine recomputes on first use anyway). INSERT dbo.Script(GenerationId, ScriptId, Name, SourceCode, SourceHash, Language) VALUES (@Gen, @VtScript, 'doubled-source', N'return ((double)ctx.GetTag("/lab-floor/galaxy-line/reactor-1/Source").Value) * 2.0;', '0000000000000000000000000000000000000000000000000000000000000000', 'CSharp'), (@Gen, @AlScript, 'overtemp-predicate', N'return ((double)ctx.GetTag("/lab-floor/galaxy-line/reactor-1/Source").Value) > 50.0;', '0000000000000000000000000000000000000000000000000000000000000000', 'CSharp'); -- 8. VirtualTag — derived value computed by Roslyn each time Source changes. INSERT dbo.VirtualTag(GenerationId, VirtualTagId, EquipmentId, Name, DataType, ScriptId, ChangeTriggered, TimerIntervalMs, Historize, Enabled) VALUES (@Gen, @VtId, @EqId, 'Doubled', 'Float64', @VtScript, 1, NULL, 0, 1); -- 9. ScriptedAlarm — Active when Source > 50. INSERT dbo.ScriptedAlarm(GenerationId, ScriptedAlarmId, EquipmentId, Name, AlarmType, Severity, MessageTemplate, PredicateScriptId, HistorizeToAveva, Retain, Enabled) VALUES (@Gen, @AlId, @EqId, 'OverTemp', 'LimitAlarm', 800, N'Reactor source value {/lab-floor/galaxy-line/reactor-1/Source} exceeded 50', @AlScript, 1, 1, 1); -- 10. Publish — flip the generation Status. sp_PublishGeneration takes -- concurrency locks + does ExternalIdReservation merging; we drive it via -- EXEC rather than UPDATE so the rest of the publish workflow runs. EXEC dbo.sp_PublishGeneration @ClusterId = @ClusterId, @DraftGenerationId = @Gen, @Notes = N'Phase 7 live smoke — task #240'; COMMIT; PRINT ''; PRINT 'Phase 7 smoke seed complete.'; PRINT ' Cluster: ' + @ClusterId; PRINT ' Node: ' + @NodeId + ' (set Node:NodeId in appsettings.json)'; PRINT ' Generation: ' + CONVERT(nvarchar(20), @Gen); PRINT ''; PRINT 'Next steps:'; PRINT ' 1. Edit src/ZB.MOM.WW.OtOpcUa.Server/appsettings.json:'; PRINT ' Node:NodeId = "p7-smoke-node"'; PRINT ' Node:ClusterId = "p7-smoke"'; PRINT ' 2. Edit the placeholder Galaxy attribute in dbo.Tag.TagConfig above'; PRINT ' so it points at a real attribute on this Galaxy — replace'; PRINT ' REPLACE_WITH_REAL_GALAXY_ATTRIBUTE with e.g. "Plant1.Reactor1.Temp".'; PRINT ' 3. Start the Server in a non-elevated shell so the Galaxy.Host pipe ACL'; PRINT ' accepts the connection:'; PRINT ' dotnet run --project src/ZB.MOM.WW.OtOpcUa.Server'; PRINT ' 4. Validate via Client.CLI per docs/v2/implementation/phase-7-e2e-smoke.md';