diff --git a/archreview/plans/R2-06-serverhistorian-failfast-plan.md.tasks.json b/archreview/plans/R2-06-serverhistorian-failfast-plan.md.tasks.json index 19e36248..a4cdd1fa 100644 --- a/archreview/plans/R2-06-serverhistorian-failfast-plan.md.tasks.json +++ b/archreview/plans/R2-06-serverhistorian-failfast-plan.md.tasks.json @@ -73,7 +73,7 @@ { "id": "T10", "subject": "C-7: keep-populating decision + dead-Quality doc comments at GatewayHistorianValueWriter.cs:63, ContinuousHistorizationRecorder.GoodQuality, docs/Historian.md (cites 0.2.0 Contracts C-002); commit 3 (C-7)", - "status": "pending", + "status": "completed", "blockedBy": [] }, { diff --git a/docs/Historian.md b/docs/Historian.md index 183ef1b7..3549760b 100644 --- a/docs/Historian.md +++ b/docs/Historian.md @@ -102,6 +102,12 @@ write path) and the `ContinuousHistorization` section (driver-value capture). Al **same** gateway — but only `ServerHistorian` carries the connection (endpoint/key/TLS); the other two source it from there. +> **Continuous-historization value quality is server-stamped.** The gateway's `WriteLiveValues` SQL path +> **discards** the quality the recorder sends (a server-managed column; 0.2.0 Contracts C-002), so persisted +> quality is stamped gateway-side regardless of what the `ContinuousHistorizationRecorder` provides (it +> hardcodes OPC-DA Good, `192`). Capturing real per-node quality will not persist until the gateway honors +> the field — see `GatewayHistorianValueWriter`'s remarks (archreview 06/C-7). + --- ## Tag auto-provisioning (EnsureTags) diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/Recorder/GatewayHistorianValueWriter.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/Recorder/GatewayHistorianValueWriter.cs index ac3ccacb..c862c4a3 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/Recorder/GatewayHistorianValueWriter.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/Recorder/GatewayHistorianValueWriter.cs @@ -24,6 +24,17 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Recorder; /// A success ack OR a store-forward-queued ack maps to true: a value the gateway /// durably queued must not be re-drained. /// +/// +/// Quality is a documented-dead input on the WriteLiveValues SQL path +/// (HistorianGateway 0.2.0 Contracts C-002): HistorianLiveValue.Quality / +/// QualityDetail are server-managed columns — any value sent is accepted on the wire and +/// then silently discarded. We populate Quality anyway (see the mapping below): proto3 +/// transmits 0 when a field is left unset, and 0 is "Bad" in OPC-DA terms, so +/// not setting it would send a wrong quality to any future gateway version that +/// starts honoring the field — whereas the recorder's 192 ("Good") is the correct value. +/// Do NOT extend the recorder to capture real node quality expecting it to persist — it +/// will not, until the gateway honors the field. +/// /// public sealed class GatewayHistorianValueWriter : IHistorianValueWriter, IAsyncDisposable { @@ -60,6 +71,8 @@ public sealed class GatewayHistorianValueWriter : IHistorianValueWriter, IAsyncD var live = new HistorianLiveValue { NumericValue = value.Value, + // Documented-dead input on the WriteLiveValues SQL path (0.2.0 Contracts C-002 — + // server-managed, silently discarded). Populated deliberately; see the class . Quality = value.Quality, }; diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Historian/ContinuousHistorizationRecorder.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Historian/ContinuousHistorizationRecorder.cs index 8c589e6f..bbb4122a 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Historian/ContinuousHistorizationRecorder.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Historian/ContinuousHistorizationRecorder.cs @@ -41,7 +41,14 @@ namespace ZB.MOM.WW.OtOpcUa.Runtime.Historian; public sealed class ContinuousHistorizationRecorder : ReceiveActor, IWithTimers { /// OPC-DA quality byte recorded for mux-fanned values (the mux drops quality; driver-published - /// values are Good by the same convention the scripted-alarm host applies). + /// values are Good by the same convention the scripted-alarm host applies). + /// + /// Note (archreview 06/C-7): the historian gateway's WriteLiveValues SQL path discards + /// quality (server-managed column, 0.2.0 Contracts C-002 — see + /// GatewayHistorianValueWriter's remarks). This constant is therefore what a future + /// quality-honoring gateway path would receive; capturing real per-node quality here will + /// NOT persist until the gateway honors the field. + /// public const ushort GoodQuality = 192; private const string DrainTimerKey = "drain";