Replace direct SQL queries against Historian Runtime database with the Wonderware Historian managed SDK (ArchestrA.HistorianAccess). Add HistoryServerCapabilities node, AggregateFunctions folder, continuation points, ReadAtTime interpolation, ReturnBounds, ReadModified rejection, HistoricalDataConfiguration per node, historical event access, and client-side StandardDeviation aggregate support. Remove screenshot tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
148 lines
10 KiB
Markdown
148 lines
10 KiB
Markdown
# Historian Implementation Gap Analysis
|
|
|
|
Comparison of the current LmxOpcUa server historian implementation against the OPC UA Part 11 Historical Access specification requirements.
|
|
|
|
## Current Implementation Summary
|
|
|
|
| Feature | Status |
|
|
|---------|--------|
|
|
| HistoryRead — ReadRawModifiedDetails | Implemented (raw only, no modified) |
|
|
| HistoryRead — ReadProcessedDetails | Implemented (7 aggregates) |
|
|
| Historizing attribute on nodes | Implemented |
|
|
| AccessLevel.HistoryRead on nodes | Implemented |
|
|
| Quality mapping (OPC DA → OPC UA) | Implemented |
|
|
| Historian SDK (aahClientManaged) | Implemented (replaced direct SQL) |
|
|
| Configurable enable/disable | Implemented |
|
|
| SDK packet timeout | Implemented |
|
|
| Max values per read | Implemented |
|
|
| HistoryServerCapabilities node | Implemented |
|
|
| AggregateFunctions folder | Implemented (7 functions) |
|
|
| Continuation points for history reads | Implemented |
|
|
|
|
## Gaps
|
|
|
|
### 1. ~~HistoryServerCapabilities Node (Required)~~ — RESOLVED
|
|
|
|
**Spec requirement:** All OPC UA servers supporting Historical Access SHALL include a `HistoryServerCapabilities` object under `ServerCapabilities`. This is mandatory, not optional.
|
|
|
|
**Current state:** Implemented. The server populates all `HistoryServerCapabilities` variables at startup via `LmxOpcUaServer.ConfigureHistoryCapabilities()`. All boolean capabilities are set, and `MaxReturnDataValues` reflects the configured limit.
|
|
|
|
**Required variables:**
|
|
|
|
| Variable | Expected Value | Priority |
|
|
|----------|---------------|----------|
|
|
| `AccessHistoryDataCapability` | `true` | High |
|
|
| `AccessHistoryEventsCapability` | `false` | High |
|
|
| `MaxReturnDataValues` | configurable (e.g., 10000) | High |
|
|
| `MaxReturnEventValues` | 0 | Medium |
|
|
| `InsertDataCapability` | `false` | Medium |
|
|
| `ReplaceDataCapability` | `false` | Medium |
|
|
| `UpdateDataCapability` | `false` | Medium |
|
|
| `DeleteRawCapability` | `false` | Medium |
|
|
| `DeleteAtTimeCapability` | `false` | Medium |
|
|
| `InsertAnnotationCapability` | `false` | Low |
|
|
| `InsertEventCapability` | `false` | Low |
|
|
| `ReplaceEventCapability` | `false` | Low |
|
|
| `UpdateEventCapability` | `false` | Low |
|
|
| `DeleteEventCapability` | `false` | Low |
|
|
| `ServerTimestampSupported` | `true` | Medium |
|
|
|
|
**Files to modify:** `LmxOpcUaServer.cs` or `LmxNodeManager.cs` — create and populate the `HistoryServerCapabilities` node in the server's address space during startup.
|
|
|
|
### 2. ~~AggregateFunctions Folder (Required)~~ — RESOLVED
|
|
|
|
**Spec requirement:** The `HistoryServerCapabilities` object SHALL contain an `AggregateFunctions` folder listing all supported aggregate functions as child nodes. Clients browse this folder to discover available aggregates.
|
|
|
|
**Current state:** Implemented. The `AggregateFunctions` folder under `HistoryServerCapabilities` is populated with references to all 7 supported aggregate function ObjectIds at startup.
|
|
|
|
**Required:** Create `AggregateFunctions` folder under `HistoryServerCapabilities` with references to the 7 supported aggregate ObjectIds:
|
|
- `AggregateFunction_Average`
|
|
- `AggregateFunction_Minimum`
|
|
- `AggregateFunction_Maximum`
|
|
- `AggregateFunction_Count`
|
|
- `AggregateFunction_Start`
|
|
- `AggregateFunction_End`
|
|
- `AggregateFunction_StandardDeviationPopulation`
|
|
|
|
**Priority:** Medium
|
|
|
|
### 3. ~~Continuation Points for History Reads (Required)~~ — RESOLVED
|
|
|
|
**Spec requirement:** When a HistoryRead result exceeds `MaxReturnDataValues` or the client's `NumValuesPerNode`, the server SHALL return a `ContinuationPoint` in the result. The client then issues follow-up HistoryRead calls with the continuation point to retrieve remaining data. The server must maintain state for active continuation points and release them when complete or on timeout.
|
|
|
|
**Current state:** Implemented. `HistoryContinuationPointManager` stores remaining data keyed by GUID. Both `HistoryReadRawModified` and `HistoryReadProcessed` return a `ContinuationPoint` when results exceed `NumValuesPerNode`. Follow-up requests with the continuation point resume from stored state. Points expire after 5 minutes. Invalid or expired points return `BadContinuationPointInvalid`.
|
|
|
|
**Priority:** High (resolved)
|
|
|
|
### 4. ~~ReadModified Support~~ — RESOLVED
|
|
|
|
**Spec requirement:** `ReadRawModifiedDetails` has an `IsReadModified` flag. When `true`, the server should return the original value before modification along with the modification info (who modified, when, what the original value was). This is part of audit trail / data integrity use cases.
|
|
|
|
**Current state:** Implemented. `HistoryReadRawModified` checks `details.IsReadModified` and returns `BadHistoryOperationUnsupported` when true, since the Wonderware Historian does not expose modification history.
|
|
|
|
### 5. ~~ReadAtTimeDetails~~ — RESOLVED
|
|
|
|
**Spec requirement:** `ReadAtTimeDetails` allows a client to request interpolated values at specific timestamps (not raw samples). The server interpolates between the two nearest raw values for each requested timestamp.
|
|
|
|
**Current state:** Implemented. `LmxNodeManager` overrides `HistoryReadAtTime`. `HistorianDataSource.ReadAtTimeAsync` uses the Historian SDK with `HistorianRetrievalMode.Interpolated` to query interpolated values at each requested timestamp.
|
|
|
|
### 6. ~~HistoryUpdate Service (Insert/Replace/Delete)~~ — RESOLVED (N/A)
|
|
|
|
**Spec requirement:** The HistoryUpdate service allows clients to insert new values, replace existing values, update (insert or replace), and delete historical data. Each capability is separately advertised via the `HistoryServerCapabilities` node.
|
|
|
|
**Current state:** Not applicable. The Historian is read-only. All write capability booleans (`InsertDataCapability`, `ReplaceDataCapability`, `UpdateDataCapability`, `DeleteRawCapability`, `DeleteAtTimeCapability`) are explicitly set to `false` in `ConfigureHistoryCapabilities()`. No `HistoryUpdate` override exists, which is correct.
|
|
|
|
### 7. ~~HistoryReadEventDetails (Historical Events)~~ — RESOLVED
|
|
|
|
**Spec requirement:** Servers supporting historical event access implement `HistoryReadEventDetails` to retrieve past event notifications (e.g., alarm history).
|
|
|
|
**Current state:** Implemented. `LmxNodeManager` overrides `HistoryReadEvents`. `HistorianDataSource.ReadEventsAsync` uses the Historian SDK with a separate `HistorianConnectionType.Event` connection and `EventQuery` to retrieve historical alarm/event records. Events are mapped to OPC UA `HistoryEventFieldList` entries with standard fields (EventId, EventType, SourceNode, SourceName, Time, ReceiveTime, Message, Severity). `AccessHistoryEventsCapability` is set to `true` when alarm tracking is enabled.
|
|
|
|
### 8. ~~HistoricalDataConfiguration Node~~ — RESOLVED
|
|
|
|
**Spec requirement:** Each historized node SHOULD have a `HistoricalDataConfiguration` child object with properties describing how its history is stored: `Stepped` (interpolation type), `MinTimeInterval`, `MaxTimeInterval`, `ExceptionDeviation`, etc.
|
|
|
|
**Current state:** Implemented. Historized variables receive a `HistoricalDataConfigurationState` child node with `Stepped = false` and `Definition = "Wonderware Historian"`. Recording parameters (intervals, deadbands) are not available from the Galaxy DB, so default values are used.
|
|
|
|
### 9. ~~AggregateConfiguration~~ — RESOLVED (N/A)
|
|
|
|
**Spec requirement:** The `AggregateConfiguration` object (child of `HistoricalDataConfiguration` or `HistoryServerCapabilities`) defines default aggregate behavior: `TreatUncertainAsBad`, `PercentDataBad`, `PercentDataGood`, `UseSlopedExtrapolation`.
|
|
|
|
**Current state:** Not applicable. The server delegates aggregation entirely to the Wonderware Historian's pre-computed summary tables, so these parameters are not actionable. Aggregate discovery is fully supported via the `AggregateFunctions` folder.
|
|
|
|
### 10. ~~ReturnBounds Parameter~~ — RESOLVED
|
|
|
|
**Spec requirement:** When `ReturnBounds=true` in `ReadRawModifiedDetails`, the server should return bounding values at the start and end of the requested time range, even if no raw samples exist at those exact times.
|
|
|
|
**Current state:** Implemented. When `ReturnBounds` is true, `AddBoundingValues` inserts boundary `DataValue` entries at `StartTime` and `EndTime` with `StatusCodes.BadBoundNotFound` if no sample exists at those exact times.
|
|
|
|
### 11. ~~Client-Side: StandardDeviation Aggregate~~ — RESOLVED
|
|
|
|
**Current state:** Implemented. `AggregateType.StandardDeviation` added to enum, `AggregateTypeMapper`, CLI parser (aliases: `stddev`, `stdev`), and UI dropdown. Full end-to-end support from client to server for the `AggregateFunction_StandardDeviationPopulation` aggregate.
|
|
|
|
## Recommended Implementation Order
|
|
|
|
| Priority | Gap | Effort |
|
|
|----------|-----|--------|
|
|
| ~~**High**~~ | ~~HistoryServerCapabilities node~~ | ~~RESOLVED~~ |
|
|
| ~~**High**~~ | ~~Continuation points for history reads~~ | ~~RESOLVED~~ |
|
|
| ~~**Medium**~~ | ~~AggregateFunctions folder~~ | ~~RESOLVED~~ |
|
|
| ~~**Medium**~~ | ~~ReadAtTimeDetails (interpolation)~~ | ~~RESOLVED~~ |
|
|
| ~~**Medium**~~ | ~~Advertise all capabilities as true/false~~ | ~~RESOLVED~~ |
|
|
| ~~**Low**~~ | ~~Return BadHistoryOperationUnsupported for ReadModified~~ | ~~RESOLVED~~ |
|
|
| ~~**Low**~~ | ~~HistoricalDataConfiguration per node~~ | ~~RESOLVED~~ |
|
|
| ~~**Low**~~ | ~~Client StdDev aggregate support~~ | ~~RESOLVED~~ |
|
|
| ~~**Low**~~ | ~~HistoryUpdate (write/delete)~~ | ~~RESOLVED (N/A)~~ |
|
|
| ~~**Low**~~ | ~~Historical event access~~ | ~~RESOLVED~~ |
|
|
| ~~**Low**~~ | ~~AggregateConfiguration~~ | ~~RESOLVED (N/A)~~ |
|
|
| ~~**Low**~~ | ~~ReturnBounds~~ | ~~RESOLVED~~ |
|
|
|
|
## References
|
|
|
|
- [OPC UA Part 11: Historical Access — Concepts](https://reference.opcfoundation.org/Core/Part11/v105/docs/4)
|
|
- [OPC UA Part 11: Historical Access — HistoryServerCapabilitiesType](https://reference.opcfoundation.org/Core/Part11/v104/docs/5.4.2)
|
|
- [OPC UA Part 11: Historical Access — Service Usage](https://reference.opcfoundation.org/Core/Part11/v104/docs/6)
|
|
- [OPC UA Part 11: Historical Access — Data Architecture](https://reference.opcfoundation.org/Core/Part11/v104/docs/4.2)
|
|
- [UA-.NETStandard Historical Access Overview](http://opcfoundation.github.io/UA-.NETStandard/help/historical_access_overview.htm)
|
|
- [OPC Foundation Historical Data Access Wiki](http://wiki.opcfoundation.org/index.php?title=Historical_Data_Access)
|