P1: Wire OPC UA monitored items to MXAccess subscriptions
- Override OnCreateMonitoredItemsComplete/OnDeleteMonitoredItemsComplete
in LmxNodeManager to trigger ref-counted SubscribeTag/UnsubscribeTag
- Clients subscribing to tags now start live MXAccess data pushes
P1: Write timeout now returns false instead of true
- Previously a missing OnWriteComplete callback was treated as success
- Now correctly reports failure so OPC UA clients see the error
P1: Auto-reconnect retries from Error state (not just Disconnected)
- Monitor loop now checks both Disconnected and Error states
- Prevents permanent outages after a single failed reconnect attempt
P2: Topological sort on hierarchy before building address space
- Parents guaranteed to appear before children regardless of input order
- Prevents misplaced nodes when SQL returns unsorted results
P3: Skip redundant first-poll rebuild on startup
- ChangeDetectionService accepts initial deploy time from OpcUaService
- First poll only triggers rebuild if deploy time is actually unknown
- Eliminates duplicate DB fetch and address space rebuild at startup
All 212 tests pass (205 unit + 7 integration).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
LmxOpcUa Server
OPC UA server on .NET Framework 4.8 (x86) that exposes AVEVA System Platform (Wonderware) Galaxy tags via the MXAccess toolkit. Mirrors the Galaxy object hierarchy as an OPC UA address space, translating between contained-name browse paths and tag-name runtime references.
Architecture
OPC UA Clients
|
v
+-----------------+ +------------------+ +-----------------+
| Galaxy Repo DB |---->| OPC UA Server |<--->| MXAccess Client |
| (SQL Server) | | (address space) | | (STA + COM) |
+-----------------+ +------------------+ +-----------------+
|
+-------+--------+
| Status Dashboard|
| (HTTP/JSON) |
+----------------+
Galaxy Repository queries the ZB database for the deployed object hierarchy and attribute definitions, building the OPC UA address space at startup and rebuilding on deploy changes.
MXAccess Client connects to the Galaxy runtime via COM interop on a dedicated STA thread with a Win32 message pump. Handles subscriptions, read/write, reconnection, and probe-based health monitoring.
OPC UA Server exposes the hierarchy as browse nodes (folders for areas, objects for containers) with variable nodes for each attribute. Clients browse using contained names but reads/writes translate to tag_name.AttributeName for MXAccess.
Contained Name vs Tag Name
| Browse Path (contained names) | Runtime Reference (tag name) |
|---|---|
TestMachine_001/DelmiaReceiver/DownloadPath |
DelmiaReceiver_001.DownloadPath |
TestMachine_001/MESReceiver/MoveInBatchID |
MESReceiver_001.MoveInBatchID |
Quick Start
Prerequisites
- .NET Framework 4.8 SDK
- AVEVA System Platform with ArchestrA Framework installed
- Galaxy repository database (SQL Server, Windows Auth)
- MXAccess COM registered (
LMXProxy.LMXProxyServer)
Build & Run
dotnet restore ZB.MOM.WW.LmxOpcUa.slnx
dotnet build ZB.MOM.WW.LmxOpcUa.slnx
dotnet run --project src/ZB.MOM.WW.LmxOpcUa.Host
The server starts on opc.tcp://localhost:4840/LmxOpcUa with SecurityPolicy None.
Install as Windows Service
cd src/ZB.MOM.WW.LmxOpcUa.Host/bin/Debug/net48
ZB.MOM.WW.LmxOpcUa.Host.exe install
ZB.MOM.WW.LmxOpcUa.Host.exe start
Test with CLI Tool
# Connect
dotnet run --project tools/opcuacli-dotnet -- connect -u opc.tcp://localhost:4840/LmxOpcUa
# Browse Galaxy hierarchy
dotnet run --project tools/opcuacli-dotnet -- browse -u opc.tcp://localhost:4840/LmxOpcUa -n "ns=1;s=ZB" -r -d 5
# Read a tag
dotnet run --project tools/opcuacli-dotnet -- read -u opc.tcp://localhost:4840/LmxOpcUa -n "ns=1;s=TestMachine_001.MachineID"
# Write a tag
dotnet run --project tools/opcuacli-dotnet -- write -u opc.tcp://localhost:4840/LmxOpcUa -n "ns=1;s=TestChildObject.TestString" -v "Hello"
# Subscribe to changes
dotnet run --project tools/opcuacli-dotnet -- subscribe -u opc.tcp://localhost:4840/LmxOpcUa -n "ns=1;s=TestChildObject.TestInt" -i 500
Run Tests
dotnet test ZB.MOM.WW.LmxOpcUa.slnx
Configuration
All settings in appsettings.json, overridable via environment variables:
| Section | Key | Default | Description |
|---|---|---|---|
| OpcUa | Port | 4840 | OPC UA server port |
| OpcUa | EndpointPath | /LmxOpcUa | Endpoint path |
| OpcUa | GalaxyName | ZB | Galaxy name (used in namespace URI) |
| OpcUa | MaxSessions | 100 | Maximum concurrent sessions |
| MxAccess | ClientName | LmxOpcUa | MXAccess registration name |
| MxAccess | AutoReconnect | true | Auto-reconnect on disconnect |
| MxAccess | ProbeTag | null | Tag for connection health probing |
| GalaxyRepository | ConnectionString | Server=localhost;Database=ZB;... | ZB database connection |
| GalaxyRepository | ChangeDetectionIntervalSeconds | 30 | Deploy change polling interval |
| Dashboard | Enabled | true | Enable HTTP status dashboard |
| Dashboard | Port | 8081 | Dashboard port |
Project Structure
src/ZB.MOM.WW.LmxOpcUa.Host/
Configuration/ Config binding and validation
Domain/ Interfaces, DTOs, enums, mappers
Metrics/ Performance tracking (rolling P95)
MxAccess/ STA thread, COM interop, subscriptions
GalaxyRepository/ SQL queries, change detection
OpcUa/ Server, node manager, address space
Status/ HTTP dashboard, health checks
OpcUaService.cs Service wiring (startup/shutdown)
Program.cs TopShelf entry point
tests/ZB.MOM.WW.LmxOpcUa.Tests/
Configuration/ Config binding tests
Domain/ Type mapping, quality, error code tests
Metrics/ Performance metrics tests
MxAccess/ STA thread, connection, subscription, R/W tests
GalaxyRepository/ Change detection tests
OpcUa/ Address space build/rebuild, data conversion tests
Status/ Health check, dashboard, web server tests
Wiring/ Component integration tests
EndToEnd/ Full data flow smoke test
tools/opcuacli-dotnet/ OPC UA CLI test tool
gr/ Galaxy repository docs, SQL queries, schema
Data Type Mapping
| Galaxy Type | mx_data_type | OPC UA Type | NodeId |
|---|---|---|---|
| Boolean | 1 | Boolean | i=1 |
| Integer | 2 | Int32 | i=6 |
| Float | 3 | Float | i=10 |
| Double | 4 | Double | i=11 |
| String | 5 | String | i=12 |
| DateTime | 6 | DateTime | i=13 |
| ElapsedTime | 7 | Double (seconds) | i=11 |
| Reference | 8 | String | i=12 |
| Enumeration | 13 | Int32 | i=6 |
| InternationalizedString | 15 | LocalizedText | i=21 |
Array attributes use ValueRank=1 with ArrayDimensions from the Galaxy attribute definition.
Startup Sequence
- Load and validate configuration
- Register
AppDomain.UnhandledExceptionhandler - Create performance metrics
- Connect MXAccess (STA thread + COM registration)
- Start MXAccess monitor loop
- Test Galaxy database connection
- Start OPC UA server with programmatic config
- Query hierarchy + attributes, build address space
- Start change detection polling (rebuilds on deploy)
- Start status dashboard (HTML + JSON + health endpoint)
License
Internal use only.