Screen scripts
A screen event script runs on the gateway when someone uses a component on a screen: a click, a value typed or picked. It can read tags, write tags as that person, call system.*, and tell the screen to show a toast, navigate or open a popup. This page is for engineers who build screens; see Scripting for the sandbox and limits, and The designer for the screen editor.
Where a script is attached
In the designer, select a component and open the Events panel. Each event a component offers (On click, On value change) holds a list of actions; Add action and choose Script (server) to add a script. The other action kinds (write tag, toggle tag, navigate, open popup, open URL, set variable, confirm) run in the browser; only a Script (server) action runs on the gateway.
The script is stored with the screen (the API calls screens views), under the component's events. When the event fires, the client sends only an address over /sync: screen.event {requestId, screenId, componentLocalId, event, actionPath, value, paramValues}. The source always comes from the stored screen, so a client cannot run code it wrote itself. The reply is screen.event {requestId, ok, directives, errors}.
Scripts run under the screen limits: 500,000 statements, 32 MiB, 2 s of CPU. A connection runs at most 8 scripts at once; one more is refused with "This connection is already running too many scripts."
What the script sees
| Global | What it is |
|---|---|
event.name | The event, such as click or valueChange |
event.value | The event's value: what was typed or picked. undefined for a click |
event.params | The screen's parameter values, as strings, by name |
event.screenId | The screen's id |
event.componentLocalId | The component's id within the screen |
val | Same as event.value |
tag(path) | The tag's live value. Literal paths in tag('...') are read before the script starts; other paths are read on demand, at most 256 per script |
tags | The values read before the script started, by path |
writeTag(path, value) | Asks for a write, applied after the script finishes. At most 64 per script |
system.* | All of the system API, answering as the signed-in user |
console.log is accepted but its output is not kept for screen scripts.
{param} in tag paths
A screen's parameters (for example a tag type instance passed to a template) are filled in tag paths only: tag('{Motor}.Speed') and writeTag('{Motor}.Run', true) replace {Motor} with the value the client sent in paramValues. Parameters never reach the source itself, so a parameter cannot change what the script does, only which tags it touches. event.params holds the same values for the script to read.
Who it runs as
The script runs with the identity of the /sync session: the signed-in user, with their role as it is at that moment. In consequence:
tag()reads see that user's user-scoped and the session's session-scoped memory tags (see Tags).- Writes need the operator role. A script that asks for writes as a viewer fails with "Your role doesn't allow writing to tags." and nothing is written.
system.alarm.acknowledge,system.db.queryand the other role-checked calls check that user's role (see each namespace page).
While the socket is in a replay, every screen event is refused with ok: false and the error Replay is on. Return to live to change values.
Writes
Writes wait until the script has finished, then are applied in order, each with a 15 s timeout, stopping at the first failure. A script that throws or hits a limit changes nothing and returns no directives. After a failed write the reply names it: Could not write 'Line1.Setpoint': .... Inside the script, tag(path) after writeTag(path, value) returns the value just written, as it would after a real write.
Directives
The script's return value tells the screen what to do. Return one object or a list of them; each may hold any of four keys. Anything else in the object is dropped, and a return value that is not an object (a number, a string) is ignored.
| Key | Short form | Full form |
|---|---|---|
toast | "Saved" | { message, variant }; variant is default, success, warning or error |
navigate | "Overview" (a screen name) | { name, screenId, params }; params values are strings |
openPopup | "Motor detail" | { name, screenId, params, width, height, modal }; width and height are numbers, modal a boolean |
openUrl | "/reports" | { url, newTab } |
Rules the gateway checks:
- A toast needs a message.
navigateandopenPopupneed a screennameorscreenId.openUrlaccepts onlyhttp:andhttps:URLs, or a relative URL that starts with a single/. Anything else (javascript:,//host) fails the script with "openUrl needs an http, https, or relative URL."
A directive error fails the whole event: its writes have already been applied by then, but no directive is returned.
Examples
Write a setpoint from the value entered and confirm it:
writeTag('{Motor}.Setpoint', Number(val));
return { toast: { message: 'Setpoint ' + val + ' sent', variant: 'success' } };
Start a motor only when no alarm stands on it:
var standing = system.alarm.active({ tagPath: event.params.Motor });
if (standing.length > 0) {
return { toast: { message: standing[0].name + ' is active', variant: 'warning' } };
}
writeTag('{Motor}.Run', true);
return { toast: 'Started' };
Open a popup for the component that was clicked, passing a parameter through:
return { openPopup: { name: 'Motor detail', params: { Motor: event.params.Motor }, width: 720, height: 520 } };
Log who did it in an outside database and navigate:
var who = system.user.current();
system.db.update('INSERT INTO operator_actions (who, action, at) VALUES (?, ?, ?)', [who.name, 'reset', system.date.now()]);
writeTag('Line1.Counter', 0);
return [{ toast: 'Counter reset' }, { navigate: 'Line 1' }];
system.db.update needs the engineer role, so this last script only works for engineers and administrators; for an operator it fails before writing anything.