Tags
A tag is a named value with engineering metadata. This page is the complete property reference.
Tags hold configuration only. The live value lives in the runtime store; history lives in the historian.
General properties
| Property | Type | Description |
|---|---|---|
| Name | string | The leaf name. Combined with its ancestors it forms the tag path. |
| Enabled | bool | A disabled tag is not polled, not evaluated for alarms and not historised. Its configuration is kept. |
| Data type | enum | See data types. |
| Unit | string | Engineering unit — °C, bar, %, RPM. Used as a label by widgets, trends and exports. |
| Description | text | What this value means. Shown in pickers, tooltips and the AI assistant's context. Write it. |
| Writable | bool | Whether the tag accepts writes at all. Off by default. |
| Write level | 0–3 | Required operator write-security level. 0 = any writer, 3 = admin. Meaningful only when writable; enforced server-side. |
| Source | device | internal | expression | Device-backed (has a binding), a QUBIQ-owned memory tag, or computed from other tags. |
| Format string | string | Display format, e.g. #,##0.00. Applied consistently wherever the tag is shown. |
Data types
| Type | Range / notes |
|---|---|
boolean | true / false |
sbyte, byte | 8-bit signed / unsigned |
int16, uint16 | 16-bit |
int32, uint32 | 32-bit |
int64, uint64 | 64-bit |
float | 32-bit IEEE-754 |
double | 64-bit IEEE-754 |
string | Text |
datetime | Timestamp |
json | Structured payload |
array | Array, with dimensions carried from the source |
Match the tag's data type to the source. A mismatch surfaces as a conversion failure with Bad
quality — which is correct behaviour, and much better than a silently truncated number.
Scaling
Scaling converts the device's raw range into engineering units. It is applied after the protocol read and before history, alarms and screens — so everything downstream sees the same engineering value. It is offered for numeric data types only.
| Property | Description |
|---|---|
| Engineering range (Eng Low / Eng High) 1.0.8 | The tag's value range in engineering units, e.g. 0 – 100 °C. Always shown, and optional when scaling is off. |
| Enable Scaling | Turns the raw-to-engineering mapping on. |
| Raw Low / Raw High | The device's range, e.g. 0 – 27648, or 4 – 20 for a current loop. |
| Invert Scaling | Reverses the curve, for sources where raw-high means engineering-low. |
| Clamp Scaled Value | Bound the result to the engineering range. On by default. |
| Test Scaling | Type a raw value and see what it scales to. |
With scaling on, all four range values must be set. Scaling maps the raw range onto the engineering range.
Raw 4 – 20 (mA) → Eng 0 – 100 (°C)
raw 12 mA → 50 °C
One engineering range Changed in 1.0.8
The engineering range belongs to the tag, not to scaling. A tag that is not scaled can still have one, and two things read it: a percent deadband is a percentage of it, and charts use it. Its single home is the Scaling tab. The History tab used to carry a second Low/High pair for the deadband, which could disagree with the first; it now shows what a percent band works out to and an Edit range in Scaling link.
- Turning scaling off keeps the engineering range and clears only the raw range.
- A range without scaling needs both ends, high above low. Clearing an end clears it.
- No range is invented. A tag without one opens blank. A new tag used to start at
0–100, which a save then stored.
Until 1.0.8, creating a tag dropped its scaling ranges and its deadband: the editor sent them and the server did not store them, so a tag created with scaling published raw values until someone edited it. Editing an existing tag also silently discarded a changed deadband. Both are saved now. Check tags created with scaling on an earlier version: if the engineering range is blank, the tag has never been scaled.
Scaling in each widget means the historian stores raw counts, alarms compare against raw counts, and every new screen has to repeat the arithmetic. Do it here.
Quality and freshness
| Property | Where | Description |
|---|---|---|
| Deadband | History tab | Change threshold for what is stored and sent to brokers. See deadband. |
| Stale after (ms) 1.0.8 | Binding tab | Mark the value Uncertain if no reading arrives within this window. See stale after. |
Quality
Quality travels with every value, through bindings, expressions, alarms and charts.
| Quality | Meaning |
|---|---|
Good | The value is trustworthy. |
Uncertain | Readable but suspect — a stale value, a degraded source. |
Bad | Not trustworthy — connection down, read error, conversion failure. |
Unknown | The tag exists but has no value yet — see below. 1.0.7 |
NotFound | Nothing answers to that name: a read named a tag that does not exist, or a binding's address does not exist on the source. |
Widgets show bad quality rather than rendering a stale number as if it were current. Expressions propagate the worst quality of their inputs, so a computed value cannot look better than the data behind it.
A tag with no value yet Changed in 1.0.7
A read of a tag that exists but has never carried a value answers Unknown, with the tag's real
path and a sentence saying why. Until 1.0.7 it answered NotFound — the same word as a typo — and
sent operators hunting for a misspelled path when the tag was sitting right there.
| Tag | What the read says | What to do |
|---|---|---|
| Device, with an enabled binding | The device has not reported a value yet | Wait: it arrives on the next poll or subscription update. If it never does, diagnose the binding. |
| Device, with no enabled binding | No enabled protocol binding for this path | Nothing will ever fill it. Bind it, or change its source to internal. |
| Expression | The expression has not been evaluated yet | A value is coming; if it stays this way, the expression is not being evaluated. |
| Disabled | The tag is disabled | Enable it. Reading a disabled tag does not bring it to life. |
The reason reaches the AI assistant too, so it can tell you what to fix instead of reporting
"no live value". NotFound now means one thing only: nothing by that name exists.
An internal tag never reads Unknown — it always has a value. See
initial value.
Deadband
The single most effective control on history volume and broker traffic. It is set on the tag's History tab, in engineering units or 1.0.8 as a % of range — a percentage of the engineering range, shown worked out under the field (1 % of 0 – 200 bar = 2 bar). A percent band on a tag with no range stores every change.
| Deadband | Effect |
|---|---|
0 | Every change is stored. Correct for discrete states and counters. |
| Small (0.1–0.5% of span) | Removes sensor noise; charts are visually unchanged. |
| Larger (1–2% of span) | Substantial storage saving; adequate for slow analogue values. |
A change smaller than the deadband is not stored and not sent by a broker publisher that publishes on change. It still reaches screens, alarms and expressions, which see every reading. A change of quality is always stored, whatever the deadband.
Stale after New in 1.0.8
Stale after (ms) on the tag's Binding tab is the longest the value may go without a reading.
Past it, the last value is re-published as Uncertain with the reason, and the next reading
restores it. At least 1000 ms; blank is off.
Use it for a source that reports on a cycle. An OPC UA subscription, or an MQTT device that publishes on change, sends nothing while the value holds steady, and would be marked stale while it is perfectly fine. → Stale after
Broker publishing New in 1.0.6
The tag's Publishing tab carries one more setting: How often this tag is sent to an external MQTT broker — follow the broker, only when it changes, or every reading. Cadence is a property of the signal rather than of the broker it goes to: a counter or a batch step has to arrive at its scan rate wherever it is sent, and one tag can be in scope of several publishers at once.
Changing it on an existing tag now saves. 1.0.7 In 1.0.6 only the choice made when the tag was created stuck; a later edit was accepted and then silently discarded, and the tag went on being sent at its old cadence.
It decides how often, never whether. Which tags leave the building is a publisher's fences, and nothing on this tab can widen them. → MQTT publishing
History
The History tab decides what reaches the historian.
| Property | Description |
|---|---|
| Enable History | Record this tag's values. Not available for datetime and array tags. |
| Storage Provider | Read-only: the system historian chosen in Settings → Historian. There is exactly one. |
| Deadband | In units or % of range. See deadband. |
| Min time between samples (ms) 1.0.8 | Readings sooner than this after the last stored one are not stored. A quality change always is. |
| Store interval (ms) | While the tag is Good, store the current value again after this long without a stored value, so a value that holds still keeps a recent sample. |
| Interpolation 1.0.8 | How stored values are joined on trends and in time-weighted statistics: Auto (by data type), Linear or Step. |
Interpolation is on the tag's own History tab 1.0.8, saved with the tag. It used to be reachable only from the tree's Enable History dialog — and that dialog opened on Auto every time, so re-running it to change the deadband silently reset a tag set to Linear or Step. It now opens on the tag's current setting, and for several tags or a folder on Keep current, which changes nobody's choice.
How the rules combine, and what Auto means for integers, is described with the historian. → Storage rules · Interpolation
Alarming
| Property | Description |
|---|---|
| Enable alarming for this tag | On the Alarms tab: the per-tag master switch. Alarm definitions are only evaluated when this is on. |
Individual alarms — mode, setpoints, priority, delays, deadband, latching — are configured per alarm
on the tag. One tag can carry many alarms (Hi, HiHi, Lo, LoLo, Fault).
Write security
Two independent gates protect device writes:
- Writable — off by default. A non-writable tag rejects every write.
- Write level (0–3) — the minimum runtime operator level required. Enforced server-side in the write gate, so a modified client cannot bypass it.
| Level | Typical use |
|---|---|
| 0 | Unrestricted for anyone with write capability — HMI setpoints of no consequence |
| 1 | Routine operator actions |
| 2 | Supervisor actions — recipe changes, mode switches |
| 3 | Administrator only — calibration, safety-adjacent parameters |
Copying a tag keeps its write level. 1.0.7 Before 1.0.7 a
copy was reset to level 0, so a copy of an administrator-only tag could be written by any
operator. A copy also keeps its source, publish mode, storage connection and format string. It does
not carry the tag's alarms, and so it does not carry Alarm enabled either — arming a tag
that has nothing to evaluate would only look configured.
Internal (memory) tags
Set source to internal for a value QUBIQ owns:
- Operator setpoints and targets
- Counters and accumulators
- Computed KPIs written by a script or pipeline
- Mode and state flags
Internal tags have no binding, are persisted without expiry, and are written from scripts
(system.tag.write), pipelines or screens. An initial value can be supplied at creation to seed
them.
Initial value Changed in 1.0.7
When you pick Internal as the source, the Initial Value box is filled in with the data type's default — the value the tag will actually start at. It used to open blank, with the default shown only as a faint hint, so you never saw what the tag was about to hold.
| Data type | Default |
|---|---|
boolean | false |
every integer type, float, double | 0 |
string, datetime | empty |
json | {} |
array | [] |
The value must fit the data type, and the field says so while you type:
- Changing the data type re-derives the value. Type
truefor a boolean, switch toint32, and the box no longer holdstrue. A value that is still valid for the new type is kept. - Integers are range-checked.
300in abytetag is refused, not wrapped round to44— a wrong value that looks like a real one. - JSON must parse, and an
arraytag needs a JSON array.
The server checks the same rules again before the tag is created, so a value that does not fit is
refused with a message naming the field rather than stored. Until 1.0.7 a typo in a number box
silently became 0, and invalid JSON was stored as a plain string in a json tag.
The initial value exists only at creation; it is not a property you can edit later. To change an internal tag's value afterwards, write to it.
Only internal tags take one. A device tag's value comes from an instrument, and a default would be a measurement nothing measured — an operator could not tell a stopped pump from one that never reported. An expression tag's value is computed, and a default would mask an expression that is not evaluating at all.
An internal tag that arrives by bulk import, or by copying, starts at its type's default too. 1.0.7 Before 1.0.7 imported internal tags showed blank until somebody wrote to them, and a copied internal tag came out as a device tag with no binding, which neither read nor accepted a write. The opening value is also historised now when the tag has Enable history on; it used to be dropped, so the series began at the first write, some time after the tag was created, with nothing to explain the gap.
Expression tags
Set source to expression for a value computed from other tags. The expression engine
recalculates it whenever an input changes, and the result behaves like any other tag: it can be
bound to a widget, trended, alarmed and read from a script.
Line1/Filler/Motor1/Power = Speed * Torque * 0.105
Line1/OEE = Availability * Performance * Quality
Line1/Filler/Motor1/Overspeed = Speed > 2800
An expression tag is a read-only projection. It has no protocol binding and rejects external writes — there is nowhere for a written value to go, because the next input change would overwrite it.
Settings
| Setting | Values | Purpose |
|---|---|---|
| Expression | text | The formula. Reads other tags by absolute path. |
| Evaluation mode | on_change (default) · interval | Recompute when an input changes, or on a fixed period. |
| Evaluation interval | ms | Required for interval mode. Also the tick period when the expression reads the clock. |
| Minimum interval | ms, 0 = unlimited | Rate-limits the output. A fast input does not have to mean a fast output. |
| Quality policy | worst_of_inputs (default) · always_good · explicit | How the result's quality is derived. |
Dependencies are worked out for you
You never list the inputs. They are extracted from the expression when you save it, and the engine indexes by them, so a tag recomputes only when something it actually reads changes.
That index answers the other direction too: what breaks if I delete this tag? is a question the system can answer before you delete it, rather than after.
Quality is not an afterthought
The default worst_of_inputs means an expression reading one Bad input produces a Bad result.
That is almost always what you want — a computed KPI built on a dead sensor should not look healthy.
Reach for always_good only when the expression genuinely does not depend on input quality (a
constant, or a pure clock reading), and explicit when the expression decides quality itself.
→ Expression functions for the built-in function reference.
Using the clock
An expression that reads the current time is recognised as such and subscribed to a periodic tick, so it keeps recomputing even when no input tag changes. Set the evaluation interval to control how often — a "minutes since last batch" tag does not need to tick every second.
When not to use one
| Arithmetic, unit conversion, bit unpacking, thresholds | An expression tag |
| Anything with a database round trip, an API call, or a loop | A pipeline |
| A value a human or a screen sets | An internal tag |
An expression that needs a comment to explain it has usually outgrown being an expression.
Reading and writing tags in scripts
result = system.tag.read("Line1/Filler/Motor1/Speed")
if result["quality"] == "Good" and result["value"] > 2800:
system.tag.write("Line1/Filler/Motor1/SpeedSetpoint", 2500)
const r = await system.tag.read("Line1/Filler/Motor1/Speed");
if (r.quality === "Good" && r.value > 2800) {
await system.tag.write("Line1/Filler/Motor1/SpeedSetpoint", 2500);
}
Always check quality before acting on a value. A Bad-quality read still returns a value — the
last known one — and acting on it is how a dead sensor drives a live process.
A tag that exists but has no value yet reads Unknown with no value, and error says why — see
a tag with no value yet. Only a name that matches no tag reads NotFound.
Batch where you can: system.tag.readAll([...]) is one round trip, a loop of read is N.
Permissions
Reading tags and their bindings needs View Namespace. Creating, editing, deleting and bulk-deleting tags all need Manage Namespace; changing a binding needs Bind Protocol.
Checklist for a well-formed tag
- Data type matches the source
- Unit set
- Description written in plain language
- Scaling configured if the device sends raw counts
- Deadband set for analogue values
- Stale-after set for sources that report on a cycle
- History enabled only where someone will look at it
- Writable off unless it needs to be on — and a write level if it is