Phase 7 Stream G follow-up — DriverNodeManager dispatch routing by NodeSourceKind #186

Merged
dohertj2 merged 1 commits from phase-7-stream-g-followup-dispatch into v2 2026-04-20 20:14:27 -04:00
Owner

Honors the ADR-002 discriminator at OPC UA Read/Write dispatch time. Virtual tag reads route to the VirtualTagEngine-backed IReadable; scripted alarm reads route to the ScriptedAlarmEngine-backed IReadable; driver reads continue to route to the driver's own IReadable — no regression for any existing driver test.

Changes

  • DriverNodeManager ctor gains optional virtualReadable + scriptedAlarmReadable parameters. When callers omit them (every existing driver test) the manager behaves exactly as before.
  • Per-variable NodeSourceKind tracked in _sourceByFullRef during Variable() registration alongside the existing _writeIdempotentByFullRef / _securityByFullRef maps.
  • OnReadValue picks the IReadable by source kind via the new internal SelectReadable helper. When the engine-backed IReadable isn't wired (virtual tag node but no engine provided) the read returns BadNotFound rather than silently falling back to the driver — surfaces a misconfiguration instead of masking it.
  • OnWriteValue gates on IsWriteAllowedBySource which returns true only for Driver. Plan decision #6: virtual tags + scripted alarms reject direct OPC UA writes with BadUserAccessDenied. Scripts write virtual tags via ctx.SetVirtualTag; operators ack alarms via the Part 9 method nodes.

Tests — 7/7

DriverNodeManagerSourceDispatchTests (internal helpers via InternalsVisibleTo):

  • Driver source routes to driver IReadable
  • Virtual source routes to virtual IReadable
  • ScriptedAlarm source routes to alarm IReadable
  • Virtual source with null virtual IReadable returns null (→ BadNotFound)
  • ScriptedAlarm source with null alarm IReadable returns null
  • Driver source with null driver IReadable returns null (preserves BadNotReadable)
  • IsWriteAllowedBySource: only Driver=true (Virtual=false, ScriptedAlarm=false)

Full solution builds clean. Phase 7 test total 197 green.

Next

Stream H (exit gate) — SealedBootstrap composition root for VirtualTagEngine + ScriptedAlarmEngine + historian sink, compliance script, full-solution baseline.

Honors the ADR-002 discriminator at OPC UA Read/Write dispatch time. Virtual tag reads route to the `VirtualTagEngine`-backed `IReadable`; scripted alarm reads route to the `ScriptedAlarmEngine`-backed `IReadable`; driver reads continue to route to the driver's own `IReadable` — no regression for any existing driver test. ## Changes - `DriverNodeManager` ctor gains optional `virtualReadable` + `scriptedAlarmReadable` parameters. When callers omit them (every existing driver test) the manager behaves exactly as before. - Per-variable `NodeSourceKind` tracked in `_sourceByFullRef` during `Variable()` registration alongside the existing `_writeIdempotentByFullRef` / `_securityByFullRef` maps. - `OnReadValue` picks the `IReadable` by source kind via the new internal `SelectReadable` helper. When the engine-backed `IReadable` isn't wired (virtual tag node but no engine provided) the read returns `BadNotFound` rather than silently falling back to the driver — surfaces a misconfiguration instead of masking it. - `OnWriteValue` gates on `IsWriteAllowedBySource` which returns true only for `Driver`. Plan decision #6: virtual tags + scripted alarms reject direct OPC UA writes with `BadUserAccessDenied`. Scripts write virtual tags via `ctx.SetVirtualTag`; operators ack alarms via the Part 9 method nodes. ## Tests — 7/7 `DriverNodeManagerSourceDispatchTests` (internal helpers via `InternalsVisibleTo`): - `Driver` source routes to driver `IReadable` - `Virtual` source routes to virtual `IReadable` - `ScriptedAlarm` source routes to alarm `IReadable` - `Virtual` source with null virtual `IReadable` returns null (→ `BadNotFound`) - `ScriptedAlarm` source with null alarm `IReadable` returns null - `Driver` source with null driver `IReadable` returns null (preserves `BadNotReadable`) - `IsWriteAllowedBySource`: only `Driver=true` (`Virtual=false`, `ScriptedAlarm=false`) Full solution builds clean. Phase 7 test total 197 green. ## Next Stream H (exit gate) — SealedBootstrap composition root for VirtualTagEngine + ScriptedAlarmEngine + historian sink, compliance script, full-solution baseline.
dohertj2 added 1 commit 2026-04-20 20:14:14 -04:00
Honors the ADR-002 discriminator at OPC UA Read/Write dispatch time. Virtual tag
reads route to the VirtualTagEngine-backed IReadable; scripted alarm reads route
to the ScriptedAlarmEngine-backed IReadable; driver reads continue to route to the
driver's own IReadable (no regression for any existing driver test).

## Changes

DriverNodeManager ctor gains optional `virtualReadable` + `scriptedAlarmReadable`
parameters. When callers omit them (every existing driver test) the manager behaves
exactly as before. SealedBootstrap wires the engines' IReadable adapters once the
Phase 7 composition root is added.

Per-variable NodeSourceKind tracked in `_sourceByFullRef` during Variable() registration
alongside the existing `_writeIdempotentByFullRef` / `_securityByFullRef` maps.

OnReadValue now picks the IReadable by source kind via the new internal
SelectReadable helper. When the engine-backed IReadable isn't wired (virtual tag
node but no engine provided), returns BadNotFound rather than silently falling
back to the driver — surfaces a misconfiguration instead of masking it.

OnWriteValue gates on IsWriteAllowedBySource which returns true only for Driver.
Plan decision #6: virtual tags + scripted alarms reject direct OPC UA writes with
BadUserAccessDenied. Scripts write virtual tags via `ctx.SetVirtualTag`; operators
ack alarms via the Part 9 method nodes.

## Tests — 7/7 (internal helpers exposed via InternalsVisibleTo)

DriverNodeManagerSourceDispatchTests covers:
- Driver source routes to driver IReadable
- Virtual source routes to virtual IReadable
- ScriptedAlarm source routes to alarm IReadable
- Virtual source with null virtual IReadable returns null (→ BadNotFound)
- ScriptedAlarm source with null alarm IReadable returns null
- Driver source with null driver IReadable returns null (preserves BadNotReadable)
- IsWriteAllowedBySource: only Driver=true (Virtual=false, ScriptedAlarm=false)

Full solution builds clean. Phase 7 test total now 197 green.
dohertj2 merged commit 4e41f196b2 into v2 2026-04-20 20:14:27 -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#186