Overview
A global script is a shared function library, not a scheduled job. Mango by Radix IoT evaluates every global script into the scope of the scripts it runs — meta points, Scripting data sources, the set point and email handlers' scripts, and Excel report scripts — so the functions you define in one become available in all of them, with nothing to import. The one exception is the Script event handler, which runs on a different script service and does not load global scripts. Understanding that one mechanism explains everything else about them, including what you must never put in one.
Warning
Global scripts have no schedule. There is no cron field, no enabled toggle and no permission field on a global script, and nothing runs one on a timer. If you came here to schedule work, see Running something on a schedule below — the scheduled thing is a data source or a handler that calls your function.
Prerequisites
- The SST Global Scripts module
- A superadmin role — the page and its API are admin-only
- Somewhere to call the function from: a meta point, a Scripting data source or an event handler
What a Global Script Actually Is
When one of those scripts is loaded, Mango concatenates the text of all global scripts and evaluates that block into the engine scope. A meta point or Scripting data source loads it once, when it starts; a set point handler, email handler or Excel report script loads it on every run. Three consequences follow, and they are the whole lesson:
- Your functions are already in scope. A meta point can call
celsiusToFahrenheit()as soon as it has loaded the global script that defines it. There is no import, no reference, no "mark as a utility". This is also the only route by which helper functions likeavg()orclamp()become available to a script — and note that Mango ships none of them: theglobalScriptstable is created empty, so if a helper works on your instance it is because someone defined it here - Top-level code runs everywhere the block is loaded. A statement outside a function does not run on a schedule — it runs every time any of those scripts loads the block: each time a meta point or Scripting data source starts, and on every run of a set point, email handler or Excel report script. Across a whole instance, that adds up fast
- A global script that throws takes everything with it. That evaluation happens inside every script's setup, so an exception thrown at the top level of one global script fails every script that loads the block: set point and email handler scripts on their next run, and each meta point and Scripting data source the next time it starts. After a Mango restart, that is all of them at once
Saving is safe in one important respect: a global script that does not compile is rejected at save time, so a typo cannot reach production. Code that compiles and then throws when evaluated is the case to be careful about.
Creating a Global Script
- Go to Data integration > Global scripts
- Click New global script
- Enter a Name
- Enter an XID. Mango's own prefix for these is
GS_, soGS_UNIT_CONVERSIONSfits the convention - Write your functions in the editor
- Click Validate to compile and check it
- Click Save
That is the entire form: Name, XID, the script editor, Save and Delete. Saving or deleting clears Mango's cached copy of the shared block. Set point, email handler and Excel report scripts pick up the change on their next run. A running meta point or Scripting data source keeps the version it loaded when it started, so restart (disable and re-enable) the ones that use the function.
Writing Shared Functions
Define functions and nothing else. This is the shape that works:
// GS_UNIT_CONVERSIONS
function celsiusToFahrenheit(c) {
return (c * 9/5) + 32;
}
function clamp(value, low, high) {
if (value < low) return low;
if (value > high) return high;
return value;
}
Any meta point can then return celsiusToFahrenheit(sensor.value); with no further setup.
Danger
A global script is compiled unwrapped, so a return outside a function is a syntax error and the script will not validate. This is the opposite of a meta point script, which Mango wraps in a function for you and where a top-level return is required. Do not copy a meta point body into a global script.
Running Something on a Schedule
Put the logic in a global script function, then have one of these call it:
| You want | Use | How it is scheduled |
|---|---|---|
| A script on a clock, producing values | Scripting data source | Polling on, then an Update period, or Use cron with a Cron pattern. Use cron fills in * * * * * ?, which is every second, so replace it. With Polling off, the script runs only on its Updates context points. There is also an Execution delay(seconds) |
| A calculation refreshed on an interval | Meta data source point | Timer update event — start of minute, hour, day, week, month, year, or a cron pattern |
| A recurring report | Excel report template | Its own schedule and cron expression |
The scripting data source also has its own Log Level, from None through Trace, which is the easiest way to get output from scheduled work without turning up logging globally.
Calling the Mango API
The global scripts editor has no context points, so a global script cannot reference context variables — your functions receive what the calling script passes them. The utility bindings are available in the calling script's scope, so a function you write can use them:
Finding points
// One known point by XID; returns null if there is no such point
var point = DataPointQuery.byXid("DP_BLDG_A_RTU_01_TEMP");
// Several points by RQL. Tags are queryable as tags.<key>.
// The default limit is 100 – add &limit(-1) for all of them.
var points = DataPointQuery.query(“eq(tags.site,Building-A)&limit(-1)”);
// A query returns a Java List: use size() and get(i), never length or [i]
for (var i = 0; i < points.size(); i++) {
var p = points.get(i);
if (p.runtime !== null) {
LOG.info(p.name + “ = “ + p.runtime.value);
}
}
Note
A point’s runtime is null when the point exists but is not running, so point.runtime.value throws on a disabled point. Checking that the point is not null is not enough — check runtime too, which is also how you detect a stopped point.
Writing a value
var point = DataPointQuery.byXid("DP_BLDG_A_SUMMARY_STATUS");
if (point !== null && point.settable && point.runtime !== null) {
point.runtime.set(1); // or set(value, timestamp[, annotation])
}
The write is set() on the point's runtime, and it throws if the calling script's roles are not allowed to set that point.
Calling an external service
// get(url, headers, parameters) -- all three arguments are required
HttpBuilder.get("https://api.example.com/data", {"Authorization": "Bearer token"}, {})
.resp(function(status, headers, content) {
var data = JSON.parse(content);
LOG.info("Got " + data.length + " records");
return true;
})
.err(function(status, headers, content) {
LOG.error("Bad response: " + status);
})
.excp(function(exception) {
LOG.error("Request failed: " + exception.getMessage());
})
.execute();
The body arrives as the content argument of the resp callback. A status of 200 goes to resp, anything else to err, and a local exception to excp.
The other bindings
- DataSourceQuery — the same
query(rql)shape for data sources - RuntimeManager — refresh, enable and disable points and data sources, and
sleep(). It does not set values and it cannot change a polling interval - PointValueQuery — bulk history across several points, including rollups, delivered to a callback
- JsonEmport — configuration import and export from a script
- DateTimeUtility and UnitConverter — formatting, timezone offsets and unit conversion. Note the binding is
UnitConverter, not the class name you may see elsewhere - LOG —
trace,debug,info,warn,error,fatal
Logging and Debugging
- Validate compiles the script and reports errors. It is the only action on the page — there is no Run button, because there is nothing to run on its own
- Script output does not reach the main Mango log, and it goes wherever the caller's
LOGgoes. A global function has no log of its own. Called from a meta point it writes that point'smetaPoint-N.log, and from a Scripting data source that data source'sscripting-N.log(N is the internal id, not the XID), and only once the caller's Log Level is above None. Called from a set point or email handler's script,LOGoutput is discarded. Searchingma.logfinds nothing either way — which looks exactly like a script that never ran - To test a function end to end, call it from the script that will use it and validate that script, where you have context points and a result
Permissions
A global script has no permissions of its own. Editing one requires superadmin, and when a function runs it runs with the script roles of whatever called it — so the same function can succeed in one meta point and throw a permission error in another. Grant roles on the calling script, not here.
Troubleshooting
| Issue | Cause | Fix |
|---|---|---|
| Nothing happens on a schedule | Global scripts have no schedule | Call the function from a Scripting data source that polls on an update period or a cron pattern, or from a meta point with a timer update event |
| Set point and email handler scripts fail right after a global script edit, and meta points and Scripting data sources after their next start | Top-level code in a global script threw, and that block is evaluated in every script that loads global scripts | Move all executable statements inside functions and keep the top level to declarations only, then restart the meta points and Scripting data sources |
| A meta point or Scripting data source still runs the old version of a changed function | It loaded the global scripts when it started; saving a global script does not reload them in a running script | Disable and re-enable it |
| The script will not validate | A return or other function-only construct at the top level | Wrap it in a function; global scripts are not wrapped for you |
| No log output | Looking in ma.log, or the calling point's Log Level is None | Raise the calling point's Log Level and read its own metaPoint-N.log or scripting-N.log. A set point or email handler script has no log output |
| A loop over query results does nothing | points.length and points[i] on a Java list | Use points.size() and points.get(i) |
| Permission error setting a point | The calling script's roles cannot write it | Add the role to the calling script, and check settable first |
Summary
Treat a global script as a library: define functions, keep the top level free of anything that executes, and call those functions from the place that has the trigger and the context. The scheduling lives on a Scripting data source or a meta point, the permissions live on the caller, the output lands in the caller's log, Validate is how you check your work, and a running meta point or Scripting data source sees a changed function only after a restart. Get the mechanism right and global scripts become the cheapest way to stop duplicating logic across a Mango instance.