docs: MES-reportable alarm flag — component docs, CLAUDE.md, register rows 121–122
Claude-Session: https://claude.ai/code/session_01Y5ef34iUUMjmwnMpL9xgmB
This commit is contained in:
@@ -68,8 +68,8 @@ 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] [--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 add --template-id <id> --name <name> --trigger-type <type> --priority <n> [--description <desc>] [--trigger-config <json>] [--locked] [--trigger-kind advisory|strict] [--mes-reportable]
|
||||
scadabridge template alarm update --id <id> --name <name> --trigger-type <type> --priority <n> [--description <desc>] [--trigger-config <json>] [--locked] [--trigger-kind advisory|strict] [--mes-reportable]
|
||||
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>] [--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]
|
||||
@@ -93,7 +93,7 @@ scadabridge instance set-overrides --id <id> --overrides <json>
|
||||
scadabridge instance attribute-override delete --instance-id <id> --attribute <canonical-name>
|
||||
scadabridge instance native-alarm-source list --instance-id <id>
|
||||
scadabridge instance import-overrides --id <id> --file <path.csv>
|
||||
scadabridge instance alarm-override set --instance-id <id> --alarm <name> [--trigger-config <json>] [--priority <n>]
|
||||
scadabridge instance alarm-override set --instance-id <id> --alarm <name> [--trigger-config <json>] [--priority <n>] [--mes-reportable true|false]
|
||||
scadabridge instance alarm-override delete --instance-id <id> --alarm <name>
|
||||
scadabridge instance alarm-override list --instance-id <id>
|
||||
scadabridge instance native-alarm-source set --instance-id <id> --source <name> [--connection <name>] [--source-ref <ref>] [--filter <expr>]
|
||||
|
||||
@@ -63,6 +63,7 @@ Central cluster only. Sites have no user interface.
|
||||
- Manage template hierarchy (inheritance) — visual tree of parent/child relationships.
|
||||
- Manage composition — add/remove feature module instances within templates. **Naming collision detection** provides immediate feedback if composed modules introduce duplicate attribute, alarm, or script names.
|
||||
- Define and edit attributes, alarms, and scripts on templates.
|
||||
- **`TemplateAlarmDialog`** carries an **"MES reportable"** checkbox bound to `TemplateAlarm.IsMesReportable` (default unchecked, 2026-09-11) alongside the existing Name/Trigger Type/Priority/Trigger Definition/Description/Lock fields.
|
||||
- **Member authoring dialogs are hosted, not page-embedded** (M10 residual, 2026-08-01): the template editor's Attribute, Alarm, Native Alarm Source, and Script forms all open through `IDialogService.ShowAsync`, so the single `DialogHost` in `MainLayout` owns the backdrop, focus trap, Escape, and focus restoration. Each form body is its own component beside the page (`TemplateAttributeDialog`, `TemplateAlarmDialog`, `TemplateNativeAlarmSourceDialog`, `TemplateScriptDialog`), following the `MoveDataConnectionDialog` pattern — the body renders validation and server errors **inline and stays open**, closing only on a successful save; persistence stays on the page (which owns `TemplateService`) behind an `OnSaveAsync` delegate.
|
||||
- **Native Alarms tab** (`TemplateEdit`): a tab alongside Attributes / Alarms / Scripts / Compositions that lists the template's **native alarm source bindings** — the OPC UA Alarms & Conditions / MxAccess Gateway sources whose alarm state the instance mirrors. Each binding carries Name, Connection, Source Reference, optional Condition Filter, Description, and a Lock flag. Add / edit / delete go through a **hosted dialog** (`TemplateNativeAlarmSourceDialog`):
|
||||
- **Name** — unique within the template (lock/inherit bookkeeping mirrors `TemplateAlarm`).
|
||||
@@ -149,6 +150,7 @@ Central cluster only. Sites have no user interface.
|
||||
- **Type column** — Variable rows display best-effort type info (data type friendly name, scalar/array value rank, writable flag) read from the server during browse.
|
||||
- Set instance-level attribute overrides (non-locked attributes only).
|
||||
- **Bulk override CSV import** (`InstanceConfigure`): a Blazor `InputFile` upload accepts a CSV of `AttributeName, Value, ElementType?` rows (`ElementType` only for `List` attributes). Each row is validated against the instance's flattened attribute schema (name exists + value type-compatible, reusing the existing override validation); the import is **all-or-nothing** — any per-row error aborts the whole upload with a per-row error summary and nothing is applied. On success the rows are upserted through the same ManagementActor add/update-override handlers used by the inline editor. The same import is available from the CLI (`instance import-overrides --file`, see Component-CLI.md).
|
||||
- **Alarm Overrides card** (`InstanceConfigure`) gained an **"MES reportable"** tri-state select (Inherit / Yes / No, 2026-09-11) per alarm row, bound to `InstanceAlarmOverride.IsMesReportableOverride` — Inherit maps to `null` (use the template value); the row is disabled for locked alarms, same as the existing Priority/Trigger override fields. `instance alarm-override set --mes-reportable true|false` (omit the flag to inherit) is the CLI equivalent.
|
||||
- **Native Alarm Source Overrides card** (`InstanceConfigure`): a card placed **after the Alarm Overrides card**, listing the template's native alarm sources for per-instance binding. Each row offers **inline override** of the three fields that typically vary per physical instance:
|
||||
- **Connection** — a dropdown (same alarm-capable filtering as the template editor).
|
||||
- **Source Reference** — the concrete native key for this instance.
|
||||
|
||||
@@ -358,6 +358,7 @@ A CI grep guard (`AuditLogAppendOnlyGuardTests`) fails the build on any `UPDATE`
|
||||
- Each migration is a versioned, incremental schema change.
|
||||
- New tables are introduced as their own migration — for example, the `Notifications` table for the Notification Outbox ships as a dedicated EF Core migration that creates the table, its `Type`/`Status` value conversions, and its dispatcher and KPI indexes.
|
||||
- The native alarm source tables ship in a dedicated `AddNativeAlarmSources` migration, parallel in shape to the existing `AddInstanceAlarmOverrides` migration: it creates `NativeAlarmSources` and `InstanceNativeAlarmSourceOverrides` with their columns, the `TemplateId` → `Templates` and `InstanceId` → `Instances` cascade-delete foreign keys, and the `(TemplateId, Name)` / `(InstanceId, SourceCanonicalName)` unique indexes.
|
||||
- **`20260911135501_AddAlarmMesReportable`** (2026-09-11) adds `TemplateAlarms.IsMesReportable` (`bit`, default `0`) and `InstanceAlarmOverrides.IsMesReportableOverride` (`bit` NULL, null = inherit the template value). No new tables, no index changes — see `docs/plans/2026-09-11-mes-reportable-alarm-flag-design.md`.
|
||||
- The initial `AuditLog` migration creates the monthly partition function `pf_AuditLog_Month` and partition scheme `ps_AuditLog_Month`, then creates the `AuditLog` table aligned to that scheme on `OccurredAtUtc`, along with the indexes listed under Database Schema. The migration also creates the `scadabridge_audit_writer` and `scadabridge_audit_purger` DB roles with the grants described in Database Roles. The ongoing **partition-maintenance job** that rolls the scheme forward each month (creating the next month's partition ahead of time) and switches out expired partitions is owned by the **Audit Log component** (`AuditLogPurgeActor` and its monthly roll-forward step), not by the Configuration Database component — this component is responsible only for the initial schema, roles, and any EF migrations against the table going forward.
|
||||
|
||||
### Development Environment
|
||||
|
||||
@@ -252,6 +252,8 @@ Inbound API scripts **cannot** call shared scripts directly — shared scripts a
|
||||
- `Route.To("instanceUniqueCode").WaitForAttribute("attributeName", targetValue, timeout)` — Wait, event-driven, until an attribute on a specific instance at any site reaches `targetValue` (value-equality only across the wire), bounded by `timeout`. Returns `true` if matched within the timeout, `false` if it timed out. **The wait is bounded by its own `timeout`, not the generic method-level timeout** — this is the one routed call that may legitimately outlive the method timeout (the site enforces `timeout` and returns `false` when it elapses). A client disconnect still cancels the wait. This is the deliberate exception to the rule below that routed calls inherit the method-level timeout (see "Routing Behavior"): a long event-driven wait is the explicit reason `timeout` governs here.
|
||||
|
||||
> **Reading alarm state via a routed script.** There is deliberately **no** `Route.To(...).GetAlarms(...)` verb. An inbound method that needs a machine's current alarms resolves the instance and calls a **site script** that uses the Site Runtime `Alarms.CurrentAsync()` accessor — `Route.To(code).Call("SimpleAlarmStatus", new { ... })`. Alarm state is per-instance and lives on the site's Instance Actor; keeping the read inside a site script means the filtering/shaping happens where the data is, and central stays a thin router. This is the shape the MES alarm-status endpoints use (`docs/plans/2026-06-30-mes-alarm-status-api.md` §5.1); see [Component-SiteRuntime.md](Component-SiteRuntime.md) → *Alarm State (read-only)* for the accessor.
|
||||
>
|
||||
> **Example — `AlarmStatus`.** The rig demo template `MesAlarmDemo`'s site script (`docker/seed-mes-alarm-demo.sh`) answers `Route.To(MachineCode).Call("AlarmStatus", new { AlarmFilter = new { FlaggedOnly, IncludeAcked, MinSeverity, MaxSeverity, NameFilter } })` with the legacy `AlarmStatusResponse { WasSuccessful, ErrorText, Alarms: AlarmInfo[] }`, setting `IsFlaggedForMES = a.IsMesReportable` per row for **computed** alarms (the template's `TemplateAlarm.IsMesReportable` flag, instance-overridable) and the **900–999 severity band** for **native** alarms, which carry no per-alarm `IsMesReportable` row. `SimpleAlarmStatus` is the same shape with `FlaggedOnly` hard-wired true.
|
||||
|
||||
#### Input/Output
|
||||
- **Input parameters** are available as defined in the method definition.
|
||||
|
||||
@@ -148,7 +148,7 @@ Used by the Template Engine deploy gate:
|
||||
```csharp
|
||||
var errors = RoslynScriptCompiler.Compile(code, typeof(ScriptCompileSurface));
|
||||
```
|
||||
This allows the compile to bind `Attributes["name"]`, `Notify.To("x").Send(...)`, `ExternalSystem.Call(...)`, `Alarms.CurrentAsync()`, and similar API calls against real types, catching undefined-symbol and type-mismatch errors before deployment.
|
||||
This allows the compile to bind `Attributes["name"]`, `Notify.To("x").Send(...)`, `ExternalSystem.Call(...)`, `Alarms.CurrentAsync()`, `Alarms.MesReportableAsync()` (2026-09-11), and similar API calls against real types, catching undefined-symbol and type-mismatch errors before deployment.
|
||||
|
||||
The surface also mirrors the **`Log` global** (step logs design `docs/plans/2026-09-09-flow-debugger-step-logs-design.md` D4) as the compile-only `ScriptCompileSurface.CompileLog` — `Debug` / `Info` / `Warn` / `Error(string message, Exception? exception = null)`, including the optional exception parameter, so `Log.Warn("…", ex)` binds at the design-time gate exactly as it does at the site. `Log` needs no `ScriptTrustPolicy` change for the same reason `Alarms` did not: the trust boundary is a deny-list over API roots, not an allow-list of context members.
|
||||
|
||||
|
||||
@@ -510,11 +510,13 @@ Available to all script runs and alarm on-trigger runs:
|
||||
|
||||
### Alarm State (read-only)
|
||||
- `Alarms.CurrentAsync()` — Returns a snapshot of the instance's **current** alarm conditions as `IReadOnlyList<ScriptAlarm>` (`Commons.Types.Scripts`), covering computed alarms and mirrored native (OPC UA A&C / MxAccess Gateway) conditions alike. Served by a **local Ask** (`GetAlarmSnapshotRequest`/`GetAlarmSnapshotResponse`) to the script's own Instance Actor — the same mechanism attribute reads use, never a cross-cluster hop.
|
||||
- `Alarms.MesReportableAsync()` — `CurrentAsync()` filtered to rows where `ScriptAlarm.IsMesReportable` is true (2026-09-11). Native alarms carry no per-alarm config row, so they never satisfy the filter — they still convey MES relevance via the 900–999 severity band (see `docs/plans/2026-06-30-mes-alarm-status-api.md`).
|
||||
- Available to **every** script, unlike the `Alarm` global, which exists only inside an on-trigger handler and describes the single alarm that fired. This accessor is what lets an inbound-API-routed `Call` script answer "what is in alarm on this machine right now?"; before it, native mirrored conditions were reachable only from the Debug View.
|
||||
- **Read-only by design** — native alarms are a read-only mirror of the source (no ack-back), so the accessor exposes no acknowledge/shelve operation.
|
||||
- **Not scope-prefixed**, unlike `Attributes`: alarm identity is not a scope-relative attribute name (computed alarms are keyed by configured name, native conditions by a source-supplied reference), so every scope sees the same list rather than a silently truncated one.
|
||||
- The snapshot is built by the same `BuildAlarmStatesSnapshot()` the Debug View uses, so it **includes placeholder rows** for configured-but-quiet native source bindings (`IsConfiguredPlaceholder`). Callers enumerating real alarms filter on `Active && !IsConfiguredPlaceholder`.
|
||||
- `ScriptAlarm` is a flat projection of the retained `AlarmStateChanged`: `Name`, `SourceReference`, `NativeSourceCanonicalName`, `Active`, `Acknowledged`, `Confirmed?`, `Shelved` (all shelve sub-states collapse to one boolean), `Suppressed`, `Severity` (0–1000), `Kind`, `Message`, `AlarmTypeName`, `Category`, `OperatorUser`, `OperatorComment`, `OriginalRaiseTime?`, `Timestamp`, `AckTime?`, `CurrentValue`, `LimitValue`, `IsConfiguredPlaceholder`. The `Condition` is the authority for active/acked/severity, so computed and native alarms read identically from one filter expression.
|
||||
- `ScriptAlarm` is a flat projection of the retained `AlarmStateChanged`: `Name`, `SourceReference`, `NativeSourceCanonicalName`, `Active`, `Acknowledged`, `Confirmed?`, `Shelved` (all shelve sub-states collapse to one boolean), `Suppressed`, `Severity` (0–1000), `Kind`, `Message`, `AlarmTypeName`, `Category`, `OperatorUser`, `OperatorComment`, `OriginalRaiseTime?`, `Timestamp`, `AckTime?`, `CurrentValue`, `LimitValue`, `IsConfiguredPlaceholder`, `IsMesReportable` (trailing, 2026-09-11 — always `false` on native rows). The `Condition` is the authority for active/acked/severity, so computed and native alarms read identically from one filter expression.
|
||||
- **`IsMesReportable` is stamped in one place.** `InstanceActor.BuildAlarmStatesSnapshot()` sets `AlarmStateChanged.IsMesReportable` from `ResolvedAlarm.IsMesReportable` on every row it produces — retained (fired/active) rows and the fallback/idle rows alike — so `Alarms.CurrentAsync()`/`MesReportableAsync()` and the Debug View snapshot always agree regardless of whether the alarm has fired. The flag is **not** carried on the gRPC stream or `DebugAlarmStateDto` (no proto change in this slice — deferred-work register row 122), so central-side surfaces (Debug View live stream, Alarm Summary) see `false` until that follow-up lands.
|
||||
- The type lives in Commons so the runtime accessor and the Script Analysis compile-only surface (`ScriptCompileSurface.CompileAlarmsAccessor`) project to the **same** type — a script binding at the design-time gate binds identically at the site. Parity is pinned by `CompileSurfaceParityTests`.
|
||||
- Design record: `docs/plans/2026-06-30-mes-alarm-status-api.md` §5.2.
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ breadcrumb.
|
||||
Override and lock rules apply per entity type at the following granularity:
|
||||
|
||||
- **Attributes**: Value and Description are overridable. Data Type is fixed by the defining level. `DataSourceReference` on a template attribute defines the **default** physical address for that attribute. Instances may override per attribute via `InstanceConnectionBinding.DataSourceReferenceOverride`; the override replaces the template default at flattening time. When the override is null (the default), the template value is used. Lock applies to the entire attribute (when locked, no fields can be overridden).
|
||||
- **Alarms**: Priority Level and Trigger Definition (thresholds/ranges/rates) are instance-overridable. Description and On-Trigger Script reference are **not** instance-overridable — `InstanceAlarmOverride` carries only `TriggerConfigurationOverride` and `PriorityLevelOverride`, so an instance cannot re-point an alarm's on-trigger script or reword its description (those are template-level authoring decisions). Name and Trigger Type (Value Match vs. Range vs. Rate of Change) are fixed. Lock applies to the entire alarm. `TriggerConfigurationOverride` is a **partial merge for `HiLo`** triggers — setpoints left unset in the override keep their inherited values — and a **whole-config replacement for every other trigger type**. (Recorded decision, arch-review 05: this narrower granularity is the implemented behavior; adding Description/OnTriggerScript override columns is future feature work, not spec debt.)
|
||||
- **Alarms**: Priority Level and Trigger Definition (thresholds/ranges/rates) are instance-overridable. Description and On-Trigger Script reference are **not** instance-overridable — `InstanceAlarmOverride` carries only `TriggerConfigurationOverride` and `PriorityLevelOverride`, so an instance cannot re-point an alarm's on-trigger script or reword its description (those are template-level authoring decisions). Name and Trigger Type (Value Match vs. Range vs. Rate of Change) are fixed. Lock applies to the entire alarm. `TriggerConfigurationOverride` is a **partial merge for `HiLo`** triggers — setpoints left unset in the override keep their inherited values — and a **whole-config replacement for every other trigger type**. (Recorded decision, arch-review 05: this narrower granularity is the implemented behavior; adding Description/OnTriggerScript override columns is future feature work, not spec debt.) `TemplateAlarm.IsMesReportable` (bool, default false, "MES reportable" checkbox — `template alarm add|update --mes-reportable`) is instance-overridable via the nullable `InstanceAlarmOverride.IsMesReportableOverride` (null = inherit the template value; `instance alarm-override set --mes-reportable true|false`, omitted = inherit). A locked alarm cannot take this override either, same as every other alarm field. Migration `20260911135501_AddAlarmMesReportable` (Config DB, 2026-09-11); flattening resolves `ResolvedAlarm.IsMesReportable = override ?? template` and ships it in the deployed config JSON — no site schema change. See `docs/plans/2026-09-11-mes-reportable-alarm-flag-design.md`.
|
||||
- **Native alarm sources**: An instance overrides a non-locked source via `InstanceNativeAlarmSourceOverride`, keyed by `SourceCanonicalName`. `ConnectionNameOverride`, `SourceReferenceOverride`, and `ConditionFilterOverride` are individually overridable — each is applied only when non-null; a null field **keeps the inherited value**. Name is fixed. Lock applies to the entire source.
|
||||
- **Scripts**: C# source code, Trigger configuration, minimum time between runs, and parameter/return definitions are overridable. Name is fixed. Lock applies to the entire script.
|
||||
- **Composed module members**: A composing template or child template can override non-locked members inside a composed module using the canonical path-qualified name.
|
||||
|
||||
Reference in New Issue
Block a user