Docs/Infohub (Flow)/Tag-Level Calculations
Infohub (Flow)Concept

Tag-Level Calculations

Custom-expression tags in depth — the per-point execution model, windowed filtering, TagProxy in functions, and why null handling decides everything.

Updated 15 Oct 2026Infohub Docs

Whole systems get built on tag-level calculations — calculated measures at the Tag interval, evaluated per raw point rather than per period. The expression reference gives them one section; the ticket record says they deserve their own article, because their execution model differs from interval calcs in exactly the ways that produce quarantined tags and stuck chains.

The execution model, precisely#

An interval calculation runs once per period. A tag calculation runs per row of a transposed point stream: the engine merges every dependency's raw points onto one timeline, and evaluates your expression at each merged timestamp. At each row:

  • Timestamp is the current point's time
  • Each Measure_<id> carries that dependency's value as of this row — .Value, .Quality, .StringValue, .Points, and .Duration (milliseconds until the next row)
  • The output emits only when value or quality changes from the previous emission — the result is a sparse, step-shaped series, by design

Three consequences fall straight out:

  1. Constant results collapse. A difference-calc over a value that changes once daily emits one point per change — sites asking "why one value per change instead of per hour" are describing the design. If you need per-period output, that is an interval measure over the tag calc, not a tag calc setting.
  2. A dependency with no point yet at a row's time is null. Every tag expression must survive nulls at any input — this is the zero guard discipline, per point.
  3. Duration weighting is available and easy to get wrong — .Duration at the final row extends to the window's end; time-weighted logic must expect that edge.

The failure mode: quarantine#

A tag expression that throws (rather than returning null) fails the point, and a point that fails deterministically fails on every retry — the engine quarantines the tag and the log fills with the same error for the same timestamps. The field signature: custom-expression tags faulted/quarantined; backfill and restarts don't clear it; the same hours fail every time.

Deterministic quarantine is an input-handling bug in the expression, not engine state: some real point combination (usually a null, occasionally a divide) makes the expression throw. Restarts cannot fix arithmetic. Find the failing timestamp in the log, evaluate the expression's inputs at that moment, and guard the case. Distinguish this from intermittent processing faults — the deterministic/same-timestamps test is exactly how a null-value quarantine was separated from a suspected race in the field.

The house rules that prevent it:

  • Null-guard every .Value (?? , HasValue) — a tag calc sees nulls interval calcs never do
  • Never throw; return null for can't-compute (custom function rules apply doubly here)
  • Guard divides, always

Windows and ranges on tags#

Tag dependencies take range indexers — Measure_<id>[-7, 0] — giving the points across the window (with the corresponding @-7:0-style range declared on the dependency). Two patterns dominate:

Statistical windows — Measure_x[-7, 0].Values.Average(), StdDev-over-mean, and friends. Remember quality: a window whose leading points predate good data carries degraded quality forward — correct behaviour that reads as a defect on quality-coloured charts.

Condition windows — the ±N-minutes filter. The recurring ask: exclude a temperature reading for five minutes around a flow dropping below threshold. Looking backward ("was the condition met in the last 5 minutes?") is a range read; looking forward ("will it drop within 5 minutes?") means the point cannot be finalised until 5 minutes of future data exists — expect emissions to trail real time by the look-ahead, and expect points near the window's leading edge to revise. If the trailing latency is unacceptable, the backward-only approximation is the honest compromise.

TagProxy in functions#

Passing a whole ranged tag into a UDF — MyFunc(Measure_x[-10,0]) — hands the function the tag-proxy object; the function's parameter must be typed to match what the calculation compiler passes (the documented shape for point access), and a mismatch fails validation, not runtime, with a signature-mismatch flavour of error. Practical guidance: keep function parameters to the simple shapes (double?, arrays from .Values) unless you genuinely need per-point timestamps; extracting .Values at the call site sidesteps the proxy-typing trap entirely: MyFunc(Measure_x[-10,0].Values).

Validation has one environmental failure worth recognising: expression validation compiles to a temp directory, and a locked-down/full temp path fails validation with a file-path error that has nothing to do with your expression. That is a server-environment fix (temp space, permissions for the Config user), not a syntax hunt.

Null-filtering pipelines — the pattern and its cost#

The common architecture: tag calcs that return null for irrelevant data (line stopped, out of spec), then hourly aggregations over the filtered stream. Sound — with two costs to design for:

  • An hour that is all nulls yields no value; everything above must tolerate gaps (the zero-vs-null decision, again)
  • Filtered tag chains are the documented stuck-chain hotspot: many sparse tag calcs feeding aggregations create long dependency fans, and one quarantined tag in the fan stalls the hour. When "the hourlies over filtered tags keep sticking", audit the tag layer for quarantine first (Diagnosing Stuck or Faulted Measures).

When not to use a tag calc#

Per-point evaluation is the expensive tier. If the question is per-period ("hourly average when running"), prefer: connector-side filtering (Filter Tag) where a simple tag condition suffices, or an event scheme where the condition is really production context. Reserve tag calcs for logic that genuinely lives between raw points — unit conversions on the raw stream, cross-tag point math, windowed statistics feeding SPC.

esc
↑↓ navigate · ↵ open · ⌘K anywhere