From 866c73dcd458d994e53ff60618e6919d362f1741 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sun, 22 Mar 2026 04:47:23 -0400 Subject: [PATCH] =?UTF-8?q?docs(lmxproxy):=20add=20deviation=20#8=20?= =?UTF-8?q?=E2=80=94=20SubscriptionManager=20COM=20subscription=20wiring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- lmxproxy/docs/deviations.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lmxproxy/docs/deviations.md b/lmxproxy/docs/deviations.md index 7ff9149..c8e4321 100644 --- a/lmxproxy/docs/deviations.md +++ b/lmxproxy/docs/deviations.md @@ -50,3 +50,10 @@ Decisions made during implementation that differ from or extend the original pla **Actual**: Write is confirmed synchronously — if `_lmxProxy.Write()` returns without throwing, the write succeeded. The `OnWriteComplete` callback is kept wired for diagnostic logging only. **Why**: MxAccess completes supervisory writes synchronously. The `OnWriteComplete` COM callback never fires for simple supervisory writes, causing the original implementation to timeout waiting for a callback that would never arrive. This caused WriteAndReadBack and WriteBatchAndWait integration tests to fail. **How to apply**: Do not await `OnWriteComplete` for write confirmation. The `Write()` COM call succeeding (not throwing a COM exception) is the confirmation. Clean up (UnAdvise + RemoveItem) happens immediately after the write in a finally block. + +## 8. SubscriptionManager must create MxAccess COM subscriptions + +**Plan specified**: SubscriptionManager manages per-client channels and routes updates from MxAccess. +**Actual**: SubscriptionManager must also call `IScadaClient.SubscribeAsync()` to create the underlying COM subscriptions when a tag is first subscribed, and dispose them when the last client unsubscribes. +**Why**: The Phase 2 implementation tracked client-to-tag routing in internal dictionaries but never called `MxAccessClient.SubscribeAsync()` to create the actual MxAccess COM subscriptions (`AddItem` + `AdviseSupervisory`). Without the COM subscription, `OnDataChange` never fired and no updates were delivered to clients. This caused the `Subscribe_ReceivesUpdates` integration test to receive 0 updates over 30 seconds. +**How to apply**: `SubscriptionManager.Subscribe()` collects newly-seen tags (those without an existing `TagSubscription`) and calls `_scadaClient.SubscribeAsync()` for them, passing `OnTagValueChanged` as the callback. The returned `IAsyncDisposable` handles are tracked in `_mxAccessHandles` per address and disposed in `UnsubscribeClient()` when the last client for a tag leaves.