Files
lmxopcua/docs/Raw.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.3 KiB

The Raw project tree (/raw)

v3 authors device I/O in a Raw project tree — a cluster-rooted, Kepware-style hierarchy that is the single surface for driver/device/tag authoring. It is the peer of the /uns unified-namespace page; the two OPC UA namespaces (ns=Raw / ns=UNS) light up in Batch 4.

Enterprise → Cluster → Folder(s) → Driver → Device → TagGroup(s) → Tag

Every node's identity is its RawPath (<Folder/…>/<Driver>/<Device>/<TagGroup/…>/<Tag>, cluster-scoped, /-separated, ordinal). Names are validated as RawPath segments (no /, no leading/trailing whitespace) at authoring and at the deploy gate.

The tree

/raw (GlobalRaw.razorRawTree.razor, backed by IRawTreeService) lazily loads each container level on expand (unlike /uns, which eager-loads). Each node has a right-click context menu and a fallback (the reusable ContextMenu component):

Node Actions
Cluster New folder · New driver
Folder New folder · New driver · Rename · Delete
Driver Configure · New device · Enable/Disable · Rename · Delete
Device Configure (endpoint + Test connect) · New tag-group · Add tags ▸ · Delete
TagGroup New group · Add tags ▸ · Rename · Delete
Tag Edit · Delete

Deletes are blocked while a node has children (or, for a raw tag, while a UnsTagReference points at it — the error names the referencing equipment). Renames return non-blocking warnings: renaming a node changes the RawPath of every historized / UNS-referenced tag beneath it (its historian tagname / UNS projection path moves).

Endpoint → DeviceConfig (the channel/device split)

v3 moves the connection endpoint off the driver and onto the Device (DeviceConfig), mirroring Kepware's channel/device split. The driver's DriverConfig holds protocol/channel settings; the device's DeviceConfig holds host/port/endpoint. At deploy/probe time DriverDeviceConfigMerger merges the device's endpoint up into the driver config, and Test connect (inside the Device modal) probes that merged config. Driver forms no longer serialize the endpoint keys, so DeviceConfig is the single source of truth.

Authoring tags

Add tags ▸ on a Device or TagGroup offers:

  • Manual entry — a grid of Name / DataType / AccessLevel / WriteIdempotent / PollGroup + the driver-typed TagConfig editor per row.
  • Import/Export CSV — a staged flow (upload → parse → review grid with per-row verdicts → commit) over an RFC-4180 parser. Columns: the common set (Name, TagGroupPath, DataType, AccessLevel, WriteIdempotent, PollGroup, IsHistorized, HistorianTagname, IsArray, ArrayLength, Alarm.*, TagConfigJson) plus the per-driver typed columns (e.g. Modbus Region, Address, ModbusDataType, ByteOrder, …). TagGroupPath auto-creates nested groups; typed columns win over TagConfigJson on conflict (flagged in the review grid). Import is all-or-nothing — any invalid row blocks the whole commit. Export round-trips the same shape.
  • Browse device… — enabled per the two-tier resolution (bespoke IDriverBrowser → universal DiscoveryDriverBrowser when the driver's ITagDiscovery.SupportsOnlineDiscovery is true → grayed out otherwise). Multi-selected leaves become raw Tag rows under the target, with the browsed reference written into the driver-typed TagConfig address field (nodeId / tagPath / …), never an identity key. An opt-in "create matching tag-groups" toggle mirrors the browse folder nesting. The browser dials a real ephemeral driver against the merged Driver+Device config.

The Calculation driver

Signal-level calculated tags are ordinary raw tags bound to a Calculation driver (DriverTypeNames.Calculation), computed by C# scripts that read other tags' live values via ctx.GetTag("<RawPath>"). See docs/plans/2026-07-15-calculation-driver-mini-design.md for the full design. Highlights:

  • One auto-created default Engine device; per-tag TagConfig is { "scriptId": "…", "changeTriggered": true, "timerIntervalMs": 5000 }.
  • The host feeds dependency values via the new IDependencyConsumer capability + a DependencyConsumerMuxAdapter on the per-node dependency mux; calc-of-calc chains work because a calc output re-enters the mux. The driver rebuilds its tag/dependency table on every redeploy (ReinitializeAsync) and re-registers its refs after the delta applies.
  • Deploy gates (DraftValidator): scriptId existence, and a hard cycle gate (DependencyGraph.DetectCycles over calc→calc edges; cross-driver refs are terminal) that rejects an A→B→A oscillation loop naming the members. Compile is not hard-gated (VirtualTag parity); a runtime compile/throw/timeout lands as Bad quality + a script-logs entry.

Retirement note

The routed /clusters/{id}/drivers driver-authoring flow (DriverTypePicker, DriverEditRouter, the 8 per-type *DriverPage shells, and the per-cluster Drivers list) is retired — authoring lives in /raw. The extracted driver/device form bodies live on inside the /raw modals. The DriverType dispatch maps (TagConfigEditorMap, TagConfigValidator, EquipmentTagConfigInspector) are keyed off the single-source-of-truth DriverTypeNames constants (fixing the historical TwinCat/Focas drift).