deprecate(lmxproxy): move all LmxProxy code, tests, and docs to deprecated/
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.
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
# 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:
|
||||
|
||||
1. Validates not disposed.
|
||||
2. Returns early if already connected.
|
||||
3. Sets state to `Connecting`.
|
||||
4. `InitializeMxAccessConnection()` — creates new `LMXProxyServer` COM object, wires event handlers (OnDataChange, OnWriteComplete, OperationComplete).
|
||||
5. `RegisterWithMxAccess()` — calls `_lmxProxy.Register("ZB.MOM.WW.LmxProxy.Host")`, stores the returned connection handle.
|
||||
6. Sets state to `Connected`.
|
||||
7. 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`:
|
||||
|
||||
1. Checks `IsConnected`.
|
||||
2. Sets state to `Disconnecting`.
|
||||
3. `RemoveAllSubscriptions()` — unsubscribes all tags from MXAccess but retains subscription state in `_storedSubscriptions` for reconnection.
|
||||
4. `UnregisterFromMxAccess()` — calls `_lmxProxy.Unregister(_connectionHandle)`.
|
||||
5. `Cleanup()` — removes event handlers, calls `Marshal.ReleaseComObject(_lmxProxy)` to force-release all COM references, nulls the proxy and resets the connection handle.
|
||||
6. Sets state to `Disconnected`.
|
||||
|
||||
### 1.3 Connection State
|
||||
|
||||
- `IsConnected` property: `_lmxProxy != null && _connectionState == Connected && _connectionHandle > 0`.
|
||||
- `ConnectionState` enum: Disconnected, Connecting, Connected, Disconnecting, Error, Reconnecting.
|
||||
- `ConnectionStateChanged` event 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 `IsConnected` every `MonitorIntervalSeconds` (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.Run` for STA thread context (MXAccess is 32-bit COM).
|
||||
- Concurrency control: `_readSemaphore` and `_writeSemaphore` limit concurrent MXAccess operations to `MaxConcurrentOperations` (default 10, configurable).
|
||||
- Default max concurrency constant: `DefaultMaxConcurrency = 10`.
|
||||
|
||||
## 3. Read Operations
|
||||
|
||||
- `ReadAsync(address, ct)` — Applies Polly retry policy, calls `ReadSingleValueAsync()`, returns `Vtq`.
|
||||
- `ReadBatchAsync(addresses, ct)` — Creates parallel tasks per address via `ReadAddressWithSemaphoreAsync()`. Each task acquires `_readSemaphore` before reading. Returns `IReadOnlyDictionary<address, Vtq>`.
|
||||
|
||||
## 4. Write Operations
|
||||
|
||||
- `WriteAsync(address, value, ct)` — Applies Polly retry policy, calls `WriteInternalAsync(address, value, ct)`.
|
||||
- `WriteBatchAsync(values, ct)` — Parallel tasks via `WriteAddressWithSemaphoreAsync()`. Each task acquires `_writeSemaphore` before writing.
|
||||
- `WriteBatchAndWaitAsync(values, flagAddress, flagValue, responseAddress, responseValue, ct)` — Writes batch, writes flag, polls response tag until match.
|
||||
|
||||
## 5. Subscription Management
|
||||
|
||||
- Subscriptions stored in `_storedSubscriptions` for 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** — `ConnectionConfiguration` for timeouts, concurrency limits, and auto-reconnect settings.
|
||||
|
||||
## Interactions
|
||||
|
||||
- **GrpcServer** (ScadaGrpcService) delegates all SCADA operations to MxAccessClient via the `IScadaClient` interface.
|
||||
- **SubscriptionManager** receives value change callbacks originating from MxAccessClient's COM event handlers.
|
||||
- **HealthAndMetrics** queries `IsConnected` and `ConnectionState` for health checks.
|
||||
- **ServiceHost** manages the MxAccessClient lifecycle (create at startup, dispose at shutdown).
|
||||
Reference in New Issue
Block a user