Files
lmxopcua/current.md
T
Joseph Doherty 02e37a6d68
v2-ci / build (push) Failing after 37s
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
docs(notes): track pending.md + current.md session state
Snapshot of Milestone 1b completion (equipment-tag live values across
OpcUaClient / protocol drivers / Galaxy, read + write) and the open
follow-ups, for cross-session continuity.
2026-06-13 23:41:06 -04:00

7.9 KiB

Current status — Galaxy standard-driver effort

Updated 2026-06-13.

Where we are

Phase A (Galaxy = standard Equipment-kind driver) is DONE, merged to master (c3c56172), pushed, and live-verified.

  • GalaxyMxGateway is now an ordinary Equipment-kind driver. Retired: the SystemPlatform NamespaceKind + its mirror (Phase7Applier.MaterialiseGalaxyTags, the composer/artifact GalaxyTags contract + GalaxyTagPlan), the byte-parity || DriverType=="GalaxyMxGateway" exception clauses, the authz NodeHierarchyKind.SystemPlatform + PermissionTrie.WalkSystemPlatform, and the entire alias-tag/relay AdminUI machinery.
  • A Galaxy point is now a plain equipment Tag{EquipmentId set, DriverInstanceId=GalaxyMxGateway, TagConfig={"FullName":"tag.attr"}}, authored through the standard TagModal + the "Browse Galaxy" picker.
  • Forward-only data-only migration CleanupSystemPlatformNamespaces deletes orphaned SystemPlatform config + NodeAcl grants and releases ExternalIdReservations. Kept UX_Namespace_Cluster_Kind (Simulated kind still reserved).
  • Gate: 1009 unit tests green + full docker-dev live /run (migration applied clean, server boots on migrated DB, alias UI gone, namespace picker drops SystemPlatform, Galaxy driver created under Equipment, Galaxy tag authored via the new picker).

Design: docs/plans/2026-06-12-galaxy-standard-driver-design.md (3-phase). Phase A plan: docs/plans/2026-06-12-galaxy-standard-driver-phase-a.md (+ .tasks.json).


The rest of the plan (all NOT started)

Milestone 1 — Equipment-tag live-value DELIVERY (the FullName→NodeId router) — DONE, merged c4435e4f, pushed

Design docs/plans/2026-06-13-equipment-tag-live-values-design.md; plan …-plan.md (+ .tasks.json). Mirrored the proven VirtualTagHostActor._nodeIdByVtag pattern for DRIVER values:

  • NEW ZB.MOM.WW.OtOpcUa.Commons.OpcUa.EquipmentNodeIds = single source of truth for the folder-scoped NodeId formula (Variable(eq,fp,name)); repointed Phase7Applier + VirtualTagHostActor to it (byte-identical, kills the drift risk).
  • DriverInstanceId added to AttributeValuePublished (the router key — FullName is unique only WITHIN a driver instance).
  • DriverHostActor builds a (DriverInstanceId, FullName) → NodeId[] map each apply (in PushDesiredSubscriptions, via EquipmentNodeIds.Variable) and resolves it in ForwardToMux, emitting the existing AttributeValueUpdate per resolved NodeId (1→many handled; no-match drops+debug-logs). NO OpcUaPublishActor change.
  • Gate: build 0 errors; 322 tests (incl. 3 Akka.TestKit tests driving the full router through a real deploy/apply); final integration review = READY TO MERGE (end-to-end NodeId coherence confirmed). Live /run: the deploy applied + the variable materialised at the exact router NodeId + SubscribeBulk pushed refs — the materialisation + router wiring is confirmed live.

🔴 Milestone 1b — Per-driver "actually PUBLISH a value" gaps (NOT started — the router has nothing to route until these land; live value could NOT be shown in dev for this reason)

The router is correct, but no dev driver currently publishes an equipment-tag value, so a variable still won't show data until the owning driver's publish path is wired. Three orthogonal, independent gaps (each its own follow-up):

  • OpcUaClient — MISSING FACTORY (real bug). src/Server/.../Host/Drivers/DriverFactoryBootstrap.cs:98-107 registers AbCip/AbLegacy/FOCAS/Galaxy/Modbus/S7/TwinCAT but OMITS OpcUaClient — and the OpcUaClient driver project has no …FactoryExtensions class at all (only OpcUaClientDriver.cs + OpcUaClientDriverProbe.cs). So OpcUaClient is always stubbed ("no factory for driver type OpcUaClient … falling back to stub") — completely non-functional. Fix: add an OpcUaClientDriverFactoryExtensions (CreateInstance parsing driverConfigJsonOpcUaClientDriverOptions + new OpcUaClientDriver(options, id, …), mirroring ModbusDriverFactoryExtensions) + register it in the bootstrap. This is the cheapest path to a live value (OpcUaClient is direct-ref: TagConfig.FullName="ns=3;s=FastUInt1" → subscribes to opc-plc → publishes → router delivers). opc-plc sim is up at opc.tcp://10.100.0.35:50000 (ns=3;s=FastUInt1).
  • Protocol drivers (Modbus/S7/AbCip/…) — equipment-tag↔driver-subscription linkage. The Modbus driver subscribes by tag NAME from DriverConfig.Tags (_tagsByName, keyed by t.Name), but an equipment tag's TagConfig (region/address, via ModbusTagConfigModel) carries no top-level FullName, so ExtractTagFullName falls back to the raw JSON blob → never matches a DriverConfig.Tags name → SubscribeAsync silently skips it (if (!_tagsByName.TryGetValue(...)) continue;). The equipment tag's register config is never connected to the driver. This is the bigger "equipment tags work for protocol drivers" gap (needs the deploy to build the driver's tag table from equipment tags, or the equipment-tag FullName to address a register directly).
  • Galaxy — needs a reachable mxaccessgw. Direct-ref (FullName = MxAccess ref), so the router would deliver — but the gateway must be up + ApiKeySecretRef resolvable (dev MX_API_KEY unset).

Recommended next: build the OpcUaClient factory (small, fixes a real bug, gives the first live value). Then the protocol-driver linkage (larger).

🟡 Phase B — Native alarms on the equipment-tag path (NOT started)

Galaxy implements IAlarmSource (native MXAccess alarms), but that wiring lived only on the retired SystemPlatform-mirror path (GenericDriverNodeManager registers an alarm sink per MarkAsAlarmCondition variable keyed by FullName, routes OnAlarmEvent.SourceNodeId to it). After Phase A, native Galaxy alarms have no materialization path until this is ported.

Work: teach MaterialiseEquipmentTags/OtOpcUaNodeManager to register an alarm-condition sink for IsAlarm equipment tags (keyed by FullName) and subscribe the owning driver's IAlarmSource, routing OnAlarmEvent.SourceNodeId == FullName → sink. Reuse the existing Part 9 AlarmConditionState materialization + alarm-commands/ack plumbing (scripted alarms already use it). Extract GenericDriverNodeManager's forwarder so both paths share it. Mirror its unsubscribe-before-rewalk for IRediscoverable redeploys.

🟢 Phase C — Server-side historian / HistoryRead (NOT started)

Driver-agnostic server-side history backend over the existing Wonderware Historian reader (Historian.Wonderware.Client already implements IHistoryProvider). No mxaccessgw history RPC needed.

Work: add an OPC UA HistoryRead service override in OtOpcUaNodeManager (none exists today) → a router that, for an IsHistorized node, resolves a historian tagname (default = the tag's driver FullName, optional override) and calls the registered IHistorianDataSource. Apply DriverAttributeInfo.IsHistorized → UA Historizing + AccessLevel.HistoryRead at materialization (currently hardcoded false). Config: a server Historian section (Null default; Wonderware when configured), mirroring the existing AlarmHistorian pattern. Works for any IsHistorized tag, not just Galaxy.


Dev rig state

docker-dev runs locally (Docker 29.4.0 on this Mac); login disabled in dev (Security__Auth__DisableLogin: "true"); central UI at http://localhost:9200, SQL at localhost:14330 (sa/OtOpcUa!Dev123). Currently on the Phase-A build with the clean-break migration applied. Verification artifacts left in place: MAIN-galaxy-eq Galaxy driver + GalaxyTestTag under the nw-uns Equipment namespace. Prior 1,391-tag Galaxy config is restorable from OtOpcUa-prePhaseA-20260612-224908.bak (in the SQL volume /var/opt/mssql/backup/). Rebuild central from a branch: docker compose -f docker-dev/docker-compose.yml up -d --build migrator central-1 central-2.