Phase 7 Stream E — Config DB schema for scripts, virtual tags, scripted alarms, and alarm state
Adds the four tables Streams B/C/F consume — Script (generation-scoped source code), VirtualTag (generation-scoped calculated-tag config), ScriptedAlarm (generation-scoped alarm config), and ScriptedAlarmState (logical-id-keyed persistent runtime state). ## New entities (net10, EF Core) - Script — stable logical ScriptId carries across generations; SourceHash is the compile-cache key (matches Core.Scripting's CompiledScriptCache). - VirtualTag — mandatory EquipmentId FK (plan decision #2, unified Equipment tree); ChangeTriggered/TimerIntervalMs + Historize flags; check constraints enforce "at least one trigger" + "timer >= 50ms". - ScriptedAlarm — required AlarmType ('AlarmCondition'/'LimitAlarm'/'OffNormalAlarm'/ 'DiscreteAlarm'); Severity 1..1000 range check; HistorizeToAveva default true per plan decision #15. - ScriptedAlarmState — keyed ONLY on ScriptedAlarmId (NOT generation-scoped) per plan decision #14 — ack state + audit trail must follow alarm identity across Modified generations. CommentsJson has ISJSON check for GxP audit. ## Migration EF-generated 20260420231641_AddPhase7ScriptingTables covers all 4 tables + indexes + check constraints + FKs to ConfigGeneration. sp_PublishGeneration required no changes — it only flips Draft->Published status; the new entities already carry GenerationId so they publish atomically with the rest of the config. ## Tests — 12/12 (design-time model introspection) Phase7ScriptingEntitiesTests covers: table registration, column maxlength + column types, unique indexes (Generation+LogicalId, Generation+EquipmentPath for VirtualTag and ScriptedAlarm), secondary indexes (SourceHash for cache lookup), check constraints (trigger-required, timer-min, severity-range, alarm-type-enum, CommentsJson-IsJson), ScriptedAlarmState PK is alarm-id not generation-scoped, ScriptedAlarm defaults (HistorizeToAveva=true, Retain=true, Severity=500, Enabled=true), DbSets wired, and the generated migration type exists for rollforward.
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Configuration.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddPhase7ScriptingTables : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Script",
|
||||
columns: table => new
|
||||
{
|
||||
ScriptRowId = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWSEQUENTIALID()"),
|
||||
GenerationId = table.Column<long>(type: "bigint", nullable: false),
|
||||
ScriptId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
||||
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
||||
SourceCode = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
SourceHash = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
Language = table.Column<string>(type: "nvarchar(16)", maxLength: 16, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Script", x => x.ScriptRowId);
|
||||
table.ForeignKey(
|
||||
name: "FK_Script_ConfigGeneration_GenerationId",
|
||||
column: x => x.GenerationId,
|
||||
principalTable: "ConfigGeneration",
|
||||
principalColumn: "GenerationId",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ScriptedAlarm",
|
||||
columns: table => new
|
||||
{
|
||||
ScriptedAlarmRowId = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWSEQUENTIALID()"),
|
||||
GenerationId = table.Column<long>(type: "bigint", nullable: false),
|
||||
ScriptedAlarmId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
||||
EquipmentId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
||||
AlarmType = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false),
|
||||
Severity = table.Column<int>(type: "int", nullable: false),
|
||||
MessageTemplate = table.Column<string>(type: "nvarchar(1024)", maxLength: 1024, nullable: false),
|
||||
PredicateScriptId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
HistorizeToAveva = table.Column<bool>(type: "bit", nullable: false),
|
||||
Retain = table.Column<bool>(type: "bit", nullable: false),
|
||||
Enabled = table.Column<bool>(type: "bit", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ScriptedAlarm", x => x.ScriptedAlarmRowId);
|
||||
table.CheckConstraint("CK_ScriptedAlarm_AlarmType", "AlarmType IN ('AlarmCondition','LimitAlarm','OffNormalAlarm','DiscreteAlarm')");
|
||||
table.CheckConstraint("CK_ScriptedAlarm_Severity_Range", "Severity BETWEEN 1 AND 1000");
|
||||
table.ForeignKey(
|
||||
name: "FK_ScriptedAlarm_ConfigGeneration_GenerationId",
|
||||
column: x => x.GenerationId,
|
||||
principalTable: "ConfigGeneration",
|
||||
principalColumn: "GenerationId",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ScriptedAlarmState",
|
||||
columns: table => new
|
||||
{
|
||||
ScriptedAlarmId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
EnabledState = table.Column<string>(type: "nvarchar(16)", maxLength: 16, nullable: false),
|
||||
AckedState = table.Column<string>(type: "nvarchar(16)", maxLength: 16, nullable: false),
|
||||
ConfirmedState = table.Column<string>(type: "nvarchar(16)", maxLength: 16, nullable: false),
|
||||
ShelvingState = table.Column<string>(type: "nvarchar(16)", maxLength: 16, nullable: false),
|
||||
ShelvingExpiresUtc = table.Column<DateTime>(type: "datetime2(3)", nullable: true),
|
||||
LastAckUser = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||
LastAckComment = table.Column<string>(type: "nvarchar(1024)", maxLength: 1024, nullable: true),
|
||||
LastAckUtc = table.Column<DateTime>(type: "datetime2(3)", nullable: true),
|
||||
LastConfirmUser = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||
LastConfirmComment = table.Column<string>(type: "nvarchar(1024)", maxLength: 1024, nullable: true),
|
||||
LastConfirmUtc = table.Column<DateTime>(type: "datetime2(3)", nullable: true),
|
||||
CommentsJson = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
UpdatedAtUtc = table.Column<DateTime>(type: "datetime2(3)", nullable: false, defaultValueSql: "SYSUTCDATETIME()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ScriptedAlarmState", x => x.ScriptedAlarmId);
|
||||
table.CheckConstraint("CK_ScriptedAlarmState_CommentsJson_IsJson", "ISJSON(CommentsJson) = 1");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "VirtualTag",
|
||||
columns: table => new
|
||||
{
|
||||
VirtualTagRowId = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWSEQUENTIALID()"),
|
||||
GenerationId = table.Column<long>(type: "bigint", nullable: false),
|
||||
VirtualTagId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
||||
EquipmentId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
||||
DataType = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false),
|
||||
ScriptId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
ChangeTriggered = table.Column<bool>(type: "bit", nullable: false),
|
||||
TimerIntervalMs = table.Column<int>(type: "int", nullable: true),
|
||||
Historize = table.Column<bool>(type: "bit", nullable: false),
|
||||
Enabled = table.Column<bool>(type: "bit", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_VirtualTag", x => x.VirtualTagRowId);
|
||||
table.CheckConstraint("CK_VirtualTag_TimerInterval_Min", "TimerIntervalMs IS NULL OR TimerIntervalMs >= 50");
|
||||
table.CheckConstraint("CK_VirtualTag_Trigger_AtLeastOne", "ChangeTriggered = 1 OR TimerIntervalMs IS NOT NULL");
|
||||
table.ForeignKey(
|
||||
name: "FK_VirtualTag_ConfigGeneration_GenerationId",
|
||||
column: x => x.GenerationId,
|
||||
principalTable: "ConfigGeneration",
|
||||
principalColumn: "GenerationId",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Script_Generation_SourceHash",
|
||||
table: "Script",
|
||||
columns: new[] { "GenerationId", "SourceHash" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "UX_Script_Generation_LogicalId",
|
||||
table: "Script",
|
||||
columns: new[] { "GenerationId", "ScriptId" },
|
||||
unique: true,
|
||||
filter: "[ScriptId] IS NOT NULL");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ScriptedAlarm_Generation_Script",
|
||||
table: "ScriptedAlarm",
|
||||
columns: new[] { "GenerationId", "PredicateScriptId" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "UX_ScriptedAlarm_Generation_EquipmentPath",
|
||||
table: "ScriptedAlarm",
|
||||
columns: new[] { "GenerationId", "EquipmentId", "Name" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "UX_ScriptedAlarm_Generation_LogicalId",
|
||||
table: "ScriptedAlarm",
|
||||
columns: new[] { "GenerationId", "ScriptedAlarmId" },
|
||||
unique: true,
|
||||
filter: "[ScriptedAlarmId] IS NOT NULL");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_VirtualTag_Generation_Script",
|
||||
table: "VirtualTag",
|
||||
columns: new[] { "GenerationId", "ScriptId" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "UX_VirtualTag_Generation_EquipmentPath",
|
||||
table: "VirtualTag",
|
||||
columns: new[] { "GenerationId", "EquipmentId", "Name" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "UX_VirtualTag_Generation_LogicalId",
|
||||
table: "VirtualTag",
|
||||
columns: new[] { "GenerationId", "VirtualTagId" },
|
||||
unique: true,
|
||||
filter: "[VirtualTagId] IS NOT NULL");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Script");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ScriptedAlarm");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ScriptedAlarmState");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "VirtualTag");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user