Files
lmxopcua/docs/plans/2026-07-15-v3-batch3-uns-rework-plan.md
T
Joseph Doherty cf03ca279d docs(v3): implementation plans — master orchestration + 4 batch plans
- v3 design finalized: all 7 open items resolved (live historian probe:
  255-char tagname limit; SDK multi-notifier spike: native AddNotifier,
  never duplicate ReportEvent; NamespaceKind.Simulated retired; CSV column
  dictionaries; ScadaBridge re-bind sized; rename-warning scan decided),
  reconciled with the pre-v3 universal discovery browser (its new §11
  v3 forward note re-targets the browse commit contract).
- Calculation pseudo-driver mini-design (IDependencyConsumer capability,
  mux-fed change/timer triggers, Tarjan cycle deploy gate, VT-parity
  error semantics).
- Implementation plans for Opus-agent execution, grounded against exact
  files/symbols: master plan (batch DAG, worktree-isolated parallel
  waves, contract-first fan-out, global gotcha list) + one plan per
  batch (1 schema+RawPath identity, 2 /raw UI+Calculation, 3 UNS
  reference-only+{{equip}}, 4 dual-namespace address space = v3.0),
  each with per-wave file ownership and a mandatory docker-dev live gate.
2026-07-15 16:50:30 -04:00

113 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# v3 Batch 3 — UNS reference-only Equipment + `{{equip}}` reference-relative resolution
**Branch:** `v3/batch3-uns-rework` (from master after Batch 2 merges)
**Design authority:** v3 design §"UNS Equipment — reference-only", §"Scripting — paths and `{{equip}}` under v3", §"Error handling / edge cases" (effective-name collision, rename-warning scan).
**End state:** Equipment holds `UnsTagReference` rows instead of authored tags; scripts resolve `{{equip}}/<RefName>` through references; every collision/unresolved case is a clear deploy error. Address space still dark for values (Batch 4), so verification is authoring + deploy-gate level.
## Work packages
### Wave A (3 agents, parallel)
**B3-WP1 — Equipment Tags tab → reference list + raw-tag picker**
- `Components/Pages/Uns/EquipmentPage.razor` Tags tab: replace the Batch-1-stubbed
authored-tag flow with a `UnsTagReference` list — columns: effective name, raw path,
inherited datatype/access (read-only, from the raw tag), display-name override,
remove. `TagModal.razor`'s driver-binding path is deleted (VirtualTag/ScriptedAlarm
modals untouched).
- **"+ Add reference"** opens a raw-tree picker modal reusing Batch 2's `RawTree`
component in picker mode (multi-select checkboxes on Tag leaves; Device/TagGroup
select-all), **scoped to the equipment's cluster** (cross-cluster structurally
impossible — the service query is cluster-filtered, not just the UI).
- `ImportEquipmentModal.razor` + `Uns/EquipmentInput.cs` + `UnsTreeService.ImportEquipmentAsync`:
drop the `DriverInstanceId` column (equipment no longer carries a driver).
- Service mutations in `UnsTreeService` (+ `IUnsTreeService`): add/remove references,
set override — each enforcing WP2's uniqueness check and returning readable errors.
**B3-WP2 — effective-name uniqueness (authoring + deploy gate)**
- Rule: within an equipment, effective name (`DisplayNameOverride` else raw `Name`) is
unique across **references, VirtualTags, and ScriptedAlarms**.
- Authoring-time: service-level check in every mutation that can collide (add reference,
set override, add/rename VirtualTag or ScriptedAlarm).
- Deploy-time: `DraftValidator` gains the same rule over the draft snapshot — this is
what catches **rename-induced** collisions (a raw rename the authoring check never
saw); the error names both colliding sources and the equipment.
- `DraftSnapshotFactory` extended to load `UnsTagReference` + raw-tag names into the
snapshot.
- Unit tests: authoring rejection, deploy rejection post-rename, override-vs-VT clash,
case-sensitivity pinned (ordinal, matching NodeId semantics).
**B3-WP3 — rename-warning script scan**
- Extend Batch 2's rename-warning seam in `RawTreeService`: on rename of a tag or any
ancestor, compute affected RawPaths (prefix scan) and warn when a tag is (a)
historized without a `historianTagname` override (history forks), (b) UNS-referenced
(names the equipment), (c) matched by a **substring scan of `Script` bodies for the
old RawPath** (service-level, tolerates false positives — decided in the design; no
new bookkeeping). `{{equip}}`-relative refs need no scan (they resolve through
references; the deploy gate catches breakage).
- UI: warning list in the rename confirm dialog; rename proceeds on confirm
(Kepware-equivalent, deliberate).
### Wave B (1 agent — single integration point)
**B3-WP4 — `{{equip}}` reference-relative resolution**
- `src/Core/ZB.MOM.WW.OtOpcUa.Commons/Types/EquipmentScriptPaths.cs`:
**delete `DeriveEquipmentBase`** (shared first-dot prefix of child FullNames — cannot
survive reference-only equipment). `SubstituteEquipmentToken` is replaced by
**per-reference resolution**: `{{equip}}/<RefName>` → look up the equipment's
`UnsTagReference` rows by effective name → substitute the backing RawPath. Token
syntax note: today's derivation used a **dot** joint (`{{equip}}.X`); v3's is
slash-shaped (`{{equip}}/<RefName>`) per the design — update `ExtractDependencyRefs` /
`ExtractAlarmDependencyRefs` and the `EquipToken` handling accordingly, and pin the
new syntax in tests.
- The **same two compose seams** substitute as today:
`OpcUaServer/AddressSpaceComposer.cs` (~line 210 branch) and
`Runtime/Drivers/DeploymentArtifact.cs` (`SubstituteEquipmentToken` call, line ~563) —
both now receive the equipment's reference map instead of a derived base prefix.
- **Unresolved `<RefName>` = deploy-time validation error** (in `DraftValidator`, where
the reference map + script sources are both in the snapshot), naming the script, the
equipment, and the missing ref name — replacing today's silent null-base
no-substitution. Alarm `{TagPath}` message tokens follow the same rule.
- Monaco/editor parity: `AdminUI/ScriptAnalysis/ScriptAnalysisService.cs` — the
`{{equip}}` completion branch (lines ~176182) now completes **reference effective
names** for the equipment context; the diagnostics branch (~280284) flags unresolved
refs identically to the deploy gate (editor accepts ⇔ publish accepts — the
established invariant). `IScriptTagCatalog` gains the per-equipment reference list;
absolute-path completions list RawPaths (already moved in Batch 1's catalog rework —
verify, don't assume).
- Tests: substitution unit tests (resolved, unresolved→error, override-named ref,
alarm-token), composer/artifact parity tests, ScriptAnalysis completion/diagnostic
tests.
## Wave schedule
| Wave | Packages | Agents | Parallel-safe because |
|---|---|---|---|
| A | WP1 ∥ WP2 ∥ WP3 | 3 | WP1 owns Razor + service mutation methods; WP2 owns validator/snapshot + service *check* helpers (shared file `UnsTreeService.cs` — WP1 owns it, WP2 delivers its check as a separate injectable `EffectiveNameGuard` consumed by WP1); WP3 owns `RawTreeService` warning code |
| B | WP4 | 1 | Touches Commons + composer + artifact + validator + ScriptAnalysis — single integration agent, rebased on Wave A |
## Batch verification gate (live `/run` on docker-dev)
Rebuild **both** centrals; fixtures up; Batch 2's raw tags present (re-author or re-seed).
1. Equipment page: "+ Add reference" picker is cluster-scoped (raw tags of another
cluster absent), multi-select pulls 3+ tags at once; rows show raw path + inherited
type/access; set a `DisplayNameOverride` → effective name updates.
2. Collision: add a reference whose effective name equals an existing VirtualTag →
**authoring rejected** with both sources named. Then force a rename-induced clash
(rename the raw tag in `/raw` so two references collide) → authoring allowed at
rename, **deploy blocked** by `DraftValidator` naming both.
3. `{{equip}}`: author a VirtualTag script using `ctx.GetTag("{{equip}}/<RefName>")`
Monaco completes the reference names and shows no diagnostic; deploy succeeds.
Misspell the ref → Monaco diagnostic AND deploy error agree (same message family).
4. Rename warning: rename a raw tag that is historized + referenced + named in a script
literal → confirm dialog lists all three warnings; renaming an unrelated tag warns
nothing.
5. ImportEquipmentModal: import runs without the driver column.
Deploys via `POST /api/deployments` (`X-Api-Key`). Values still dark — assert nothing
about live data. Record evidence in the PR.