Phase 7 follow-up #244 — DriverSubscriptionBridge #192

Merged
dohertj2 merged 1 commits from phase-7-fu-244-driver-bridge into v2 2026-04-20 21:55:17 -04:00
Owner

Pumps live driver OnDataChange notifications into CachedTagUpstreamSource so ctx.GetTag in user scripts sees the freshest driver value. The last missing piece between #243 (composition kernel) and #246 (Program.cs wire-in).

DriverSubscriptionBridge

IAsyncDisposable. Per DriverFeed: groups all paths for one ISubscribable into a single SubscribeAsync call (consolidating polled drivers' work + giving native-subscription drivers one watch list), keeps a per-feed reverse map from driver-opaque fullRef back to script-side UNS path, hooks OnDataChange to translate + push into the cache. DisposeAsync awaits UnsubscribeAsync per active subscription + unhooks every handler so events post-dispose are silent.

Empty PathToFullRef map → feed skipped. Subscribe failure on any feed unhooks that feed's handler + propagates so misconfiguration aborts bridge start cleanly. Double-StartAsync throws; double-DisposeAsync is idempotent.

OTOPCUA0001 suppressed at the two ISubscribable call sites with comments explaining the carve-out: bridge is the lifecycle-coordinator for Phase 7 subscriptions (one Subscribe at engine compose, one Unsubscribe at shutdown), not the per-call hot-path. Driver Read dispatch still goes through CapabilityInvoker via DriverNodeManager.

Tests — 9 new = 29 Phase 7 tests total

DriverSubscriptionBridgeTests covers: SubscribeAsync called with distinct fullRefs, OnDataChange pushes to cache keyed by UNS path, unmapped fullRef ignored, empty PathToFullRef skips Subscribe, DisposeAsync unsubscribes + unhooks (post-dispose events don't push), StartAsync called twice throws, DisposeAsync idempotent, Subscribe failure unhooks handler + propagates, ctor null guards.

Phase 7 production wiring chain status

  • #243 composition kernel
  • #245 scripted-alarm IReadable adapter
  • #244 this — driver bridge
  • 🟡 #246Program.cs Compose call + SqliteStoreAndForwardSink lifecycle
  • 🟡 #240 — live E2E smoke (unblocks once #246 lands)
Pumps live driver `OnDataChange` notifications into `CachedTagUpstreamSource` so `ctx.GetTag` in user scripts sees the freshest driver value. The last missing piece between #243 (composition kernel) and #246 (Program.cs wire-in). ## `DriverSubscriptionBridge` `IAsyncDisposable`. Per `DriverFeed`: groups all paths for one `ISubscribable` into a single `SubscribeAsync` call (consolidating polled drivers' work + giving native-subscription drivers one watch list), keeps a per-feed reverse map from driver-opaque fullRef back to script-side UNS path, hooks `OnDataChange` to translate + push into the cache. `DisposeAsync` awaits `UnsubscribeAsync` per active subscription + unhooks every handler so events post-dispose are silent. Empty `PathToFullRef` map → feed skipped. Subscribe failure on any feed unhooks that feed's handler + propagates so misconfiguration aborts bridge start cleanly. Double-`StartAsync` throws; double-`DisposeAsync` is idempotent. `OTOPCUA0001` suppressed at the two `ISubscribable` call sites with comments explaining the carve-out: bridge is the lifecycle-coordinator for Phase 7 subscriptions (one Subscribe at engine compose, one Unsubscribe at shutdown), not the per-call hot-path. Driver `Read` dispatch still goes through `CapabilityInvoker` via `DriverNodeManager`. ## Tests — 9 new = **29 Phase 7 tests total** `DriverSubscriptionBridgeTests` covers: SubscribeAsync called with distinct fullRefs, OnDataChange pushes to cache keyed by UNS path, unmapped fullRef ignored, empty PathToFullRef skips Subscribe, DisposeAsync unsubscribes + unhooks (post-dispose events don't push), StartAsync called twice throws, DisposeAsync idempotent, Subscribe failure unhooks handler + propagates, ctor null guards. ## Phase 7 production wiring chain status - ✅ #243 composition kernel - ✅ #245 scripted-alarm IReadable adapter - ✅ #244 this — driver bridge - 🟡 #246 — `Program.cs` Compose call + `SqliteStoreAndForwardSink` lifecycle - 🟡 #240 — live E2E smoke (unblocks once #246 lands)
dohertj2 added 1 commit 2026-04-20 21:55:06 -04:00
Pumps live driver OnDataChange notifications into CachedTagUpstreamSource so
ctx.GetTag in user scripts sees the freshest driver value. The last missing piece
between #243 (composition kernel) and #246 (Program.cs wire-in).

## DriverSubscriptionBridge

IAsyncDisposable. Per DriverFeed: groups all paths for one ISubscribable into a
single SubscribeAsync call (consolidating polled drivers' work + giving
native-subscription drivers one watch list), keeps a per-feed reverse map from
driver-opaque fullRef back to script-side UNS path, hooks OnDataChange to
translate + push into the cache. DisposeAsync awaits UnsubscribeAsync per active
subscription + unhooks every handler so events post-dispose are silent.

Empty PathToFullRef map → feed skipped (no SubscribeAsync call). Subscribe failure
on any feed unhooks that feed's handler + propagates so misconfiguration aborts
bridge start cleanly. Double-Start throws InvalidOperationException; double-Dispose
is idempotent.

OTOPCUA0001 suppressed at the two ISubscribable call sites with comments
explaining the carve-out: bridge is the lifecycle-coordinator for Phase 7
subscriptions (one Subscribe at engine compose, one Unsubscribe at shutdown),
not the per-call hot-path. Driver Read dispatch still goes through CapabilityInvoker
via DriverNodeManager.

## Tests — 9 new = 29 Phase 7 tests total

DriverSubscriptionBridgeTests covers: SubscribeAsync called with distinct fullRefs,
OnDataChange pushes to cache keyed by UNS path, unmapped fullRef ignored, empty
PathToFullRef skips Subscribe, DisposeAsync unsubscribes + unhooks (post-dispose
events don't push), StartAsync called twice throws, DisposeAsync idempotent,
Subscribe failure unhooks handler + propagates, ctor null guards.

## Phase 7 production wiring chain status
- #243  composition kernel
- #245  scripted-alarm IReadable adapter
- #244  this — driver bridge
- #246 pending — Program.cs Compose call + SqliteStoreAndForwardSink lifecycle
- #240 pending — live E2E smoke (unblocks once #246 lands)
dohertj2 merged commit 8388ddc033 into v2 2026-04-20 21:55:17 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#192