Expression tags
An expression tag holds a value computed from other tags: a sum, a scaled reading, a flag taken from a status word, a text label. This page is for engineers who create or edit one. For the sandbox, limits and the editor's type checking, see Scripting.
How an expression runs
The expression is a JavaScript function body, so it needs return:
return tag('Plant.Tank1.Level') + tag('Plant.Tank2.Level');
The gateway runs it whenever one of its input tags changes, with the inputs' values as they were at that moment. The result becomes the tag's value, in the tag's own type, with:
- the worst quality of its inputs: an expression over one Uncertain and one Good tag is Uncertain, with
qualityDetailnaming the input ('Plant.Tank2.Level' is Uncertain.); - the gateway's time of the recalculation.
An expression has the smallest limits of any script: 50,000 statements, 16 MiB and 250 ms of CPU per run. Reading is all it does, so that is enough.
Reading inputs
| Global | What it gives |
|---|---|
tag(path) | The input's value. undefined when the path is not an input (see below) |
tags[path] | The same values as an object, by path |
system.tag.read(path) | Same as tag(path) |
Math, system.date, system.util | Helpers that run inside the script |
Only literal paths are dependencies. The gateway finds the expression's inputs by looking for tag('...') with a string literal inside, subscribes to those tags, and reruns the expression when one changes. A path built at run time, as in tag('Plant.' + name), is not an input: it reads as undefined, in the gateway and in the editor's preview alike.
A path that starts with . is relative to the tag's parent: ./Level is the sibling Level, ../Other.Level goes one level up. This is how an expression member of a tag type (UDT) reads the members beside it in every instance. A path without a leading . is absolute.
What is not available
An expression recalculates on every input change, so it reads and computes, nothing more:
- There is no
writeTag, andsystem.tag.writefails withsystem.tag.write: an expression tag can't write tags. systemholds onlytag.read,dateandutil. Calling anything that needs the gateway, such assystem.alarm.active()orsystem.tag.history(), fails withthis kind of script can't call the gateway; an expression tag only reads tags.console.logis accepted and its output dropped.- There is no
eventand noval.
Results and errors
The tag goes to Bad quality, keeping its last value, with the reason in qualityDetail, when:
- the script throws or hits a limit (the message is the error text);
- the body returns nothing (
undefined); - the result is a number that is not finite (
NaN,Infinity):The expression's result is not a finite number.; - the expressions form a cycle (A reads B, B reads A).
Booleans and strings are valid results, so an expression can produce a flag or a label as well as a number. A result is converted to the tag's data type like any other tag value.
A script that is killed for exceeding its limit, or that crashes its worker, is refused for 5 seconds, doubling up to 5 minutes, each time it happens again (see Scripting).
Editing and the preview
An expression tag is created and edited in the tag browser's side panel (see Tags). While you type, the editor:
- checks the script against the contract, so a misspelled tag path or an unknown function is underlined;
- evaluates the expression live in the browser, with the same
tag,tagsandMathglobals, subscribed to every literal path it reads, so the result updates as the plant changes.
The preview runs in the browser rather than in the gateway's sandbox: it has no statement or memory limit, and system.* is not available in it, so a script that works in the preview can still fail for exceeding a limit once saved. Save it and check the tag's quality.
Examples
A sum of two levels:
return tag('Plant.Tank1.Level') + tag('Plant.Tank2.Level');
Scaling a raw count of 0–27648 to 0–100 %, rounded to one decimal:
var raw = tag('PLC.AI.Flow_Raw');
return system.util.round(raw * 100 / 27648, 1);
A boolean from a status word: true while bit 3 is set and bit 7 is clear.
var word = tag('Line1.Motor1.Status');
return (word & (1 << 3)) !== 0 && (word & (1 << 7)) === 0;
A text label from a state number and a timestamp in the gateway's time zone:
var state = tag('Line1.State');
var names = { 0: 'Stopped', 1: 'Starting', 2: 'Running', 3: 'Fault' };
var since = system.date.format(tag('Line1.StateChangedAt'), 'HH:mm');
return (names[state] || 'Unknown') + ' since ' + since;
In a tag type, a member that averages two sibling members:
return (tag('./TempA') + tag('./TempB')) / 2;