fix(historian): #693 AtTime type hint — Boolean nodes stepped, integer nodes rounded to their type
Review follow-up: on the Gateway every value is a Double (AVEVA stores a Boolean as a 0/1 Int2), so the raw-evaluated AtTime interpolated a Boolean node to 0.5. - IHistorianDataSource gains a default-interface overload ReadAtTimeAsync(fullReference, timestamps, TypeCode? valueType, ct) that forwards to the existing one (SqlServer/QuestDB/Null/fakes unchanged). - OtOpcUaNodeManager.HistoryReadAtTime passes the node's scalar DataType (AtTimeValueType: built-in type -> TypeCode; arrays/other types => null); Raw and UNS NodeIds alike. - GatewayAtTimeReader converts each raw row to the hinted kind BEFORE the shared AtTimeEvaluator runs (Boolean = value != 0 => stepped; integers => Int64 => interpolated, rounded half away from zero, returned as the node's integer type; floats Double), so the evaluator's own type branch decides. - AtTimeEvaluator boxed every interpolated Int64 as a Double (`cond ? (long)x : v` widens to double); its "keeping its type" test passed because Shouldly ShouldBe compares numbers across types. Fixed (affects SqlServer/QuestDB too) and pinned with ShouldBeOfType.
This commit is contained in:
+12
-4
@@ -793,10 +793,18 @@ data.
|
||||
(#690). Nothing is asked past now + 1 day (the gateway's AVEVA may refuse a far-future end; a time beyond it is stepped from the last
|
||||
sample). Every stream is drained. A strict gateway's ceiling (#691.3) is learned on its first refusal and honoured; a full read is
|
||||
ordinary paging here, so it is not re-probed.
|
||||
- ⚠️ **Values are Double (or String).** The gateway wire carries no data type — every historizable type is stored as an AVEVA analog
|
||||
(a Boolean as a 0/1 `Int2`) — so every numeric tag is **interpolated linearly**: a Boolean or integer node can read a fractional
|
||||
value between two samples. The SQL backends step a Boolean and round an Int64; AVEVA's own interpolation follows the tag's
|
||||
interpolation type (stair-step by default for integer tags). Matching them needs the node's data type plumbed to the data source.
|
||||
- **Values follow the node's type (type hint).** The gateway wire carries no data type — every historizable type is stored as an
|
||||
AVEVA analog (a Boolean as a 0/1 `Int2`) — so the node manager passes each node's scalar DataType with the AtTime call
|
||||
(`IHistorianDataSource.ReadAtTimeAsync(…, TypeCode? valueType, …)`, a default-interface overload: SqlServer / QuestDB / Null
|
||||
ignore it; Raw and UNS NodeIds give the same hint, arrays and other types none). The Gateway converts each raw row to that type
|
||||
**before** the shared evaluator runs, so its own type rules apply exactly as on the SQL backends: a **Boolean** node
|
||||
(value ≠ 0) is **stepped** — never 0.5; an **integer** node is interpolated, rounded half away from zero and returned as the
|
||||
node's own integer type (`Int32` ⇒ `int`); Float / Double interpolate as Double (as the SQL backends return them). With no
|
||||
hint (an unknown node) every numeric tag interpolates as Double.
|
||||
- ⚠️ **Shared-evaluator fix (all backends that use it):** `AtTimeEvaluator` returned every interpolated `Int64` boxed as a
|
||||
**Double** (a C# `cond ? (long)x : double` conditional widens the long); its "keeping the Int64 type" tests passed because
|
||||
Shouldly's `ShouldBe` compares numbers across types. SqlServer / QuestDB interpolated `Int64` AtTime values are now `Int64`,
|
||||
as documented in [AtTime reads](#attime-reads).
|
||||
|
||||
### Bounding values (`ReturnBounds`, #691)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user