-- ScadaLink Machine Data Database seed script -- Populates ScadaLinkMachineData with realistic SCADA/MES tables, -- sample data, and stored procedures for development and testing. -- -- Run after setup.sql: -- docker exec -i scadalink-mssql /opt/mssql-tools18/bin/sqlcmd \ -- -S localhost -U sa -P 'ScadaLink_Dev1#' -C \ -- -i /docker-entrypoint-initdb.d/machinedata_seed.sql USE ScadaLinkMachineData; GO -- ========================================================================= -- Tables -- ========================================================================= -- Tag history: time-series data collected from OPC UA / custom protocols IF OBJECT_ID('dbo.TagHistory', 'U') IS NULL CREATE TABLE dbo.TagHistory ( Id BIGINT IDENTITY(1,1) PRIMARY KEY, TagPath NVARCHAR(256) NOT NULL, Timestamp DATETIME2(3) NOT NULL, Value FLOAT NULL, StringValue NVARCHAR(512) NULL, Quality TINYINT NOT NULL DEFAULT 192, -- 192 = Good SiteId NVARCHAR(64) NOT NULL, INDEX IX_TagHistory_TagPath_Timestamp (TagPath, Timestamp DESC), INDEX IX_TagHistory_SiteId_Timestamp (SiteId, Timestamp DESC) ); GO -- Production counts: shift/line production totals IF OBJECT_ID('dbo.ProductionCounts', 'U') IS NULL CREATE TABLE dbo.ProductionCounts ( Id BIGINT IDENTITY(1,1) PRIMARY KEY, SiteId NVARCHAR(64) NOT NULL, LineName NVARCHAR(128) NOT NULL, ShiftDate DATE NOT NULL, ShiftNumber TINYINT NOT NULL, GoodCount INT NOT NULL DEFAULT 0, RejectCount INT NOT NULL DEFAULT 0, Efficiency DECIMAL(5,2) NULL, RecordedAt DATETIME2(3) NOT NULL DEFAULT SYSUTCDATETIME(), INDEX IX_ProductionCounts_Site_Date (SiteId, ShiftDate DESC) ); GO -- Equipment events: state changes, faults, maintenance IF OBJECT_ID('dbo.EquipmentEvents', 'U') IS NULL CREATE TABLE dbo.EquipmentEvents ( Id BIGINT IDENTITY(1,1) PRIMARY KEY, SiteId NVARCHAR(64) NOT NULL, EquipmentId NVARCHAR(128) NOT NULL, EventType NVARCHAR(32) NOT NULL, -- 'StateChange', 'Fault', 'Maintenance', 'Alarm' PreviousState NVARCHAR(64) NULL, NewState NVARCHAR(64) NOT NULL, Description NVARCHAR(512) NULL, Timestamp DATETIME2(3) NOT NULL, INDEX IX_EquipmentEvents_Equipment_Time (EquipmentId, Timestamp DESC), INDEX IX_EquipmentEvents_Site_Type (SiteId, EventType, Timestamp DESC) ); GO -- Batch records: production batch tracking IF OBJECT_ID('dbo.BatchRecords', 'U') IS NULL CREATE TABLE dbo.BatchRecords ( Id BIGINT IDENTITY(1,1) PRIMARY KEY, BatchId NVARCHAR(64) NOT NULL UNIQUE, SiteId NVARCHAR(64) NOT NULL, RecipeId NVARCHAR(64) NOT NULL, Status NVARCHAR(32) NOT NULL DEFAULT 'InProgress', -- 'InProgress', 'Complete', 'Aborted' StartTime DATETIME2(3) NOT NULL, EndTime DATETIME2(3) NULL, TotalQuantity DECIMAL(10,2) NULL, Operator NVARCHAR(128) NULL, Notes NVARCHAR(1024) NULL, INDEX IX_BatchRecords_Site_Status (SiteId, Status), INDEX IX_BatchRecords_StartTime (StartTime DESC) ); GO -- Alarm history: historical alarm events IF OBJECT_ID('dbo.AlarmHistory', 'U') IS NULL CREATE TABLE dbo.AlarmHistory ( Id BIGINT IDENTITY(1,1) PRIMARY KEY, SiteId NVARCHAR(64) NOT NULL, AlarmName NVARCHAR(256) NOT NULL, Severity TINYINT NOT NULL, -- 1=Low, 2=Medium, 3=High, 4=Critical State NVARCHAR(32) NOT NULL, -- 'Active', 'Acknowledged', 'Cleared' ActivatedAt DATETIME2(3) NOT NULL, AcknowledgedAt DATETIME2(3) NULL, ClearedAt DATETIME2(3) NULL, AcknowledgedBy NVARCHAR(128) NULL, Message NVARCHAR(512) NULL, INDEX IX_AlarmHistory_Site_Active (SiteId, State, ActivatedAt DESC), INDEX IX_AlarmHistory_Severity (Severity, ActivatedAt DESC) ); GO -- ========================================================================= -- Stored Procedures -- ========================================================================= -- Get tag history for a tag path within a date range IF OBJECT_ID('dbo.usp_GetTagHistory', 'P') IS NOT NULL DROP PROCEDURE dbo.usp_GetTagHistory; GO CREATE PROCEDURE dbo.usp_GetTagHistory @TagPath NVARCHAR(256), @StartTime DATETIME2(3), @EndTime DATETIME2(3), @MaxRows INT = 10000 AS BEGIN SET NOCOUNT ON; SELECT TOP (@MaxRows) TagPath, Timestamp, Value, StringValue, Quality, SiteId FROM dbo.TagHistory WHERE TagPath = @TagPath AND Timestamp >= @StartTime AND Timestamp <= @EndTime ORDER BY Timestamp DESC; END; GO -- Get production summary for a site over a date range IF OBJECT_ID('dbo.usp_GetProductionSummary', 'P') IS NOT NULL DROP PROCEDURE dbo.usp_GetProductionSummary; GO CREATE PROCEDURE dbo.usp_GetProductionSummary @SiteId NVARCHAR(64), @StartDate DATE, @EndDate DATE AS BEGIN SET NOCOUNT ON; SELECT LineName, SUM(GoodCount) AS TotalGood, SUM(RejectCount) AS TotalReject, SUM(GoodCount) + SUM(RejectCount) AS TotalProduced, CASE WHEN SUM(GoodCount) + SUM(RejectCount) > 0 THEN CAST(SUM(GoodCount) * 100.0 / (SUM(GoodCount) + SUM(RejectCount)) AS DECIMAL(5,2)) ELSE 0 END AS YieldPercent, AVG(Efficiency) AS AvgEfficiency, COUNT(DISTINCT ShiftDate) AS DaysReported FROM dbo.ProductionCounts WHERE SiteId = @SiteId AND ShiftDate >= @StartDate AND ShiftDate <= @EndDate GROUP BY LineName ORDER BY LineName; END; GO -- Insert a batch record (used by CachedWrite from scripts) IF OBJECT_ID('dbo.usp_InsertBatchRecord', 'P') IS NOT NULL DROP PROCEDURE dbo.usp_InsertBatchRecord; GO CREATE PROCEDURE dbo.usp_InsertBatchRecord @BatchId NVARCHAR(64), @SiteId NVARCHAR(64), @RecipeId NVARCHAR(64), @TotalQuantity DECIMAL(10,2) = NULL, @Operator NVARCHAR(128) = NULL, @Notes NVARCHAR(1024) = NULL AS BEGIN SET NOCOUNT ON; INSERT INTO dbo.BatchRecords (BatchId, SiteId, RecipeId, Status, StartTime, TotalQuantity, Operator, Notes) VALUES (@BatchId, @SiteId, @RecipeId, 'InProgress', SYSUTCDATETIME(), @TotalQuantity, @Operator, @Notes); SELECT SCOPE_IDENTITY() AS Id, @BatchId AS BatchId; END; GO -- Complete or abort a batch IF OBJECT_ID('dbo.usp_CompleteBatch', 'P') IS NOT NULL DROP PROCEDURE dbo.usp_CompleteBatch; GO CREATE PROCEDURE dbo.usp_CompleteBatch @BatchId NVARCHAR(64), @Status NVARCHAR(32), -- 'Complete' or 'Aborted' @TotalQuantity DECIMAL(10,2) = NULL, @Notes NVARCHAR(1024) = NULL AS BEGIN SET NOCOUNT ON; UPDATE dbo.BatchRecords SET Status = @Status, EndTime = SYSUTCDATETIME(), TotalQuantity = COALESCE(@TotalQuantity, TotalQuantity), Notes = COALESCE(@Notes, Notes) WHERE BatchId = @BatchId AND Status = 'InProgress'; IF @@ROWCOUNT = 0 THROW 50001, 'Batch not found or not in progress.', 1; SELECT BatchId, Status, StartTime, EndTime, TotalQuantity FROM dbo.BatchRecords WHERE BatchId = @BatchId; END; GO -- Get recent equipment events IF OBJECT_ID('dbo.usp_GetEquipmentEvents', 'P') IS NOT NULL DROP PROCEDURE dbo.usp_GetEquipmentEvents; GO CREATE PROCEDURE dbo.usp_GetEquipmentEvents @SiteId NVARCHAR(64), @EquipmentId NVARCHAR(128) = NULL, @EventType NVARCHAR(32) = NULL, @Hours INT = 24 AS BEGIN SET NOCOUNT ON; DECLARE @Since DATETIME2(3) = DATEADD(HOUR, -@Hours, SYSUTCDATETIME()); SELECT EquipmentId, EventType, PreviousState, NewState, Description, Timestamp FROM dbo.EquipmentEvents WHERE SiteId = @SiteId AND Timestamp >= @Since AND (@EquipmentId IS NULL OR EquipmentId = @EquipmentId) AND (@EventType IS NULL OR EventType = @EventType) ORDER BY Timestamp DESC; END; GO -- Get active alarms for a site IF OBJECT_ID('dbo.usp_GetActiveAlarms', 'P') IS NOT NULL DROP PROCEDURE dbo.usp_GetActiveAlarms; GO CREATE PROCEDURE dbo.usp_GetActiveAlarms @SiteId NVARCHAR(64), @MinSeverity TINYINT = 1 AS BEGIN SET NOCOUNT ON; SELECT AlarmName, Severity, State, ActivatedAt, AcknowledgedAt, AcknowledgedBy, Message FROM dbo.AlarmHistory WHERE SiteId = @SiteId AND State IN ('Active', 'Acknowledged') AND Severity >= @MinSeverity ORDER BY Severity DESC, ActivatedAt DESC; END; GO -- ========================================================================= -- Sample Data -- ========================================================================= -- Tag history (last few hours of data for two sites) DECLARE @now DATETIME2(3) = SYSUTCDATETIME(); INSERT INTO dbo.TagHistory (TagPath, Timestamp, Value, Quality, SiteId) VALUES ('SiteA/Pump-001/Pressure', DATEADD(MINUTE, -120, @now), 45.2, 192, 'SiteA'), ('SiteA/Pump-001/Pressure', DATEADD(MINUTE, -110, @now), 46.1, 192, 'SiteA'), ('SiteA/Pump-001/Pressure', DATEADD(MINUTE, -100, @now), 44.8, 192, 'SiteA'), ('SiteA/Pump-001/Pressure', DATEADD(MINUTE, -90, @now), 47.3, 192, 'SiteA'), ('SiteA/Pump-001/Pressure', DATEADD(MINUTE, -80, @now), 45.9, 192, 'SiteA'), ('SiteA/Pump-001/Flow', DATEADD(MINUTE, -120, @now), 120.5, 192, 'SiteA'), ('SiteA/Pump-001/Flow', DATEADD(MINUTE, -110, @now), 121.2, 192, 'SiteA'), ('SiteA/Pump-001/Flow', DATEADD(MINUTE, -100, @now), 119.8, 192, 'SiteA'), ('SiteA/Tank-001/Level', DATEADD(MINUTE, -120, @now), 72.0, 192, 'SiteA'), ('SiteA/Tank-001/Level', DATEADD(MINUTE, -110, @now), 73.5, 192, 'SiteA'), ('SiteA/Tank-001/Level', DATEADD(MINUTE, -100, @now), 75.1, 192, 'SiteA'), ('SiteA/Tank-001/Level', DATEADD(MINUTE, -90, @now), 76.8, 192, 'SiteA'), ('SiteA/Tank-001/Temperature', DATEADD(MINUTE, -120, @now), 65.3, 192, 'SiteA'), ('SiteA/Tank-001/Temperature', DATEADD(MINUTE, -110, @now), 65.5, 192, 'SiteA'), ('SiteA/Conv-001/Speed', DATEADD(MINUTE, -120, @now), 2.4, 192, 'SiteA'), ('SiteA/Conv-001/Speed', DATEADD(MINUTE, -110, @now), 0.0, 0, 'SiteA'), -- Bad quality (stopped) ('SiteA/Conv-001/Speed', DATEADD(MINUTE, -100, @now), 2.3, 192, 'SiteA'), ('SiteB/Mixer-001/RPM', DATEADD(MINUTE, -120, @now), 450.0, 192, 'SiteB'), ('SiteB/Mixer-001/RPM', DATEADD(MINUTE, -110, @now), 452.0, 192, 'SiteB'), ('SiteB/Mixer-001/RPM', DATEADD(MINUTE, -100, @now), 448.0, 192, 'SiteB'), ('SiteB/Mixer-001/Temperature',DATEADD(MINUTE, -120, @now), 82.1, 192, 'SiteB'), ('SiteB/Mixer-001/Temperature',DATEADD(MINUTE, -110, @now), 83.0, 192, 'SiteB'); -- Production counts (last 3 days, 2 shifts per day) DECLARE @today DATE = CAST(SYSUTCDATETIME() AS DATE); INSERT INTO dbo.ProductionCounts (SiteId, LineName, ShiftDate, ShiftNumber, GoodCount, RejectCount, Efficiency) VALUES ('SiteA', 'Line-1', DATEADD(DAY, -2, @today), 1, 4100, 82, 92.5), ('SiteA', 'Line-1', DATEADD(DAY, -2, @today), 2, 3900, 95, 91.2), ('SiteA', 'Line-2', DATEADD(DAY, -2, @today), 1, 3050, 120, 88.1), ('SiteA', 'Line-2', DATEADD(DAY, -2, @today), 2, 2900, 105, 87.5), ('SiteA', 'Line-1', DATEADD(DAY, -1, @today), 1, 4200, 75, 93.1), ('SiteA', 'Line-1', DATEADD(DAY, -1, @today), 2, 4050, 88, 92.0), ('SiteA', 'Line-2', DATEADD(DAY, -1, @today), 1, 3100, 98, 89.2), ('SiteA', 'Line-2', DATEADD(DAY, -1, @today), 2, 3000, 110, 88.0), ('SiteA', 'Line-1', @today, 1, 2100, 40, 93.5), ('SiteA', 'Line-2', @today, 1, 1550, 65, 88.8), ('SiteB', 'Mixing', DATEADD(DAY, -1, @today), 1, 850, 12, 95.0), ('SiteB', 'Mixing', DATEADD(DAY, -1, @today), 2, 820, 15, 94.2), ('SiteB', 'Packing', DATEADD(DAY, -1, @today), 1, 1600, 45, 90.5), ('SiteB', 'Packing', DATEADD(DAY, -1, @today), 2, 1550, 50, 89.8); -- Equipment events INSERT INTO dbo.EquipmentEvents (SiteId, EquipmentId, EventType, PreviousState, NewState, Description, Timestamp) VALUES ('SiteA', 'PUMP-001', 'StateChange', 'Idle', 'Running', 'Shift start', DATEADD(HOUR, -8, @now)), ('SiteA', 'PUMP-001', 'Fault', 'Running', 'Faulted', 'Overcurrent detected', DATEADD(HOUR, -5, @now)), ('SiteA', 'PUMP-001', 'StateChange', 'Faulted', 'Running', 'Manual reset by operator', DATEADD(HOUR, -4, @now)), ('SiteA', 'TANK-001', 'Alarm', NULL, 'HighLevel','Tank level exceeded 90%', DATEADD(HOUR, -3, @now)), ('SiteA', 'TANK-001', 'Alarm', NULL, 'Normal', 'Tank level returned to normal',DATEADD(HOUR, -2, @now)), ('SiteA', 'CONV-001', 'Maintenance', 'Running', 'Maintenance', 'Scheduled belt inspection', DATEADD(HOUR, -6, @now)), ('SiteA', 'CONV-001', 'StateChange', 'Maintenance', 'Running', 'Maintenance complete', DATEADD(HOUR, -5, @now)), ('SiteB', 'MIXER-001','StateChange', 'Idle', 'Running', 'Batch R-100 started', DATEADD(HOUR, -7, @now)), ('SiteB', 'MIXER-001','StateChange', 'Running', 'Idle', 'Batch R-100 complete', DATEADD(HOUR, -3, @now)); -- Batch records INSERT INTO dbo.BatchRecords (BatchId, SiteId, RecipeId, Status, StartTime, EndTime, TotalQuantity, Operator, Notes) VALUES ('BATCH-20260314-001', 'SiteA', 'R-100', 'Complete', DATEADD(HOUR, -26, @now), DATEADD(HOUR, -22, @now), 450.00, 'jsmith', 'Normal run'), ('BATCH-20260314-002', 'SiteA', 'R-200', 'Complete', DATEADD(HOUR, -20, @now), DATEADD(HOUR, -16, @now), 380.50, 'jsmith', 'Slight yield loss on Material-B'), ('BATCH-20260315-001', 'SiteB', 'R-100', 'Complete', DATEADD(HOUR, -10, @now), DATEADD(HOUR, -6, @now), 445.00, 'mdoe', NULL), ('BATCH-20260315-002', 'SiteA', 'R-150', 'Aborted', DATEADD(HOUR, -8, @now), DATEADD(HOUR, -7, @now), NULL, 'jsmith', 'Material-A out of spec, aborted early'), ('BATCH-20260316-001', 'SiteA', 'R-100', 'InProgress', DATEADD(HOUR, -2, @now), NULL, NULL, 'bwilson', 'Current batch'); -- Alarm history INSERT INTO dbo.AlarmHistory (SiteId, AlarmName, Severity, State, ActivatedAt, AcknowledgedAt, ClearedAt, AcknowledgedBy, Message) VALUES ('SiteA', 'Tank-001 High Level', 3, 'Cleared', DATEADD(HOUR, -3, @now), DATEADD(HOUR, -3, @now), DATEADD(HOUR, -2, @now), 'jsmith', 'Level exceeded 90% setpoint'), ('SiteA', 'Pump-001 Overcurrent', 4, 'Cleared', DATEADD(HOUR, -5, @now), DATEADD(HOUR, -5, @now), DATEADD(HOUR, -4, @now), 'jsmith', 'Current draw 15.2A (limit 12A)'), ('SiteA', 'Conv-001 Belt Slip', 2, 'Active', DATEADD(HOUR, -1, @now), NULL, NULL, NULL, 'Belt speed deviation >5%'), ('SiteA', 'Tank-001 Temperature High', 3, 'Acknowledged', DATEADD(MINUTE, -30, @now), DATEADD(MINUTE, -25, @now), NULL, 'bwilson', 'Temperature 68.2C (limit 65C)'), ('SiteB', 'Mixer-001 Vibration', 2, 'Active', DATEADD(HOUR, -2, @now), NULL, NULL, NULL, 'Vibration level elevated'); GO PRINT 'ScadaLinkMachineData seed complete.'; PRINT 'Tables: TagHistory, ProductionCounts, EquipmentEvents, BatchRecords, AlarmHistory'; PRINT 'Stored Procedures: usp_GetTagHistory, usp_GetProductionSummary, usp_InsertBatchRecord, usp_CompleteBatch, usp_GetEquipmentEvents, usp_GetActiveAlarms'; GO