Remove the periodic 5-minute device re-browse (tag-set drift detection) from FOCAS/TwinCAT/MTConnect/AbCip #523

Closed
opened 2026-07-28 13:26:41 -04:00 by dohertj2 · 1 comment
Owner

Decision (owner, 2026-07-28): remove the periodic re-browse. Shipped in e77c8a35, merged to master in 914d70bf. This issue tracks taking it back out.

What is in master today

DriverInstanceActor starts a periodic timer on every Connected entry for drivers that declare ITagDiscovery.SupportsOnlineDiscovery and report AuthoredDiscoveryRefs. Each tick runs a full DiscoverAsync against the live device and diffs the result against the authored tag set, raising the /hosts re-browse prompt on a change.

Four drivers qualify: FOCAS, TwinCAT, MTConnect, AbCip. Interval is DriverInstanceActor.DefaultDriftCheckInterval = 5 minutes, a constructor parameter with no appsettings key — so a deployment cannot currently turn it off or slow it down without a code change. That is the core objection: it is recurring, unconfigurable, unsolicited traffic to real PLCs, CNCs and MTConnect Agents, and it was never live-gated.

What to remove

  • DriverInstanceActor: DriftCheckTick, StartDriftCheck, StopDriftCheck, HandleDriftCheckAsync, QualifiesForDriftCheck, _driftCheckInterval, _lastDriftSignature, DefaultDriftCheckInterval, DriftBrowseTimeout, CollectLeafRefs, the driftCheckInterval params on Props/ctor, the ReceiveAsync<DriftCheckTick> registration and the StartDriftCheck()/StopDriftCheck() call sites.
  • src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/TagSetDrift.cs (whole file).
  • Tests: TagSetDriftDetectorTests, DriverInstanceActorDriftCheckTests.
  • Driver members: AuthoredDiscoveryRefs + DiscoveryStreamIncludesAuthoredTags on FocasDriver, TwinCATDriver, AbCipDriver, MTConnectDriver.
  • ITagDiscovery: the AuthoredDiscoveryRefs and DiscoveryStreamIncludesAuthoredTags members. Check for external implementors first — these are default-implemented interface members, so removing them is source-compatible for anyone who did not override them, but a driver in a sibling repo that did override would break.
  • Docs: the drift paragraphs in CLAUDE.md §Change Detection and the 2026-07-28 entry in deferment.md §9.

Do NOT remove the IRediscoverable consumption itself (09a401b8). That is a separate, event-driven signal — the driver tells us, we do not poll — and it costs nothing when nothing changes.

Consequence to record, not to argue

AbCip and FOCAS browse a live backend but implement no IRediscoverable, so after this removal they have no change signal at all: a PLC program re-download that adds or removes tags will be invisible until someone re-browses under /raw by hand. TwinCAT (ADS symbol-version) and MTConnect (Agent instanceId) keep their native event-driven signals and lose only the redundant polling.

If that turns out to matter for AbCip/FOCAS later, the event-driven shape is the one to reach for — a device-side change notification, or a drift check that runs only when something else already indicates a change (a reconnect, a deploy) rather than on a wall-clock timer.

Alternative considered and NOT chosen

Gating the interval behind an appsettings key (default off) would keep the capability available without the unsolicited traffic. The owner's decision is removal; recorded here only so the option is not re-discovered from scratch.

**Decision (owner, 2026-07-28): remove the periodic re-browse.** Shipped in `e77c8a35`, merged to `master` in `914d70bf`. This issue tracks taking it back out. ## What is in `master` today `DriverInstanceActor` starts a periodic timer on every Connected entry for drivers that declare `ITagDiscovery.SupportsOnlineDiscovery` **and** report `AuthoredDiscoveryRefs`. Each tick runs a full `DiscoverAsync` against the live device and diffs the result against the authored tag set, raising the `/hosts` re-browse prompt on a change. **Four drivers qualify: FOCAS, TwinCAT, MTConnect, AbCip.** Interval is `DriverInstanceActor.DefaultDriftCheckInterval` = **5 minutes**, a constructor parameter with no appsettings key — so a deployment cannot currently turn it off or slow it down without a code change. That is the core objection: it is recurring, unconfigurable, unsolicited traffic to real PLCs, CNCs and MTConnect Agents, and it was never live-gated. ## What to remove - `DriverInstanceActor`: `DriftCheckTick`, `StartDriftCheck`, `StopDriftCheck`, `HandleDriftCheckAsync`, `QualifiesForDriftCheck`, `_driftCheckInterval`, `_lastDriftSignature`, `DefaultDriftCheckInterval`, `DriftBrowseTimeout`, `CollectLeafRefs`, the `driftCheckInterval` params on `Props`/ctor, the `ReceiveAsync<DriftCheckTick>` registration and the `StartDriftCheck()`/`StopDriftCheck()` call sites. - `src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/TagSetDrift.cs` (whole file). - Tests: `TagSetDriftDetectorTests`, `DriverInstanceActorDriftCheckTests`. - Driver members: `AuthoredDiscoveryRefs` + `DiscoveryStreamIncludesAuthoredTags` on `FocasDriver`, `TwinCATDriver`, `AbCipDriver`, `MTConnectDriver`. - `ITagDiscovery`: the `AuthoredDiscoveryRefs` and `DiscoveryStreamIncludesAuthoredTags` members. **Check for external implementors first** — these are default-implemented interface members, so removing them is source-compatible for anyone who did not override them, but a driver in a sibling repo that did override would break. - Docs: the drift paragraphs in `CLAUDE.md` §Change Detection and the 2026-07-28 entry in `deferment.md` §9. **Do NOT remove** the `IRediscoverable` consumption itself (`09a401b8`). That is a separate, event-driven signal — the driver tells us, we do not poll — and it costs nothing when nothing changes. ## Consequence to record, not to argue **AbCip and FOCAS browse a live backend but implement no `IRediscoverable`**, so after this removal they have **no change signal at all**: a PLC program re-download that adds or removes tags will be invisible until someone re-browses under `/raw` by hand. TwinCAT (ADS symbol-version) and MTConnect (Agent `instanceId`) keep their native event-driven signals and lose only the redundant polling. If that turns out to matter for AbCip/FOCAS later, the event-driven shape is the one to reach for — a device-side change notification, or a drift check that runs **only** when something else already indicates a change (a reconnect, a deploy) rather than on a wall-clock timer. ## Alternative considered and NOT chosen Gating the interval behind an appsettings key (default off) would keep the capability available without the unsolicited traffic. The owner's decision is removal; recorded here only so the option is not re-discovered from scratch.
Author
Owner

DONE — removed, merged to master as 2a0461a1 (commit 5fff5973).

Removed

Where
TagSetDrift + TagSetDriftDetector Runtime/Drivers/TagSetDrift.cs (file deleted)
The drift timer, DriftCheckTick, DefaultDriftCheckInterval, the driftCheckInterval Props/ctor parameter, QualifiesForDriftCheck, Start/StopDriftCheck, HandleDriftCheckAsync, CollectLeafRefs, _lastDriftSignature DriverInstanceActor
ITagDiscovery.AuthoredDiscoveryRefs + .DiscoveryStreamIncludesAuthoredTags, and their four implementations Core.Abstractions; FOCAS, TwinCAT, MTConnect, AbCip
DriverInstanceActorDriftCheckTests (6), TagSetDriftDetectorTests (8) Runtime.Tests

Kept, deliberately

  • ITagDiscovery.SupportsOnlineDiscovery — it predates drift detection and is what the universal browse picker dispatches on.
  • The whole IRediscoverable consumption (09a401b8) and the /hosts re-browse chip, now fed by that one source. The driver tells us; we do not poll, and it costs nothing when nothing changes.

⚠️ Consequence, carried forward rather than buried

AbCip and FOCAS now have no tag-change signal at all. Both browse a live backend, neither implements IRediscoverable, and the periodic compare was the only thing that would have noticed a PLC program re-download or a CNC option install. Galaxy, TwinCAT, MTConnect and MQTT raise natively and are unaffected.

Recorded in CLAUDE.md §Change Detection so the next reader does not have to rediscover it, together with the shape to reach for if that coverage is wanted back: event-driven, or triggered by something that already indicates change (a reconnect, a deploy) — not a wall-clock timer.

Note on the design that was removed

The design itself was sound: the tautology drivers (Modbus, S7, MQTT, AbLegacy, Sql — which echo authored config from DiscoverAsync) were correctly gated out, and the structurally undetectable "vanished" direction was declared rather than faked. None of that was the objection. The objection was to the feature existing in that shape at all — it browsed real plant equipment on a recurring timer, at a frequency nobody could configure, and that behaviour was never once observed against real hardware. A well-built mechanism for something that should not run is still something that should not run.

Build clean; Runtime.Tests 476 passed / 13 skipped (skip set unchanged at the time), FOCAS 275, TwinCAT 192, MTConnect 491, AbCip 342, Core.Abstractions 266 — all green.

**DONE** — removed, merged to `master` as `2a0461a1` (commit `5fff5973`). ### Removed | | Where | |---|---| | `TagSetDrift` + `TagSetDriftDetector` | `Runtime/Drivers/TagSetDrift.cs` (file deleted) | | The drift timer, `DriftCheckTick`, `DefaultDriftCheckInterval`, the `driftCheckInterval` Props/ctor parameter, `QualifiesForDriftCheck`, `Start`/`StopDriftCheck`, `HandleDriftCheckAsync`, `CollectLeafRefs`, `_lastDriftSignature` | `DriverInstanceActor` | | `ITagDiscovery.AuthoredDiscoveryRefs` + `.DiscoveryStreamIncludesAuthoredTags`, and their four implementations | `Core.Abstractions`; FOCAS, TwinCAT, MTConnect, AbCip | | `DriverInstanceActorDriftCheckTests` (6), `TagSetDriftDetectorTests` (8) | `Runtime.Tests` | ### Kept, deliberately - **`ITagDiscovery.SupportsOnlineDiscovery`** — it predates drift detection and is what the universal browse picker dispatches on. - **The whole `IRediscoverable` consumption** (`09a401b8`) and the `/hosts` re-browse chip, now fed by that one source. The driver tells us; we do not poll, and it costs nothing when nothing changes. ### ⚠️ Consequence, carried forward rather than buried **AbCip and FOCAS now have no tag-change signal at all.** Both browse a live backend, neither implements `IRediscoverable`, and the periodic compare was the only thing that would have noticed a PLC program re-download or a CNC option install. Galaxy, TwinCAT, MTConnect and MQTT raise natively and are unaffected. Recorded in `CLAUDE.md` §Change Detection so the next reader does not have to rediscover it, together with the shape to reach for if that coverage is wanted back: **event-driven, or triggered by something that already indicates change (a reconnect, a deploy) — not a wall-clock timer.** ### Note on the design that was removed The design itself was sound: the tautology drivers (Modbus, S7, MQTT, AbLegacy, Sql — which echo authored config from `DiscoverAsync`) were correctly gated out, and the structurally undetectable "vanished" direction was **declared** rather than faked. None of that was the objection. The objection was to the feature existing in that shape at all — it browsed real plant equipment on a recurring timer, at a frequency nobody could configure, and that behaviour was never once observed against real hardware. A well-built mechanism for something that should not run is still something that should not run. Build clean; `Runtime.Tests` 476 passed / 13 skipped (skip set unchanged at the time), FOCAS 275, TwinCAT 192, MTConnect 491, AbCip 342, `Core.Abstractions` 266 — all green.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#523