LmxProxy is no longer needed. Moved the entire lmxproxy/ workspace, DCL adapter files, and related docs to deprecated/. Removed LmxProxy registration from DataConnectionFactory, project reference from DCL, protocol option from UI, and cleaned up all requirement docs.
6.0 KiB
6.0 KiB
Component: MxAccessClient
Purpose
The core component that wraps the ArchestrA MXAccess COM API, providing connection management, tag read/write operations, and subscription-based value change notifications. This is the bridge between the gRPC service layer and AVEVA System Platform.
Location
src/ZB.MOM.WW.LmxProxy.Host/MxAccess/MxAccessClient.cs — partial class split across 6 files:
MxAccessClient.cs— Main class, properties, disposal, factory.MxAccessClient.Connection.cs— Connection lifecycle (connect, disconnect, reconnect, cleanup).MxAccessClient.ReadWrite.cs— Read and write operations with retry and concurrency control.MxAccessClient.Subscription.cs— Subscription management and stored subscription state.MxAccessClient.EventHandlers.cs— COM event handlers (OnDataChange, OnWriteComplete, OperationComplete).MxAccessClient.NestedTypes.cs— Internal types and enums.
Responsibilities
- Manage the MXAccess COM object lifecycle (create, register, unregister, release).
- Maintain connection state (Disconnected, Connecting, Connected, Disconnecting, Error, Reconnecting) and fire state change events.
- Execute read and write operations against MXAccess with concurrency control via semaphores.
- Manage tag subscriptions via MXAccess advise callbacks and store subscription state for reconnection.
- Handle COM threading constraints (STA thread context via
Task.Run).
1. Connection Lifecycle
1.1 Connect
ConnectAsync() wraps ConnectInternal() in Task.Run for STA thread context:
- Validates not disposed.
- Returns early if already connected.
- Sets state to
Connecting. InitializeMxAccessConnection()— creates newLMXProxyServerCOM object, wires event handlers (OnDataChange, OnWriteComplete, OperationComplete).RegisterWithMxAccess()— calls_lmxProxy.Register("ZB.MOM.WW.LmxProxy.Host"), stores the returned connection handle.- Sets state to
Connected. - On error, calls
Cleanup()and re-throws.
After successful connection, calls RecreateStoredSubscriptionsAsync() to restore any previously active subscriptions.
1.2 Disconnect
DisconnectAsync() wraps DisconnectInternal() in Task.Run:
- Checks
IsConnected. - Sets state to
Disconnecting. RemoveAllSubscriptions()— unsubscribes all tags from MXAccess but retains subscription state in_storedSubscriptionsfor reconnection.UnregisterFromMxAccess()— calls_lmxProxy.Unregister(_connectionHandle).Cleanup()— removes event handlers, callsMarshal.ReleaseComObject(_lmxProxy)to force-release all COM references, nulls the proxy and resets the connection handle.- Sets state to
Disconnected.
1.3 Connection State
IsConnectedproperty:_lmxProxy != null && _connectionState == Connected && _connectionHandle > 0.ConnectionStateenum: Disconnected, Connecting, Connected, Disconnecting, Error, Reconnecting.ConnectionStateChangedevent fires on all state transitions with previous state, current state, and optional message.
1.4 Auto-Reconnect
When AutoReconnect is enabled (default), the MonitorConnectionAsync loop runs continuously:
- Checks
IsConnectedeveryMonitorIntervalSeconds(default 5 seconds). - On disconnect, attempts reconnect via semaphore-protected
ConnectAsync(). - On failure, logs warning and retries at the next interval.
- Reconnection restores stored subscriptions automatically.
2. Thread Safety & COM Constraints
- State mutations protected by
lock (_lock). - COM operations wrapped in
Task.Runfor STA thread context (MXAccess is 32-bit COM). - Concurrency control:
_readSemaphoreand_writeSemaphorelimit concurrent MXAccess operations toMaxConcurrentOperations(default 10, configurable). - Default max concurrency constant:
DefaultMaxConcurrency = 10.
3. Read Operations
ReadAsync(address, ct)— Applies Polly retry policy, callsReadSingleValueAsync(), returnsVtq.ReadBatchAsync(addresses, ct)— Creates parallel tasks per address viaReadAddressWithSemaphoreAsync(). Each task acquires_readSemaphorebefore reading. ReturnsIReadOnlyDictionary<address, Vtq>.
4. Write Operations
WriteAsync(address, value, ct)— Applies Polly retry policy, callsWriteInternalAsync(address, value, ct).WriteBatchAsync(values, ct)— Parallel tasks viaWriteAddressWithSemaphoreAsync(). Each task acquires_writeSemaphorebefore writing.WriteBatchAndWaitAsync(values, flagAddress, flagValue, responseAddress, responseValue, ct)— Writes batch, writes flag, polls response tag until match.
5. Subscription Management
- Subscriptions stored in
_storedSubscriptionsfor reconnection persistence. SubscribeInternalAsync(addresses, callback, storeSubscription)— registers tags with MXAccess and stores subscription state.RecreateStoredSubscriptionsAsync()— called after reconnect to re-subscribe all previously active tags without re-storing.RemoveAllSubscriptions()— unsubscribes from MXAccess but retains_storedSubscriptions.
6. Event Handlers
- OnDataChange — Fired by MXAccess when a subscribed tag value changes. Routes the update to the SubscriptionManager.
- OnWriteComplete — Fired when an async write operation completes.
- OperationComplete — General operation completion callback.
Dependencies
- ArchestrA.MXAccess COM interop assembly (
lib/ArchestrA.MXAccess.dll). - Polly — retry policies for read/write operations.
- Configuration —
ConnectionConfigurationfor timeouts, concurrency limits, and auto-reconnect settings.
Interactions
- GrpcServer (ScadaGrpcService) delegates all SCADA operations to MxAccessClient via the
IScadaClientinterface. - SubscriptionManager receives value change callbacks originating from MxAccessClient's COM event handlers.
- HealthAndMetrics queries
IsConnectedandConnectionStatefor health checks. - ServiceHost manages the MxAccessClient lifecycle (create at startup, dispose at shutdown).