# 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).
