Skip to main content
Version: 0.1

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 qualityDetail naming 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​

GlobalWhat 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.utilHelpers 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, and system.tag.write fails with system.tag.write: an expression tag can't write tags.
  • system holds only tag.read, date and util. Calling anything that needs the gateway, such as system.alarm.active() or system.tag.history(), fails with this kind of script can't call the gateway; an expression tag only reads tags.
  • console.log is accepted and its output dropped.
  • There is no event and no val.

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, tags and Math globals, 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;