- Every InitializeAsync/ShutdownAsync bumps a lifecycle generation. The session, namespace map and endpoint are installed under _probeLock only while the init's generation is current, so an init the host abandoned that finishes after the next shutdown discards its session instead of resurrecting it. Every health write (lifecycle, keep-alive, publish-error, read path) is generation-stamped, so a late writer can no longer overwrite newer state. - O-2: the init catch closes and disposes the LOCAL session it opened (it closed the still-null Session property, orphaning an activated session + keep-alive timer). - O-9: ShutdownAsync spends ONE budget (4 s, inside the host's 5 s PostStop budget) on wire calls: every subscription delete concurrently, then the session close with what remains; waits are abandoned when the budget fires even if the SDK ignores the token. It used to be 5 s per subscription, serially. Refs #743 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
OtOpcUa
OPC UA server (.NET 10 AnyCPU) that exposes a fleet of industrial drivers as a single OPC UA address space. Drivers ship in-process for AVEVA System Platform Galaxy (via the sibling mxaccessgw repo), Modbus TCP, Siemens S7, Allen-Bradley CIP (ControlLogix / CompactLogix), Allen-Bradley Legacy (SLC 500 / MicroLogix), Beckhoff TwinCAT (ADS), FANUC FOCAS, OPC UA Client (gateway), and MQTT (broker subscribe; Sparkplug B in progress).
A cross-platform client stack (.NET 10) — shared library, CLI, and Avalonia desktop app — connects to any OPC UA server.
Architecture
OPC UA Clients (CLI, Desktop UI, 3rd-party)
|
v
+-------------------------------------+
| OtOpcUa.Server (.NET 10 AnyCPU) |
| address space + capability fan-out|
+-------------------------------------+
| | | | | | | |
Galaxy Modbus S7 AbCip AbLeg TwinCAT FOCAS OpcUaClient MQTT
|
v
mxaccessgw (sibling repo, gRPC)
|
v
MXAccess COM (x86 worker, on AVEVA box)
Galaxy is the only driver with an external runtime: it speaks gRPC to a separately installed mxaccessgw server (sibling repo at c:\Users\dohertj2\Desktop\mxaccessgw\) which owns the MXAccess COM apartment and the x86/STA bitness constraint server-side. Everything in this repo is platform-agnostic .NET 10.
Prerequisites
- .NET 10 SDK (server, drivers, clients all target .NET 10)
- SQL Server reachable for the central config DB
- For Galaxy specifically: a running
mxaccessgwdeployment — see docs/v2/Galaxy.ParityRig.md - For historian read-back / alarm history / continuous historization: a running
ZB.MOM.WW.HistorianGatewaydeployment (the sole historian backend; consumed as theZB.MOM.WW.HistorianGateway.ClientgRPC package). It must runRuntimeDb:Enabled=true+RuntimeDb:EventReadsEnabled=true, and the API key must carryhistorian:read+historian:write+historian:tags:writescopes.
Quick Start
dotnet restore ZB.MOM.WW.OtOpcUa.slnx
dotnet build ZB.MOM.WW.OtOpcUa.slnx
dotnet test ZB.MOM.WW.OtOpcUa.slnx
# Run the server in dev (foreground)
dotnet run --project src/Server/ZB.MOM.WW.OtOpcUa.Host
The server starts on opc.tcp://localhost:4840 with the None security profile. Configure Security.Profiles in src/Server/ZB.MOM.WW.OtOpcUa.Host/appsettings.json to enable Basic256Sha256-Sign or Basic256Sha256-SignAndEncrypt. See docs/security.md.
Install as Windows Services
Production deployment is driven by scripts/install/Install-Services.ps1, which registers the OtOpcUa server service under a chosen service account. Historian support requires a separately deployed ZB.MOM.WW.HistorianGateway and Galaxy support a separately installed mxaccessgw — neither this repo nor the install script provisions them.
.\scripts\install\Install-Services.ps1 `
-InstallRoot 'C:\Program Files\OtOpcUa' `
-ServiceAccount 'DOMAIN\svc-otopcua'
The historian backend is the external ZB.MOM.WW.HistorianGateway (not installed by this script). See the script header and docs/ServiceHosting.md for full options.
Client CLI
dotnet run --project src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI -- connect -u opc.tcp://localhost:4840
dotnet run --project src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI -- browse -u opc.tcp://localhost:4840 -r -d 3
dotnet run --project src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI -- read -u opc.tcp://localhost:4840 -n "ns=2;s=SomeNode"
dotnet run --project src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI -- write -u opc.tcp://localhost:4840 -n "ns=2;s=SomeNode" -v 42
dotnet run --project src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI -- subscribe -u opc.tcp://localhost:4840 -n "ns=2;s=SomeNode" -i 500
See docs/Client.CLI.md and docs/Client.UI.md.
Documentation
Architecture deep-dives
| Topic | Doc |
|---|---|
| OPC UA server composition, namespace fan-out, Polly invoker | docs/OpcUaServer.md |
| Address space layout | docs/AddressSpace.md |
| Read / Write dispatch (driver vs virtual vs scripted-alarm) | docs/ReadWriteOperations.md |
| Incremental sync (driver-backend rediscovery + config publishes) | docs/IncrementalSync.md |
| Service hosting (Server + Admin; external HistorianGateway backend) | docs/ServiceHosting.md |
| Security (transport, LDAP, certificates) | docs/security.md |
| Redundancy | docs/Redundancy.md |
| Status dashboard | docs/StatusDashboard.md |
Drivers
| Topic | Doc |
|---|---|
| Driver specs (per-driver capability surface, config, addressing) | docs/v2/driver-specs.md |
| Galaxy driver | docs/drivers/Galaxy.md |
| Modbus / S7 / AbCip / AbLegacy / TwinCAT / FOCAS / OpcUaClient / MQTT | docs/drivers/ |
| Galaxy parity rig (mxaccessgw setup) | docs/v2/Galaxy.ParityRig.md |
| Galaxy performance + tracing | docs/v2/Galaxy.Performance.md |
Clients
| Topic | Doc |
|---|---|
| Client CLI | docs/Client.CLI.md |
| Client UI (Avalonia desktop) | docs/Client.UI.md |
v1 archive
The original v1 in-process MXAccess docs (Galaxy.Host topology, Configuration env vars, AlarmTracking, DataTypeMapping, HistoricalDataAccess, Subscriptions, etc.) are preserved under docs/v1/ — historical reference only. PR 7.2 retired the v1 architecture on 2026-04-30; current state is documented in the sections above.
License
Internal use only.