Some checks failed
v2-ci / build (push) Failing after 47s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
Closes the gap where Tag rows with EquipmentId=NULL + Namespace.Kind=SystemPlatform (Galaxy hierarchy) existed in ConfigDb but were never surfaced in the OPC UA address space. Now they materialise as Variable nodes under a folder named for their FolderPath, browseable through any OPC UA client. Layers touched: - IOpcUaAddressSpaceSink: new EnsureVariable(nodeId, parentFolderId, displayName, dataType) signature on the sink interface, NullSink, DeferredSink, SdkSink. - OtOpcUaNodeManager.EnsureVariable: creates a BaseDataVariableState parented under the named folder (or root), initial Value=null + StatusCode=BadWaitingForInitialData; resolves Tag.DataType strings to the matching OPC UA built-in NodeId. Idempotent. - Phase7CompositionResult: new GalaxyTags collection of GalaxyTagPlan records carrying (TagId, DriverInstanceId, FolderPath, DisplayName, DataType, MxAccessRef). Constructor overloads keep existing call sites compiling. - Phase7Composer.Compose: now takes Tag + Namespace inputs, filters for SystemPlatform-namespace tags with EquipmentId=NULL, emits GalaxyTagPlan rows with MXAccess ref "FolderPath.Name". - Phase7Plan: new AddedGalaxyTags / RemovedGalaxyTags / ChangedGalaxyTags collections + GalaxyTagDelta record; IsEmpty + needsRebuild updated. - Phase7Planner.Compute: diffs GalaxyTags by TagId via existing DiffById helper. - DeploymentArtifact.ParseComposition: reads the Tags + Namespaces + DriverInstances arrays the ConfigComposer already emits, applies the same SystemPlatform filter, returns the same GalaxyTagPlan list as the composer so artifact-side and compose-side plans agree. - Phase7Applier: new MaterialiseGalaxyTags pass that ensures one folder per distinct FolderPath then one Variable per tag. NodeId for the variable is "<FolderPath>.<Name>" matching the MXAccess ref so the future Galaxy SubscribeBulk wiring can address them directly. - OpcUaPublishActor.RebuildAddressSpace: invokes MaterialiseGalaxyTags after MaterialiseHierarchy. _lastApplied initialiser updated for the new ctor. - seed-clusters.sql: pre-existing TestMachine_001.TestAlarm001..003 rows needed no change — the composer/applier now picks them up automatically. Verified end-to-end via docker-dev: deploy click → driver-a logs "Phase7Applier: Galaxy tags materialised (tags=3, folders=1)" → OPC UA Client CLI browses the three Variable nodes under TestMachine_001 folder. Reads return BadWaitingForInitialData status (expected — Galaxy driver's SubscribeBulk wiring to push values into the nodes is the remaining follow-up).
196 lines
9.6 KiB
SQL
196 lines
9.6 KiB
SQL
-- docker-dev cluster seed. Idempotent — safe to re-run on every `docker compose up`.
|
|
--
|
|
-- Populates:
|
|
-- ServerCluster MAIN, SITE-A, SITE-B
|
|
-- ClusterNode driver-a, driver-b → MAIN
|
|
-- site-a-1, site-a-2 → SITE-A
|
|
-- site-b-1, site-b-2 → SITE-B
|
|
--
|
|
-- ServerCluster.NodeCount + RedundancyMode are coupled by CHECK constraint:
|
|
-- NodeCount=1 ⇒ RedundancyMode='None'
|
|
-- NodeCount=2 ⇒ RedundancyMode∈('Warm','Hot')
|
|
--
|
|
-- Each ClusterNode.ApplicationUri MUST be globally unique (UX_ClusterNode_ApplicationUri).
|
|
-- Convention: urn:OtOpcUa:<NodeId>.
|
|
--
|
|
-- Host = Compose service name (resolves inside the otopcua-dev network).
|
|
-- OpcUaPort stays at the container-internal 4840; the host-side port mapping is in
|
|
-- docker-compose.yml ports: blocks and is irrelevant to ClusterNode rows.
|
|
|
|
SET NOCOUNT ON;
|
|
SET XACT_ABORT ON;
|
|
|
|
BEGIN TRANSACTION;
|
|
|
|
------------------------------------------------------------------------------
|
|
-- ServerCluster
|
|
------------------------------------------------------------------------------
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM dbo.ServerCluster WHERE ClusterId = 'MAIN')
|
|
INSERT INTO dbo.ServerCluster
|
|
(ClusterId, Name, Enterprise, Site, NodeCount, RedundancyMode, Enabled, Notes, CreatedBy)
|
|
VALUES
|
|
('MAIN', 'Main cluster', 'zb', 'docker-dev',
|
|
2, 'Warm', 1,
|
|
'docker-dev seed — admin-a/admin-b control-plane, driver-a/driver-b OPC UA publishers.',
|
|
'docker-dev-seed');
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM dbo.ServerCluster WHERE ClusterId = 'SITE-A')
|
|
INSERT INTO dbo.ServerCluster
|
|
(ClusterId, Name, Enterprise, Site, NodeCount, RedundancyMode, Enabled, Notes, CreatedBy)
|
|
VALUES
|
|
('SITE-A', 'Site A', 'zb', 'site-a',
|
|
2, 'Warm', 1,
|
|
'docker-dev seed — 2-node fused admin+driver cluster.',
|
|
'docker-dev-seed');
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM dbo.ServerCluster WHERE ClusterId = 'SITE-B')
|
|
INSERT INTO dbo.ServerCluster
|
|
(ClusterId, Name, Enterprise, Site, NodeCount, RedundancyMode, Enabled, Notes, CreatedBy)
|
|
VALUES
|
|
('SITE-B', 'Site B', 'zb', 'site-b',
|
|
2, 'Warm', 1,
|
|
'docker-dev seed — 2-node fused admin+driver cluster.',
|
|
'docker-dev-seed');
|
|
|
|
------------------------------------------------------------------------------
|
|
-- ClusterNode — main cluster OPC UA publishers
|
|
--
|
|
-- NodeId is "<compose-service>:4053" so it matches what ClusterRoleInfo +
|
|
-- ConfigPublishCoordinator derive from Akka.Cluster.Get(system).State.Members
|
|
-- (member.Address.Host:Port). NodeDeploymentState.NodeId is FK-bound to
|
|
-- ClusterNode.NodeId; mismatched values cause FK 547 on deploy.
|
|
------------------------------------------------------------------------------
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'driver-a:4053')
|
|
INSERT INTO dbo.ClusterNode
|
|
(NodeId, ClusterId, Host, OpcUaPort, DashboardPort, ApplicationUri, ServiceLevelBase, Enabled, CreatedBy)
|
|
VALUES ('driver-a:4053', 'MAIN', 'driver-a', 4840, 8081, 'urn:OtOpcUa:driver-a', 200, 1, 'docker-dev-seed');
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'driver-b:4053')
|
|
INSERT INTO dbo.ClusterNode
|
|
(NodeId, ClusterId, Host, OpcUaPort, DashboardPort, ApplicationUri, ServiceLevelBase, Enabled, CreatedBy)
|
|
VALUES ('driver-b:4053', 'MAIN', 'driver-b', 4840, 8081, 'urn:OtOpcUa:driver-b', 150, 1, 'docker-dev-seed');
|
|
|
|
------------------------------------------------------------------------------
|
|
-- ClusterNode — site A
|
|
------------------------------------------------------------------------------
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'site-a-1:4053')
|
|
INSERT INTO dbo.ClusterNode
|
|
(NodeId, ClusterId, Host, OpcUaPort, DashboardPort, ApplicationUri, ServiceLevelBase, Enabled, CreatedBy)
|
|
VALUES ('site-a-1:4053', 'SITE-A', 'site-a-1', 4840, 8081, 'urn:OtOpcUa:site-a-1', 200, 1, 'docker-dev-seed');
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'site-a-2:4053')
|
|
INSERT INTO dbo.ClusterNode
|
|
(NodeId, ClusterId, Host, OpcUaPort, DashboardPort, ApplicationUri, ServiceLevelBase, Enabled, CreatedBy)
|
|
VALUES ('site-a-2:4053', 'SITE-A', 'site-a-2', 4840, 8081, 'urn:OtOpcUa:site-a-2', 150, 1, 'docker-dev-seed');
|
|
|
|
------------------------------------------------------------------------------
|
|
-- ClusterNode — site B
|
|
------------------------------------------------------------------------------
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'site-b-1:4053')
|
|
INSERT INTO dbo.ClusterNode
|
|
(NodeId, ClusterId, Host, OpcUaPort, DashboardPort, ApplicationUri, ServiceLevelBase, Enabled, CreatedBy)
|
|
VALUES ('site-b-1:4053', 'SITE-B', 'site-b-1', 4840, 8081, 'urn:OtOpcUa:site-b-1', 200, 1, 'docker-dev-seed');
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'site-b-2:4053')
|
|
INSERT INTO dbo.ClusterNode
|
|
(NodeId, ClusterId, Host, OpcUaPort, DashboardPort, ApplicationUri, ServiceLevelBase, Enabled, CreatedBy)
|
|
VALUES ('site-b-2:4053', 'SITE-B', 'site-b-2', 4840, 8081, 'urn:OtOpcUa:site-b-2', 150, 1, 'docker-dev-seed');
|
|
|
|
------------------------------------------------------------------------------
|
|
-- Galaxy MxAccess gateway — MAIN cluster
|
|
--
|
|
-- Namespace.Kind=SystemPlatform is required for Galaxy/MXAccess data per
|
|
-- decision #107; raw equipment drivers use Equipment. DriverInstance points
|
|
-- at the external mxaccessgw process. The driver code lives in this repo
|
|
-- (.NET 10, cross-platform); only the gateway worker needs Windows.
|
|
--
|
|
-- ApiKeySecretRef = env:GALAXY_MXGW_API_KEY → resolved at runtime by
|
|
-- GalaxyDriver.ResolveApiKey. The env var is set on every driver-role
|
|
-- container in docker-compose.yml.
|
|
------------------------------------------------------------------------------
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM dbo.Namespace WHERE NamespaceId = 'MAIN-galaxy')
|
|
INSERT INTO dbo.Namespace
|
|
(NamespaceRowId, NamespaceId, ClusterId, Kind, NamespaceUri, Enabled, Notes)
|
|
VALUES
|
|
(NEWID(), 'MAIN-galaxy', 'MAIN', 'SystemPlatform',
|
|
'urn:zb:docker-dev:galaxy', 1,
|
|
'docker-dev seed — Galaxy / MXAccess namespace served by the MAIN cluster.');
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM dbo.DriverInstance WHERE DriverInstanceId = 'MAIN-galaxy-mxgw')
|
|
INSERT INTO dbo.DriverInstance
|
|
(DriverInstanceRowId, DriverInstanceId, ClusterId, NamespaceId, Name, DriverType, Enabled, DriverConfig)
|
|
VALUES
|
|
(NEWID(), 'MAIN-galaxy-mxgw', 'MAIN', 'MAIN-galaxy',
|
|
'MxAccess gateway (10.100.0.48:5120)', 'GalaxyMxGateway', 1,
|
|
N'{
|
|
"Gateway": {
|
|
"Endpoint": "http://10.100.0.48:5120",
|
|
"ApiKeySecretRef": "env:GALAXY_MXGW_API_KEY",
|
|
"UseTls": false,
|
|
"ConnectTimeoutSeconds": 10,
|
|
"DefaultCallTimeoutSeconds": 30
|
|
},
|
|
"MxAccess": {
|
|
"ClientName": "OtOpcUa-MAIN-docker-dev",
|
|
"PublishingIntervalMs": 1000
|
|
},
|
|
"Repository": {
|
|
"DiscoverPageSize": 5000,
|
|
"WatchDeployEvents": true
|
|
},
|
|
"Reconnect": {
|
|
"InitialBackoffMs": 500,
|
|
"MaxBackoffMs": 30000,
|
|
"ReplayOnSessionLost": true
|
|
}
|
|
}');
|
|
|
|
------------------------------------------------------------------------------
|
|
-- Galaxy test tags — TestMachine_001.TestAlarm001..003
|
|
--
|
|
-- SystemPlatform-namespace tags have EquipmentId=NULL and use FolderPath +
|
|
-- Name to address the MXAccess item. The Galaxy driver subscribes via the
|
|
-- "FolderPath.Name" MXAccess reference form; OPC UA browse path is the
|
|
-- equivalent "FolderPath/Name" under the SystemPlatform namespace.
|
|
------------------------------------------------------------------------------
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM dbo.Tag WHERE TagId = 'MAIN-galaxy-TestMachine_001-TestAlarm001')
|
|
INSERT INTO dbo.Tag
|
|
(TagRowId, TagId, DriverInstanceId, DeviceId, EquipmentId, Name, FolderPath, DataType, AccessLevel, WriteIdempotent, PollGroupId, TagConfig)
|
|
VALUES
|
|
(NEWID(), 'MAIN-galaxy-TestMachine_001-TestAlarm001', 'MAIN-galaxy-mxgw', NULL, NULL,
|
|
'TestAlarm001', 'TestMachine_001', 'Boolean', 0, 0, NULL, N'{}');
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM dbo.Tag WHERE TagId = 'MAIN-galaxy-TestMachine_001-TestAlarm002')
|
|
INSERT INTO dbo.Tag
|
|
(TagRowId, TagId, DriverInstanceId, DeviceId, EquipmentId, Name, FolderPath, DataType, AccessLevel, WriteIdempotent, PollGroupId, TagConfig)
|
|
VALUES
|
|
(NEWID(), 'MAIN-galaxy-TestMachine_001-TestAlarm002', 'MAIN-galaxy-mxgw', NULL, NULL,
|
|
'TestAlarm002', 'TestMachine_001', 'Boolean', 0, 0, NULL, N'{}');
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM dbo.Tag WHERE TagId = 'MAIN-galaxy-TestMachine_001-TestAlarm003')
|
|
INSERT INTO dbo.Tag
|
|
(TagRowId, TagId, DriverInstanceId, DeviceId, EquipmentId, Name, FolderPath, DataType, AccessLevel, WriteIdempotent, PollGroupId, TagConfig)
|
|
VALUES
|
|
(NEWID(), 'MAIN-galaxy-TestMachine_001-TestAlarm003', 'MAIN-galaxy-mxgw', NULL, NULL,
|
|
'TestAlarm003', 'TestMachine_001', 'Boolean', 0, 0, NULL, N'{}');
|
|
|
|
COMMIT TRANSACTION;
|
|
|
|
------------------------------------------------------------------------------
|
|
-- Summary (logged by sqlcmd output)
|
|
------------------------------------------------------------------------------
|
|
|
|
SELECT ClusterId, Name, NodeCount, RedundancyMode FROM dbo.ServerCluster ORDER BY ClusterId;
|
|
SELECT NodeId, ClusterId, Host, OpcUaPort, ApplicationUri, ServiceLevelBase
|
|
FROM dbo.ClusterNode ORDER BY ClusterId, NodeId;
|
|
SELECT NamespaceId, ClusterId, Kind, NamespaceUri FROM dbo.Namespace ORDER BY ClusterId, NamespaceId;
|
|
SELECT DriverInstanceId, ClusterId, DriverType, NamespaceId, Name
|
|
FROM dbo.DriverInstance ORDER BY ClusterId, DriverInstanceId;
|
|
SELECT TagId, DriverInstanceId, FolderPath, Name, DataType FROM dbo.Tag ORDER BY DriverInstanceId, FolderPath, Name;
|