diff --git a/src/ZB.MOM.WW.OtOpcUa.Configuration/Entities/Script.cs b/src/ZB.MOM.WW.OtOpcUa.Configuration/Entities/Script.cs new file mode 100644 index 0000000..67174bf --- /dev/null +++ b/src/ZB.MOM.WW.OtOpcUa.Configuration/Entities/Script.cs @@ -0,0 +1,38 @@ +namespace ZB.MOM.WW.OtOpcUa.Configuration.Entities; + +/// +/// Per Phase 7 plan decision #8 — user-authored C# script source, referenced by +/// and . One row per script, +/// per generation. SourceHash is the compile-cache key. +/// +/// +/// +/// Scripts are generation-scoped: a draft's edit creates a new row in the draft +/// generation, the old row stays frozen in the published generation. Shape mirrors +/// the other generation-scoped entities (Equipment, Tag, etc.) — ScriptId is +/// the stable logical id that carries across generations; ScriptRowId is the +/// row identity. +/// +/// +public sealed class Script +{ + public Guid ScriptRowId { get; set; } + public long GenerationId { get; set; } + + /// Stable logical id. Carries across generations. + public required string ScriptId { get; set; } + + /// Operator-friendly name for log filtering + Admin UI list view. + public required string Name { get; set; } + + /// Raw C# source. Size bounded by the DB column (nvarchar(max)). + public required string SourceCode { get; set; } + + /// SHA-256 of — compile-cache key for Phase 7 Stream A's CompiledScriptCache. + public required string SourceHash { get; set; } + + /// Language — always "CSharp" today; placeholder for future engines (Python/Lua). + public string Language { get; set; } = "CSharp"; + + public ConfigGeneration? Generation { get; set; } +} diff --git a/src/ZB.MOM.WW.OtOpcUa.Configuration/Entities/ScriptedAlarm.cs b/src/ZB.MOM.WW.OtOpcUa.Configuration/Entities/ScriptedAlarm.cs new file mode 100644 index 0000000..f99f4be --- /dev/null +++ b/src/ZB.MOM.WW.OtOpcUa.Configuration/Entities/ScriptedAlarm.cs @@ -0,0 +1,59 @@ +namespace ZB.MOM.WW.OtOpcUa.Configuration.Entities; + +/// +/// Per Phase 7 plan decisions #5, #13, #15 — a scripted OPC UA Part 9 alarm whose +/// condition is the predicate referenced by +/// . Materialized by Core.ScriptedAlarms as a +/// concrete AlarmConditionType subtype per . +/// +/// +/// +/// Message tokens ({TagPath}) resolved at emission time per plan decision #13. +/// (plan decision #15) gates whether transitions +/// route through the Core.AlarmHistorian SQLite queue + Galaxy.Host to the Aveva +/// Historian alarm schema. +/// +/// +public sealed class ScriptedAlarm +{ + public Guid ScriptedAlarmRowId { get; set; } + public long GenerationId { get; set; } + + /// Stable logical id — drives AlarmConditionType.ConditionName. + public required string ScriptedAlarmId { get; set; } + + /// Logical FK to — owner of this alarm. + public required string EquipmentId { get; set; } + + /// Operator-facing alarm name. + public required string Name { get; set; } + + /// Concrete Part 9 type — "AlarmCondition" / "LimitAlarm" / "OffNormalAlarm" / "DiscreteAlarm". + public required string AlarmType { get; set; } + + /// Numeric severity 1..1000 per OPC UA Part 9 (usual bands: 1-250 Low, 251-500 Medium, 501-750 High, 751-1000 Critical). + public int Severity { get; set; } = 500; + + /// Template with {TagPath} tokens resolved at emission time. + public required string MessageTemplate { get; set; } + + /// Logical FK to — predicate script returning bool. + public required string PredicateScriptId { get; set; } + + /// + /// Plan decision #15 — when true, transitions route through the SQLite store-and-forward + /// queue to the Aveva Historian. Defaults on for scripted alarms because they are the + /// primary motivation for the historian sink; operator can disable per alarm. + /// + public bool HistorizeToAveva { get; set; } = true; + + /// + /// OPC UA Part 9 Retain flag — whether the alarm keeps active-state between + /// sessions. Most plant alarms are retained; one-shot event-style alarms are not. + /// + public bool Retain { get; set; } = true; + + public bool Enabled { get; set; } = true; + + public ConfigGeneration? Generation { get; set; } +} diff --git a/src/ZB.MOM.WW.OtOpcUa.Configuration/Entities/ScriptedAlarmState.cs b/src/ZB.MOM.WW.OtOpcUa.Configuration/Entities/ScriptedAlarmState.cs new file mode 100644 index 0000000..d0d7837 --- /dev/null +++ b/src/ZB.MOM.WW.OtOpcUa.Configuration/Entities/ScriptedAlarmState.cs @@ -0,0 +1,62 @@ +namespace ZB.MOM.WW.OtOpcUa.Configuration.Entities; + +/// +/// Per Phase 7 plan decision #14 — persistent runtime state for each scripted alarm. +/// Survives process restart so operators don't re-ack and ack history survives for +/// GxP / 21 CFR Part 11 compliance. Keyed on ScriptedAlarmId logically (not +/// per-generation) because ack state follows the alarm's stable identity across +/// generations — a Modified alarm keeps its ack history. +/// +/// +/// +/// ActiveState is deliberately NOT persisted — it rederives from the current +/// predicate evaluation on startup. Only operator-supplied state (, +/// , ) + audit trail persist. +/// +/// +/// is an append-only JSON array of {user, utc, text} +/// tuples — one per operator comment. Core.ScriptedAlarms' AlarmConditionState.Comments +/// serializes directly into this column. +/// +/// +public sealed class ScriptedAlarmState +{ + /// Logical FK — matches . One row per alarm identity. + public required string ScriptedAlarmId { get; set; } + + /// Enabled/Disabled. Persists across restart per plan decision #14. + public required string EnabledState { get; set; } = "Enabled"; + + /// Unacknowledged / Acknowledged. + public required string AckedState { get; set; } = "Unacknowledged"; + + /// Unconfirmed / Confirmed. + public required string ConfirmedState { get; set; } = "Unconfirmed"; + + /// Unshelved / OneShotShelved / TimedShelved. + public required string ShelvingState { get; set; } = "Unshelved"; + + /// When a TimedShelve expires — null if not shelved or OneShotShelved. + public DateTime? ShelvingExpiresUtc { get; set; } + + /// User who last acknowledged. Null if never acked. + public string? LastAckUser { get; set; } + + /// Operator-supplied ack comment. Null if no comment or never acked. + public string? LastAckComment { get; set; } + + public DateTime? LastAckUtc { get; set; } + + /// User who last confirmed. + public string? LastConfirmUser { get; set; } + + public string? LastConfirmComment { get; set; } + + public DateTime? LastConfirmUtc { get; set; } + + /// JSON array of operator comments, append-only (GxP audit). + public string CommentsJson { get; set; } = "[]"; + + /// Row write timestamp — tracks last state change. + public DateTime UpdatedAtUtc { get; set; } +} diff --git a/src/ZB.MOM.WW.OtOpcUa.Configuration/Entities/VirtualTag.cs b/src/ZB.MOM.WW.OtOpcUa.Configuration/Entities/VirtualTag.cs new file mode 100644 index 0000000..eff66a6 --- /dev/null +++ b/src/ZB.MOM.WW.OtOpcUa.Configuration/Entities/VirtualTag.cs @@ -0,0 +1,53 @@ +namespace ZB.MOM.WW.OtOpcUa.Configuration.Entities; + +/// +/// Per Phase 7 plan decision #2 — a virtual (calculated) tag that lives in the +/// Equipment tree alongside driver tags. Value is produced by the +/// referenced by . +/// +/// +/// +/// is mandatory — virtual tags are always scoped to an +/// Equipment node per plan decision #2 (unified Equipment tree, not a separate +/// /Virtual namespace). matches the shape used by +/// Tag.DataType. +/// +/// +/// and together realize +/// plan decision #3 (change + timer). At least one must produce evaluations; the +/// Core.VirtualTags engine rejects an all-disabled tag at load time. +/// +/// +public sealed class VirtualTag +{ + public Guid VirtualTagRowId { get; set; } + public long GenerationId { get; set; } + + /// Stable logical id. + public required string VirtualTagId { get; set; } + + /// Logical FK to — owner of this virtual tag. + public required string EquipmentId { get; set; } + + /// Browse name — unique within owning Equipment. + public required string Name { get; set; } + + /// DataType string — same vocabulary as . + public required string DataType { get; set; } + + /// Logical FK to — the script that computes this tag's value. + public required string ScriptId { get; set; } + + /// Re-evaluate when any referenced input tag changes. Default on. + public bool ChangeTriggered { get; set; } = true; + + /// Timer re-evaluation cadence in milliseconds. null = no timer. + public int? TimerIntervalMs { get; set; } + + /// Per plan decision #10 — checkbox to route this tag's values through IHistoryWriter. + public bool Historize { get; set; } + + public bool Enabled { get; set; } = true; + + public ConfigGeneration? Generation { get; set; } +} diff --git a/src/ZB.MOM.WW.OtOpcUa.Configuration/Migrations/20260420231641_AddPhase7ScriptingTables.Designer.cs b/src/ZB.MOM.WW.OtOpcUa.Configuration/Migrations/20260420231641_AddPhase7ScriptingTables.Designer.cs new file mode 100644 index 0000000..06319e6 --- /dev/null +++ b/src/ZB.MOM.WW.OtOpcUa.Configuration/Migrations/20260420231641_AddPhase7ScriptingTables.Designer.cs @@ -0,0 +1,1793 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using ZB.MOM.WW.OtOpcUa.Configuration; + +#nullable disable + +namespace ZB.MOM.WW.OtOpcUa.Configuration.Migrations +{ + [DbContext(typeof(OtOpcUaConfigDbContext))] + [Migration("20260420231641_AddPhase7ScriptingTables")] + partial class AddPhase7ScriptingTables + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ClusterNode", b => + { + b.Property("NodeId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ApplicationUri") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ClusterId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2(3)") + .HasDefaultValueSql("SYSUTCDATETIME()"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("DashboardPort") + .HasColumnType("int"); + + b.Property("DriverConfigOverridesJson") + .HasColumnType("nvarchar(max)"); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("Host") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("LastSeenAt") + .HasColumnType("datetime2(3)"); + + b.Property("OpcUaPort") + .HasColumnType("int"); + + b.Property("RedundancyRole") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("ServiceLevelBase") + .HasColumnType("tinyint"); + + b.HasKey("NodeId"); + + b.HasIndex("ApplicationUri") + .IsUnique() + .HasDatabaseName("UX_ClusterNode_ApplicationUri"); + + b.HasIndex("ClusterId") + .IsUnique() + .HasDatabaseName("UX_ClusterNode_Primary_Per_Cluster") + .HasFilter("[RedundancyRole] = 'Primary'"); + + b.ToTable("ClusterNode", (string)null); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ClusterNodeCredential", b => + { + b.Property("CredentialId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWSEQUENTIALID()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2(3)") + .HasDefaultValueSql("SYSUTCDATETIME()"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("Kind") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("NodeId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("RotatedAt") + .HasColumnType("datetime2(3)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.HasKey("CredentialId"); + + b.HasIndex("Kind", "Value") + .IsUnique() + .HasDatabaseName("UX_ClusterNodeCredential_Value") + .HasFilter("[Enabled] = 1"); + + b.HasIndex("NodeId", "Enabled") + .HasDatabaseName("IX_ClusterNodeCredential_NodeId"); + + b.ToTable("ClusterNodeCredential", (string)null); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ClusterNodeGenerationState", b => + { + b.Property("NodeId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("CurrentGenerationId") + .HasColumnType("bigint"); + + b.Property("LastAppliedAt") + .HasColumnType("datetime2(3)"); + + b.Property("LastAppliedError") + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.Property("LastAppliedStatus") + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("LastSeenAt") + .HasColumnType("datetime2(3)"); + + b.HasKey("NodeId"); + + b.HasIndex("CurrentGenerationId") + .HasDatabaseName("IX_ClusterNodeGenerationState_Generation"); + + b.ToTable("ClusterNodeGenerationState", (string)null); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ConfigAuditLog", b => + { + b.Property("AuditId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("AuditId")); + + b.Property("ClusterId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DetailsJson") + .HasColumnType("nvarchar(max)"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("GenerationId") + .HasColumnType("bigint"); + + b.Property("NodeId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Principal") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Timestamp") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2(3)") + .HasDefaultValueSql("SYSUTCDATETIME()"); + + b.HasKey("AuditId"); + + b.HasIndex("GenerationId") + .HasDatabaseName("IX_ConfigAuditLog_Generation") + .HasFilter("[GenerationId] IS NOT NULL"); + + b.HasIndex("ClusterId", "Timestamp") + .IsDescending(false, true) + .HasDatabaseName("IX_ConfigAuditLog_Cluster_Time"); + + b.ToTable("ConfigAuditLog", null, t => + { + t.HasCheckConstraint("CK_ConfigAuditLog_DetailsJson_IsJson", "DetailsJson IS NULL OR ISJSON(DetailsJson) = 1"); + }); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ConfigGeneration", b => + { + b.Property("GenerationId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("GenerationId")); + + b.Property("ClusterId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2(3)") + .HasDefaultValueSql("SYSUTCDATETIME()"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Notes") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("ParentGenerationId") + .HasColumnType("bigint"); + + b.Property("PublishedAt") + .HasColumnType("datetime2(3)"); + + b.Property("PublishedBy") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.HasKey("GenerationId"); + + b.HasIndex("ClusterId") + .IsUnique() + .HasDatabaseName("UX_ConfigGeneration_Draft_Per_Cluster") + .HasFilter("[Status] = 'Draft'"); + + b.HasIndex("ParentGenerationId"); + + b.HasIndex("ClusterId", "Status", "GenerationId") + .IsDescending(false, false, true) + .HasDatabaseName("IX_ConfigGeneration_Cluster_Published"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("ClusterId", "Status", "GenerationId"), new[] { "PublishedAt" }); + + b.ToTable("ConfigGeneration", (string)null); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.Device", b => + { + b.Property("DeviceRowId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWSEQUENTIALID()"); + + b.Property("DeviceConfig") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DeviceId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DriverInstanceId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("GenerationId") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("DeviceRowId"); + + b.HasIndex("GenerationId", "DeviceId") + .IsUnique() + .HasDatabaseName("UX_Device_Generation_LogicalId") + .HasFilter("[DeviceId] IS NOT NULL"); + + b.HasIndex("GenerationId", "DriverInstanceId") + .HasDatabaseName("IX_Device_Generation_Driver"); + + b.ToTable("Device", null, t => + { + t.HasCheckConstraint("CK_Device_DeviceConfig_IsJson", "ISJSON(DeviceConfig) = 1"); + }); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.DriverHostStatus", b => + { + b.Property("NodeId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DriverInstanceId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("HostName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Detail") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("LastSeenUtc") + .HasColumnType("datetime2(3)"); + + b.Property("State") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("StateChangedUtc") + .HasColumnType("datetime2(3)"); + + b.HasKey("NodeId", "DriverInstanceId", "HostName"); + + b.HasIndex("LastSeenUtc") + .HasDatabaseName("IX_DriverHostStatus_LastSeen"); + + b.HasIndex("NodeId") + .HasDatabaseName("IX_DriverHostStatus_Node"); + + b.ToTable("DriverHostStatus", (string)null); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.DriverInstance", b => + { + b.Property("DriverInstanceRowId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWSEQUENTIALID()"); + + b.Property("ClusterId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DriverConfig") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DriverInstanceId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DriverType") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("GenerationId") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("NamespaceId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ResilienceConfig") + .HasColumnType("nvarchar(max)"); + + b.HasKey("DriverInstanceRowId"); + + b.HasIndex("ClusterId"); + + b.HasIndex("GenerationId", "ClusterId") + .HasDatabaseName("IX_DriverInstance_Generation_Cluster"); + + b.HasIndex("GenerationId", "DriverInstanceId") + .IsUnique() + .HasDatabaseName("UX_DriverInstance_Generation_LogicalId") + .HasFilter("[DriverInstanceId] IS NOT NULL"); + + b.HasIndex("GenerationId", "NamespaceId") + .HasDatabaseName("IX_DriverInstance_Generation_Namespace"); + + b.ToTable("DriverInstance", null, t => + { + t.HasCheckConstraint("CK_DriverInstance_DriverConfig_IsJson", "ISJSON(DriverConfig) = 1"); + + t.HasCheckConstraint("CK_DriverInstance_ResilienceConfig_IsJson", "ResilienceConfig IS NULL OR ISJSON(ResilienceConfig) = 1"); + }); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.DriverInstanceResilienceStatus", b => + { + b.Property("DriverInstanceId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("HostName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("BaselineFootprintBytes") + .HasColumnType("bigint"); + + b.Property("ConsecutiveFailures") + .HasColumnType("int"); + + b.Property("CurrentBulkheadDepth") + .HasColumnType("int"); + + b.Property("CurrentFootprintBytes") + .HasColumnType("bigint"); + + b.Property("LastCircuitBreakerOpenUtc") + .HasColumnType("datetime2(3)"); + + b.Property("LastRecycleUtc") + .HasColumnType("datetime2(3)"); + + b.Property("LastSampledUtc") + .HasColumnType("datetime2(3)"); + + b.HasKey("DriverInstanceId", "HostName"); + + b.HasIndex("LastSampledUtc") + .HasDatabaseName("IX_DriverResilience_LastSampled"); + + b.ToTable("DriverInstanceResilienceStatus", (string)null); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.Equipment", b => + { + b.Property("EquipmentRowId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWSEQUENTIALID()"); + + b.Property("AssetLocation") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("DeviceId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DeviceManualUri") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("DriverInstanceId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("EquipmentClassRef") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("EquipmentId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("EquipmentUuid") + .HasColumnType("uniqueidentifier"); + + b.Property("GenerationId") + .HasColumnType("bigint"); + + b.Property("HardwareRevision") + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("MachineCode") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Manufacturer") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ManufacturerUri") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("Model") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("SAPID") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("SerialNumber") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("SoftwareRevision") + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("UnsLineId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("YearOfConstruction") + .HasColumnType("smallint"); + + b.Property("ZTag") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.HasKey("EquipmentRowId"); + + b.HasIndex("GenerationId", "DriverInstanceId") + .HasDatabaseName("IX_Equipment_Generation_Driver"); + + b.HasIndex("GenerationId", "EquipmentId") + .IsUnique() + .HasDatabaseName("UX_Equipment_Generation_LogicalId") + .HasFilter("[EquipmentId] IS NOT NULL"); + + b.HasIndex("GenerationId", "EquipmentUuid") + .IsUnique() + .HasDatabaseName("UX_Equipment_Generation_Uuid"); + + b.HasIndex("GenerationId", "MachineCode") + .HasDatabaseName("IX_Equipment_Generation_MachineCode"); + + b.HasIndex("GenerationId", "SAPID") + .HasDatabaseName("IX_Equipment_Generation_SAPID") + .HasFilter("[SAPID] IS NOT NULL"); + + b.HasIndex("GenerationId", "UnsLineId") + .HasDatabaseName("IX_Equipment_Generation_Line"); + + b.HasIndex("GenerationId", "ZTag") + .HasDatabaseName("IX_Equipment_Generation_ZTag") + .HasFilter("[ZTag] IS NOT NULL"); + + b.HasIndex("GenerationId", "UnsLineId", "Name") + .IsUnique() + .HasDatabaseName("UX_Equipment_Generation_LinePath"); + + b.ToTable("Equipment", (string)null); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.EquipmentImportBatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ClusterId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime2(3)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("FinalisedAtUtc") + .HasColumnType("datetime2(3)"); + + b.Property("RowsAccepted") + .HasColumnType("int"); + + b.Property("RowsRejected") + .HasColumnType("int"); + + b.Property("RowsStaged") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy", "FinalisedAtUtc") + .HasDatabaseName("IX_EquipmentImportBatch_Creator_Finalised"); + + b.ToTable("EquipmentImportBatch", (string)null); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.EquipmentImportRow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AssetLocation") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("BatchId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeviceManualUri") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("EquipmentId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("EquipmentUuid") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("HardwareRevision") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("IsAccepted") + .HasColumnType("bit"); + + b.Property("LineNumberInFile") + .HasColumnType("int"); + + b.Property("MachineCode") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Manufacturer") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ManufacturerUri") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("Model") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("RejectReason") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("SAPID") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("SerialNumber") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("SoftwareRevision") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("UnsAreaName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("UnsLineName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("YearOfConstruction") + .HasMaxLength(8) + .HasColumnType("nvarchar(8)"); + + b.Property("ZTag") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("BatchId") + .HasDatabaseName("IX_EquipmentImportRow_Batch"); + + b.ToTable("EquipmentImportRow", (string)null); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ExternalIdReservation", b => + { + b.Property("ReservationId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWSEQUENTIALID()"); + + b.Property("ClusterId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("EquipmentUuid") + .HasColumnType("uniqueidentifier"); + + b.Property("FirstPublishedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2(3)") + .HasDefaultValueSql("SYSUTCDATETIME()"); + + b.Property("FirstPublishedBy") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Kind") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("LastPublishedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2(3)") + .HasDefaultValueSql("SYSUTCDATETIME()"); + + b.Property("ReleaseReason") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("ReleasedAt") + .HasColumnType("datetime2(3)"); + + b.Property("ReleasedBy") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.HasKey("ReservationId"); + + b.HasIndex("EquipmentUuid") + .HasDatabaseName("IX_ExternalIdReservation_Equipment"); + + b.HasIndex("Kind", "Value") + .IsUnique() + .HasDatabaseName("UX_ExternalIdReservation_KindValue_Active") + .HasFilter("[ReleasedAt] IS NULL"); + + b.ToTable("ExternalIdReservation", (string)null); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.LdapGroupRoleMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ClusterId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("CreatedAtUtc") + .HasColumnType("datetime2(3)"); + + b.Property("IsSystemWide") + .HasColumnType("bit"); + + b.Property("LdapGroup") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("Notes") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.HasKey("Id"); + + b.HasIndex("ClusterId"); + + b.HasIndex("LdapGroup") + .HasDatabaseName("IX_LdapGroupRoleMapping_Group"); + + b.HasIndex("LdapGroup", "ClusterId") + .IsUnique() + .HasDatabaseName("UX_LdapGroupRoleMapping_Group_Cluster") + .HasFilter("[ClusterId] IS NOT NULL"); + + b.ToTable("LdapGroupRoleMapping", (string)null); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.Namespace", b => + { + b.Property("NamespaceRowId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWSEQUENTIALID()"); + + b.Property("ClusterId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("GenerationId") + .HasColumnType("bigint"); + + b.Property("Kind") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("NamespaceId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("NamespaceUri") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Notes") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.HasKey("NamespaceRowId"); + + b.HasIndex("ClusterId"); + + b.HasIndex("GenerationId", "ClusterId") + .HasDatabaseName("IX_Namespace_Generation_Cluster"); + + b.HasIndex("GenerationId", "NamespaceId") + .IsUnique() + .HasDatabaseName("UX_Namespace_Generation_LogicalId") + .HasFilter("[NamespaceId] IS NOT NULL"); + + b.HasIndex("GenerationId", "NamespaceUri") + .IsUnique() + .HasDatabaseName("UX_Namespace_Generation_NamespaceUri"); + + b.HasIndex("GenerationId", "ClusterId", "Kind") + .IsUnique() + .HasDatabaseName("UX_Namespace_Generation_Cluster_Kind"); + + b.HasIndex("GenerationId", "NamespaceId", "ClusterId") + .IsUnique() + .HasDatabaseName("UX_Namespace_Generation_LogicalId_Cluster") + .HasFilter("[NamespaceId] IS NOT NULL"); + + b.ToTable("Namespace", (string)null); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.NodeAcl", b => + { + b.Property("NodeAclRowId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWSEQUENTIALID()"); + + b.Property("ClusterId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("GenerationId") + .HasColumnType("bigint"); + + b.Property("LdapGroup") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NodeAclId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Notes") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("PermissionFlags") + .HasColumnType("int"); + + b.Property("ScopeId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ScopeKind") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.HasKey("NodeAclRowId"); + + b.HasIndex("GenerationId", "ClusterId") + .HasDatabaseName("IX_NodeAcl_Generation_Cluster"); + + b.HasIndex("GenerationId", "LdapGroup") + .HasDatabaseName("IX_NodeAcl_Generation_Group"); + + b.HasIndex("GenerationId", "NodeAclId") + .IsUnique() + .HasDatabaseName("UX_NodeAcl_Generation_LogicalId") + .HasFilter("[NodeAclId] IS NOT NULL"); + + b.HasIndex("GenerationId", "ScopeKind", "ScopeId") + .HasDatabaseName("IX_NodeAcl_Generation_Scope") + .HasFilter("[ScopeId] IS NOT NULL"); + + b.HasIndex("GenerationId", "ClusterId", "LdapGroup", "ScopeKind", "ScopeId") + .IsUnique() + .HasDatabaseName("UX_NodeAcl_Generation_GroupScope") + .HasFilter("[ScopeId] IS NOT NULL"); + + b.ToTable("NodeAcl", (string)null); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.PollGroup", b => + { + b.Property("PollGroupRowId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWSEQUENTIALID()"); + + b.Property("DriverInstanceId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("GenerationId") + .HasColumnType("bigint"); + + b.Property("IntervalMs") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PollGroupId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.HasKey("PollGroupRowId"); + + b.HasIndex("GenerationId", "DriverInstanceId") + .HasDatabaseName("IX_PollGroup_Generation_Driver"); + + b.HasIndex("GenerationId", "PollGroupId") + .IsUnique() + .HasDatabaseName("UX_PollGroup_Generation_LogicalId") + .HasFilter("[PollGroupId] IS NOT NULL"); + + b.ToTable("PollGroup", null, t => + { + t.HasCheckConstraint("CK_PollGroup_IntervalMs_Min", "IntervalMs >= 50"); + }); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.Script", b => + { + b.Property("ScriptRowId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWSEQUENTIALID()"); + + b.Property("GenerationId") + .HasColumnType("bigint"); + + b.Property("Language") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ScriptId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("SourceCode") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("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("ScriptedAlarmRowId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWSEQUENTIALID()"); + + b.Property("AlarmType") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("EquipmentId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("GenerationId") + .HasColumnType("bigint"); + + b.Property("HistorizeToAveva") + .HasColumnType("bit"); + + b.Property("MessageTemplate") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PredicateScriptId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Retain") + .HasColumnType("bit"); + + b.Property("ScriptedAlarmId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("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("ScriptedAlarmId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("AckedState") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("CommentsJson") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ConfirmedState") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("EnabledState") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("LastAckComment") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("LastAckUser") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("LastAckUtc") + .HasColumnType("datetime2(3)"); + + b.Property("LastConfirmComment") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("LastConfirmUser") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("LastConfirmUtc") + .HasColumnType("datetime2(3)"); + + b.Property("ShelvingExpiresUtc") + .HasColumnType("datetime2(3)"); + + b.Property("ShelvingState") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("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("ClusterId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2(3)") + .HasDefaultValueSql("SYSUTCDATETIME()"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("Enterprise") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("ModifiedAt") + .HasColumnType("datetime2(3)"); + + b.Property("ModifiedBy") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("NodeCount") + .HasColumnType("tinyint"); + + b.Property("Notes") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("RedundancyMode") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("Site") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.HasKey("ClusterId"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("UX_ServerCluster_Name"); + + b.HasIndex("Site") + .HasDatabaseName("IX_ServerCluster_Site"); + + b.ToTable("ServerCluster", null, t => + { + t.HasCheckConstraint("CK_ServerCluster_RedundancyMode_NodeCount", "((NodeCount = 1 AND RedundancyMode = 'None') OR (NodeCount = 2 AND RedundancyMode IN ('Warm', 'Hot')))"); + }); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.Tag", b => + { + b.Property("TagRowId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWSEQUENTIALID()"); + + b.Property("AccessLevel") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("DataType") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("DeviceId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DriverInstanceId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("EquipmentId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("FolderPath") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("GenerationId") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PollGroupId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("TagConfig") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TagId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("WriteIdempotent") + .HasColumnType("bit"); + + b.HasKey("TagRowId"); + + b.HasIndex("GenerationId", "EquipmentId") + .HasDatabaseName("IX_Tag_Generation_Equipment") + .HasFilter("[EquipmentId] IS NOT NULL"); + + b.HasIndex("GenerationId", "TagId") + .IsUnique() + .HasDatabaseName("UX_Tag_Generation_LogicalId") + .HasFilter("[TagId] IS NOT NULL"); + + b.HasIndex("GenerationId", "DriverInstanceId", "DeviceId") + .HasDatabaseName("IX_Tag_Generation_Driver_Device"); + + b.HasIndex("GenerationId", "EquipmentId", "Name") + .IsUnique() + .HasDatabaseName("UX_Tag_Generation_EquipmentPath") + .HasFilter("[EquipmentId] IS NOT NULL"); + + b.HasIndex("GenerationId", "DriverInstanceId", "FolderPath", "Name") + .IsUnique() + .HasDatabaseName("UX_Tag_Generation_FolderPath") + .HasFilter("[EquipmentId] IS NULL"); + + b.ToTable("Tag", null, t => + { + t.HasCheckConstraint("CK_Tag_TagConfig_IsJson", "ISJSON(TagConfig) = 1"); + }); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.UnsArea", b => + { + b.Property("UnsAreaRowId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWSEQUENTIALID()"); + + b.Property("ClusterId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("GenerationId") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("Notes") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("UnsAreaId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.HasKey("UnsAreaRowId"); + + b.HasIndex("ClusterId"); + + b.HasIndex("GenerationId", "ClusterId") + .HasDatabaseName("IX_UnsArea_Generation_Cluster"); + + b.HasIndex("GenerationId", "UnsAreaId") + .IsUnique() + .HasDatabaseName("UX_UnsArea_Generation_LogicalId") + .HasFilter("[UnsAreaId] IS NOT NULL"); + + b.HasIndex("GenerationId", "ClusterId", "Name") + .IsUnique() + .HasDatabaseName("UX_UnsArea_Generation_ClusterName"); + + b.ToTable("UnsArea", (string)null); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.UnsLine", b => + { + b.Property("UnsLineRowId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWSEQUENTIALID()"); + + b.Property("GenerationId") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("Notes") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("UnsAreaId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("UnsLineId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.HasKey("UnsLineRowId"); + + b.HasIndex("GenerationId", "UnsAreaId") + .HasDatabaseName("IX_UnsLine_Generation_Area"); + + b.HasIndex("GenerationId", "UnsLineId") + .IsUnique() + .HasDatabaseName("UX_UnsLine_Generation_LogicalId") + .HasFilter("[UnsLineId] IS NOT NULL"); + + b.HasIndex("GenerationId", "UnsAreaId", "Name") + .IsUnique() + .HasDatabaseName("UX_UnsLine_Generation_AreaName"); + + b.ToTable("UnsLine", (string)null); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.VirtualTag", b => + { + b.Property("VirtualTagRowId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWSEQUENTIALID()"); + + b.Property("ChangeTriggered") + .HasColumnType("bit"); + + b.Property("DataType") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("EquipmentId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("GenerationId") + .HasColumnType("bigint"); + + b.Property("Historize") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ScriptId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("TimerIntervalMs") + .HasColumnType("int"); + + b.Property("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") + .WithMany("Nodes") + .HasForeignKey("ClusterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Cluster"); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ClusterNodeCredential", b => + { + b.HasOne("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ClusterNode", "Node") + .WithMany("Credentials") + .HasForeignKey("NodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Node"); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ClusterNodeGenerationState", b => + { + b.HasOne("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ConfigGeneration", "CurrentGeneration") + .WithMany() + .HasForeignKey("CurrentGenerationId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ClusterNode", "Node") + .WithOne("GenerationState") + .HasForeignKey("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ClusterNodeGenerationState", "NodeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CurrentGeneration"); + + b.Navigation("Node"); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ConfigGeneration", b => + { + b.HasOne("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ServerCluster", "Cluster") + .WithMany("Generations") + .HasForeignKey("ClusterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ConfigGeneration", "Parent") + .WithMany() + .HasForeignKey("ParentGenerationId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Cluster"); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.Device", 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.DriverInstance", b => + { + b.HasOne("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ServerCluster", "Cluster") + .WithMany() + .HasForeignKey("ClusterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ConfigGeneration", "Generation") + .WithMany() + .HasForeignKey("GenerationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Cluster"); + + b.Navigation("Generation"); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.Equipment", 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.EquipmentImportRow", b => + { + b.HasOne("ZB.MOM.WW.OtOpcUa.Configuration.Entities.EquipmentImportBatch", "Batch") + .WithMany("Rows") + .HasForeignKey("BatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Batch"); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.LdapGroupRoleMapping", b => + { + b.HasOne("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ServerCluster", "Cluster") + .WithMany() + .HasForeignKey("ClusterId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Cluster"); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.Namespace", b => + { + b.HasOne("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ServerCluster", "Cluster") + .WithMany("Namespaces") + .HasForeignKey("ClusterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ConfigGeneration", "Generation") + .WithMany() + .HasForeignKey("GenerationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Cluster"); + + b.Navigation("Generation"); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.NodeAcl", 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.PollGroup", 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.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") + .WithMany() + .HasForeignKey("GenerationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Generation"); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.UnsArea", b => + { + b.HasOne("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ServerCluster", "Cluster") + .WithMany() + .HasForeignKey("ClusterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ConfigGeneration", "Generation") + .WithMany() + .HasForeignKey("GenerationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Cluster"); + + b.Navigation("Generation"); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.UnsLine", 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.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"); + + b.Navigation("GenerationState"); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.EquipmentImportBatch", b => + { + b.Navigation("Rows"); + }); + + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.ServerCluster", b => + { + b.Navigation("Generations"); + + b.Navigation("Namespaces"); + + b.Navigation("Nodes"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/ZB.MOM.WW.OtOpcUa.Configuration/Migrations/20260420231641_AddPhase7ScriptingTables.cs b/src/ZB.MOM.WW.OtOpcUa.Configuration/Migrations/20260420231641_AddPhase7ScriptingTables.cs new file mode 100644 index 0000000..3730ecf --- /dev/null +++ b/src/ZB.MOM.WW.OtOpcUa.Configuration/Migrations/20260420231641_AddPhase7ScriptingTables.cs @@ -0,0 +1,186 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ZB.MOM.WW.OtOpcUa.Configuration.Migrations +{ + /// + public partial class AddPhase7ScriptingTables : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Script", + columns: table => new + { + ScriptRowId = table.Column(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWSEQUENTIALID()"), + GenerationId = table.Column(type: "bigint", nullable: false), + ScriptId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + SourceCode = table.Column(type: "nvarchar(max)", nullable: false), + SourceHash = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + Language = table.Column(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(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWSEQUENTIALID()"), + GenerationId = table.Column(type: "bigint", nullable: false), + ScriptedAlarmId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + EquipmentId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + AlarmType = table.Column(type: "nvarchar(32)", maxLength: 32, nullable: false), + Severity = table.Column(type: "int", nullable: false), + MessageTemplate = table.Column(type: "nvarchar(1024)", maxLength: 1024, nullable: false), + PredicateScriptId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + HistorizeToAveva = table.Column(type: "bit", nullable: false), + Retain = table.Column(type: "bit", nullable: false), + Enabled = table.Column(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(type: "nvarchar(64)", maxLength: 64, nullable: false), + EnabledState = table.Column(type: "nvarchar(16)", maxLength: 16, nullable: false), + AckedState = table.Column(type: "nvarchar(16)", maxLength: 16, nullable: false), + ConfirmedState = table.Column(type: "nvarchar(16)", maxLength: 16, nullable: false), + ShelvingState = table.Column(type: "nvarchar(16)", maxLength: 16, nullable: false), + ShelvingExpiresUtc = table.Column(type: "datetime2(3)", nullable: true), + LastAckUser = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), + LastAckComment = table.Column(type: "nvarchar(1024)", maxLength: 1024, nullable: true), + LastAckUtc = table.Column(type: "datetime2(3)", nullable: true), + LastConfirmUser = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), + LastConfirmComment = table.Column(type: "nvarchar(1024)", maxLength: 1024, nullable: true), + LastConfirmUtc = table.Column(type: "datetime2(3)", nullable: true), + CommentsJson = table.Column(type: "nvarchar(max)", nullable: false), + UpdatedAtUtc = table.Column(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(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWSEQUENTIALID()"), + GenerationId = table.Column(type: "bigint", nullable: false), + VirtualTagId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + EquipmentId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + DataType = table.Column(type: "nvarchar(32)", maxLength: 32, nullable: false), + ScriptId = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), + ChangeTriggered = table.Column(type: "bit", nullable: false), + TimerIntervalMs = table.Column(type: "int", nullable: true), + Historize = table.Column(type: "bit", nullable: false), + Enabled = table.Column(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"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Script"); + + migrationBuilder.DropTable( + name: "ScriptedAlarm"); + + migrationBuilder.DropTable( + name: "ScriptedAlarmState"); + + migrationBuilder.DropTable( + name: "VirtualTag"); + } + } +} diff --git a/src/ZB.MOM.WW.OtOpcUa.Configuration/Migrations/OtOpcUaConfigDbContextModelSnapshot.cs b/src/ZB.MOM.WW.OtOpcUa.Configuration/Migrations/OtOpcUaConfigDbContextModelSnapshot.cs index 1019e8e..7985573 100644 --- a/src/ZB.MOM.WW.OtOpcUa.Configuration/Migrations/OtOpcUaConfigDbContextModelSnapshot.cs +++ b/src/ZB.MOM.WW.OtOpcUa.Configuration/Migrations/OtOpcUaConfigDbContextModelSnapshot.cs @@ -1027,6 +1027,193 @@ namespace ZB.MOM.WW.OtOpcUa.Configuration.Migrations }); }); + modelBuilder.Entity("ZB.MOM.WW.OtOpcUa.Configuration.Entities.Script", b => + { + b.Property("ScriptRowId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWSEQUENTIALID()"); + + b.Property("GenerationId") + .HasColumnType("bigint"); + + b.Property("Language") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ScriptId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("SourceCode") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("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("ScriptedAlarmRowId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWSEQUENTIALID()"); + + b.Property("AlarmType") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("EquipmentId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("GenerationId") + .HasColumnType("bigint"); + + b.Property("HistorizeToAveva") + .HasColumnType("bit"); + + b.Property("MessageTemplate") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PredicateScriptId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Retain") + .HasColumnType("bit"); + + b.Property("ScriptedAlarmId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("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("ScriptedAlarmId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("AckedState") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("CommentsJson") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ConfirmedState") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("EnabledState") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("LastAckComment") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("LastAckUser") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("LastAckUtc") + .HasColumnType("datetime2(3)"); + + b.Property("LastConfirmComment") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("LastConfirmUser") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("LastConfirmUtc") + .HasColumnType("datetime2(3)"); + + b.Property("ShelvingExpiresUtc") + .HasColumnType("datetime2(3)"); + + b.Property("ShelvingState") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("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("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("VirtualTagRowId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWSEQUENTIALID()"); + + b.Property("ChangeTriggered") + .HasColumnType("bit"); + + b.Property("DataType") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("EquipmentId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("GenerationId") + .HasColumnType("bigint"); + + b.Property("Historize") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ScriptId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("TimerIntervalMs") + .HasColumnType("int"); + + b.Property("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"); diff --git a/src/ZB.MOM.WW.OtOpcUa.Configuration/OtOpcUaConfigDbContext.cs b/src/ZB.MOM.WW.OtOpcUa.Configuration/OtOpcUaConfigDbContext.cs index d2ca409..9862de8 100644 --- a/src/ZB.MOM.WW.OtOpcUa.Configuration/OtOpcUaConfigDbContext.cs +++ b/src/ZB.MOM.WW.OtOpcUa.Configuration/OtOpcUaConfigDbContext.cs @@ -32,6 +32,10 @@ public sealed class OtOpcUaConfigDbContext(DbContextOptions LdapGroupRoleMappings => Set(); public DbSet EquipmentImportBatches => Set(); public DbSet EquipmentImportRows => Set(); + public DbSet