33915c9e4d
docs/Raw.md (the /raw project tree: lazy tree, endpoint→DeviceConfig split, manual/CSV/browse authoring, Calculation driver, routed-flow retirement); CLAUDE.md AdminUI section notes /raw + DriverTypeNames rewire + Calculation driver; PR body with the 7-item live-gate evidence. Claude-Session: https://claude.ai/code/session_01LVneM3eh1UtJxEisFXgmox
75 lines
5.1 KiB
Markdown
75 lines
5.1 KiB
Markdown
# v3 Batch 2 — `/raw` project-tree AdminUI + `Calculation` driver
|
||
|
||
Implements `docs/plans/2026-07-15-v3-batch2-raw-ui-calculation-plan.md` (Track 0 + WP1–WP8),
|
||
executed via the `/v3-batch 2` coordinator: contracts-first fan-out, worktree-isolated Opus
|
||
agents per wave, a code-reviewer pass + green build/test after each wave, then the
|
||
non-negotiable 7-item live gate on docker-dev.
|
||
|
||
## What landed
|
||
|
||
- **Track 0** — reusable `ContextMenu` (right-click + `⋯`, keyboard/focus); RFC-4180
|
||
`CsvParser`/`CsvWriter`; `DriverTypeNames` constants + a reflection-discovery guard;
|
||
`CalculationEvaluator` core (VirtualTag-evaluator parity).
|
||
- **Wave A** — `IRawTreeService`/`RawTreeService` (lazy tree + mutations, integrity guards,
|
||
rename-warnings); `/raw` page + lazy `RawTree` with per-node context menus.
|
||
- **Wave B** — driver→embeddable-form refactor + `DriverConfigModal`/`DeviceModal`
|
||
(endpoint→`DeviceConfig`, Test-connect via merged config); manual tag entry + tag edit
|
||
modal + `Calculation` tag editor; CSV import (staged review grid) / export; all wired into
|
||
the tree (name/confirm/driver-type dialogs + post-mutation refresh).
|
||
- **Wave C** — browse re-target (two-tier gate, merged-config feed, commit → raw tags with
|
||
address-field mapping + folder mirroring); `Calculation` driver (`IDependencyConsumer` +
|
||
mux adapter, timer + change triggers, `DraftValidator` scriptId + cycle gates);
|
||
`DriverTypeNames` rewire + retirement of the routed driver flow.
|
||
|
||
**Address space stays dark** (values light up in Batch 4). Full solution builds 0 errors;
|
||
`AdminUI.Tests` 638 passed / 3 skipped, `Runtime.Tests` 357 passed, driver + guard suites green.
|
||
|
||
## Reviewer findings fixed in-branch
|
||
|
||
- **Wave B H1** — driver forms still serialized the endpoint into `DriverConfig`; stripped
|
||
the endpoint keys (Modbus/S7/OpcUaClient) so `DeviceConfig` is the sole source (a 2nd
|
||
device on a single-Host driver no longer reverts to `127.0.0.1`).
|
||
- **Wave C HIGH** — `CalculationDriver.ReinitializeAsync` ignored the new config, so calc
|
||
tag/script edits were silently inert after a redeploy and the re-register loop was
|
||
decorative. Now rebuilds the tag/dependency table on reinit and re-registers deps after
|
||
the delta applies (`DeltaApplied` notification, race-free). New redeploy test proves it.
|
||
- Wave-A/B/0 mediums (auto-expand, friendly create-race/delete failures, discovery-driven
|
||
guard, CSV comment). Deferred (documented): pre-existing Modbus/S7 form `TimeSpan`↔`*Ms`
|
||
DTO mismatch; CSV alarm-subfield/flag-precedence edge cases; OpcUaClient endpoint
|
||
precedence; unused `LoadMergedProbeConfigAsync`; refresh collapses sibling expansion.
|
||
|
||
## Live `/run` gate (docker-dev `:9200`, both central nodes rebuilt)
|
||
|
||
1. **Authoring + Test-connect** — `/raw` → New Modbus driver `gate-modbus` (form body renders,
|
||
`PLC family`/`MELSEC` enum dropdowns correct) → Device1 endpoint `10.100.0.35:5020` in
|
||
`DeviceConfig` → **Test connect OK · 94 ms** against the live Modbus fixture; SQL confirms
|
||
endpoint in `DeviceConfig` (`{"Host":"10.100.0.35","Port":5020,"UnitId":1}`).
|
||
2. **Historian-tagname deploy block** — a historized tag with a 260-char effective tagname →
|
||
`POST /api/deployments` **422** `[HistorianTagnameTooLong] tag 'TAG-gatelong' … is 260 chars
|
||
(max 255)`; shortened override → **202 Accepted**.
|
||
3. **CSV round-trip + bad-enum** — export produces the exact column dictionary
|
||
(`…,Region,Address,ModbusDataType,ByteOrder,…`); importing 1 valid + 1 bad-enum row →
|
||
review grid "2 rows, 1 valid, 1 invalid", row-2 error *"Column 'ModbusDataType':
|
||
'NOTATYPE' is not a valid ModbusDataType (expected one of: Bool, Int16, UInt16, …)"*,
|
||
**Commit disabled** (all-or-nothing; SQL confirms nothing committed); a valid-only import →
|
||
Commit → `ImportedTag` lands with typed columns assembled into `TagConfig`.
|
||
4. **Browse-commit** — OpcUaClient device → opc-plc (`opc.tcp://10.100.0.35:50000`): two-tier
|
||
gate enabled browse, merged config connected, real tree walked (NetworkSet/…/OpcPlc);
|
||
multi-selected 2 leaves with "create matching tag-groups" → raw tags land under the
|
||
mirrored `Basic` group with DataType + the browse ref in the `nodeId` address field
|
||
(`nsu=…;s=AlternatingBoolean`), not an identity key.
|
||
5. **Calculation deploy + cycle gate** — a calc tag reading a Modbus RawPath deploys **202**;
|
||
a 2-cycle (`calc1/Engine/A`↔`calc1/Engine/B`) → **422** `[CalculationDependencyCycle] …
|
||
(members: calc1/Engine/A, calc1/Engine/B)`; clean redeploy 202, no crash loops (both
|
||
central nodes stable; only a deploy-reject WRN in the log).
|
||
6. **Rename warning** — renaming `gate-modbus`→`gate-modbus-renamed` fired the warning modal
|
||
*"1 historized tag beneath this node will change its RawPath (historian tagname)…"*.
|
||
7. **Retired routes** — `/clusters/{id}/drivers`, `…/new`, `…/new/{slug}`, `…/{id}` all
|
||
return **404**; `/raw` + `/uns` still 200.
|
||
|
||
## Docs
|
||
|
||
`docs/Raw.md` (new — the `/raw` authoring tree + endpoint split + CSV + Calculation +
|
||
retirement note); `CLAUDE.md` AdminUI section updated; the Calculation mini-design remains the
|
||
driver's authority.
|