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:
@@ -1027,6 +1027,193 @@ namespace ZB.MOM.WW.OtOpcUa.Configuration.Migrations
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.Script", b =>
|
||||
{
|
||||
b.Property<Guid>("ScriptRowId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasDefaultValueSql("NEWSEQUENTIALID()");
|
||||
|
||||
b.Property<long>("GenerationId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Language")
|
||||
.IsRequired()
|
||||
.HasMaxLength(16)
|
||||
.HasColumnType("nvarchar(16)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("ScriptId")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<string>("SourceCode")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SourceHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.HasKey("ScriptRowId");
|
||||
|
||||
b.HasIndex("GenerationId", "ScriptId")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UX_Script_Generation_LogicalId")
|
||||
.HasFilter("[ScriptId] IS NOT NULL");
|
||||
|
||||
b.HasIndex("GenerationId", "SourceHash")
|
||||
.HasDatabaseName("IX_Script_Generation_SourceHash");
|
||||
|
||||
b.ToTable("Script", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ScriptedAlarm", b =>
|
||||
{
|
||||
b.Property<Guid>("ScriptedAlarmRowId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasDefaultValueSql("NEWSEQUENTIALID()");
|
||||
|
||||
b.Property<string>("AlarmType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("nvarchar(32)");
|
||||
|
||||
b.Property<bool>("Enabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("EquipmentId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<long>("GenerationId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<bool>("HistorizeToAveva")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("MessageTemplate")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("nvarchar(1024)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("PredicateScriptId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<bool>("Retain")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("ScriptedAlarmId")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<int>("Severity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("ScriptedAlarmRowId");
|
||||
|
||||
b.HasIndex("GenerationId", "PredicateScriptId")
|
||||
.HasDatabaseName("IX_ScriptedAlarm_Generation_Script");
|
||||
|
||||
b.HasIndex("GenerationId", "ScriptedAlarmId")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UX_ScriptedAlarm_Generation_LogicalId")
|
||||
.HasFilter("[ScriptedAlarmId] IS NOT NULL");
|
||||
|
||||
b.HasIndex("GenerationId", "EquipmentId", "Name")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UX_ScriptedAlarm_Generation_EquipmentPath");
|
||||
|
||||
b.ToTable("ScriptedAlarm", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("CK_ScriptedAlarm_AlarmType", "AlarmType IN ('AlarmCondition','LimitAlarm','OffNormalAlarm','DiscreteAlarm')");
|
||||
|
||||
t.HasCheckConstraint("CK_ScriptedAlarm_Severity_Range", "Severity BETWEEN 1 AND 1000");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ScriptedAlarmState", b =>
|
||||
{
|
||||
b.Property<string>("ScriptedAlarmId")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<string>("AckedState")
|
||||
.IsRequired()
|
||||
.HasMaxLength(16)
|
||||
.HasColumnType("nvarchar(16)");
|
||||
|
||||
b.Property<string>("CommentsJson")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ConfirmedState")
|
||||
.IsRequired()
|
||||
.HasMaxLength(16)
|
||||
.HasColumnType("nvarchar(16)");
|
||||
|
||||
b.Property<string>("EnabledState")
|
||||
.IsRequired()
|
||||
.HasMaxLength(16)
|
||||
.HasColumnType("nvarchar(16)");
|
||||
|
||||
b.Property<string>("LastAckComment")
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("nvarchar(1024)");
|
||||
|
||||
b.Property<string>("LastAckUser")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<DateTime?>("LastAckUtc")
|
||||
.HasColumnType("datetime2(3)");
|
||||
|
||||
b.Property<string>("LastConfirmComment")
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("nvarchar(1024)");
|
||||
|
||||
b.Property<string>("LastConfirmUser")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<DateTime?>("LastConfirmUtc")
|
||||
.HasColumnType("datetime2(3)");
|
||||
|
||||
b.Property<DateTime?>("ShelvingExpiresUtc")
|
||||
.HasColumnType("datetime2(3)");
|
||||
|
||||
b.Property<string>("ShelvingState")
|
||||
.IsRequired()
|
||||
.HasMaxLength(16)
|
||||
.HasColumnType("nvarchar(16)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAtUtc")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2(3)")
|
||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
||||
|
||||
b.HasKey("ScriptedAlarmId");
|
||||
|
||||
b.ToTable("ScriptedAlarmState", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("CK_ScriptedAlarmState_CommentsJson_IsJson", "ISJSON(CommentsJson) = 1");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ServerCluster", b =>
|
||||
{
|
||||
b.Property<string>("ClusterId")
|
||||
@@ -1274,6 +1461,74 @@ namespace ZB.MOM.WW.OtOpcUa.Configuration.Migrations
|
||||
b.ToTable("UnsLine", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.VirtualTag", b =>
|
||||
{
|
||||
b.Property<Guid>("VirtualTagRowId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasDefaultValueSql("NEWSEQUENTIALID()");
|
||||
|
||||
b.Property<bool>("ChangeTriggered")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("DataType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("nvarchar(32)");
|
||||
|
||||
b.Property<bool>("Enabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("EquipmentId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<long>("GenerationId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<bool>("Historize")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<string>("ScriptId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<int?>("TimerIntervalMs")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("VirtualTagId")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.HasKey("VirtualTagRowId");
|
||||
|
||||
b.HasIndex("GenerationId", "ScriptId")
|
||||
.HasDatabaseName("IX_VirtualTag_Generation_Script");
|
||||
|
||||
b.HasIndex("GenerationId", "VirtualTagId")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UX_VirtualTag_Generation_LogicalId")
|
||||
.HasFilter("[VirtualTagId] IS NOT NULL");
|
||||
|
||||
b.HasIndex("GenerationId", "EquipmentId", "Name")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UX_VirtualTag_Generation_EquipmentPath");
|
||||
|
||||
b.ToTable("VirtualTag", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("CK_VirtualTag_TimerInterval_Min", "TimerIntervalMs IS NULL OR TimerIntervalMs >= 50");
|
||||
|
||||
t.HasCheckConstraint("CK_VirtualTag_Trigger_AtLeastOne", "ChangeTriggered = 1 OR TimerIntervalMs IS NOT NULL");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ClusterNode", b =>
|
||||
{
|
||||
b.HasOne("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ServerCluster", "Cluster")
|
||||
@@ -1435,6 +1690,28 @@ namespace ZB.MOM.WW.OtOpcUa.Configuration.Migrations
|
||||
b.Navigation("Generation");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.Script", b =>
|
||||
{
|
||||
b.HasOne("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ConfigGeneration", "Generation")
|
||||
.WithMany()
|
||||
.HasForeignKey("GenerationId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Generation");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ScriptedAlarm", b =>
|
||||
{
|
||||
b.HasOne("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ConfigGeneration", "Generation")
|
||||
.WithMany()
|
||||
.HasForeignKey("GenerationId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Generation");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.Tag", b =>
|
||||
{
|
||||
b.HasOne("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ConfigGeneration", "Generation")
|
||||
@@ -1476,6 +1753,17 @@ namespace ZB.MOM.WW.OtOpcUa.Configuration.Migrations
|
||||
b.Navigation("Generation");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.VirtualTag", b =>
|
||||
{
|
||||
b.HasOne("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ConfigGeneration", "Generation")
|
||||
.WithMany()
|
||||
.HasForeignKey("GenerationId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Generation");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ClusterNode", b =>
|
||||
{
|
||||
b.Navigation("Credentials");
|
||||
|
||||
Reference in New Issue
Block a user