cf03ca279d
- 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.
113 lines
7.2 KiB
Markdown
113 lines
7.2 KiB
Markdown
# 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 ~176–182) now completes **reference effective
|
||
names** for the equipment context; the diagnostics branch (~280–284) 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.
|