STRUCTURAL: no broken links/paths for this doc (links-report had zero rows); check_links.py confirms zero rows. All cited src paths verified on disk. STALE-STATUS (v1->v2): - Removed v1 'two separate Server/Admin processes' framing; documented the single role-gated Host binary + OTOPCUA_ROLES gate (src/Server/ZB.MOM.WW.OtOpcUa.Host/Program.cs; AkkaClusterOptions.cs). - Server class is OtOpcUaSdkServer (not 'OtOpcUaServer'); it wires ONE OtOpcUaNodeManager via CreateMasterNodeManager, not one DriverNodeManager per driver. OtOpcUaSdkServer.cs:12-26. - Removed nonexistent OnServerStarted / LoadServerProperties overrides and the 'DriverNodeManagers' member (no such member; grep found none). CODE-REALITY (doc corrected to match source; no code changed): - Class name: OtOpcUaSdkServer : StandardServer — OtOpcUaSdkServer.cs:12. - Address space: OtOpcUaNodeManager : CustomNodeManager2, namespace 'https://zb.com/otopcua/ns', single 'OtOpcUa' root folder; push-driven via IOpcUaAddressSpaceSink — OtOpcUaNodeManager.cs:25,27,225-251. - Impersonation lives in OpcUaApplicationHost (not the SDK server). Uses IOpcUaUserAuthenticator, attaches a UserIdentity (NOT RoleBasedIdentity/ IRoleBearer — neither exists), Anonymous+X509 fall through to SDK default, failures -> BadIdentityTokenRejected (not BadIdentityTokenInvalid). OpcUaApplicationHost.cs:159-288. - Certificate stores default to PkiStoreRoot='pki' (relative to cwd), NOT %LOCALAPPDATA%. Substores own/issuer/trusted/rejected. AutoAcceptUntrustedClientCertificates default=false (doc had Security.AutoAcceptClientCertificates default=true; key does not exist). Removed RejectSHA1Certificates claim (not present). OpcUaApplicationHost.cs:51,71,298-355. - Security profiles: EnabledSecurityProfiles default = all three baseline profiles, one endpoint per profile; not 'resolved from ServerInstance.Security JSON, default None'. Endpoint path is .../OtOpcUa. OpcUaApplicationHost.cs:59-64,321. - Dispatch: CapabilityInvoker is one per (DriverInstance, IDriver); pipeline keyed (DriverInstanceId, hostName, DriverCapability). Enum member is 'Discover' (not 'Discovery'). Alarm surfaces route via AlarmSurfaceInvoker (SubscribeAlarmsAsync/UnsubscribeAlarmsAsync/AcknowledgeAsync), per-host fan-out. CapabilityInvoker.cs:7-19,61-156; AlarmSurfaceInvoker.cs:5-51; DriverCapability.cs:20-41. OTOPCUA0001 analyzer is category OtOpcUa.Resilience, severity Warning — UnwrappedCapabilityCallAnalyzer.cs:67; AnalyzerReleases.Shipped.md:10. - Authorization: removed nonexistent AuthorizationGate / NodeScopeResolver / Authorization:StrictMode / lax-strict mode / WriteAuthzPolicy. Documented the real permission-trie infra under Core/Authorization/ (PermissionTrie, TriePermissionEvaluator, NodeScope, UserAuthorizationState, AuthorizationDecision). - Config DB: optimistic concurrency is RowVersion (per-entity), not a 'DraftRevisionToken' (no such field). sp_PublishGeneration + sp_ComputeGenerationDiff verified in Configuration migrations. - Redundancy: ServiceLevel republished via SdkServiceLevelPublisher (IServiceLevelPublisher); ServiceLevelCalculator 0-255. Dropped invented 'RedundantServerArray' node; standard props are RedundancySupport + ServerUriArray. SdkServiceLevelPublisher.cs:9-58; ServiceLevelCalculator.cs:13-23. INLINE COMPLETENESS: documented EnabledSecurityProfiles binding key in the Transport section (inventory-diff G3 row owner).
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, and OPC UA Client (gateway).
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
|
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 Wonderware Historian read-back: optional
OtOpcUaWonderwareHistoriansidecar — see docs/ServiceHosting.md
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.Server
The server starts on opc.tcp://localhost:4840 with the None security profile. Configure Security.Profiles in src/Server/ZB.MOM.WW.OtOpcUa.Server/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 (and optionally the OtOpcUaWonderwareHistorian sidecar) under a chosen service account. Galaxy support requires a separately installed mxaccessgw — neither this repo nor the install script provisions it.
.\scripts\install\Install-Services.ps1 `
-InstallRoot 'C:\Program Files\OtOpcUa' `
-ServiceAccount 'DOMAIN\svc-otopcua'
Add -InstallWonderwareHistorian for the historian sidecar. 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 + optional historian sidecar) | 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 | 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.