Files
lmxopcua/src/ZB.MOM.WW.OtOpcUa.Configuration/Migrations/20260417212220_InitialSchema.cs
Joseph Doherty 01fd90c178 Phase 1 Streams B–E scaffold + Phase 2 Streams A–C scaffold — 8 new projects with ~70 new tests, all green alongside the 494 v1 IntegrationTests baseline (parity preserved: no v1 tests broken; legacy OtOpcUa.Host untouched). Phase 1 finish: Configuration project (16 entities + 10 enums + DbContext + DesignTimeDbContextFactory + InitialSchema/StoredProcedures/AuthorizationGrants migrations — 8 procs including sp_PublishGeneration with MERGE on ExternalIdReservation per decision #124, sp_RollbackToGeneration cloning rows into a new published generation, sp_ValidateDraft with cross-cluster-namespace + EquipmentUuid-immutability + ZTag/SAPID reservation pre-flight, sp_ComputeGenerationDiff with CHECKSUM-based row signature — plus OtOpcUaNode/OtOpcUaAdmin SQL roles with EXECUTE grants scoped to per-principal-class proc sets and DENY UPDATE/DELETE/INSERT/SELECT on dbo schema); managed DraftValidator covering UNS segment regex, path length, EquipmentUuid immutability across generations, same-cluster namespace binding (decision #122), reservation pre-flight, EquipmentId derivation (decision #125), driver↔namespace compatibility — returning every failing rule in one pass; LiteDB local cache with round-trip + ring pruning + corruption-fast-fail; GenerationApplier with per-entity Added/Removed/Modified diff and dependency-ordered callbacks (namespace → driver → device → equipment → poll-group → tag, Removed before Added); Core project with GenericDriverNodeManager (scaffold for the Phase 2 Galaxy port) and DriverHost lifecycle registry; Server project using Microsoft.Extensions.Hosting BackgroundService replacing TopShelf, with NodeBootstrap that falls back to LiteDB cache when the central DB is unreachable (decision #79); Admin project scaffolded as Blazor Server with Bootstrap 5 sidebar layout, cookie auth, three admin roles (ConfigViewer/ConfigEditor/FleetAdmin), Cluster + Generation services fronting the stored procs. Phase 2 scaffold: Driver.Galaxy.Shared (netstandard2.0) with full MessagePack IPC contract surface — Hello version negotiation, Open/CloseSession, Heartbeat, DiscoverHierarchy + GalaxyObjectInfo/GalaxyAttributeInfo, Read/WriteValues, Subscribe/Unsubscribe/OnDataChange, AlarmSubscribe/Event/Ack, HistoryRead, HostConnectivityStatus, Recycle — plus length-prefixed framing (decision #28) with a 16 MiB cap and thread-safe FrameWriter/FrameReader; Driver.Galaxy.Host (net48) implementing the Tier C cross-cutting protections from driver-stability.md — strict PipeAcl (allow configured server SID only, explicit deny on LocalSystem + Administrators), PipeServer with caller-SID verification via pipe.RunAsClient + WindowsIdentity.GetCurrent and per-process shared-secret Hello, Galaxy-specific MemoryWatchdog (warn at max(1.5×baseline, +200 MB), soft-recycle at max(2×baseline, +200 MB), hard ceiling 1.5 GB, slope ≥5 MB/min over 30-min rolling window), RecyclePolicy (1 soft recycle per hour cap + 03:00 local daily scheduled), PostMortemMmf (1000-entry ring buffer in %ProgramData%\OtOpcUa\driver-postmortem\galaxy.mmf, survives hard crash, readable cross-process), MxAccessHandle : SafeHandle (ReleaseHandle loops Marshal.ReleaseComObject until refcount=0 then calls optional unregister callback), StaPump with responsiveness probe (BlockingCollection dispatcher for Phase 1 — real Win32 GetMessage/DispatchMessage pump slots in with the same semantics when the Galaxy code lift happens), IsExternalInit shim for init setters on .NET 4.8; Driver.Galaxy.Proxy (net10) implementing IDriver + ITagDiscovery forwarding over the IPC channel with MX data-type and security-classification mapping, plus Supervisor pieces — Backoff (5s → 15s → 60s capped, reset-on-stable-run), CircuitBreaker (3 crashes per 5 min opens; 1h → 4h → manual cooldown escalation; sticky alert doesn't auto-clear), HeartbeatMonitor (2s cadence, 3 consecutive misses = host dead per driver-stability.md). Infrastructure: docker SQL Server remapped to host port 14330 to coexist with the native MSSQL14 Galaxy ZB DB instance on 1433; NuGetAuditSuppress applied per-project for two System.Security.Cryptography.Xml advisories that only reach via EF Core Design with PrivateAssets=all (fix ships in 11.0.0-preview); .slnx gains 14 project registrations. Deferred with explicit TODOs in docs/v2/implementation/phase-2-partial-exit-evidence.md: Phase 1 Stream E Admin UI pages (Generations listing + draft-diff-publish, Equipment CRUD with OPC 40010 fields, UNS Areas/Lines tabs, ACLs + permission simulator, Generic JSON config editor, SignalR real-time, Release-Reservation + Merge-Equipment workflows, LDAP login page, AppServer smoke test per decision #142), Phase 2 Stream D (Galaxy MXAccess code lift out of legacy OtOpcUa.Host, dual-service installer, appsettings → DriverConfig migration script, legacy Host deletion — blocked by parity), Phase 2 Stream E (v1 IntegrationTests against v2 topology, Client.CLI walkthrough diff, four 2026-04-13 stability findings regression tests, adversarial review — requires live MXAccess runtime).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 21:35:25 -04:00

812 lines
42 KiB
C#

using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ZB.MOM.WW.OtOpcUa.Configuration.Migrations
{
/// <inheritdoc />
public partial class InitialSchema : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ConfigAuditLog",
columns: table => new
{
AuditId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Timestamp = table.Column<DateTime>(type: "datetime2(3)", nullable: false, defaultValueSql: "SYSUTCDATETIME()"),
Principal = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
EventType = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
ClusterId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
NodeId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
GenerationId = table.Column<long>(type: "bigint", nullable: true),
DetailsJson = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ConfigAuditLog", x => x.AuditId);
table.CheckConstraint("CK_ConfigAuditLog_DetailsJson_IsJson", "DetailsJson IS NULL OR ISJSON(DetailsJson) = 1");
});
migrationBuilder.CreateTable(
name: "ExternalIdReservation",
columns: table => new
{
ReservationId = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWSEQUENTIALID()"),
Kind = table.Column<string>(type: "nvarchar(16)", maxLength: 16, nullable: false),
Value = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
EquipmentUuid = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
ClusterId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
FirstPublishedAt = table.Column<DateTime>(type: "datetime2(3)", nullable: false, defaultValueSql: "SYSUTCDATETIME()"),
FirstPublishedBy = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
LastPublishedAt = table.Column<DateTime>(type: "datetime2(3)", nullable: false, defaultValueSql: "SYSUTCDATETIME()"),
ReleasedAt = table.Column<DateTime>(type: "datetime2(3)", nullable: true),
ReleasedBy = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
ReleaseReason = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ExternalIdReservation", x => x.ReservationId);
});
migrationBuilder.CreateTable(
name: "ServerCluster",
columns: table => new
{
ClusterId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
Enterprise = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false),
Site = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false),
NodeCount = table.Column<byte>(type: "tinyint", nullable: false),
RedundancyMode = table.Column<string>(type: "nvarchar(16)", maxLength: 16, nullable: false),
Enabled = table.Column<bool>(type: "bit", nullable: false),
Notes = table.Column<string>(type: "nvarchar(1024)", maxLength: 1024, nullable: true),
CreatedAt = table.Column<DateTime>(type: "datetime2(3)", nullable: false, defaultValueSql: "SYSUTCDATETIME()"),
CreatedBy = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
ModifiedAt = table.Column<DateTime>(type: "datetime2(3)", nullable: true),
ModifiedBy = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ServerCluster", x => x.ClusterId);
table.CheckConstraint("CK_ServerCluster_RedundancyMode_NodeCount", "((NodeCount = 1 AND RedundancyMode = 'None') OR (NodeCount = 2 AND RedundancyMode IN ('Warm', 'Hot')))");
});
migrationBuilder.CreateTable(
name: "ClusterNode",
columns: table => new
{
NodeId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
ClusterId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
RedundancyRole = table.Column<string>(type: "nvarchar(16)", maxLength: 16, nullable: false),
Host = table.Column<string>(type: "nvarchar(255)", maxLength: 255, nullable: false),
OpcUaPort = table.Column<int>(type: "int", nullable: false),
DashboardPort = table.Column<int>(type: "int", nullable: false),
ApplicationUri = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
ServiceLevelBase = table.Column<byte>(type: "tinyint", nullable: false),
DriverConfigOverridesJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
Enabled = table.Column<bool>(type: "bit", nullable: false),
LastSeenAt = table.Column<DateTime>(type: "datetime2(3)", nullable: true),
CreatedAt = table.Column<DateTime>(type: "datetime2(3)", nullable: false, defaultValueSql: "SYSUTCDATETIME()"),
CreatedBy = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClusterNode", x => x.NodeId);
table.ForeignKey(
name: "FK_ClusterNode_ServerCluster_ClusterId",
column: x => x.ClusterId,
principalTable: "ServerCluster",
principalColumn: "ClusterId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "ConfigGeneration",
columns: table => new
{
GenerationId = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
ClusterId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
Status = table.Column<string>(type: "nvarchar(16)", maxLength: 16, nullable: false),
ParentGenerationId = table.Column<long>(type: "bigint", nullable: true),
PublishedAt = table.Column<DateTime>(type: "datetime2(3)", nullable: true),
PublishedBy = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
Notes = table.Column<string>(type: "nvarchar(1024)", maxLength: 1024, nullable: true),
CreatedAt = table.Column<DateTime>(type: "datetime2(3)", nullable: false, defaultValueSql: "SYSUTCDATETIME()"),
CreatedBy = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ConfigGeneration", x => x.GenerationId);
table.ForeignKey(
name: "FK_ConfigGeneration_ConfigGeneration_ParentGenerationId",
column: x => x.ParentGenerationId,
principalTable: "ConfigGeneration",
principalColumn: "GenerationId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_ConfigGeneration_ServerCluster_ClusterId",
column: x => x.ClusterId,
principalTable: "ServerCluster",
principalColumn: "ClusterId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "ClusterNodeCredential",
columns: table => new
{
CredentialId = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWSEQUENTIALID()"),
NodeId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
Kind = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false),
Value = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
Enabled = table.Column<bool>(type: "bit", nullable: false),
RotatedAt = table.Column<DateTime>(type: "datetime2(3)", nullable: true),
CreatedAt = table.Column<DateTime>(type: "datetime2(3)", nullable: false, defaultValueSql: "SYSUTCDATETIME()"),
CreatedBy = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClusterNodeCredential", x => x.CredentialId);
table.ForeignKey(
name: "FK_ClusterNodeCredential_ClusterNode_NodeId",
column: x => x.NodeId,
principalTable: "ClusterNode",
principalColumn: "NodeId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "ClusterNodeGenerationState",
columns: table => new
{
NodeId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
CurrentGenerationId = table.Column<long>(type: "bigint", nullable: true),
LastAppliedAt = table.Column<DateTime>(type: "datetime2(3)", nullable: true),
LastAppliedStatus = table.Column<string>(type: "nvarchar(16)", maxLength: 16, nullable: true),
LastAppliedError = table.Column<string>(type: "nvarchar(2048)", maxLength: 2048, nullable: true),
LastSeenAt = table.Column<DateTime>(type: "datetime2(3)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ClusterNodeGenerationState", x => x.NodeId);
table.ForeignKey(
name: "FK_ClusterNodeGenerationState_ClusterNode_NodeId",
column: x => x.NodeId,
principalTable: "ClusterNode",
principalColumn: "NodeId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_ClusterNodeGenerationState_ConfigGeneration_CurrentGenerationId",
column: x => x.CurrentGenerationId,
principalTable: "ConfigGeneration",
principalColumn: "GenerationId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "Device",
columns: table => new
{
DeviceRowId = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWSEQUENTIALID()"),
GenerationId = table.Column<long>(type: "bigint", nullable: false),
DeviceId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
DriverInstanceId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
Enabled = table.Column<bool>(type: "bit", nullable: false),
DeviceConfig = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Device", x => x.DeviceRowId);
table.CheckConstraint("CK_Device_DeviceConfig_IsJson", "ISJSON(DeviceConfig) = 1");
table.ForeignKey(
name: "FK_Device_ConfigGeneration_GenerationId",
column: x => x.GenerationId,
principalTable: "ConfigGeneration",
principalColumn: "GenerationId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "DriverInstance",
columns: table => new
{
DriverInstanceRowId = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWSEQUENTIALID()"),
GenerationId = table.Column<long>(type: "bigint", nullable: false),
DriverInstanceId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
ClusterId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
NamespaceId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
DriverType = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false),
Enabled = table.Column<bool>(type: "bit", nullable: false),
DriverConfig = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_DriverInstance", x => x.DriverInstanceRowId);
table.CheckConstraint("CK_DriverInstance_DriverConfig_IsJson", "ISJSON(DriverConfig) = 1");
table.ForeignKey(
name: "FK_DriverInstance_ConfigGeneration_GenerationId",
column: x => x.GenerationId,
principalTable: "ConfigGeneration",
principalColumn: "GenerationId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_DriverInstance_ServerCluster_ClusterId",
column: x => x.ClusterId,
principalTable: "ServerCluster",
principalColumn: "ClusterId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "Equipment",
columns: table => new
{
EquipmentRowId = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWSEQUENTIALID()"),
GenerationId = table.Column<long>(type: "bigint", nullable: false),
EquipmentId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
EquipmentUuid = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
DriverInstanceId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
DeviceId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
UnsLineId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
Name = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false),
MachineCode = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
ZTag = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
SAPID = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
Manufacturer = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
Model = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
SerialNumber = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
HardwareRevision = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: true),
SoftwareRevision = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: true),
YearOfConstruction = table.Column<short>(type: "smallint", nullable: true),
AssetLocation = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
ManufacturerUri = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
DeviceManualUri = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
EquipmentClassRef = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
Enabled = table.Column<bool>(type: "bit", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Equipment", x => x.EquipmentRowId);
table.ForeignKey(
name: "FK_Equipment_ConfigGeneration_GenerationId",
column: x => x.GenerationId,
principalTable: "ConfigGeneration",
principalColumn: "GenerationId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "Namespace",
columns: table => new
{
NamespaceRowId = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWSEQUENTIALID()"),
GenerationId = table.Column<long>(type: "bigint", nullable: false),
NamespaceId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
ClusterId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
Kind = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false),
NamespaceUri = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Enabled = table.Column<bool>(type: "bit", nullable: false),
Notes = table.Column<string>(type: "nvarchar(1024)", maxLength: 1024, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Namespace", x => x.NamespaceRowId);
table.ForeignKey(
name: "FK_Namespace_ConfigGeneration_GenerationId",
column: x => x.GenerationId,
principalTable: "ConfigGeneration",
principalColumn: "GenerationId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_Namespace_ServerCluster_ClusterId",
column: x => x.ClusterId,
principalTable: "ServerCluster",
principalColumn: "ClusterId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "NodeAcl",
columns: table => new
{
NodeAclRowId = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWSEQUENTIALID()"),
GenerationId = table.Column<long>(type: "bigint", nullable: false),
NodeAclId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
ClusterId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
LdapGroup = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
ScopeKind = table.Column<string>(type: "nvarchar(16)", maxLength: 16, nullable: false),
ScopeId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
PermissionFlags = table.Column<int>(type: "int", nullable: false),
Notes = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_NodeAcl", x => x.NodeAclRowId);
table.ForeignKey(
name: "FK_NodeAcl_ConfigGeneration_GenerationId",
column: x => x.GenerationId,
principalTable: "ConfigGeneration",
principalColumn: "GenerationId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "PollGroup",
columns: table => new
{
PollGroupRowId = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWSEQUENTIALID()"),
GenerationId = table.Column<long>(type: "bigint", nullable: false),
PollGroupId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
DriverInstanceId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
IntervalMs = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PollGroup", x => x.PollGroupRowId);
table.CheckConstraint("CK_PollGroup_IntervalMs_Min", "IntervalMs >= 50");
table.ForeignKey(
name: "FK_PollGroup_ConfigGeneration_GenerationId",
column: x => x.GenerationId,
principalTable: "ConfigGeneration",
principalColumn: "GenerationId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "Tag",
columns: table => new
{
TagRowId = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWSEQUENTIALID()"),
GenerationId = table.Column<long>(type: "bigint", nullable: false),
TagId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
DriverInstanceId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
DeviceId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
EquipmentId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
FolderPath = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
DataType = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false),
AccessLevel = table.Column<string>(type: "nvarchar(16)", maxLength: 16, nullable: false),
WriteIdempotent = table.Column<bool>(type: "bit", nullable: false),
PollGroupId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
TagConfig = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Tag", x => x.TagRowId);
table.CheckConstraint("CK_Tag_TagConfig_IsJson", "ISJSON(TagConfig) = 1");
table.ForeignKey(
name: "FK_Tag_ConfigGeneration_GenerationId",
column: x => x.GenerationId,
principalTable: "ConfigGeneration",
principalColumn: "GenerationId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "UnsArea",
columns: table => new
{
UnsAreaRowId = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWSEQUENTIALID()"),
GenerationId = table.Column<long>(type: "bigint", nullable: false),
UnsAreaId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
ClusterId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
Name = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false),
Notes = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_UnsArea", x => x.UnsAreaRowId);
table.ForeignKey(
name: "FK_UnsArea_ConfigGeneration_GenerationId",
column: x => x.GenerationId,
principalTable: "ConfigGeneration",
principalColumn: "GenerationId",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_UnsArea_ServerCluster_ClusterId",
column: x => x.ClusterId,
principalTable: "ServerCluster",
principalColumn: "ClusterId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "UnsLine",
columns: table => new
{
UnsLineRowId = table.Column<Guid>(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWSEQUENTIALID()"),
GenerationId = table.Column<long>(type: "bigint", nullable: false),
UnsLineId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
UnsAreaId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
Name = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false),
Notes = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_UnsLine", x => x.UnsLineRowId);
table.ForeignKey(
name: "FK_UnsLine_ConfigGeneration_GenerationId",
column: x => x.GenerationId,
principalTable: "ConfigGeneration",
principalColumn: "GenerationId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "UX_ClusterNode_ApplicationUri",
table: "ClusterNode",
column: "ApplicationUri",
unique: true);
migrationBuilder.CreateIndex(
name: "UX_ClusterNode_Primary_Per_Cluster",
table: "ClusterNode",
column: "ClusterId",
unique: true,
filter: "[RedundancyRole] = 'Primary'");
migrationBuilder.CreateIndex(
name: "IX_ClusterNodeCredential_NodeId",
table: "ClusterNodeCredential",
columns: new[] { "NodeId", "Enabled" });
migrationBuilder.CreateIndex(
name: "UX_ClusterNodeCredential_Value",
table: "ClusterNodeCredential",
columns: new[] { "Kind", "Value" },
unique: true,
filter: "[Enabled] = 1");
migrationBuilder.CreateIndex(
name: "IX_ClusterNodeGenerationState_Generation",
table: "ClusterNodeGenerationState",
column: "CurrentGenerationId");
migrationBuilder.CreateIndex(
name: "IX_ConfigAuditLog_Cluster_Time",
table: "ConfigAuditLog",
columns: new[] { "ClusterId", "Timestamp" },
descending: new[] { false, true });
migrationBuilder.CreateIndex(
name: "IX_ConfigAuditLog_Generation",
table: "ConfigAuditLog",
column: "GenerationId",
filter: "[GenerationId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_ConfigGeneration_Cluster_Published",
table: "ConfigGeneration",
columns: new[] { "ClusterId", "Status", "GenerationId" },
descending: new[] { false, false, true })
.Annotation("SqlServer:Include", new[] { "PublishedAt" });
migrationBuilder.CreateIndex(
name: "IX_ConfigGeneration_ParentGenerationId",
table: "ConfigGeneration",
column: "ParentGenerationId");
migrationBuilder.CreateIndex(
name: "UX_ConfigGeneration_Draft_Per_Cluster",
table: "ConfigGeneration",
column: "ClusterId",
unique: true,
filter: "[Status] = 'Draft'");
migrationBuilder.CreateIndex(
name: "IX_Device_Generation_Driver",
table: "Device",
columns: new[] { "GenerationId", "DriverInstanceId" });
migrationBuilder.CreateIndex(
name: "UX_Device_Generation_LogicalId",
table: "Device",
columns: new[] { "GenerationId", "DeviceId" },
unique: true,
filter: "[DeviceId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_DriverInstance_ClusterId",
table: "DriverInstance",
column: "ClusterId");
migrationBuilder.CreateIndex(
name: "IX_DriverInstance_Generation_Cluster",
table: "DriverInstance",
columns: new[] { "GenerationId", "ClusterId" });
migrationBuilder.CreateIndex(
name: "IX_DriverInstance_Generation_Namespace",
table: "DriverInstance",
columns: new[] { "GenerationId", "NamespaceId" });
migrationBuilder.CreateIndex(
name: "UX_DriverInstance_Generation_LogicalId",
table: "DriverInstance",
columns: new[] { "GenerationId", "DriverInstanceId" },
unique: true,
filter: "[DriverInstanceId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_Equipment_Generation_Driver",
table: "Equipment",
columns: new[] { "GenerationId", "DriverInstanceId" });
migrationBuilder.CreateIndex(
name: "IX_Equipment_Generation_Line",
table: "Equipment",
columns: new[] { "GenerationId", "UnsLineId" });
migrationBuilder.CreateIndex(
name: "IX_Equipment_Generation_MachineCode",
table: "Equipment",
columns: new[] { "GenerationId", "MachineCode" });
migrationBuilder.CreateIndex(
name: "IX_Equipment_Generation_SAPID",
table: "Equipment",
columns: new[] { "GenerationId", "SAPID" },
filter: "[SAPID] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_Equipment_Generation_ZTag",
table: "Equipment",
columns: new[] { "GenerationId", "ZTag" },
filter: "[ZTag] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "UX_Equipment_Generation_LinePath",
table: "Equipment",
columns: new[] { "GenerationId", "UnsLineId", "Name" },
unique: true);
migrationBuilder.CreateIndex(
name: "UX_Equipment_Generation_LogicalId",
table: "Equipment",
columns: new[] { "GenerationId", "EquipmentId" },
unique: true,
filter: "[EquipmentId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "UX_Equipment_Generation_Uuid",
table: "Equipment",
columns: new[] { "GenerationId", "EquipmentUuid" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ExternalIdReservation_Equipment",
table: "ExternalIdReservation",
column: "EquipmentUuid");
migrationBuilder.CreateIndex(
name: "UX_ExternalIdReservation_KindValue_Active",
table: "ExternalIdReservation",
columns: new[] { "Kind", "Value" },
unique: true,
filter: "[ReleasedAt] IS NULL");
migrationBuilder.CreateIndex(
name: "IX_Namespace_ClusterId",
table: "Namespace",
column: "ClusterId");
migrationBuilder.CreateIndex(
name: "IX_Namespace_Generation_Cluster",
table: "Namespace",
columns: new[] { "GenerationId", "ClusterId" });
migrationBuilder.CreateIndex(
name: "UX_Namespace_Generation_Cluster_Kind",
table: "Namespace",
columns: new[] { "GenerationId", "ClusterId", "Kind" },
unique: true);
migrationBuilder.CreateIndex(
name: "UX_Namespace_Generation_LogicalId",
table: "Namespace",
columns: new[] { "GenerationId", "NamespaceId" },
unique: true,
filter: "[NamespaceId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "UX_Namespace_Generation_LogicalId_Cluster",
table: "Namespace",
columns: new[] { "GenerationId", "NamespaceId", "ClusterId" },
unique: true,
filter: "[NamespaceId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "UX_Namespace_Generation_NamespaceUri",
table: "Namespace",
columns: new[] { "GenerationId", "NamespaceUri" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_NodeAcl_Generation_Cluster",
table: "NodeAcl",
columns: new[] { "GenerationId", "ClusterId" });
migrationBuilder.CreateIndex(
name: "IX_NodeAcl_Generation_Group",
table: "NodeAcl",
columns: new[] { "GenerationId", "LdapGroup" });
migrationBuilder.CreateIndex(
name: "IX_NodeAcl_Generation_Scope",
table: "NodeAcl",
columns: new[] { "GenerationId", "ScopeKind", "ScopeId" },
filter: "[ScopeId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "UX_NodeAcl_Generation_GroupScope",
table: "NodeAcl",
columns: new[] { "GenerationId", "ClusterId", "LdapGroup", "ScopeKind", "ScopeId" },
unique: true,
filter: "[ScopeId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "UX_NodeAcl_Generation_LogicalId",
table: "NodeAcl",
columns: new[] { "GenerationId", "NodeAclId" },
unique: true,
filter: "[NodeAclId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_PollGroup_Generation_Driver",
table: "PollGroup",
columns: new[] { "GenerationId", "DriverInstanceId" });
migrationBuilder.CreateIndex(
name: "UX_PollGroup_Generation_LogicalId",
table: "PollGroup",
columns: new[] { "GenerationId", "PollGroupId" },
unique: true,
filter: "[PollGroupId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_ServerCluster_Site",
table: "ServerCluster",
column: "Site");
migrationBuilder.CreateIndex(
name: "UX_ServerCluster_Name",
table: "ServerCluster",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Tag_Generation_Driver_Device",
table: "Tag",
columns: new[] { "GenerationId", "DriverInstanceId", "DeviceId" });
migrationBuilder.CreateIndex(
name: "IX_Tag_Generation_Equipment",
table: "Tag",
columns: new[] { "GenerationId", "EquipmentId" },
filter: "[EquipmentId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "UX_Tag_Generation_EquipmentPath",
table: "Tag",
columns: new[] { "GenerationId", "EquipmentId", "Name" },
unique: true,
filter: "[EquipmentId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "UX_Tag_Generation_FolderPath",
table: "Tag",
columns: new[] { "GenerationId", "DriverInstanceId", "FolderPath", "Name" },
unique: true,
filter: "[EquipmentId] IS NULL");
migrationBuilder.CreateIndex(
name: "UX_Tag_Generation_LogicalId",
table: "Tag",
columns: new[] { "GenerationId", "TagId" },
unique: true,
filter: "[TagId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_UnsArea_ClusterId",
table: "UnsArea",
column: "ClusterId");
migrationBuilder.CreateIndex(
name: "IX_UnsArea_Generation_Cluster",
table: "UnsArea",
columns: new[] { "GenerationId", "ClusterId" });
migrationBuilder.CreateIndex(
name: "UX_UnsArea_Generation_ClusterName",
table: "UnsArea",
columns: new[] { "GenerationId", "ClusterId", "Name" },
unique: true);
migrationBuilder.CreateIndex(
name: "UX_UnsArea_Generation_LogicalId",
table: "UnsArea",
columns: new[] { "GenerationId", "UnsAreaId" },
unique: true,
filter: "[UnsAreaId] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_UnsLine_Generation_Area",
table: "UnsLine",
columns: new[] { "GenerationId", "UnsAreaId" });
migrationBuilder.CreateIndex(
name: "UX_UnsLine_Generation_AreaName",
table: "UnsLine",
columns: new[] { "GenerationId", "UnsAreaId", "Name" },
unique: true);
migrationBuilder.CreateIndex(
name: "UX_UnsLine_Generation_LogicalId",
table: "UnsLine",
columns: new[] { "GenerationId", "UnsLineId" },
unique: true,
filter: "[UnsLineId] IS NOT NULL");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ClusterNodeCredential");
migrationBuilder.DropTable(
name: "ClusterNodeGenerationState");
migrationBuilder.DropTable(
name: "ConfigAuditLog");
migrationBuilder.DropTable(
name: "Device");
migrationBuilder.DropTable(
name: "DriverInstance");
migrationBuilder.DropTable(
name: "Equipment");
migrationBuilder.DropTable(
name: "ExternalIdReservation");
migrationBuilder.DropTable(
name: "Namespace");
migrationBuilder.DropTable(
name: "NodeAcl");
migrationBuilder.DropTable(
name: "PollGroup");
migrationBuilder.DropTable(
name: "Tag");
migrationBuilder.DropTable(
name: "UnsArea");
migrationBuilder.DropTable(
name: "UnsLine");
migrationBuilder.DropTable(
name: "ClusterNode");
migrationBuilder.DropTable(
name: "ConfigGeneration");
migrationBuilder.DropTable(
name: "ServerCluster");
}
}
}