Implements configurable user authentication (anonymous + username/password)
with pluggable credential provider (IUserAuthenticationProvider). Anonymous
writes can be disabled via AnonymousCanWrite setting while reads remain
open. Adds -U/-P flags to all CLI commands for authenticated sessions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Set a new EventId (GUID) on AlarmConditionState each time an alarm event
is reported so the framework can match it when clients call Acknowledge.
Without this, the framework rejected all ack attempts with BadEventIdUnknown.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds alarm_ack.md documenting the two-way acknowledge flow (OPC UA client
writes AckMsg, Galaxy confirms via Acked data change). Includes external
code review fixes for subscriptions and node manager, and removes stale
plan files now superseded by component documentation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On Galaxy deploy changes, only the affected gobject subtrees are torn down
and rebuilt instead of destroying the entire address space. Unchanged nodes,
subscriptions, and alarm tracking continue uninterrupted.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Both features now default to disabled and require explicit opt-in via
OpcUa.AlarmTrackingEnabled and Historian.Enabled in appsettings.json,
preventing errors in environments without a Historian database or alarm setup.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wire Galaxy security_classification to OPC UA AccessLevel (ReadOnly for SecuredWrite/VerifiedWrite/ViewOnly).
Use deployed package chain for attribute queries to exclude undeployed attributes.
Group primitive attributes under their parent variable node (merged Variable+Object).
Add is_historized and is_alarm detection via HistoryExtension/AlarmExtension primitives.
Implement OPC UA HistoryRead backed by Wonderware Historian Runtime database.
Implement AlarmConditionState nodes driven by InAlarm with condition refresh support.
Add historyread and alarms CLI commands for testing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Windows services default to System32 as working directory, causing logs to land in the wrong location. Set Environment.CurrentDirectory to AppDomain.CurrentDomain.BaseDirectory before Serilog init. Also fix ZB root folder not appearing under Objects folder — BuildAddressSpace runs after CreateAddressSpace completes so the externalReferences dict is already consumed; use Server.NodeManager.AddReferences instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Preserve and replay subscription ref counts across address space rebuilds to prevent MXAccess subscription leaks
- Mark read timeouts and write failures as unsuccessful in PerformanceMetrics for accurate health reporting
- Add deferred MxAccess reconnect path when initial connection fails at startup
- Update code review document with verified completions and new findings
- Add covering tests for all fixes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Full OPC UA server on .NET Framework 4.8 (x86) exposing AVEVA System
Platform Galaxy tags via MXAccess. Mirrors Galaxy object hierarchy as
OPC UA address space, translating contained-name browse paths to
tag-name runtime references.
Components implemented:
- Configuration: AppConfiguration with 4 sections, validator
- Domain: ConnectionState, Quality, Vtq, MxDataTypeMapper, error codes
- MxAccess: StaComThread, MxAccessClient (partial classes), MxProxyAdapter
using strongly-typed ArchestrA.MxAccess COM interop
- Galaxy Repository: SQL queries (hierarchy, attributes, change detection),
ChangeDetectionService with auto-rebuild on deploy
- OPC UA Server: LmxNodeManager (CustomNodeManager2), LmxOpcUaServer,
OpcUaServerHost with programmatic config, SecurityPolicy None
- Status Dashboard: HTTP server with HTML/JSON/health endpoints
- Integration: Full 14-step startup, graceful shutdown, component wiring
175 tests (174 unit + 1 integration), all passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>