diff --git a/docs/Configuration.md b/docs/Configuration.md index 1a05b5d4..57c9f5e5 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -297,6 +297,28 @@ unset (⇒ `Dps`) until the rig is flipped with `Telemetry__Mode=Grpc` / `Teleme The Config DB itself (the EF Core `OtOpcUaConfigDbContext`, entities, draft/publish generations, `NodeAcl`, `LdapGroupRoleMapping`, migrations) is the durable home for the fleet's drivers, UNS hierarchy, ACLs, and audit log. For the **full schema** see [`docs/v2/config-db-schema.md`](v2/config-db-schema.md). This page does not duplicate it. +### Audit log (`ConfigAuditLog`) + +The cluster audit log lives in the Config DB and is written by `ActorAuditWriter`; the AdminUI reads it +per cluster at `/clusters/{id}/audit`. It has no `appsettings` section — it is on whenever an admin node +has a `ConfigDb` — but one schema property is worth recording here because it constrains how callers may +write to it: + +- **`EventId` gives cross-restart deduplication, and the DB enforces it.** Each row carries the + stable per-event `AuditEvent.EventId`, under a *filtered* unique index + (`UX_ConfigAuditLog_EventId`, `WHERE [EventId] IS NOT NULL` — so the nullable pre-v2 backfill rows + do not collide with each other). That is what makes an Akka actor safe to retry an apply against + the audit log: the batched writer flushing again after a process crash, a supervision restart, or + an at-least-once redelivery re-sends the same `EventId` and does **not** insert a duplicate row. + `CorrelationId` is the companion column — it joins a row to its originating request/workflow, so + the several rows of one logical operation group together. +- Consequently, **mint the `EventId` per event, never per write attempt.** An id generated inside + the retry path defeats the unique index rather than tripping it, and nothing downstream flags the + result — the log simply grows a second copy of a deployment's audit trail. Carry the id with the + command. + +For the full column list see [`docs/v2/config-db-schema.md`](v2/config-db-schema.md). + ### Galaxy / MxAccess driver config (`DriverConfig` JSON, not `appsettings`) The Galaxy/MxAccess connection settings are **not an `appsettings` section.** They are driver-instance options stored in the `DriverConfig` JSON column of the Config DB (edited via the Admin UI), bound to `GalaxyDriverOptions` (`src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Contracts/GalaxyDriverOptions.cs`, namespace `...Driver.Galaxy.Config`). It decomposes into nested records: diff --git a/docs/Telemetry.md b/docs/Telemetry.md index f873e8e6..d1364b46 100644 --- a/docs/Telemetry.md +++ b/docs/Telemetry.md @@ -97,6 +97,21 @@ single stream per (central, driver-node) pair carries all four, rather than one Proto field evolution is additive-only (never renumber/reuse a tag), locked by a contract test that reflects over the `oneof` cases. +### `driver-health` is keyed per driver instance, not per Akka member + +`DriverHealthChanged` carries no per-Akka-member identity — a snapshot names the driver instance it +describes, never the node that published it. `IDriverStatusSnapshotStore` therefore keys on the +driver instance, and the `/hosts` driver table renders **cluster-scoped** rows: one entry per +configured driver instance, grouped under its cluster, rather than one entry per (member, driver) +pair. A pair's two nodes running the same deployed driver contribute to the same row, with the most +recent snapshot winning. + +That is the correct granularity for what the channel reports — a driver instance's transport health +is a property of the instance's configuration and its device, not of which member happens to host +it — but it does mean the panel cannot answer "which node saw this?". Adding that would mean +widening the wire record, not changing the page. The one sub-instance breakdown the channel *does* +carry is per-host connectivity, below. + ### Per-host connectivity rides `driver-health` (Gitea #521) `DriverHealthChanged.HostStatuses` carries each driver's `IHostConnectivityProbe.GetHostStatuses()` diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/App.razor b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/App.razor index 07b3d1ac..1aea5f08 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/App.razor +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/App.razor @@ -16,6 +16,11 @@ + @* ASP.NET scoped-CSS bundle for this RCL's *.razor.css files (ContextMenu.razor.css and + anything added later). The RCL builds it on every compile, but nothing referenced it — + so the scoped rules were dead at runtime and the context menus rendered in-flow with + no position:fixed. It is a Reference-mode asset, hence the RCL's _content/ base path. *@ + diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/ContextMenu.razor.css b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/ContextMenu.razor.css index 7c8c9c7d..b42a8271 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/ContextMenu.razor.css +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/ContextMenu.razor.css @@ -1,5 +1,7 @@ /* Scoped styles for ContextMenu. Colours come from the shared ZB.MOM.WW.Theme - tokens (theme.css) so the menu tracks the app palette in light and dark. */ + tokens (theme.css) so the menu tracks the app palette. The kit is light-only — + Theme 0.3.1 ships no dark-scheme block, and neither does site.css or this file — + so there is no dark variant to track. */ .ctx-surface { display: inline; diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/css/site.css b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/css/site.css index 599c308e..8818a31c 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/css/site.css +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/css/site.css @@ -23,3 +23,47 @@ the kit's status tokens so they stay on-palette. */ .chip-alert { color: var(--bad); background: var(--bad-bg); border-color: var(--bad-border); } .chip-caution { color: var(--warn-ink); background: var(--warn-bg); border-color: var(--warn-border); } + +/* Button sizing — ZB.MOM.WW.Theme 0.3.1 ships no .btn rule at all, so Bootstrap's + hard-coded --bs-btn-font-size: 1rem applies against the theme's 0.9rem body and + every button reads oversized. Override the VARIABLES, never the box properties: + .btn-group relies on Bootstrap's own .btn rules for its seams, negative margins + and end-cap radii, and redefining padding/font-size wholesale breaks them. + UPSTREAMING CANDIDATE for a future Theme release — when the kit ships these, + delete this block rather than leaving a duplicate to drift. (Same gap, same fix, + same flag as the ignitionoee router's wwwroot/css/app.css.) */ +.btn { + --bs-btn-font-size: .85rem; + --bs-btn-padding-y: .35rem; + --bs-btn-padding-x: .8rem; + --bs-btn-line-height: 1.4; +} + +.btn-group-sm > .btn, +.btn-sm { + --bs-btn-font-size: .78rem; + --bs-btn-padding-y: .25rem; + --bs-btn-padding-x: .6rem; +} + +/* Red variant of the kit's .notice inline banner (theme.css styles .notice amber, + off the --warn-* tokens). Error banners across the AdminUI want the red tint, and + were reaching for it with an inline style="border-color:var(--alert)" — but no + --alert token exists in Theme 0.3.1, site.css or anywhere else, so that inline + rule resolved to nothing and the banner stayed amber. The kit's red family is the + --bad-* set (--bad / --bad-bg / --bad-border), the same tokens .chip-alert above + uses; combine as `class="panel notice notice-alert"`. */ +.notice-alert { + color: var(--bad); + background: var(--bad-bg); + border-color: var(--bad-border); +} + +/* Fixed-width expander column for the tree components (RawTree, AclScopeTree, and + friends). Their rows are d-flex, so a bare `width` on an expander button or its + empty-leaf placeholder is a suggestion the flex algorithm is free to shrink — + flex-basis is what actually holds the column, hence both properties here. */ +.tree-twist { + flex: 0 0 18px; + width: 18px; +}