-- Modbus e2e smoke seed — closes #210 (umbrella #209). -- -- Idempotent — DROP-and-recreate of one cluster's worth of Modbus test config: -- * 1 ServerCluster ('modbus-smoke') + ClusterNode ('modbus-smoke-node') -- * 1 ConfigGeneration (Draft → Published at the end) -- * 1 Namespace + UnsArea + UnsLine + Equipment -- * 1 Modbus DriverInstance pointing at the pymodbus standard fixture -- (127.0.0.1:5020 per tests/.../Modbus.IntegrationTests/Docker) -- * 1 Tag at HR[200]:UInt16 (HR[100] is auto-increment in standard.json, -- unusable as a write target — the e2e script uses HR[200] for that reason) -- -- Usage: -- sqlcmd -S "localhost,14330" -d OtOpcUaConfig -U sa -P "OtOpcUaDev_2026!" \ -- -i scripts/smoke/seed-modbus-smoke.sql -- -- After seeding, update src/.../Server/appsettings.json: -- Node:NodeId = "modbus-smoke-node" -- Node:ClusterId = "modbus-smoke" -- -- Then start the simulator + server + run the e2e script: -- docker compose -f tests/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Docker/docker-compose.yml --profile standard up -d -- dotnet run --project src/ZB.MOM.WW.OtOpcUa.Server -- ./scripts/e2e/test-modbus.ps1 -BridgeNodeId "ns=2;s=HR200" 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) = 'modbus-smoke'; DECLARE @NodeId nvarchar(64) = 'modbus-smoke-node'; DECLARE @DrvId nvarchar(64) = 'modbus-smoke-drv'; DECLARE @NsId nvarchar(64) = 'modbus-smoke-ns'; DECLARE @AreaId nvarchar(64) = 'modbus-smoke-area'; DECLARE @LineId nvarchar(64) = 'modbus-smoke-line'; DECLARE @EqId nvarchar(64) = 'modbus-smoke-eq'; DECLARE @EqUuid uniqueidentifier = '72BD5A10-72BD-45A1-B72B-D5A1072BD5A1'; DECLARE @TagHr200 nvarchar(64) = 'modbus-smoke-tag-hr200'; BEGIN TRAN; -- Clean prior smoke state (child rows first). DELETE FROM dbo.Tag WHERE TagId IN (@TagHr200); 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, 'Modbus Smoke', 'zb', 'lab', 1, 'None', 1, 'modbus-smoke'); INSERT dbo.ClusterNode(NodeId, ClusterId, RedundancyRole, Host, OpcUaPort, DashboardPort, ApplicationUri, ServiceLevelBase, Enabled, CreatedBy) VALUES (@NodeId, @ClusterId, 'Primary', 'localhost', 4840, 5000, 'urn:OtOpcUa:modbus-smoke-node', 200, 1, 'modbus-smoke'); -- 2. Draft generation. DECLARE @Gen bigint; INSERT dbo.ConfigGeneration(ClusterId, Status, CreatedBy) VALUES (@ClusterId, 'Draft', 'modbus-smoke'); SET @Gen = SCOPE_IDENTITY(); -- 3. Namespace. INSERT dbo.Namespace(GenerationId, NamespaceId, ClusterId, Kind, NamespaceUri, Enabled) VALUES (@Gen, @NsId, @ClusterId, 'Equipment', 'urn:modbus-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, 'modbus-line'); INSERT dbo.Equipment(GenerationId, EquipmentId, EquipmentUuid, DriverInstanceId, UnsLineId, Name, MachineCode, Enabled) VALUES (@Gen, @EqId, @EqUuid, @DrvId, @LineId, 'modbus-sim', 'modbus-001', 1); -- 5. Modbus DriverInstance. DriverConfig mirrors ModbusDriverConfigDto -- (mapped to ModbusDriverOptions by ModbusDriverFactoryExtensions). INSERT dbo.DriverInstance(GenerationId, DriverInstanceId, ClusterId, NamespaceId, Name, DriverType, DriverConfig, Enabled) VALUES (@Gen, @DrvId, @ClusterId, @NsId, 'pymodbus-smoke', 'Modbus', N'{ "Host": "127.0.0.1", "Port": 5020, "UnitId": 1, "TimeoutMs": 2000, "AutoReconnect": true, "Probe": { "Enabled": true, "IntervalMs": 5000, "TimeoutMs": 2000, "ProbeAddress": 0 }, "Tags": [ { "Name": "HR200", "Region": "HoldingRegisters", "Address": 200, "DataType": "UInt16", "Writable": true, "WriteIdempotent": true } ] }', 1); -- 6. Tag row bound to the Equipment. Driver reports the same tag via -- DiscoverAsync + the walker maps the UnsArea/Line/Equipment/Tag path to the -- driver's folder/variable (NodeId ends up ns=;s=HR200 per -- ModbusDriver.DiscoverAsync using FullName = tag.Name). INSERT dbo.Tag(GenerationId, TagId, DriverInstanceId, EquipmentId, Name, DataType, AccessLevel, TagConfig, WriteIdempotent) VALUES (@Gen, @TagHr200, @DrvId, @EqId, 'HR200', 'UInt16', 'ReadWrite', N'{"FullName":"HR200","DataType":"UInt16"}', 1); -- 7. Publish the generation — flips Status Draft → Published, merges -- ExternalIdReservation, claims cluster write lock. EXEC dbo.sp_PublishGeneration @ClusterId = @ClusterId, @DraftGenerationId = @Gen, @Notes = N'Modbus smoke — task #210'; COMMIT; PRINT ''; PRINT 'Modbus smoke seed complete.'; PRINT ' Cluster: ' + @ClusterId; PRINT ' Node: ' + @NodeId; PRINT ' Generation: ' + CONVERT(nvarchar(20), @Gen); PRINT ''; PRINT 'Next steps:'; PRINT ' 1. Set src/.../Server/appsettings.json Node:NodeId = "modbus-smoke-node"'; PRINT ' Node:ClusterId = "modbus-smoke"'; PRINT ' 2. docker compose -f tests/.../Modbus.IntegrationTests/Docker/docker-compose.yml --profile standard up -d'; PRINT ' 3. dotnet run --project src/ZB.MOM.WW.OtOpcUa.Server'; PRINT ' 4. ./scripts/e2e/test-modbus.ps1 -BridgeNodeId "ns=2;s=HR200"';