docs(m9): mark M9 delivered + sync TemplateEngine/TreeView/DataConnection/schema-library/CLI docs

- 2026-06-15-stillpending-completion-design.md: M9 section marked DELIVERED with per-feature
  summary and deferrals (folder drag-drop, unified outbox page).
- stillpending.md: T22–T26/T28/T30–T32 + CLI cached-call marked [DELIVERED M9]; permanent
  deferrals (folder drag-drop, unified outbox page) retained as [PERM].
- Component-TemplateEngine.md: TemplateFolder SortOrder + ReorderTemplateFolderCommand;
  Expression-trigger analysisKind (Advisory/Strict) on Alarm + Script; Script parameter
  JSON Schema / lib: ref note; Inheritance Resolve authoring section (GetResolvedTemplateMembersCommand /
  TemplateInheritanceResolver / staleness banner); updated Responsibilities.
- Component-TreeView.md: T22 search box wired note; T23 folder sibling reorder + root context
  menu note; drag-drop permanently deferred clarified in V7 worked example.
- Component-CentralUI.md: template tree search + inherited-members panel (T26 staleness banner)
  added to Template Authoring; drag-drop permanently deferred note; Schema Library page (T32)
  added as new subsection; ParameterValueForm + Monaco hover (T30/T31) noted; connection
  live-status (T25) + move-connection (T24) added to Site & Data Connection Management.
- Component-ConfigurationDatabase.md: SharedSchema entity + ISharedSchemaRepository row added.
- Component-CLI.md: --trigger-kind option added to template alarm add/update and script add/update.
- src/ZB.MOM.WW.ScadaBridge.CLI/README.md: --trigger-kind option added to template alarm
  add/update and template script add/update command tables (already had cached-call group).
This commit is contained in:
Joseph Doherty
2026-06-18 13:39:33 -04:00
parent af2d4037ce
commit ba335519f4
8 changed files with 78 additions and 20 deletions
+4 -4
View File
@@ -65,11 +65,11 @@ scadabridge template delete --id <id>
scadabridge template attribute add --template-id <id> --name <name> --data-type <type> [--value <value>] [--description <desc>] [--data-source <ref>] [--locked]
scadabridge template attribute update --id <id> [--name <name>] [--data-type <type>] [--value <value>] [--description <desc>] [--data-source <ref>] [--locked]
scadabridge template attribute delete --id <id>
scadabridge template alarm add --template-id <id> --name <name> --trigger-type <type> --priority <n> [--description <desc>] [--trigger-config <json>] [--locked]
scadabridge template alarm update --id <id> --name <name> --trigger-type <type> --priority <n> [--description <desc>] [--trigger-config <json>] [--locked]
scadabridge template alarm add --template-id <id> --name <name> --trigger-type <type> --priority <n> [--description <desc>] [--trigger-config <json>] [--locked] [--trigger-kind advisory|strict]
scadabridge template alarm update --id <id> --name <name> --trigger-type <type> --priority <n> [--description <desc>] [--trigger-config <json>] [--locked] [--trigger-kind advisory|strict]
scadabridge template alarm delete --id <id>
scadabridge template script add --template-id <id> --name <name> --code <code> --trigger-type <type> [--trigger-config <json>] [--locked] [--parameters <json>] [--return-def <json>]
scadabridge template script update --id <id> --name <name> --code <code> --trigger-type <type> [--trigger-config <json>] [--locked] [--parameters <json>] [--return-def <json>]
scadabridge template script add --template-id <id> --name <name> --code <code> --trigger-type <type> [--trigger-config <json>] [--locked] [--parameters <json>] [--return-def <json>] [--trigger-kind advisory|strict]
scadabridge template script update --id <id> --name <name> --code <code> --trigger-type <type> [--trigger-config <json>] [--locked] [--parameters <json>] [--return-def <json>] [--trigger-kind advisory|strict]
scadabridge template script delete --id <id>
scadabridge template composition add --template-id <id> --instance-name <name> --composed-template-id <id>
scadabridge template composition delete --id <id>
+10 -1
View File
@@ -39,7 +39,9 @@ Central cluster only. Sites have no user interface.
### Template Authoring (Design Role)
- The `/design/templates` page uses a **split-pane layout**: a folder/template tree sidebar on the left and the editor on the right.
- The tree shows nested `TemplateFolder` entities with their templates underneath; composition children render inline as leaf nodes beneath their owning template (right-click "Open composed template" reveals and selects the target).
- **Per-kind context menus** on folder, template, and composition nodes expose the relevant operations (new folder, new template, rename, move, delete, move to folder). Native HTML5 **drag-drop** reorganizes templates between folders and reparents folders, with cycle detection rejected via toast on drop. Tree expansion state persists in `sessionStorage`, and deep links (`/design/templates/{id}`) reveal and select the target node.
- **Per-kind context menus** on folder, template, and composition nodes expose the relevant operations (new folder, new template, rename, move, delete, move to folder). Root-level folders also carry a context menu. **Folder sibling reorder** is done via **Move up / Move down** menu items (M9/T23, `ReorderTemplateFolderCommand`); drag-drop is **not implemented** (permanently deferred). Tree expansion state persists in `sessionStorage`, and deep links (`/design/templates/{id}`) reveal and select the target node.
- A **search box** above the tree (M9/T22) filters visible nodes by substring match; it is wired to `TemplateFolderTree.Filter`.
- The `TemplateEdit` page shows a read-only **"Inherited members" panel** (M9/T26) listing the full multi-level effective inherited member set (origin, locked state, merged HiLo config) resolved by `GetResolvedTemplateMembersCommand` / `TemplateInheritanceResolver`. A **"Base changed" banner** appears when the resolver's staleness summary indicates the parent template has changed since the child was last edited. This is read-only — no "update-derived" mutation is exposed; the child is redeployed through the normal flow to pick up base changes.
- Create, edit, and delete templates.
- **Template deletion** is blocked if any instances or child templates reference the template. The UI displays the references preventing deletion.
- Manage template hierarchy (inheritance) — visual tree of parent/child relationships.
@@ -83,9 +85,16 @@ Central cluster only. Sites have no user interface.
- **Data connection form**: "Primary Endpoint Configuration" (required JSON text area) and optional "Backup Endpoint Configuration" (collapsible section, hidden by default, revealed via "Add Backup Endpoint" button; "Remove Backup" button when editing an existing backup). "Failover Retry Count" numeric input (default 3, min 1, max 20) is visible only when a backup endpoint is configured.
- **Verify endpoint** (OPC UA): the OPC UA endpoint editor (in the data connection form) carries a **"Verify endpoint"** button that asks the target site to probe the configured endpoint — a temporary, short-lived connect against the live (or edited-but-unsaved) config. The result reports success or a typed failure kind (e.g. unreachable, untrusted certificate, server error). When the failure is an **untrusted server certificate**, the probe captures the cert (Subject / Issuer / Thumbprint / validity / DER) and the editor shows a detail panel with a **"Trust certificate"** button. The probe itself **never trusts** the cert — trusting is an explicit, Admin-gated action (see Server certificate management). After a Trust, Verify re-runs automatically and should then succeed.
- **Data connection list page**: Shows Primary Config and Backup Config columns. Active Endpoint column populated from health reports.
- **Connection live-status indicators** (M9/T25): the design DataConnections page polls `ConnectionHealthQueryService` (~10 s interval) which reads `SiteHealthReport.DataConnectionStatuses` already delivered by Health Monitoring; no new site-side code. Each connection row displays its current status badge (Connected / Disconnected / Unknown).
- **Move data connection between sites** (M9/T24): a **Move…** action on a data connection opens `MoveDataConnectionDialog`. The underlying `MoveDataConnectionCommand` guards against: target site does not exist, name collision at the target, any `InstanceConnectionBinding` referencing the connection, and any `InstanceNativeAlarmSourceOverride.ConnectionNameOverride` reference at the source site's templates. Blocked connections display the blocking reason.
- **Server certificate management** (`/design/connections/{id}/certificates`, Admin role): a per-connection page that lists the contents of the site's OPC UA trusted-peer and rejected certificate stores (Subject / Issuer / Thumbprint / validity / Trusted-or-Rejected status) with a **Remove** action. The page makes clear the store is **node-wide for the site** (shared by every site node), not per data connection — trusting or removing a certificate affects all OPC UA connections at that site. Trust and Remove are central commands relayed to **both** site nodes so the node-local PKI stores stay consistent across failover (see Component-SiteRuntime.md, Component-DataConnectionLayer.md).
- The site detail page exposes a new **"Audit feed"** tab that hosts the Audit Log page pre-filtered to `Site = <site>` — an in-context view of every operational audit event for that site.
### Schema Library (Design Role)
- A **Schema Library** page (M9/T32, under the Design nav group) provides CRUD for `SharedSchema` entries — named, reusable JSON Schema definitions. Each entry has a name (globally unique) and a JSON Schema body.
- Schemas are referenced from parameter/return definitions using `{"$ref":"lib:Name"}`. The resolver expands refs at deploy-time, design-time validation, and inbound-API runtime (cycle and depth guards enforced; a dangling ref blocks deployment).
- The Central UI also uses the `ParameterValueForm` (M9/T30) to render `object` and `list` parameters as typed nested inputs when a JSON Schema (or `$ref`) is attached to the parameter definition. Monaco-based JSON fields gain hover and completion (M9/T31) from the attached schema.
### Inbound API Management (Admin Role for keys, Design Role for methods)
- Manage inbound API keys (create, enable / disable, delete) and define API methods (name, parameters, return values, approved keys, implementation script).
- The API key detail page includes a **"Recent calls"** link that opens the Audit Log page pre-filtered to `Actor = <key name>` and `Channel = ApiInbound` — surfacing the key's recent inbound-call audit history.
@@ -43,6 +43,7 @@ The configuration database stores all central system data, organized by domain a
### Shared Scripts
- **Shared Scripts**: System-wide reusable script definitions (name, C# source code, parameter definitions, return value definitions).
- **Shared Schemas** (M9/T32): Named, reusable JSON Schema definitions (`SharedSchema` entity — `Id`, `Name` (unique), `Schema` `nvarchar(max)`). Referenced from parameter/return definitions using `{"$ref":"lib:Name"}`; the `$ref` resolver (cycle/depth-guarded) expands them at deploy-time, design-time validation, and inbound-API runtime. CRUD is exposed via `ISharedSchemaRepository` (Commons) and the Central UI Schema Library page.
### Sites & Data Connections
- **Sites**: Site definitions (name, identifier, description, NodeAAddress, NodeBAddress, GrpcNodeAAddress, GrpcNodeBAddress).
@@ -155,6 +156,7 @@ Repository interfaces are defined in **Commons** alongside the POCO entity class
| `ISiteCallAuditRepository` | Site Call Audit | The `SiteCalls` table — insert-if-not-exists ingest, upsert-on-newer-status, KPI aggregate queries, and bulk delete of terminal rows used by the daily purge job |
| `IHealthMonitoringRepository` | Health Monitoring | (Minimal — health data is in-memory; repository needed only if connectivity history is persisted in the future) |
| `ICentralUiRepository` | Central UI | Read-oriented queries spanning multiple domain areas for display purposes |
| `ISharedSchemaRepository` | Template Engine, Inbound API, Central UI | `SharedSchema` CRUD (M9/T32) — named, reusable JSON Schema definitions referenced via `{"$ref":"lib:Name"}` in parameter/return definitions |
Each implementation class uses the DbContext internally and works with the POCO entity classes from Commons. Consuming components depend only on Commons (for interfaces and entities) — they never reference this component or EF Core directly. The DI container in the Host wires the implementations to the interfaces.
+19 -2
View File
@@ -24,6 +24,7 @@ Central cluster only. Sites receive flattened output and have no awareness of te
- Provide on-demand validation for Design users during template authoring.
- Enforce template deletion constraints — templates cannot be deleted if any instances or child templates reference them.
- Organize templates into nested folders (`TemplateFolder` entity) and validate folder hierarchy invariants (acyclicity, sibling uniqueness, non-empty-on-delete).
- Resolve and surface the full multi-level effective inherited member set for a template (read-only authoring view), including origin annotation, locked state, merged trigger config, and a staleness summary comparing the child's effective member set against the base.
## Key Entities
@@ -42,6 +43,7 @@ Central cluster only. Sites receive flattened output and have no awareness of te
- Folders carry **no semantic meaning** for template resolution, flattening, validation, or inheritance — they exist purely for UI organization.
- Folder deletion is blocked if the folder contains any subfolders or templates.
- The folder graph is enforced acyclic on move (a folder cannot become its own descendant).
- Each folder carries an integer **`SortOrder`** (assigned distinctly on create; default 0) used for sibling reorder. `ReorderTemplateFolderCommand` swaps the `SortOrder` of two adjacent siblings; the UI exposes Move-up / Move-down menu items in the folder context menu. Drag-drop reorganization is deliberately deferred.
### Attribute
- Name, Value, Data Type (Boolean, Integer, Float, String), Lock Flag, Description.
@@ -50,8 +52,9 @@ Central cluster only. Sites receive flattened output and have no awareness of te
### Alarm
- Name, Description, Priority Level (01000), Lock Flag.
- Trigger Definition: Value Match, Range Violation, or Rate of Change.
- Trigger Definition: Value Match, Range Violation, Rate of Change, HiLo, or Expression.
- Optional On-Trigger Script reference.
- Expression triggers carry an **`analysisKind`** field (`Advisory` | `Strict`) in the trigger-config JSON. `Advisory` (default) keeps the current behavior where a blank expression is a non-blocking advisory finding. `Strict` escalates a blank expression to a **deploy-blocking error**. The `analysisKind` is set per-trigger in the UI or via the CLI `--trigger-kind` option.
### Native Alarm Source (`TemplateNativeAlarmSource`)
- A read-only binding that mirrors **native alarms** raised by an upstream system — OPC UA Alarms & Conditions or the MxAccess Gateway — rather than alarms evaluated by the Site Runtime from attribute values.
@@ -63,8 +66,9 @@ Central cluster only. Sites receive flattened output and have no awareness of te
### Script (Template-Level)
- Name, Lock Flag, C# source code.
- Trigger configuration: Interval, Value Change, Conditional, Expression, or invoked by alarm/other script. Conditional and Expression triggers also carry a fire mode — **OnTrue** (fire as the condition becomes true) or **WhileTrue** (re-fire on a timer while it stays true).
- Expression triggers carry an **`analysisKind`** field (`Advisory` | `Strict`) in the trigger-config JSON, with the same semantics as alarm Expression triggers above.
- Optional minimum time between runs — also the re-fire cadence for a WhileTrue trigger.
- **Parameter Definition** *(optional)*: Defines input parameters (name and data type per parameter). Scripts without parameters accept no arguments.
- **Parameter Definition** *(optional)*: Defines input parameters (name and data type per parameter). Scripts without parameters accept no arguments. Parameters that take a JSON `object` or `list` value may carry a JSON Schema definition (plain schema or `{"$ref":"lib:Name"}` pointing to the schema library) used by the Central UI value-entry form and Monaco hover/completion.
- **Return Value Definition** *(optional)*: Defines the structure of the script's return value (field names and data types). Supports single objects and lists of objects. Scripts without a return definition return void.
### Instance
@@ -139,6 +143,19 @@ The `FlatteningService` resolves native alarm sources alongside alarms, emitting
- **Composition**: a composed module's sources are path-qualified to the canonical name `[ModuleInstanceName].[Name]`, subject to the same naming-collision checks as other members. Because `SourceReference` is a raw connection address (not an attribute path), composition performs **no attribute-reference rewriting** on it.
- **Instance overrides**: `InstanceNativeAlarmSourceOverride` applies its non-null fields (`ConnectionNameOverride`, `SourceReferenceOverride`, `ConditionFilterOverride`) over the inherited/composed result and sets `Source = Override`.
## Inheritance Resolve — Authoring View
`GetResolvedTemplateMembersCommand` (handled by `TemplateInheritanceResolver`) returns the **full multi-level effective inherited member set** for a template — the set of members a child template would see if it were the leaf of the inheritance chain. This is a **read-only** query; it does not mutate any template or instance, and it has no effect on the deploy pipeline.
The resolved result carries, per member:
- **Canonical name** and **member kind** (Attribute, Alarm, Script, NativeAlarmSource).
- **Origin** — which template in the chain (by name and ID) first defined the member.
- **Locked** — whether the member is locked at the level it was last defined, preventing further override.
- **Merged HiLo trigger config** — for alarms with a HiLo trigger, the merged setpoint object (base + any intermediate override).
- **Staleness summary** — a flag indicating whether the inherited effective value differs from what the requesting template's own copy has stored (i.e., the base has changed since the child was last edited). This drives the read-only "base changed" banner in `TemplateEdit`.
The resolver is consumed only by the Central UI `TemplateEdit` page. It is not part of the flattening pipeline and is not called during deployment.
## Diff Calculation
The Template Engine can compare:
+5 -1
View File
@@ -417,7 +417,7 @@ Three semantic badge roles. The meta slot holds **at most two** badges per row.
### V7 — Worked Example: `/design/templates`
**Page model**: the templates page is a **tree browser only**. Selecting a template in the tree navigates to a dedicated edit page (`/design/templates/{id}`); creating a template navigates to `/design/templates/create`. No split-pane editor. Reorganization (move folder, move template) happens exclusively through the **right-click context menu** with modal dialog pickers — there is no drag-and-drop on this page.
**Page model**: the templates page is a **tree browser only**. Selecting a template in the tree navigates to a dedicated edit page (`/design/templates/{id}`); creating a template navigates to `/design/templates/create`. No split-pane editor. Reorganization (move folder, move template) happens exclusively through the **right-click context menu** with modal dialog pickers — there is **no drag-and-drop** on this page (permanently deferred; M9/T23 delivers menu-based folder sibling reorder instead).
Three node kinds; concrete recipes following V1V6.
@@ -427,6 +427,10 @@ Three node kinds; concrete recipes following V1V6.
| Template | `bi-file-earmark-text` | same (templates with compositions still use the same glyph — chevron carries state) | `$Name` (semibold when has compositions, regular otherwise) | — | none |
| Composition | `bi-arrow-return-right` | n/a (leaf, no expanded state) | composition instance name (regular weight) | — | none |
**Template tree search (M9/T22):** A search box above the tree is wired to `TemplateFolderTree.Filter`. Typing a term dims non-matching nodes (case-insensitive substring against folder/template name) while preserving tree shape and expansion state.
**Folder sibling reorder (M9/T23):** The folder context menu exposes **Move up** / **Move down** items that invoke `ReorderTemplateFolderCommand` to swap `SortOrder` values for adjacent siblings. Root-level folders also have a context menu (previously only nested folders did). Drag-drop reorganization is **not implemented** — it is permanently deferred; the menu is the intended interaction model.
**`NodeContent` fragment** for the templates page (replaces the current `RenderNodeLabel` in `Templates.razor`):
```razor