feat(historian): #677 Part 13/SDK reverse anchoring, start-of-data Partial, AggregateConfiguration, IntegralDivisor
- BucketPlan: reverse processed reads anchored at the later time for every aggregate: window (end, start], partial interval earliest, results stamped with the later edge, latest first (SDK CreateSlice/GetTimestamp/backward GetValues). SqlServer adds @bucketAdjustTicks + flips the window predicate; QuestDB binds BucketBaseUnixTicks. IndexOf is the C# statement of the math. - Partial bit on the interval the tag's data starts inside (SDK m_startOfData), every aggregate but Average; per-sample reads fetch the earliest sample. - Node manager: non-default client AggregateConfiguration => BadAggregateConfigurationRejected per node (ServerAggregateConfiguration, pinned to the SDK AggregateManager defaults). - GatewayTagProvisioner sends IntegralDivisor = 1. - SDK parity oracle (Opc.Ua.Server calculators) in unit + both backends' conformance suites, forward and reverse; remaining divergences documented. - docs/Historian.md + CLAUDE.md release note (client-visible).
This commit is contained in:
@@ -5009,6 +5009,8 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
|
||||
{
|
||||
// OPC UA ProcessingInterval is a Duration in milliseconds — convert once per batch.
|
||||
var interval = TimeSpan.FromMilliseconds(details.ProcessingInterval);
|
||||
// #677: a client AggregateConfiguration other than the server defaults is refused per node, never silently ignored.
|
||||
var configurationServable = ServerAggregateConfiguration.IsServable(details.AggregateConfiguration);
|
||||
nodesToProcess = AuthorizeHistoryRead(context, nodesToProcess, results, errors);
|
||||
WithHistoryReadBatchLimiter(nodesToProcess, results, errors, () =>
|
||||
{
|
||||
@@ -5027,6 +5029,12 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!configurationServable)
|
||||
{
|
||||
errors[handle.Index] = StatusCodes.BadAggregateConfigurationRejected;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Processed is SINGLE-SHOT (no continuation point). Unlike Raw, ReadProcessedDetails carries
|
||||
// NO client count cap (NumValuesPerNode) — the bucket count is deterministic (window / interval)
|
||||
// and the single-shot backend returns every bucket in one read, so there is no "full page ⇒
|
||||
|
||||
Reference in New Issue
Block a user