feat(historian-gateway): native TimeAverage/Total on by default, SendEvents batches, server-side max_events (#694, #746)
#694 item 3 — historiangw#27 is fixed and now live-verified through OtOpcUa: - ServerHistorian:Gateway:EnableNativeTimeWeighted defaults to true (false is the kill switch for a gateway older than 9f711c1). TimeAverage / Total are served and advertised again (12 aggregates). - The gateway sends the interval as max(1, round(ms)) and AVEVA clips its last interval at the request end (live). NativeAggregatePlan therefore sends a multi-interval grid only for whole-ms intervals; a fractional interval is read one interval per request (at most 1,000, else BadTooManyOperations), and every one-interval request goes out rounded UP to whole ms so AVEVA answers one clipped row. Live, a 3.0004 s clipped interval sent at its own width lost its last 0.4 ms (Total 123 vs 123.0172). #746 — HistorianGateway Contracts/Client 1.0.0 surface: - Alarm history: GatewayAlarmHistorianWriter sends SendEvents batches of <=200. A failed per-event result is re-sent alone via SendEvent (classified as before); a refused batch (InvalidArgument etc.) goes event by event so a poison event dead-letters alone; a systemic fault retries the rest without sending more; Unimplemented (older gateway) falls back to SendEvent for good. IHistorianGatewayClient.SendEventsAsync defaults to Unimplemented. - Events reads: a capped page sends cap + 1 as ReadEventsRequest.max_events, so the gateway completes the stream instead of the client cancelling it. An ambiguous capped answer (foreign rows filtered out of the server's cut) or a refused cap is re-read uncapped with the existing EventDrainBudget drain. Tests: GatewayServerCappedEventsTests, GatewayAlarmWriterBatchTests, GatewayNativeWholeMillisecondGridTests (gateway-faithful rounding fake), gate tests flipped to the new default; every new test mutation-checked (each mutation of the production change fails at least one). Live harness AvevaGatewayHistorianFollowupsLiveGateTests (read-only) for #694/#700/#746. Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
This commit is contained in:
@@ -284,13 +284,15 @@ public sealed class GatewayHistorianSettings
|
||||
public int EventDrainBudget { get; init; } = DefaultEventDrainBudget;
|
||||
|
||||
/// <summary>
|
||||
/// Serve HistoryRead-Processed <c>TimeAverage</c> / <c>Total</c> through AVEVA's native retrieval modes. Default
|
||||
/// <c>false</c>: the gateway hands AVEVA the interval in the wrong unit (historiangw#27), so AVEVA answers ONE
|
||||
/// Good-status whole-span value on the earliest interval and nothing for the rest — wrong values a client cannot
|
||||
/// tell from right ones. Off, both are <c>BadAggregateNotSupported</c> and are not advertised (like the other five
|
||||
/// time-weighted aggregates on this backend). Turn it on only once a fixed gateway is live-verified.
|
||||
/// Serve HistoryRead-Processed <c>TimeAverage</c> / <c>Total</c> through AVEVA's native retrieval modes
|
||||
/// (<c>TimeWeightedAverage</c> / <c>Integral</c>). Default <c>true</c> since lmxopcua#694 (2026-11-13): the gateway
|
||||
/// interval fix (historiangw#27, gateway server <c>9f711c1</c> or later) is live-verified — forward and reverse
|
||||
/// per-interval values, all-NULL intervals and sub-millisecond clipped intervals. <c>false</c> is the kill switch:
|
||||
/// both are then <c>BadAggregateNotSupported</c> and are not advertised (like the other five time-weighted aggregates
|
||||
/// on this backend). Set it <c>false</c> when the gateway is older than the #27 fix (HistorianGateway <c>9f711c1</c>):
|
||||
/// such a gateway makes AVEVA answer ONE Good-status whole-span value on the earliest interval.
|
||||
/// </summary>
|
||||
public bool EnableNativeTimeWeighted { get; init; }
|
||||
public bool EnableNativeTimeWeighted { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// How often the read client probes the gateway (<c>ProbeAsync</c>) for the historian health snapshot's
|
||||
|
||||
Reference in New Issue
Block a user