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.
187 lines
10 KiB
C#
187 lines
10 KiB
C#
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");
|
|
}
|
|
}
|
|
}
|