Historical data
Four historized variables, a 1-second recording interval, and HistoryRead support for raw, processed, and at-time queries. The history-test surface.
Path: TestServer / Historical
Four variables with accessLevel = CurrentRead + HistoryRead.
The server records samples in-memory at 1 second intervals,
buffering up to 10 000 samples per variable (rolling).
The variables
| BrowseName | Type | Pattern |
|---|---|---|
HistoricalTemperature |
Double | 25 + 10·sin(t / 30) + (rand·2 - 1) — roughly [14, 36] |
HistoricalPressure |
Double | 1013 + 20·cos(t / 45) + (rand·3 - 1.5) — roughly [992, 1034] |
HistoricalCounter |
UInt32 | Increments by 1 every second; the first historized value is 1 |
HistoricalBoolean |
Boolean | Toggles every ~5 s — deterministic, follows (int)(t / 5) % 2 == 0 |
t is the elapsed seconds component of DateTime.UtcNow.TimeOfDay.
All four are read-only (R + HR). HistoricalCounter initialises
to 0 but is incremented before the first sample is written to
history, so the first historized value is 1, not 0.
Recording
| Setting | Value |
|---|---|
| Sample interval | 1 000 ms |
| Buffer size | 10 000 samples / variable |
| Storage | In-memory |
| Persistence | None — buffer resets on restart |
After ~2.8 hours of uptime (10000 / 3600), the oldest samples
start being overwritten.
HistoryRead operations supported
| Operation | Status in this server |
|---|---|
ReadRawModifiedDetails |
Implemented by TestNodeManager.HistoryReadRawModified. |
ReadProcessedDetails |
Not implemented. Falls through to the UA-.NETStandard base class, which returns Bad_HistoryOperationUnsupported. |
ReadAtTimeDetails |
Not implemented. Same as above — Bad_HistoryOperationUnsupported. |
ReadEventDetails |
Not implemented. No event history is recorded. |
Only the raw read path is exercised end-to-end. If your test matrix expects aggregates or at-time interpolation, the suite is not the right surface for it today.
ReadRawModifiedDetails
Inputs:
| Param | Notes |
|---|---|
StartTime |
Beginning of time range |
EndTime |
End of time range |
NumValuesPerNode |
Cap on returned values; 0 = no cap |
ReturnBounds |
If true, include bounding values just outside range |
IsReadModified |
Not applicable (suite has no modify history) |
Returns: array of DataValue with Value, StatusCode,
SourceTimestamp, ServerTimestamp.
Continuation
If NumValuesPerNode is smaller than the result set, the
response includes a ContinuationPoint. Repeated calls with
the continuation point fetch successive pages.
ReadProcessedDetails
Not implemented in this server. Calling it returns
Bad_HistoryOperationUnsupported. The aggregates listed in
AggregateFunctions (Average, Minimum, Maximum, Count, ...) are
the standard NodeIds advertised by the stack, not invocable
endpoints in this build.
ReadAtTimeDetails
Not implemented. Calling it returns
Bad_HistoryOperationUnsupported. There is no server-side
interpolation between recorded samples.
Test patterns
Basic raw read
- Wait at least 10 s after server start (for history to accumulate).
ReadRawModifiedDetails(HistoricalTemperature, t-10s, t, NumValuesPerNode=100).- Expect ~10 samples, each 1 s apart.
- Each value is
22 + ~8·sin(...) + noise— broadly in the range[10, 35].
Counter monotonicity
HistoricalCounter starts at 0 in the live value but the
first sample written to the history store is 1 (the counter is
incremented before each insert). A 30-second range over a
freshly started server returns [1, 2, …, 30]; over a long-
running server it returns [k, k+1, …, k+29] for some k ≥ 1.
Boolean history
HistoricalBoolean toggles every ~5 s on a deterministic
schedule ((int)(t/5) % 2 == 0). A 30-s read returns ~30
samples grouped in runs of 5 identical values, not a random mix.
Aggregates (not supported)
Calling ReadProcessedDetails returns
Bad_HistoryOperationUnsupported — see the operations table
above. If your client needs average/min/max, compute them from
the raw samples after a ReadRawModifiedDetails call.
Continuation
ReadRawModifiedDetails(HistoricalCounter, ..., NumValuesPerNode = 5)
→ 5 values + a ContinuationPoint
ReadRawModifiedDetails(..., ContinuationPoint=<from above>)
→ next 5 values
Repeat until the continuation point is null.
Time-range filtering
read t to t+10s → 10 values
read t to t+100s → 100 values
Sanity-check that range bounds are honoured exactly.
Bounding values
The current HistoryReadRawModified implementation just filters
the in-memory list by SourceTimestamp between StartTime and
EndTime (inclusive on both sides) and sorts ascending. It does
not specifically inject bounding values when
ReturnBounds=true — the request flag is ignored by the custom
handler. If you need bounding values for plotting, pad the
requested time range by one sample-interval on each side.
Recording behaviour
The recording loop runs server-side, every 1 000 ms, sampling the current value of each historical variable. The buffer is a ring — at sample 10 001, sample 1 is discarded.
The 10 000-sample cap is the maxHistorySize constant inside
HistoricalBuilder.cs. It is not environment-driven —
there is no OPCUA_HISTORY_MAX_SAMPLES variable. Changing the
cap requires editing the source.
Where to read next
- Views — filtered browses.
- Subscription and method tests — recipes covering historical reads.