Files
lmxopcua/docs/plans/2026-07-16-v3-batch2-PR.md
T
Joseph Doherty 33915c9e4d
v2-ci / build (pull_request) Successful in 3m25s
v2-ci / unit-tests (pull_request) Failing after 8m37s
docs(v3): Batch 2 — /raw authoring tree guide + CLAUDE.md + PR description
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
2026-07-16 05:27:31 -04:00

5.1 KiB
Raw Blame History

v3 Batch 2 — /raw project-tree AdminUI + Calculation driver

Implements docs/plans/2026-07-15-v3-batch2-raw-ui-calculation-plan.md (Track 0 + WP1WP8), 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 AIRawTreeService/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 HIGHCalculationDriver.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 DeviceConfigTest 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/Acalc1/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-modbusgate-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.