feat(raw): re-migrate discovered-node injection onto the raw device subtree — IRediscoverable is inert in v3 #507

Closed
opened 2026-07-27 13:49:40 -04:00 by dohertj2 · 1 comment
Owner

IRediscoverable.OnRediscoveryNeeded currently changes nothing. DriverHostActor.HandleDiscoveredNodes
(src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverHostActor.cs:986) hard-returns after a Debug log — a
deliberate v3 Batch-4 guard, whose own comment ends "Re-migrating injection onto the raw device subtree is a
separate follow-up."
This is that follow-up.

Why it was short-circuited. In v2 a driver-connected FixedTree was grafted under an equipment folder
(equipment bound a driver). v3 retired that binding — equipment references raw tags via UnsTagReference, and
discovered raw tags are authored explicitly through the Batch-2 /raw browse-commit flow. The downstream
mapper/materialiser were only half-migrated to the Raw tree and are still rooted at an equipment id, so letting
the message through would materialise incoherent nodes. Short-circuiting was correct; leaving it permanent is
the gap.

Effect today. Every driver implementing IRediscoverable fires into a void:

Driver Trigger that is currently ignored
Mqtt a Sparkplug DBIRTH introducing a metric
Galaxy DeployWatcher observing a Galaxy redeploy
TwinCAT ADS symbol-table change
AbCip controller @tags change

The operator-visible symptom is uniform: the edge gains a point and the OPC UA tree does not change until
someone redeploys.
The driver-side decision is log-observable (each driver logs that it raised rediscovery),
so this is a platform gap, not a per-driver bug — fixing it once lights up all four.

Scope sketch. Re-root the discovered-node mapper/materialiser at a Device in the /raw tree rather
than at an equipment id, and decide the authoring contract: auto-materialise discovered leaves, or surface them
as a pending-review set the operator commits (the latter matches the existing /raw browse-commit flow and
avoids a chatty edge rewriting the address space unattended). Whichever is chosen, _discoveredByDriver and
the redeploy re-inject tail need to come back with it.

Acceptance. A driver raising OnRediscoveryNeeded results in the new leaf being reachable in the served
address space (auto, or after an explicit commit) without a redeploy, live-verified on docker-dev against the
Sparkplug simulator (docker restart otopcua-sparkplug-sim forces a fresh birth; births are never retained).

`IRediscoverable.OnRediscoveryNeeded` currently changes nothing. `DriverHostActor.HandleDiscoveredNodes` (`src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverHostActor.cs:986`) hard-returns after a Debug log — a deliberate v3 Batch-4 guard, whose own comment ends *"Re-migrating injection onto the raw device subtree is a separate follow-up."* This is that follow-up. **Why it was short-circuited.** In v2 a driver-connected `FixedTree` was grafted under an equipment folder (equipment bound a driver). v3 retired that binding — equipment references raw tags via `UnsTagReference`, and discovered raw tags are authored explicitly through the Batch-2 `/raw` browse-commit flow. The downstream mapper/materialiser were only half-migrated to the Raw tree and are still rooted at an equipment id, so letting the message through would materialise incoherent nodes. Short-circuiting was correct; leaving it permanent is the gap. **Effect today.** Every driver implementing `IRediscoverable` fires into a void: | Driver | Trigger that is currently ignored | |---|---| | Mqtt | a Sparkplug **DBIRTH** introducing a metric | | Galaxy | `DeployWatcher` observing a Galaxy redeploy | | TwinCAT | ADS symbol-table change | | AbCip | controller `@tags` change | The operator-visible symptom is uniform: **the edge gains a point and the OPC UA tree does not change until someone redeploys.** The driver-side decision is log-observable (each driver logs that it raised rediscovery), so this is a platform gap, not a per-driver bug — fixing it once lights up all four. **Scope sketch.** Re-root the discovered-node mapper/materialiser at a **Device** in the `/raw` tree rather than at an equipment id, and decide the authoring contract: auto-materialise discovered leaves, or surface them as a pending-review set the operator commits (the latter matches the existing `/raw` browse-commit flow and avoids a chatty edge rewriting the address space unattended). Whichever is chosen, `_discoveredByDriver` and the redeploy re-inject tail need to come back with it. **Acceptance.** A driver raising `OnRediscoveryNeeded` results in the new leaf being reachable in the served address space (auto, or after an explicit commit) without a redeploy, live-verified on docker-dev against the Sparkplug simulator (`docker restart otopcua-sparkplug-sim` forces a fresh birth; births are never retained).
dohertj2 added the cross-driver label 2026-07-27 13:49:41 -04:00
Author
Owner

CLOSED AS SUPERSEDED — the injection path was deleted, not migrated (adce27e7, merged 914d70bf).

Why the requested migration was not done

This issue asks to re-migrate discovered-node injection onto the raw device subtree. Reading the retained code showed there was nothing to migrate: its two inputs are structurally empty in v3.

  • EquipmentNode.DriverInstanceId — AddressSpaceComposer.cs:468-470, 514-517
  • EquipmentTags — DeploymentArtifact.cs:614

So removing the #pragma warning disable CS0162 guard would have changed a log line and injected nothing. The deferment register had recorded this as a "deferred (deliberate guard)" — too generous; it was dead code behind a guard that made it look revivable.

Deleted

HandleDiscoveredNodes, PartitionDiscoveredByDeviceHost, ApplyDiscoveredPlansForDriver, the redeploy re-inject tail, DiscoveredNodeMapper, AddressSpaceApplier.MaterialiseDiscoveredNodes, OpcUaPublishActor.MaterialiseDiscoveredNodes, the connect-time discovery loop (StartDiscovery / RediscoverTick / DiscoveredNodesReady / TriggerRediscovery), and 4 files' worth of tests.

The connect-time loop went with it because it had no remaining consumer — it was browsing real devices up to ~15× per connect and dropping the result.

What replaces it

Discovered raw tags are authored through the /raw browse-commit flow, which is the deliberate v3 posture: nodes exist because an operator approved them and a deployment artifact records them. ITagDiscovery itself stays — the browse picker drives it through Commons/Browsing/DiscoveryDriverBrowser, which never went through the actor.

IRediscoverable now surfaces a re-browse prompt on /hosts instead (#518).

One correction to the record

The 18 DiscoveryInjectionDormantV3 skipped tests were described as "blocked on #507". They were v2 characterization tests asserting an equipment-rooted graft (EquipmentRootNodeId == "EQ-1") that v3 cannot produce — they could never have unskipped as written, with or without this issue. Deleted. Runtime.Tests skipped went 31 → 13.

**CLOSED AS SUPERSEDED — the injection path was deleted, not migrated** (`adce27e7`, merged `914d70bf`). ### Why the requested migration was not done This issue asks to re-migrate discovered-node injection onto the raw device subtree. Reading the retained code showed there was nothing to migrate: its **two inputs are structurally empty in v3**. - `EquipmentNode.DriverInstanceId` — `AddressSpaceComposer.cs:468-470, 514-517` - `EquipmentTags` — `DeploymentArtifact.cs:614` So removing the `#pragma warning disable CS0162` guard would have changed a log line and **injected nothing**. The deferment register had recorded this as a "deferred (deliberate guard)" — too generous; it was dead code behind a guard that made it look revivable. ### Deleted `HandleDiscoveredNodes`, `PartitionDiscoveredByDeviceHost`, `ApplyDiscoveredPlansForDriver`, the redeploy re-inject tail, `DiscoveredNodeMapper`, `AddressSpaceApplier.MaterialiseDiscoveredNodes`, `OpcUaPublishActor.MaterialiseDiscoveredNodes`, the connect-time discovery loop (`StartDiscovery` / `RediscoverTick` / `DiscoveredNodesReady` / `TriggerRediscovery`), and 4 files' worth of tests. The connect-time loop went with it because it had no remaining consumer — it was browsing real devices **up to ~15× per connect** and dropping the result. ### What replaces it Discovered raw tags are authored through the **`/raw` browse-commit flow**, which is the deliberate v3 posture: nodes exist because an operator approved them and a deployment artifact records them. `ITagDiscovery` itself stays — the browse picker drives it through `Commons/Browsing/DiscoveryDriverBrowser`, which never went through the actor. `IRediscoverable` now surfaces a **re-browse prompt** on `/hosts` instead (#518). ### One correction to the record The 18 `DiscoveryInjectionDormantV3` skipped tests were described as "blocked on #507". They were **v2 characterization tests** asserting an equipment-rooted graft (`EquipmentRootNodeId == "EQ-1"`) that v3 cannot produce — they could never have unskipped as written, with or without this issue. Deleted. `Runtime.Tests` skipped went 31 → 13.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#507