diff --git a/docker-dev/README.md b/docker-dev/README.md index a7eede8..be7ade1 100644 --- a/docker-dev/README.md +++ b/docker-dev/README.md @@ -60,6 +60,12 @@ The SQL lives at `seed/seed-clusters.sql`; the wait-and-apply wrapper lives at ` docker compose -f docker-dev/docker-compose.yml run --rm cluster-seed ``` +### Galaxy / MxAccess gateway + +The seed also pre-creates a `SystemPlatform` Namespace + a `GalaxyMxGateway` DriverInstance in the MAIN cluster pointing at `http://10.100.0.48:5120`. The API key is resolved from the `GALAXY_MXGW_API_KEY` env var set on every driver-role container in compose; override via `GALAXY_MXGW_API_KEY=... docker compose up -d` to swap keys without editing the compose file. + +The DriverHost actor doesn't spawn drivers from raw DriverInstance rows on its own — the v2 deploy lifecycle requires a *sealed Deployment* before drivers materialise. After first bring-up, sign in to the Admin UI and click **Deploy current configuration** on `/deployments` to compose the seeded rows into an artifact and dispatch it. The Galaxy driver instance will start its gRPC connection to the gateway on the next deploy ack. + ## Bring up ```bash diff --git a/docker-dev/docker-compose.yml b/docker-dev/docker-compose.yml index f091e02..2091ce7 100644 --- a/docker-dev/docker-compose.yml +++ b/docker-dev/docker-compose.yml @@ -98,6 +98,7 @@ services: Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev" Authentication__Ldap__DevStubMode: "true" + GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua__UY_NKlBl3vWuZt8HD7usfZsU76eibMKB6CufwzabUI}" admin-b: <<: *otopcua-host @@ -114,6 +115,7 @@ services: Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev" Authentication__Ldap__DevStubMode: "true" + GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua__UY_NKlBl3vWuZt8HD7usfZsU76eibMKB6CufwzabUI}" driver-a: <<: *otopcua-host @@ -125,6 +127,9 @@ services: Cluster__PublicHostname: "driver-a" Cluster__SeedNodes__0: "akka.tcp://otopcua@admin-a:4053" Cluster__Roles__0: "driver" + # Resolved at runtime by GalaxyDriver.ResolveApiKey when a DriverInstance's + # Gateway.ApiKeySecretRef = "env:GALAXY_MXGW_API_KEY". + GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua__UY_NKlBl3vWuZt8HD7usfZsU76eibMKB6CufwzabUI}" ports: - "4840:4840" @@ -138,6 +143,7 @@ services: Cluster__PublicHostname: "driver-b" Cluster__SeedNodes__0: "akka.tcp://otopcua@admin-a:4053" Cluster__Roles__0: "driver" + GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua__UY_NKlBl3vWuZt8HD7usfZsU76eibMKB6CufwzabUI}" ports: - "4841:4840" @@ -162,6 +168,7 @@ services: Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev" Authentication__Ldap__DevStubMode: "true" + GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua__UY_NKlBl3vWuZt8HD7usfZsU76eibMKB6CufwzabUI}" ports: - "4842:4840" @@ -184,6 +191,7 @@ services: Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev" Authentication__Ldap__DevStubMode: "true" + GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua__UY_NKlBl3vWuZt8HD7usfZsU76eibMKB6CufwzabUI}" ports: - "4843:4840" @@ -205,6 +213,7 @@ services: Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev" Authentication__Ldap__DevStubMode: "true" + GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua__UY_NKlBl3vWuZt8HD7usfZsU76eibMKB6CufwzabUI}" ports: - "4844:4840" @@ -227,6 +236,7 @@ services: Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev" Authentication__Ldap__DevStubMode: "true" + GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua__UY_NKlBl3vWuZt8HD7usfZsU76eibMKB6CufwzabUI}" ports: - "4845:4840" diff --git a/docker-dev/seed/seed-clusters.sql b/docker-dev/seed/seed-clusters.sql index ff74996..0b7a1e5 100644 --- a/docker-dev/seed/seed-clusters.sql +++ b/docker-dev/seed/seed-clusters.sql @@ -95,6 +95,56 @@ IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'site-b-2') (NodeId, ClusterId, Host, OpcUaPort, DashboardPort, ApplicationUri, ServiceLevelBase, Enabled, CreatedBy) VALUES ('site-b-2', '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 + } +}'); + COMMIT TRANSACTION; ------------------------------------------------------------------------------ @@ -104,3 +154,6 @@ COMMIT TRANSACTION; 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; diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverInstanceActor.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverInstanceActor.cs index f8dbcd4..63b5c39 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverInstanceActor.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverInstanceActor.cs @@ -66,9 +66,15 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers /// /// Returns true when the driver should boot in DEV-STUB mode based on host platform and - /// configured roles. Mirrors plan §Task 55: Windows-only driver types (Galaxy, Wonderware - /// Historian) are stubbed when running on non-Windows OR when the host carries the - /// dev role. + /// configured roles. Only the v1 in-process types stay Windows-only: + /// + /// "Galaxy" — legacy MXAccess COM proxy (retired in PR 7.2; gated for any + /// leftover DriverInstance rows that still reference the old type name). + /// "Historian.Wonderware" — Wonderware Historian sidecar over Windows-only + /// named pipes. + /// + /// The v2 "GalaxyMxGateway" driver talks gRPC to an external mxaccessgw process, + /// so it runs on any platform .NET 10 supports — Linux containers included. Not stubbed. /// public static bool ShouldStub(string driverType, IEnumerable roles) {