docs(#655): history is server-side only; rewrite OPC-009 and stale driver-history references

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-08 23:36:25 -04:00
parent f7f7d948cd
commit 67059e479f
21 changed files with 72 additions and 97 deletions
+3 -3
View File
@@ -8,7 +8,7 @@ The system shall expose an OPC UA server endpoint that OPC UA clients can connec
## HLR-002: Multi-Driver Plug-In Model
The system shall support pluggable driver modules that bind to specific data sources. v2.0 ships seven drivers: Galaxy (AVEVA System Platform via MXAccess), Modbus TCP (including DL205 via `AddressFormat=DL205`), Allen-Bradley CIP (ControlLogix/CompactLogix), Allen-Bradley Legacy (SLC/MicroLogix via PCCC), Siemens S7, Beckhoff TwinCAT (ADS), FANUC FOCAS, and OPC UA Client (aggregation/gateway). Drivers implement only the capability interfaces (`IDriver`, `ITagDiscovery`, `IReadable`, `IWritable`, `ISubscribable`, `IAlarmSource`, `IHistoryProvider`, `IHostConnectivityProbe`, `IPerCallHostResolver`, `IRediscoverable`) defined in `ZB.MOM.WW.OtOpcUa.Core.Abstractions` that apply to their protocol. Multiple instances of the same driver type are supported; each instance binds to its own OPC UA namespace index.
The system shall support pluggable driver modules that bind to specific data sources. v2.0 ships seven drivers: Galaxy (AVEVA System Platform via MXAccess), Modbus TCP (including DL205 via `AddressFormat=DL205`), Allen-Bradley CIP (ControlLogix/CompactLogix), Allen-Bradley Legacy (SLC/MicroLogix via PCCC), Siemens S7, Beckhoff TwinCAT (ADS), FANUC FOCAS, and OPC UA Client (aggregation/gateway). Drivers implement only the capability interfaces (`IDriver`, `ITagDiscovery`, `IReadable`, `IWritable`, `ISubscribable`, `IAlarmSource`, `IHostConnectivityProbe`, `IPerCallHostResolver`, `IRediscoverable`) defined in `ZB.MOM.WW.OtOpcUa.Core.Abstractions` that apply to their protocol. Multiple instances of the same driver type are supported; each instance binds to its own OPC UA namespace index.
## HLR-003: Address Space Composition per Namespace
@@ -20,7 +20,7 @@ Each driver shall map its native data types to OPC UA built-in types via `Driver
## HLR-005: Live Data Access
For every data-path operation (read, write, subscribe notification, alarm event, history read, tag rediscovery, host connectivity probe), the system shall route the call through the capability interface owned by the target driver instance. Reads and subscriptions shall deliver a `DataValueSnapshot` carrying value, OPC UA `StatusCode`, and source timestamp regardless of the underlying protocol. Every async capability invocation at dispatch shall pass through `Core.Resilience.CapabilityInvoker`.
For every data-path operation (read, write, subscribe notification, alarm event, tag rediscovery, host connectivity probe), the system shall route the call through the capability interface owned by the target driver instance. Reads and subscriptions shall deliver a `DataValueSnapshot` carrying value, OPC UA `StatusCode`, and source timestamp regardless of the underlying protocol. Every async capability invocation at dispatch shall pass through `Core.Resilience.CapabilityInvoker`. History reads are not a driver capability: OPC UA HistoryRead is served by the server-wide `IHistorianDataSource`.
## HLR-006: Change Detection and Rediscovery
@@ -75,7 +75,7 @@ The Admin service shall expose a `/metrics` endpoint using OpenTelemetry → Pro
## HLR-018: Roslyn Analyzer OTOPCUA0001
All direct call sites to capability-interface methods (`IReadable.ReadAsync`, `IWritable.WriteAsync`, `ITagDiscovery.DiscoverAsync`, `ISubscribable.SubscribeAsync`, `IAlarmSource.SubscribeAlarmsAsync` / `AcknowledgeAsync`, `IHistoryProvider.*`, `IHostConnectivityProbe.*`) made outside `Core.Resilience.CapabilityInvoker` shall produce Roslyn diagnostic **OTOPCUA0001** at build time. The analyzer is shipped in `ZB.MOM.WW.OtOpcUa.Analyzers` and referenced by every project that could host a capability call, guaranteeing that resilience cannot be accidentally bypassed.
All direct call sites to capability-interface methods (`IReadable.ReadAsync`, `IWritable.WriteAsync`, `ITagDiscovery.DiscoverAsync`, `ISubscribable.SubscribeAsync`, `IAlarmSource.SubscribeAlarmsAsync` / `AcknowledgeAsync`, `IHostConnectivityProbe.*`) made outside `Core.Resilience.CapabilityInvoker` shall produce Roslyn diagnostic **OTOPCUA0001** at build time. The analyzer is shipped in `ZB.MOM.WW.OtOpcUa.Analyzers` and referenced by every project that could host a capability call, guaranteeing that resilience cannot be accidentally bypassed.
## Retired HLRs
+8 -6
View File
@@ -125,14 +125,16 @@ The server shall expose the OPC UA alarm and condition model backed by each driv
## OPC-009: Historical Access
The server shall surface OPC UA Historical Access (HA) via each driver's `IHistoryProvider` (where implemented).
The server shall surface OPC UA Historical Access (HA) through a single server-wide historian data source (`IHistorianDataSource`, backed by the HistorianGateway). Drivers carry no history capability (Gitea #655).
### Acceptance Criteria
- `HistoryRead` for `Raw`, `Processed`, `AtTime`, and `Events` dispatches through `CapabilityInvoker.InvokeAsync(DriverCapability.HistoryRead, …)`.
- Drivers implementing `IHistoryProvider` today: Galaxy (Wonderware Historian), OPC UA Client (proxy to remote historian).
- Drivers not implementing `IHistoryProvider` return `Bad_HistoryOperationUnsupported` for history requests on their nodes.
- History reads require the `Read` permission bit on the target node.
- `HistoryRead` for `Raw`, `Processed` (the Part 13 aggregates the backend serves), and `AtTime` over historized variable nodes, and for `Events` over alarm-owning equipment-folder event notifiers, is served by the registered `IHistorianDataSource`. It is not dispatched to a driver and does not pass through `CapabilityInvoker`.
- A variable is historized by `isHistorized` in its `TagConfig` (optionally `historianTagname`); such nodes carry the `HistoryRead` access bit and `Historizing = true`. A node without it answers `Bad_HistoryOperationUnsupported`.
- With `ServerHistorian:Enabled = false` (the `NullHistorianDataSource`), historized nodes return `GoodNoData`, never an error.
- A per-node backend failure surfaces as a Bad status for that node only (`Bad_Timeout` past the request deadline, `Bad_AggregateNotSupported` for an aggregate the backend cannot serve); it does not fail the batch.
- When `Security:Acl:Enabled` is true, each node requires the `HistoryRead` permission (`Bad_UserAccessDenied` otherwise). With the flag off, history reads are ungated for any admitted session.
- An OPC UA Client driver does not proxy its upstream server's history; `isHistorized` on its tags records the value into the historian.
---
@@ -250,7 +252,7 @@ The server shall expose standard OPC UA `Server` object nodes required by the sp
### Acceptance Criteria
- `ServerStatus` / `ServerState` / `CurrentTime` / `StartTime` populated and compliant with the OPC UA 1.05 spec.
- `ServerCapabilities` declares historical access capabilities for namespaces that have an `IHistoryProvider`-backed driver.
- `ServerCapabilities` declares historical access capabilities consistent with the server-wide historian data source.
- `ServerRedundancy.RedundancySupport` reflects the cluster's redundancy mode (`None` / `Warm` / `Hot`).
- `ServerRedundancy.ServerUriArray` lists both cluster members' `ApplicationUri` values.