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:
+11
-29
@@ -79,7 +79,7 @@ initialise (the AdminUI form drops them on the next save). Pinned by
|
||||
|
||||
## Capability surface
|
||||
|
||||
`OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWritable, ISubscribable, IHostConnectivityProbe, IAlarmSource, IHistoryProvider`
|
||||
`OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWritable, ISubscribable, IHostConnectivityProbe, IAlarmSource`
|
||||
(`src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/OpcUaClientDriver.cs:31`).
|
||||
|
||||
| Capability | Path | Notes |
|
||||
@@ -90,7 +90,6 @@ initialise (the AdminUI form drops them on the next save). Pinned by
|
||||
| `ISubscribable` | native OPC UA subscriptions / monitored items | The remote server pushes data changes |
|
||||
| `IHostConnectivityProbe` | session keep-alive | Host key is the endpoint URL actually connected to after the failover sweep |
|
||||
| `IAlarmSource` | `SubscribeAlarmsAsync` (EventFilter) + `AcknowledgeAsync` | Subscribes to upstream alarm/condition events and forwards acks |
|
||||
| `IHistoryProvider` | `ReadRawAsync` / `ReadProcessedAsync` / `ReadAtTimeAsync` → `Session.HistoryReadAsync` | **Unique to this driver** — passthrough history read against the upstream server |
|
||||
|
||||
> **Native alarms (#619).** An alarm raw tag is `TagConfig` `{ "nodeId": "<upstream SourceNode>", "alarm": {...} }`.
|
||||
> The host subscribes with RawPaths and routes on (driver, RawPath), so the driver matches each
|
||||
@@ -112,35 +111,18 @@ initialise (the AdminUI form drops them on the next save). Pinned by
|
||||
|
||||
> This driver does **not** implement `IRediscoverable` — there is no
|
||||
> push-driven rediscovery signal from a remote OPC UA server in this driver.
|
||||
> `IHistoryProvider` is implemented by no other driver; history reads for every
|
||||
> other source route server-side through `IHistoryRouter`.
|
||||
|
||||
### History passthrough
|
||||
### History: not supported (Gitea #655)
|
||||
|
||||
`IHistoryProvider` forwards `HistoryRead` to the upstream server's own historian.
|
||||
Raw, processed (the full `HistoryAggregateType` set — Average / Minimum / Maximum / Total / Count plus
|
||||
Interpolative, TimeAverage, Start, End, Delta, Range, Min/Max ActualTime, Start/EndBound, DeltaBounds,
|
||||
DurationGood/Bad, PercentGood/Bad, WorstQuality, NumberOfTransitions, AnnotationCount and the
|
||||
population StandardDeviation/Variance — each mapped to its OPC UA Part 13 standard aggregate NodeId; #285),
|
||||
and at-time reads are supported; each
|
||||
returned `DataValue` keeps its upstream `StatusCode` and timestamps verbatim.
|
||||
**The per-node `HistoryReadResult.StatusCode` is honoured (#649).** The driver never reads a Bad node status as
|
||||
empty data: `BadAggregateNotSupported` on a Processed read throws `HistoryAggregateNotSupportedException`, and any
|
||||
other Bad status (`BadUserAccessDenied`, `BadNodeIdUnknown`, `BadHistoryOperationUnsupported`, ...) throws
|
||||
`HistoryReadStatusException` carrying the code (the constructor rejects non-Bad codes). `Good`, `GoodNoData`,
|
||||
`GoodMoreData` (continuation point) and `Uncertain` are unchanged, and a node-level `BadNoData` is still an empty
|
||||
result. The status is matched on its code bits, so an info/overflow sub-status does not dodge the match.
|
||||
|
||||
⚠️ **This is currently latent.** The OtOpcUa server does not dispatch HistoryRead to a driver's `IHistoryProvider`
|
||||
(it serves only the server-wide `IHistorianDataSource`, see `docs/Historian.md`), so no OPC UA client of OtOpcUa
|
||||
sees this driver's history results or its refusals today. The server's HistoryRead arms (`ServeNodeAsync`,
|
||||
`ServeRawPagedAsync`) do map a `HistoryReadStatusException` thrown by an `IHistorianDataSource` to that node's
|
||||
status verbatim, and the driver/wire tests exercise that path; if driver history is ever routed to the server,
|
||||
refusals will reach clients correctly instead of as empty Good data. A routed deterministic refusal would also
|
||||
need excluding from the HistoryRead pipeline's retry/breaker handling.
|
||||
|
||||
Event-history (`ReadEventsAsync`) is left at the interface default — the
|
||||
interface doesn't yet carry the EventFilter surface needed to forward it.
|
||||
This driver does **not** proxy the upstream server's HistoryRead. The server serves
|
||||
HistoryRead only through the server-wide `IHistorianDataSource` (see `docs/Historian.md`);
|
||||
drivers carry no history capability, and the former `IHistoryProvider` passthrough
|
||||
(Raw / Processed / AtTime / Events) was never dispatched to and has been deleted.
|
||||
`isHistorized` on an OpcUaClient tag means OtOpcUa records the value into the historian
|
||||
from now on (numeric only), not that upstream history is forwarded. A tag without
|
||||
`isHistorized` has no HistoryRead access bit and answers `BadHistoryOperationUnsupported`.
|
||||
If upstream-history proxying is wanted later, it needs a new intent key, a dispatch path
|
||||
through `DriverHostActor`, and a continuation-point rework — see #655.
|
||||
|
||||
### Certificate trust
|
||||
|
||||
|
||||
Reference in New Issue
Block a user