The value pipeline
The typed conversion every tag shares, from the raw reading to the engineering value, the two invalid-reading policies, the out-of-range actions, the Boolean and String transforms, and how a write reverses the stages.
Every tag shares one typed conversion pipeline that turns the raw reading into the engineering value. The pipeline lives in the tag itself: the Conversion card on the Tags page and the device grid both show the same stages, and the value that lands in Logic, on dashboards, in the recorder and on the public OPC UA node is the one that leaves the last stage.
Raw first
The pipeline starts from the raw value coerced to the tag's data type, not from the driver's own object: a driver that hands 3.7 to an Int32 tag reads 4 at the top of the card, and every stage continues from that 4. A reading the declared type cannot hold at all (text where a number is declared) is an empty reading, never a stand-in zero. At the far end the engineering value is coerced back to the same type, so a Float tag rounds to what a float can represent.
The stages
For numeric types the pipeline is a chain of stages, each with its own checkbox; a disabled stage passes its input through, and the card shows the running value at the end of every stage so you can follow a live reading through the math:
| Stage | Default | What it does |
|---|---|---|
| Scale | on | scaled = raw × Angular + Linear. Angular defaults to 1, Linear to 0. An Angular of 0 is refused where it is typed ("A scale factor of 0 would zero every reading. Enter the factor to multiply by (1 keeps the value)."), because it would zero every reading and the preview and the persisted pipeline must never disagree. |
| Calibration | off | A second linear pair applied to the scaled value: calibrated = scaled × Angular + Linear. Kept separate from Scale so an as-found/as-left adjustment does not disturb the range math, and gated by its own permission; see Calibration. An Angular of 0 is refused the same way. |
| Invalid reading | always shown | Two policies for readings that carry no usable number (below). Not a checkbox: its "off" is itself one of the answers. |
| Limits | off | Judges the value against Min (default 0) and Max (default 100) and applies the chosen out-of-range action (below). The bounds are normalized, so Min above Max never fails: the lower of the two is the floor. |
| Decimals | on | Rounds to Decimals places, default 2, accepted from 0 to 15. |
Both linear stages carry a Two-point assistant that computes the pair from two reference points; it is described step by step on Calibration.
The device answered without a number
An open thermocouple, a channel with no sensor: the reading arrived, the equipment is talking, but the measurement is NaN. Numeric tags only, and the quality stays good either way:
| Policy | Effect |
|---|---|
| Pass it through (default) | The NaN travels on untouched and skips the Limits stage entirely, whatever action that stage carries: limits judge numbers, and there is none here. Infinity is not NaN and still saturates on the range. |
| Replace with a value | The declared number stands in from this stage on and enters Limits, Decimals and the final coercion like any reading. |
The device did not answer
A timeout, a severed cable, equipment switched off, a driver exception. Every type, though a tag that reads a timestamp, an identifier or a byte block is offered only the first two policies: a declared stand-in is a number, and none of those three can be made from one.
| Policy | Effect |
|---|---|
| Show the error (default) | The tag reads empty and its quality goes bad, so the failure surfaces everywhere at once. |
| Hold the last good value | The last reading that landed stays on display, with no expiry, until the equipment answers again. The timestamps keep pointing at that reading, because that is when the number was measured. Nothing is held before the first reading lands; the tag reads empty then. |
| Replace with a value | The declared number is presented as declared: there is no reading to walk through the stages, so it is not scaled, calibrated or rounded, only coerced to the tag's type. Offered on every type the coercion carries a number into, which is every type but DateTime, Guid and ByteString. |
A stand-in is never silent: the Live value card prints why ("Last good value, held while the device is not answering."), the live dot turns amber, and the embedded OPC UA server publishes the value with a substitute status instead of plain Good. It stays published for as long as the policy calls for it: a device that goes on not answering does not make an external client alternate between the stand-in and a read failure, so what that client reads is what the panel shows. The reading itself takes the point back the moment the device answers, and so does turning the stand-in off. Choosing a stand-in is choosing availability, so alarms and expressions go on judging the point as if it had a value; what that costs is paid back in the open, because the journal still records the failed read and device health still reports the device offline. A stand-in answers "what should this point show", never "is the equipment there". A declared replacement outside the enabled limits is warned about where it is typed: "Outside the limits [0, 100]: the value replacing a failed read."
Out-of-range values
Limits actions for a value outside Min and Max:
| Action | Value becomes | Quality |
|---|---|---|
| Null | A gap (no value). | good |
| Clamp (default) | The nearest bound. | good |
| Error and null | A gap. | bad (limit fault) |
| Error and clamp | The nearest bound. | bad (limit fault) |
| NaN | NaN. | good. Offered only for Float and Double tags, whose type can represent it; it lets a Process channel classify the invalid value. A NaN action left on an integer tag by an agent write or a restored backup degrades to the Null gap. |
A limit fault folds into the tag's effective quality, so the live dot, property expressions and alarms all see the out-of-limits value as bad with no extra wiring, and the Conversion card prints "Value 130 is outside the limits [0, 100]." It is still a process condition rather than a connection problem, and device health does not count the device offline for it.
Boolean, String and other types
Non-numeric types have their own transforms in the same Conversion card:
- Boolean: Invert reports the logical negation of the raw value.
- String: Substring start (0-based) and Length cut a window out of the raw string; a blank length runs to the end, and out-of-range bounds are clamped rather than failing.
- Every other type (DateTime, Guid, ByteString) passes through unchanged and shows no Conversion card.
Showing the error and holding the last good value apply to every type; replacing with a value is offered wherever a declared number can become a reading, so a DateTime, Guid or ByteString tag does not list it. The NaN policy applies to numeric tags only.
Writes reverse the pipeline
The write field on the Live value card takes an engineering value and reverses the enabled, invertible stages in order: clamp to Min and Max when Limits is enabled (a write is always clamped, never dropped, whatever the limit action), de-calibrate ((value − Linear) ÷ Angular), de-scale the same way, then coerce to the tag's type. Rounding is lossy and not reversed. A Boolean input re-applies Invert. A String substring is display-only, so a string write sends the whole value verbatim; DateTime, Guid and ByteString pass through. A value written by an external OPC UA client on the public node is already raw and crosses none of this.